```/*
BOWTIE.I
Detect bowties, boomerangs, and other topological oddities in

\$Id\$
*/
/*    Copyright (c) 1994.  The Regents of the University of California.

func bowtie (rt, zt, ireg)
/* DOCUMENT map= bowtie(rt, zt)
or map= bowtie(rt, zt, ireg)
returns a "bowtie map" for the quadrilateral mesh defined by
RT, ZT, and (optionally) IREG.  If IREG is present, it should be
an integer array of the same dimensions as RT and ZT; its first
row and column are ignored, otherwise each non-zero element of
IREG marks an existing zone in the mesh.  (An IREG with one fewer
row and column than RT and ZT will also be accepted.)  If IREG
is omitted, every zone is presumed to exist.

The returned MAP is a 2-D integer array with one fewer row and
column than RT and ZT.  It's values have the following meanings:

2   marks a convex zone with positive area
1   marks a concave (boomerang) zone with positive area
0   marks a bowtied zone
-1   marks a concave (boomerang) zone with negative area
-2   marks a convex zone with negative area
-9   marks a non-existent zone

Use the nbow function to print the results.

*/
{
dims= dimsof(rt);
d= dimsof(zt);
if (d(1)!=dims(1) || dims(1)!=2 || anyof(dims!=d) ||
dims(2)<2 || dims(3)<2)
error, "rt, zt are not quadrilateral mesh coordinates";

dz1= zt(dif,1:-1);    dr1= rt(dif,1:-1);
dz2= zt(2:0,dif);     dr2= rt(2:0,dif);
dz3= zt(dif,2:0);     dr3= rt(dif,2:0);
dz4= zt(1:-1,dif);    dr4= rt(1:-1,dif);

a= array(0.0, 4, dimsof(dz1));
a(1,,)= dz1*dr4 - dz4*dr1;
a(2,,)= dz1*dr2 - dz2*dr1;
a(3,,)= dz3*dr2 - dz2*dr3;
a(4,,)= dz3*dr4 - dz4*dr3;

map= long(sign(a))(sum,,)/2;

if (is_void(ireg)) return map;

if ((d=dimsof(ireg))(1)==2 && d(2)==dims(2) && d(3)==dims(3)) {
ireg= ireg(2:0, 2:0);
} else if (d(1)!=2 || d(2)!=dims(2)-1 || d(3)!=dims(3)-1) {
error, "ireg not conformable with rt, zt arrays";
}

if (allof(ireg)) return map;

map(where(!ireg))= -9;
return map;
}

local nbow_negative ;
/* DOCUMENT nbow_negative
default value is 0
set to 1 to reverse the sense of positive area for the nbow function
*/
nbow_negative= 0;

func nbow (rt, zt, ireg, all=)
/* DOCUMENT nbow, map
or nbow, file
or nbow, rt, zt
or nbow, rt, zt, ireg
prints information about topological oddities in a mesh.
MAP is a bowtie map as returned by the bowtie function.
FILE is a binary file containing rt, zt, and ireg arrays.
RT, ZT and IREG are 2-D arrays defining a quadrilateral mesh.

The information printed includes the zone index (corner with
the largest indices) of zones which are concave (boomerangs)
or bowtied, and of zones with negative area.  You can set
the global variable nbow_negative to 1 to reverse the default
sense of positive area.  By default, only the first 10 zones
in each category are printed; use the all=1 keyword argument
to print a complete (and maybe very long) list.

*/
{
if (!is_void(zt)) map= bowtie(rt, zt, ireg);
else if (is_stream(rt)) {
f= rt;
vars= get_vars(f);
if ((vars(1) && anyof(*vars(1) == "ireg")) ||
(vars(2) && anyof(*vars(2) == "ireg"))) ireg= f.ireg;
else ireg= [];
map= bowtie(f.rt, f.zt, ireg);
} else {
map= rt;
}

if (nbow_negative) map= -map;

list= where2(abs(map)<=2 & map<0);
_nbow_print, list, "Found %ld zones with negative areas:\n";

list= where2(map==0);
_nbow_print, list, "Found %ld bowtied zones:\n";

list= where2(map==1);
_nbow_print, list, "Found %ld concave (boomerang) zones:\n";

list= where(map==2);
write, format="Found %ld convex, positive area zones\n", numberof(list);
}

func _nbow_print(list, format)
{
n= numberof(list);
if (n) {
list++;
n/= 2;
write, format=format, n;
if (!all && n>10) np= 10;
else np= n;
write, format="     k= %3ld   l= %3ld\n", list(1,1:np), list(2,1:np);
if (np<n)
write, format="     (%ld more, use all=1 keyword to see them)\n", n-np;
}
}
```