%MACROS FOR DIAGRAMS - J. C. Reynolds - December 1987

%This file contains general-purpose macros for drawing diagrams in LATEX,
%followed by additional macros especially for category-theory diagrams.
%A user's manual is given in the file diagmac.doc, and a test program is
%given in the file diagmactest.tex.

%GENERAL-PURPOSE MACROS

%The following control symbols may need to be redefined by the user.
\def\diagramunit{1pt}%Redefine only in main program or at the beginning
%      of \diagram or \ctdiagram.
\def\centerheight{3pt}
\def\edgeheaddisp{4pt}
\def\circleheaddisp{2pt}
\def\diameterlist{1pt,2pt,3pt,4pt,5pt,6pt,7pt,8pt,9pt,10pt,11pt,%
12pt,13pt,14pt,15pt,16pt,20pt,24pt,28pt,32pt,36pt,40pt,}
%Redefine if circle fonts are different.

%The following registers store the representation of diagram and/or
%expression programs:
\newdimen\texpr\newdimen\bexpr\newdimen\lexpr\newdimen\rexpr%current rectangle
\newdimen\xcenter\newdimen\ycenter%center point
\newcount\xslope\newcount\yslope%slope of current edge
\newdimen\xstart\newdimen\ystart%start point of current edge
\newdimen\xend\newdimen\yend%end point of current edge
\newdimen\dcircle%diameter of current circle
\newdimen\xcircle\newdimen\ycircle%center of current circle
\newcount\zzisedge%1 if current edge is defined, 0 otherwise
\newcount\zziscircle%1 if current circle is defined, 0 otherwise
\newbox\zzdiagbox%printable material in state
\newdimen\zztotlwidth\newdimen\zztotrwidth%horizontal extent of box material
\newdimen\zztotheight\newdimen\zztotdepth%vertical extent of box material

%The following registers are assigned globally to communicate information
%across group boundaries:
\newdimen\zzglobaltotlwidth\newdimen\zzglobaltotrwidth
\newdimen\zzglobaltotheight\newdimen\zzglobaltotdepth
\newdimen\zzglobalxcenter\newdimen\zzglobalycenter
\newcount\zzglobalcnA

%The following registers are used locally for various purposes:
\newdimen\zzdmA\newdimen\zzdmB\newdimen\zzdmC\newdimen\zzdmD\newdimen\zzdmE
\newdimen\zzdmF\newdimen\zzdmG\newdimen\zzdmH\newdimen\zzdmI
\newcount\zzcnA\newcount\zzcnB\newcount\zzcnC
\newcount\zzcnD\newcount\zzcnE\newcount\zzcnF
\newcount\zzcnG\newcount\zzcnH\newcount\zzcnI

%Hidden macros and other defined control symbols:
%    generally used macros: \zzsetupbox\zznoshadow\zzissue\zzrecordwidth
%        \zzrecordheight\zzmultdiagramunit\zzmakepicture\zzsqroot\zzdistance
%        \zzreduceterms
%    error-checking macros: \zzisnegside\zzcheckedge\zzcheckslope\zzcheckslopea
%        \zzcheckcircle\zzcheckbool\zzcheckposdimen\zzchecknonnegnum
%    used by \vertex: \zzconsvertexlist\zzconsvertexlista\zzconsvertexlistb
%    used by \rect: \zzprocrect
%    used by \hexagon: \zzprochexagon
%    used by \octagon and \rorect: \zzprococtagon
%    used by \diamond: \zzprocdiamond
%    used by \rorect: \zzprocrorecta\zzsearchdiameterlist\zzsearchdiameterlista
%        \zzsearchdiameterlistb
%    used by \outline: \zzoutlinepoly\zzoutlinepolya\zzoutlinepolyb
%        \zzoutlinepolyc
%    used by \outline with \rorect: \zzoutlinerorect
%    used by \setedge: \zzsearchvertexlist\zzsearchvertexlista
%        \zzsearchvertexlistb
%    used by \shadeedge: \zzcastpoly\zzcastpolya\zzcastpolyb
%        \zzcastpolyc\zzcastpolye\zzcastpolyf\zzcastpolyg
%    used by \drawdashedge, \drawdotedge, and \drawsolidedge: \zzdrawedge
%    used by \drawedgehead: \zzdrawedgeheada
%    used by all abutment macros: \zzslidehoriz\zzslidevert\zzclosestpoly
%        \zzclosestpolya\zzclosestpolyb\zzclosestpolyc\zzclosestpolyd
%    used by edge abutment macros: \zzabut
%    used by circle abutment macros: \zzabutcircle\zzabutcirclea\zzrotate
%    used by \shadeedge and all abutment macros: \zzcastpolyd
%    used by \drawcircle: \zzdrawcirclea
%    multiply defined control symbols: \zzvertexlist\zzshadow\zzglobalshadow
%        \zztesta\zztestb\zzvertexitem\zzprocpoly\zzprocrorect\zznext
%        \zzstartshadow\zzendshadow\zzlocalshadow

%\diagram creates a box, initializes \zztotlwidth, \zztotrwidth, and
%\zzvertexlist, sets \zzisedge to 0, executes #1, which must be a diagram
%program, and then issues the resulting box, surrounded by kerns so that
%there are no horizontal overhangs.

\def\diagram#1{{\setbox\zzdiagbox=\hbox{$\mathsurround=0pt
\zztotlwidth=0pt\zztotrwidth=0pt\def\zzvertexlist{\end}\zzisedge=0\relax
#1\relax\kern\zztotrwidth\global\zzglobaltotlwidth=\zztotlwidth $}
\kern-\zzglobaltotlwidth\box\zzdiagbox}}

%\zzsetupbox sets \zzdiagbox to the expression #1 modified by the program #2.
%It also sets \zzglobalxcenter and \zzglobalycenter to the coordinates of
%the center relative to the reference point, \zzglobaltotlwidth to the
%negative of the left overhang width of the expression, and \zzglobalshadow
%to the shadow established by the program, relative to the reference point.
%Before executing #2, it initializes \texpr, \bexpr, \lexpr, \rexpr,
%\xcenter, and \ycenter appropriately, and sets \zziscircle to 0.

\def\zzsetupbox#1#2{\setbox\zzdiagbox=\hbox{$\mathsurround=0pt
\setbox\zzdiagbox=\hbox{$\mathsurround=0pt{{#1}}$}
\texpr=\ht\zzdiagbox\bexpr=-\dp\zzdiagbox\rexpr=\wd\zzdiagbox\lexpr=0pt
\xcenter=\rexpr\divide\xcenter by 2\ycenter=\centerheight
\zztotlwidth=0pt\zztotrwidth=\rexpr\def\zzshadow{\zznoshadow0pt,0pt:;}
\zziscircle=0
\box\zzdiagbox\kern-\rexpr
#2\relax
\global\zzglobalxcenter=\xcenter\global\zzglobalycenter=\ycenter
\global\zzglobaltotlwidth=\zztotlwidth\kern\zztotrwidth
\global\let\zzglobalshadow=\zzshadow
$}}

%\zznoshadow is a dummy shadowing routine that gives an error when executed.

\def\zznoshadow#1,#2:;{\errmessage{ATTEMPT TO OUTLINE OR ABUT AN EXPRESSION
WITH NO SHADOW}}

%\leftghost (\rightghost) sets \xcenter to \lexpr plus (\rexpr minus)
%half of the width of its argument.

\def\leftghost#1{\setbox\zzdiagbox=\hbox{$\mathsurround=0pt{{#1}}$}
\xcenter=\wd\zzdiagbox\divide\xcenter by 2\advance\xcenter by \lexpr}

\def\rightghost#1{\setbox\zzdiagbox=\hbox{$\mathsurround=0pt{{#1}}$}
\xcenter=\wd\zzdiagbox\divide\xcenter by -2\advance\xcenter by \rexpr}

%\zzissue should be executed after \zzsetupbox.  It issues the contents of
%\zzdiagbox with its center placed at \zzdmA, \zzdmB (which are modified),
%and adjusts \zztotlwidth and \zztotrwidth appropriately.

\def\zzissue{\advance\zzdmA by -\zzglobalxcenter
\advance\zzdmB by -\zzglobalycenter
\zzdmC=\zzdmA\advance\zzdmC by \wd\zzdiagbox
\zzdmD=\zzdmA\advance\zzdmD by \zzglobaltotlwidth
\zzrecordwidth\zzdmD\zzdmC
\kern\zzdmA\raise\zzdmB\box\zzdiagbox\kern-\zzdmC}

%\zzrecordwidth adjusts \zztotlwidth to be the minimum of its previous value
%and #1, and adjusts \zztotrwidth to be the maximum of its previous value
%and #2.

\def\zzrecordwidth#1#2{\relax\ifdim#1<\zztotlwidth\relax\zztotlwidth=#1\fi
\ifdim\zztotrwidth<#2\relax\zztotrwidth=#2\fi}

%\zzrecordheight adjusts \zztotheight to be the maximum of its previous value
%and #1, and adjusts \zztotdepth to be the minimum of its previous value
%and #2.

\def\zzrecordheight#1#2{\relax\ifdim\zztotheight<#1\relax\zztotheight=#1\fi
\ifdim#2<\zztotdepth\relax\zztotdepth=#2\fi}

%\placed executes \zzsetupbox{#3}{#4} and issues the contents of the resulting
%\zzdiagbox with its center placed at #1, #2 (which must be dimensions).
%\place is similar except that #1, #2 must be integer multiples of
%\diagramunit.

\def\placed#1#2#3#4{\zzsetupbox{#3}{#4}\zzdmA=#1\zzdmB=#2\zzissue}

\def\place#1,#2:#3#4{\zzsetupbox{#3}{#4}\zzmultdiagramunit\zzdmA{#1}
\zzmultdiagramunit\zzdmB{#2}\zzissue}

%\zzmultdiagramunit sets #1 to #2 times \diagramunit.

\def\zzmultdiagramunit#1#2{#1=\diagramunit\multiply#1 by #2\relax}

%\vertex#1,#2:#3#4 executes \zzsetupbox{#3}{#4}, issues the contents of the
%resulting \zzdiagbox with its center placed at #1, #2 times \diagramunit,
%adjusts \zztotlwidth and \zztotrwidth appropriately, and adds \zzglobalshadow
%to the beginning of \zzvertexlist (unless \zzglobalshadow is a call of
%\zznoshadow) after readjusting the shadow to be relative
%to the coordinates of the enclosing box.

\def\vertex#1,#2:#3#4{\place{#1},{#2}:{#3}{#4}\zzcnA=#1\zzcnB=#2\relax
\expandafter\zzconsvertexlist\zzglobalshadow}

\def\zzconsvertexlist#1#2,#3:#4;{\def\zztesta{#1}\def\zztestb{\zznoshadow}
\ifx\zztesta\zztestb\else
\advance\zzdmA by #2\advance\zzdmB by #3\relax
\edef\zzvertexitem{\the\zzcnA,\the\zzcnB:\noexpand #1\the\zzdmA,\the\zzdmB:#4;}
\expandafter\zzconsvertexlista\zzvertexlist\fi}

\def\zzconsvertexlista{\expandafter\zzconsvertexlistb\zzvertexitem}

\def\zzconsvertexlistb#1\end{\def\zzvertexlist{#1\end}}

%\rect, \hexagon, \octagon, and \diamond (and, roughly speaking, \rorect)
%are polygon descriptors.  A polygon descriptor defines \zzshadow to have
% the form \somecontrolsymbol #1,#2:#3; such that executing
%\zzshadow causes a call \zzprocpoly{#1}{#2}{<edgelist>}, where <edgelist>
%depends only upon the parameter #3.  Here #1, #2 are the
%coordinates of a vertex of a convex polygon, and <edgelist> is a list of
%triples describing the edges of the polygon in clockwise order.  If an
%edge is x = xs.t + x0, y = ys.t + y0 for 0 < t < tend (with the start at
%t = 0 and the end at t = tend when the edge is traversed in clockwise
%order) the the trip describing the edge is {xs}{ys}{tend}, where xs and
%ys are numbers (the brackets may be omitted for single-digit numbers)
%and tend is a dimension. xs and ys must have a least common divisor of one.

\def\rect{\zzdmC=\rexpr\advance\zzdmC by -\lexpr
\zzdmD=\texpr\advance\zzdmD by -\bexpr
\zzisnegside{\zzdmC}{RECT}\zzisnegside{\zzdmD}{RECT}
\edef\zzshadow{\noexpand\zzprocrect
\the\lexpr,\the\texpr:\the\zzdmC,\the\zzdmD;}}

\def\zzprocrect#1,#2:#3,#4;{\zzprocpoly
{#1}{#2}{10{#3}0{-1}{#4}{-1}0{#3}01{#4}}}

\def\zzisnegside#1#2{\relax\ifdim#1<0pt\errmessage
{#2 WITH NEGATIVE SIDE}\fi}

\def\hexagon{\zzdmC=\rexpr\advance\zzdmC by -\lexpr
\zzdmD=\texpr\advance\zzdmD by -\bexpr\divide\zzdmD by 4
\zzisnegside{\zzdmC}{HEXAGON}\zzisnegside{\zzdmD}{HEXAGON}
\edef\zzshadow{\noexpand\zzprochexagon
\the\lexpr,\the\texpr:\the\zzdmC,\the\zzdmD;}}

\def\zzprochexagon#1,#2:#3,#4;{\zzprocpoly{#1}{#2}
{10{#3}1{-2}{#4}{-1}{-2}{#4}{-1}0{#3}{-1}2{#4}12{#4}}}

\def\octagon#1{\zzdmC=#1\zzdmD=\zzdmC\multiply\zzdmD by -2\zzdmE=\zzdmD
\advance\zzdmD by \rexpr\advance\zzdmD by -\lexpr
\advance\zzdmE by \texpr\advance\zzdmE by -\bexpr
\zzdmF=\lexpr\advance\zzdmF by \zzdmC
\zzisnegside{\zzdmC}{OCTAGON}\zzisnegside{\zzdmD}{OCTAGON}
\zzisnegside{\zzdmE}{OCTAGON}
\edef\zzshadow{\noexpand\zzprococtagon
\the\zzdmF,\the\texpr:\the\zzdmC,\the\zzdmD,\the\zzdmE;}}

\def\zzprococtagon#1,#2:#3,#4,#5;{\zzprocpoly{#1}{#2}
{10{#4}1{-1}{#3}0{-1}{#5}{-1}{-1}{#3}{-1}0{#4}{-1}1{#3}01{#5}11{#3}}}

\def\diamond{\zzdmC=\texpr\advance\zzdmC by -\bexpr
\advance\zzdmC by \rexpr\advance\zzdmC by -\lexpr\divide\zzdmC by 2
\zzdmD=\lexpr\advance\zzdmD by \rexpr\divide\zzdmD by 2
\zzdmE=\texpr\advance\zzdmE by \bexpr\divide\zzdmE by 2\advance\zzdmE by \zzdmC
\zzisnegside{\zzdmC}{DIAMOND}
\edef\zzshadow{\noexpand\zzprocdiamond
\the\zzdmD,\the\zzdmE:\the\zzdmC;}}

\def\zzprocdiamond#1,#2:#3;{\zzprocpoly{#1}{#2}
{1{-1}{#3}{-1}{-1}{#3}{-1}1{#3}11{#3}}}

\def\rorect#1#2#3{\zzcheckposdimen{#1}{FIRST}{RORECT}
\zzcheckbool{#2}{SECOND}{RORECT}\zzcheckbool{#3}{THIRD}{RORECT}
\zzdmD=\rexpr\advance\zzdmD by -\lexpr\zzdmE=\texpr\advance\zzdmE by -\bexpr
\zzdmC=#1\relax
\ifnum#2=1\relax\ifdim\zzdmD>\zzdmC\relax\zzdmC=\zzdmD\fi\fi
\ifnum#3=1\relax\ifdim\zzdmE>\zzdmC\relax\zzdmC=\zzdmE\fi\fi
\expandafter\zzsearchdiameterlist\diameterlist\end\zzdmC=\zzdmF\relax
\ifdim\zzdmC>\zzdmD\relax\zzdmD=\zzdmC\fi
\ifdim\zzdmC>\zzdmE\relax\zzdmE=\zzdmC\fi
\zzdmF=\lexpr\advance\zzdmF by \rexpr\divide\zzdmF by 2
\zzdmG=\bexpr\advance\zzdmG by \texpr\divide\zzdmG by 2
\edef\zzshadow{\noexpand\zzprocrorect
\the\zzdmF,\the\zzdmG:\the\zzdmC,\the\zzdmD,\the\zzdmE;}}

\def\zzsearchdiameterlist#1{\def\zztesta{#1}\def\zztestb{\end}
\ifx\zztesta\zztestb\let\zznext=\zzsearchdiameterlista
\else\let\zznext=\zzsearchdiameterlistb\fi\zznext #1}

\def\zzsearchdiameterlista#1\end{}

\def\zzsearchdiameterlistb#1,{\zzdmF=#1\relax
\ifdim\zzdmF<\zzdmC\relax\let\zznext=\zzsearchdiameterlist
\else\let\zznext=\zzsearchdiameterlista\fi\zznext}

%\outline uses \zzoutlinepoly and \zzoutlinerorect to issue an outline of
%the shadow.

\def\outline{\def\zzprocpoly{\zzoutlinepoly}
\def\zzprocrorect{\zzoutlinerorect}\zzshadow}

%\zzmakepicture encapsulates all usage of the LATEX picture facility.

\def\zzmakepicture#1{{\setlength{\unitlength}{1sp}\begin{picture}(0,0)
#1\relax
\global\zzglobaltotlwidth=\zztotlwidth\global\zzglobaltotrwidth=\zztotrwidth
\global\zzglobaltotheight=\zztotheight\global\zzglobaltotdepth=\zztotdepth
\end{picture}\vrule height\zzglobaltotheight depth-\zzglobaltotdepth width0pt}
\zztotlwidth=\zzglobaltotlwidth\zztotrwidth=\zzglobaltotrwidth}

\def\zzoutlinepoly#1#2#3{\zzmakepicture{\zzdmA=#1\zzdmB=#2\relax
\zztotheight=\zzdmB\zztotdepth=\zzdmB\zzoutlinepolya #3\end}}

\def\zzoutlinepolya#1{\def\zztesta{#1}\def\zztestb{\end}
\ifx\zztesta\zztestb\let\zznext=\zzoutlinepolyb
\else\let\zznext=\zzoutlinepolyc\fi\zznext {#1}}

\def\zzoutlinepolyb#1{}

\def\zzoutlinepolyc#1#2#3{\zzrecordwidth\zzdmA\zzdmA\zzrecordheight\zzdmB\zzdmB
\zzdmE=#3\multiply\zzdmE by #1\zzdmF=#3\multiply\zzdmF by #2\relax
\zzcnA=\zzdmA\zzcnB=\zzdmB\relax
\ifnum #1=0\relax\zzcnC=\zzdmF\else\zzcnC=\zzdmE\fi
\ifnum\zzcnC<0\relax\zzcnC=-\zzcnC\fi
\put(\zzcnA,\zzcnB){\line(#1,#2){\zzcnC}}
\advance\zzdmA by \zzdmE\advance\zzdmB by \zzdmF
\zzoutlinepolya}

\def\zzoutlinerorect#1,#2:#3,#4,#5;{\zzmakepicture{
\zzdmA=#1\zzdmB=#2\zzdmC=#3\zzdmD=#4\zzdmE=#5
\zzcnA=\zzdmA\zzcnB=\zzdmB\zzcnC=\zzdmC\zzcnD=\zzdmD\zzcnE=\zzdmE
\ifnum\zzcnD=\zzcnC\relax\put(\zzcnA,\zzcnB){\oval(\zzcnD,\zzcnE)}
\else\ifnum\zzcnE=\zzcnC\relax\put(\zzcnA,\zzcnB){\oval(\zzcnD,\zzcnE)}
\else\advance\zzcnE by -\zzcnC
\zzcnF=\zzcnE\divide\zzcnF by 2\advance\zzcnF by \zzcnB
\put(\zzcnA,\zzcnF){\oval(\zzcnD,\zzcnC)[t]}
\advance\zzcnF by -\zzcnE
\put(\zzcnA,\zzcnF){\oval(\zzcnD,\zzcnC)[b]}
\zzcnC=\zzcnD\divide\zzcnC by 2\advance\zzcnA by \zzcnC
\put(\zzcnA,\zzcnF){\line(0,1){\zzcnE}}
\advance\zzcnA by -\zzcnD
\put(\zzcnA,\zzcnF){\line(0,1){\zzcnE}}\fi\fi
\zztotheight=\zzdmE\divide\zztotheight by 2\advance\zztotheight by \zzdmB
\zztotdepth=\zztotheight\advance\zztotdepth by -\zzdmE
\zzdmC=\zzdmD\divide\zzdmC by -2\advance\zzdmC by \zzdmA
\advance\zzdmD by \zzdmC\zzrecordwidth\zzdmC\zzdmD}}

%\border, \borderto, and \symmetrize adjust the current rectangle.

\def\border#1#2{\advance\texpr by #2\advance\bexpr by -#2
\advance\lexpr by -#1\advance\rexpr by #1}

\def\borderto#1#2{\zzdmA=\rexpr\advance\zzdmA by -\lexpr\relax
\ifdim#1>\zzdmA\relax\advance\zzdmA by -#1\divide\zzdmA by 2
\advance\rexpr by -\zzdmA\advance\lexpr by \zzdmA\fi
\zzdmA=\texpr\advance\zzdmA by -\bexpr\relax
\ifdim#2>\zzdmA\relax\advance\zzdmA by -#2\divide\zzdmA by 2
\advance\texpr by -\zzdmA\advance\bexpr by \zzdmA\fi}

\def\symmetrize{\zzdmA=\texpr\advance\zzdmA by -\ycenter
\zzdmB=\ycenter\advance\zzdmB by -\bexpr\relax
\ifdim\zzdmA<\zzdmB\relax\zzdmA=\zzdmB\fi
\texpr=\ycenter\advance\texpr by \zzdmA
\bexpr=\ycenter\advance\bexpr by -\zzdmA}

%\setedge#1,#2,#3,#4: accepts four numbers (giving dimensions as multiples of
%\diagramunit).  It sets \xstart, \ystart, \xend, \yend to #1, #2, #3,
%#4, each multiplied by \diagramunit, and \xslope, \yslope to numbers
% giving the slope of the line from \xstart, \ystart to \xend, \yend,
%reduced to have a least common divisor.  It uses \zzsearchvertexlist to set
%\zzstartshadow (\zzendshadow) to the \zzshadow stored on \zzvertexlist with
%coordinates #1, #2 (#3, #4).  It sets \zzisedge to 1.

\def\setedge#1,#2,#3,#4:{\zzcnA=#1\zzcnB=#2
\zzmultdiagramunit\xstart\zzcnA\zzmultdiagramunit\ystart\zzcnB
\expandafter\zzsearchvertexlist\zzvertexlist
\let\zzstartshadow=\zzshadow
\zzcnA=#3\zzcnB=#4
\zzmultdiagramunit\xend\zzcnA\zzmultdiagramunit\yend\zzcnB
\expandafter\zzsearchvertexlist\zzvertexlist
\let\zzendshadow=\zzshadow
\xslope=#3\advance\xslope by -#1\relax
\yslope=#4\advance\yslope by -#2\relax
\zzreduceterms\xslope\yslope{START AND END OF EDGE ARE BOTH THE SAME}
\xslope=\zzcnC\yslope=\zzcnD\zzisedge=1}

%\zzreduceterms sets \zzcnC and \zzcnD to the results of dividing the numbers
% #1 and #2 by their greatest common divisor.  The error message #3 is given
%if #1 and #2 are both zero.

\def\zzreduceterms#1#2#3{{
\ifnum#1<0\relax\zzcnA=-#1\else\zzcnA=#1\fi
\ifnum#2<0\relax\zzcnB=-#2\else\zzcnB=#2\fi
\loop\ifnum\zzcnB>0\relax
\zzcnC=\zzcnA\divide\zzcnC by \zzcnB\multiply\zzcnC by -\zzcnB
\advance\zzcnC by \zzcnA\zzcnA=\zzcnB\zzcnB=\zzcnC
\repeat\relax
\ifnum\zzcnA=0\errmessage{#3}\fi
\global\zzglobalcnA=\zzcnA}
\zzcnC=#1\divide\zzcnC by \zzglobalcnA\zzcnD=#2\divide\zzcnD by \zzglobalcnA}

%\zzsearchvertexlist\zzvertexlist searches \zzvertexlist for an entry of the
%form #1,#2:#3; for which #1 = \zzcnA and #2 = \zzcnB.  If such an entry is
%found, \zzshadow is defined to be #3;.  Otherwise, \zzshadow is defined to be
%\zzdmE=\zzdmA\zzdmF=\zzdmB.

\def\zzsearchvertexlist#1{\def\zztesta{#1}\def\zztestb{\end}
\ifx\zztesta\zztestb\def\zzshadow{\zzdmE=\zzdmA\zzdmF=\zzdmB}
\let\zznext=\zzsearchvertexlista
\else\let\zznext=\zzsearchvertexlistb\fi\zznext #1}

\def\zzsearchvertexlista#1\end{}

\def\zzsearchvertexlistb#1,#2:#3;{\relax
\ifnum\zzcnA=#1\relax\ifnum\zzcnB=#2\relax
\def\zzshadow{#3;}\let\zznext=\zzsearchvertexlista
\else\let\zznext=\zzsearchvertexlist\fi
\else\let\zznext=\zzsearchvertexlist\fi\zznext}

%\shadeedge shades the start and end of the current edge, changing \xstart,
%\ystart, \xend, \yend.

\def\shadeedge{\zzcheckedge{SHADE}
\def\zzprocpoly{\zzcastpoly}\def\zzprocrorect{\zzprocrorecta}
\zzdmA=\xstart\zzdmB=\ystart\zzcnA=\xslope\zzcnB=\yslope
\zzstartshadow\xstart=\zzdmE\ystart=\zzdmF
\zzdmA=\xend\zzdmB=\yend\zzcnA=-\xslope\zzcnB=-\yslope
\zzendshadow\xend=\zzdmE\yend=\zzdmF}

\def\zzcheckedge#1{\relax\ifnum\zzisedge=0\relax\errmessage
{ATTEMPT TO #1 NONEXISTENT EDGE}\fi}

%\zzprocrorecta is used as the definition of \zzprocrorect within \shadeedge
%and \zzabut.  It causes a rounded rectangle to be treated as the
%circumscribed octagon for purposes of shadowing or abutment.

\def\zzprocrorecta#1,#2:#3,#4,#5;{\zzdmC=#3
\multiply\zzdmC by 53\divide\zzdmC by 181
\zzdmD=\zzdmC\multiply\zzdmD by -2\zzdmE=\zzdmD
\advance\zzdmD by #4\advance\zzdmE by #5
\zzdmF=#4\divide\zzdmF by -2\advance\zzdmF by #1\advance\zzdmF by \zzdmC
\zzdmG=#5\divide\zzdmG by 2\advance\zzdmG by #2
\edef\zzlocalshadow{\noexpand\zzprococtagon
\the\zzdmF,\the\zzdmG:\the\zzdmC,\the\zzdmD,\the\zzdmE;}
\zzlocalshadow}

%\zzcastpoly computes the outgoing intersection of a directed line,
%x = xs.t+x0, y = ys.t+y0 with a convex polygon.  It is called
%by defining \zzprocpoly to be \zzcastpoly, setting \zzdmA, \zzdmB, \zzcnA,
%\zzcnB to x0, y0, xs, ys, and executing \zzshadow, which must have been
%defined by a polygon descriptor.  The result is left in \zzdmE, \zzdmF.
%If the directed line does not intersect the polygon, the result is the
%point on the line that is closest to the polygon.

\def\zzcastpoly#1#2#3{\zzdmC=#1\zzdmD=#2\zzcnC=0
\zzcastpolya #3\end}

\def\zzcastpolya#1{\def\zztesta{#1}\def\zztestb{\end}
\ifx\zztesta\zztestb\let\zznext=\zzcastpolyg
\else\let\zznext=\zzcastpolyc\fi\zznext {#1}}

\def\zzcastpolyb#1\end{\zzcnC=\zzcnA\multiply\zzcnC by \zzcnA
\zzcnD=\zzcnB\multiply\zzcnD by \zzcnB\advance\zzcnC by \zzcnD
\zzdmE=\zzdmC\advance\zzdmE by -\zzdmA\multiply\zzdmE by \zzcnA
\zzdmF=\zzdmD\advance\zzdmF by -\zzdmB\multiply\zzdmF by \zzcnB
\advance\zzdmE by \zzdmF\divide\zzdmE by \zzcnC\zzdmF=\zzdmE
\multiply\zzdmE by \zzcnA\advance\zzdmE by \zzdmA
\multiply\zzdmF by \zzcnB\advance\zzdmF by \zzdmB}


\def\zzcastpolyc#1#2#3{\zzcnD=\zzcnB\multiply\zzcnD by #1\relax
\zzcnE=\zzcnA\multiply\zzcnE by #2\relax\advance\zzcnD by -\zzcnE\relax
\ifnum\zzcnD>0\relax
\ifnum\zzcnC=2\zzcastpolyd{#1}{#2}{#3}\let\zznext=\zzcastpolya\else
\zzdmE=\zzdmA\advance\zzdmE by -\zzdmC\multiply\zzdmE by \zzcnB
\zzdmF=\zzdmB\advance\zzdmF by -\zzdmD\multiply\zzdmF by \zzcnA
\advance\zzdmE by -\zzdmF\divide\zzdmE by \zzcnD\relax
\ifdim\zzdmE>#3\relax\zzcnC=3\zzcastpolyd{#1}{#2}{#3}\let\zznext=\zzcastpolya
\else\ifnum\zzcnC=3\zzcastpolyf{#1}{#2}\let\zznext=\zzcastpolye\else
\ifdim\zzdmE<0pt\relax
\ifnum\zzcnC=1\let\zznext=\zzcastpolyb\else
\zzcnC=2\zzcastpolyd{#1}{#2}{#3}\let\zznext=\zzcastpolya\fi
\else\zzcastpolyf{#1}{#2}\let\zznext=\zzcastpolye\fi\fi\fi\fi
\else\ifnum\zzcnC=3\let\zznext=\zzcastpolyb
\else\zzcnC=1\zzcastpolyd{#1}{#2}{#3}\let\zznext=\zzcastpolya\fi\fi
\zznext}

\def\zzcastpolyd#1#2#3{
\zzdmE=#3\multiply\zzdmE by #1\advance\zzdmC by \zzdmE
\zzdmE=#3\multiply\zzdmE by #2\advance\zzdmD by \zzdmE}

\def\zzcastpolye#1\end{}

\def\zzcastpolyf#1#2{\zzdmF=\zzdmE
\multiply\zzdmE by #1\relax\advance\zzdmE by \zzdmC
\multiply\zzdmF by #2\relax\advance\zzdmF by \zzdmD}

\def\zzcastpolyg#1{\zzcastpolyb\end}

%\shiftedge changes \xstart, \ystart, \xend, \yend so as to displace
%the edge from \xstart, \ystart to \xend, \yend by a vector of length
%#1 (a dimension) that is rotated 90 degrees counterclockwise from the edge.

\def\shiftedge#1{\zzcheckedge{SHIFT}
\zzdistance\xslope\yslope
\zzdmA=#1\multiply\zzdmA by 100\divide\zzdmA by \zzglobalcnA\zzdmB=\zzdmA
\multiply\zzdmA by -\yslope\multiply\zzdmB by \xslope
\advance\xstart by \zzdmA \advance\xend by \zzdmA
\advance\ystart by \zzdmB \advance\yend by \zzdmB}

%\zzsqroot#1 accepts an integer and sets \zzglobalcnA to the integer part
%of its square root.  It works for numbers up to at least 1,000,000,000.

\def\zzsqroot#1{{\zzcnA=#1
%x is \zzcnA, y is \zzcnB, n is \zzcnC, z is \zzcnD
\zzcnC=0\zzcnD=1
\loop\zzcnE=\zzcnA\divide\zzcnE by \zzcnD\advance\zzcnE by 1
\relax\ifnum\zzcnD<\zzcnE\relax
\advance \zzcnC by 1\multiply\zzcnD by 2
\repeat
\zzcnB=0
\loop\ifnum\zzcnC>0\relax
\advance\zzcnC by -1\divide\zzcnD by 2
\zzcnE=\zzcnB\advance\zzcnE by \zzcnD\multiply\zzcnE by \zzcnE\relax
\ifnum\zzcnA<\zzcnE\relax\else\advance\zzcnB by \zzcnD\fi
\repeat
\global\zzglobalcnA=\zzcnB}}

%\zzdistance#1#2 accepts two integers and sets \zzglobalcnA to 100 times
%the square root of the sum of their squares.

\def\zzdistance#1#2{{\zzcnA=#1\multiply\zzcnA by \zzcnA
\zzcnB=#2\multiply\zzcnB by \zzcnB
\advance\zzcnA by \zzcnB\multiply\zzcnA by 10000
\zzsqroot\zzcnA}}

%\drawdashedge, \drawdotedge, or \drawsolidedge draws a dashed, dotted, or
%solid line along the current edge.

\def\drawdashedge#1#2#3#4{\zzcnA=1\zzdmA=#1\zzdmB=#2
\zzcheckposdimen\zzdmA{FIRST}{DRAWDASHEDGE}
\zzcheckposdimen\zzdmB{SECOND}{DRAWDASHEDGE}
\zzchecknonnegnum{#3}{THIRD}{DRAWDASHEDGE}
\zzchecknonnegnum{#4}{FOURTH}{DRAWDASHEDGE}
\zzcnI=#3\relax\advance\zzcnI by #4\relax
\ifnum\zzcnI>0\else\errmessage
{SUM OF THIRD AND FOURTH PARAMETERS OF DRAWDASHEDGE MUST BE POSITIVE}\fi
\advance\zzdmB by \zzdmA
\zzdrawedge{\advance\zzcnC by -\zzcnD
\zzcnG=\zzcnC\divide\zzcnG by \zzcnE\relax
\ifnum\zzcnG>0\relax
\zzcnH=\zzcnG\multiply\zzcnH by \zzcnE\advance\zzcnC by -\zzcnH
\zzcnH=\zzcnI\multiply\zzcnH by \zzcnG
\advance\zzcnH by #3\divide\zzcnC by \zzcnH
\zzcnH=#3\multiply\zzcnH by \zzcnC\advance\zzcnD by \zzcnH
\multiply\zzcnC by \zzcnI\advance\zzcnE by \zzcnC
\else\advance\zzcnD by \zzcnC\fi}
{\line(\xslope,\yslope){\zzcnD}}}

\def\zzcheckposdimen#1#2#3{\relax\ifdim#1>0pt\else\errmessage{
#2 PARAMETER OF #3 MUST BE POSITIVE}\fi}

\def\zzchecknonnegnum#1#2#3{\relax\ifnum#1<0\relax\errmessage{
#2 PARAMETER OF #3 MUST BE NONNEGATIVE}\fi}

\def\drawdotedge#1#2{\zzcnA=0\zzdmA=0pt\zzdmB=#1
\zzcheckposdimen\zzdmB{}{DRAWDOTEDGE}\zzcheckbool{#2}{SECOND}{DRAWDOTEDGE}
\zzdrawedge{\zzcnG=\zzcnC\divide\zzcnG by \zzcnE\relax
\ifnum\zzcnG<1\relax\zzcnG=1\fi\zzcnE=\zzcnC\divide\zzcnE by \zzcnG\relax
\ifnum#2=0\relax\advance\zzcnG by -1\fi}
{\kern-1.39pt\raise-.76pt\hbox{.}}}

\def\drawsolidedge{\zzcnA=1\zzdmA=0pt\zzdmB=0pt
\zzdrawedge{\zzcnG=0\zzcnD=\zzcnC}
{\line(\xslope,\yslope){\zzcnD}}}

\def\zzdrawedge#1#2{\zzcheckedge{DRAW}\relax
\ifnum\zzcnA=1\relax\zzcheckslope\xslope\yslope 6{SOLID OR DASHED EDGE}\fi
\zzcnA=\xstart\zzcnB=\ystart\zzcnD=\zzdmA\zzcnE=\zzdmB\relax
\ifnum\xslope=0\relax\zzcnC=\yend\advance\zzcnC by -\zzcnB\zzcnF=\yslope
\else\zzcnC=\xend\advance\zzcnC by -\zzcnA\zzcnF=\xslope
\zzdistance\xslope\yslope\relax
\ifnum\xslope<0\relax\global\zzglobalcnA=-\zzglobalcnA\fi
\multiply\zzcnD by 100\divide\zzcnD by \zzglobalcnA\multiply\zzcnD by \xslope
\multiply\zzcnE by 100\divide\zzcnE by \zzglobalcnA\multiply\zzcnE by \xslope
\fi
\ifnum\zzcnF<0\relax\zzcnC=-\zzcnC\fi
\ifnum\zzcnC>0\relax #1\relax
\ifnum\zzcnF<0\relax\zzcnE=-\zzcnE\fi\zzcnF=\zzcnE\relax
\ifnum\xslope=0\relax\zzcnE=0
\else\multiply\zzcnF by \yslope\divide\zzcnF by \xslope\fi
\zzmakepicture{\loop\put(\zzcnA,\zzcnB){#2}
\ifnum\zzcnG>0\relax\advance\zzcnG by -1
\advance\zzcnA by \zzcnE\advance\zzcnB by \zzcnF\repeat
\zzrecordwidth\xstart\xstart\zzrecordwidth\xend\xend
\zztotheight=\ystart\zztotdepth=\ystart\zzrecordheight\yend\yend}\fi}

%\drawedgehead draws an arrowhead along the current edge.

\def\drawedgehead#1#2#3{\zzcheckbool{#2}{SECOND}{DRAWEDGEHEAD}
\zzcheckbool{#3}{THIRD}{DRAWEDGEHEAD}\zzcnB=#3
\relax\ifnum#2=1\relax\zzcnA=#1\relax
\zzdrawedgeheada\xstart\ystart\xend\yend\xslope\yslope
\else\zzcnA=100\advance\zzcnA by -#1\relax
\zzdrawedgeheada\xend\yend\xstart\ystart{-\xslope}{-\yslope}\fi}

%\zzcheckbool gives an error message unless its first argument is 1 or 0.

\def\zzcheckbool#1#2#3{
\ifnum#1<0\errmessage{#2 PARAMETER OF #3 MUST BE 1 OR 0}\fi
\ifnum#1>1\errmessage{#2 PARAMETER OF #3 MUST BE 1 OR 0}\fi}

\def\zzdrawedgeheada#1#2#3#4#5#6{\zzcheckedge{DRAW ARROWHEAD FOR}
\zzcheckslope{#5}{#6}4{ARROWHEAD}
\zzdmA=#3\advance\zzdmA by -#1
\divide\zzdmA by 10\multiply\zzdmA by \zzcnA\divide\zzdmA by 10
\zzdmB=#4\advance\zzdmB by -#2
\divide\zzdmB by 10\multiply\zzdmB by \zzcnA\divide\zzdmB by 10
\relax\ifnum\zzcnB=1\relax
\zzdistance{#5}{#6}\zzdmC=\edgeheaddisp
\multiply\zzdmC by 100\divide\zzdmC by \zzglobalcnA\zzdmD=\zzdmC
\multiply\zzdmC by #5\multiply\zzdmD by #6
\advance\zzdmA by \zzdmC\advance\zzdmB by \zzdmD\fi
\advance\zzdmA by #1\zzcnA=\zzdmA\advance\zzdmB by #2\zzcnB=\zzdmB
\zzmakepicture{\put(\zzcnA,\zzcnB){\vector(#5,#6){0}}
\zzrecordwidth\zzdmA\zzdmA\zztotheight=\zzdmB\zztotdepth=\zzdmB}}

%\zzcheckslope gives an errormessage if the absolute value of #1 or #2
%is greater than #3.

\def\zzcheckslope#1#2#3#4{\relax
\ifnum#1>#3\zzcheckslopea{#1}{#2}{#4}\fi
\ifnum#1<-#3\zzcheckslopea{#1}{#2}{#4}\fi
\ifnum#2>#3\zzcheckslopea{#1}{#2}{#4}\fi
\ifnum#2<-#3\zzcheckslopea{#1}{#2}{#4}\fi}

\def\zzcheckslopea#1#2#3{\errmessage{\the#1,\the#2 IS ILLEGAL SLOPE FOR #3}}

%The following macros each call \zzsetupbox{#2}{#3} and then issue the
%resulting expression so that its shadow touches the edge x = \xslope.t
%+\xstart, y = \yslope.t+\ystart. \abutX places the expression to the X
%of the edge. For \abutleft and \abutright, #1 gives the y-coordinate
%of the expression as an integer multiple of \diagramunit.  For \abutbelow
%and \abutabove, #1 gives the x-coordinate similarly.  The macros \abutXd
%are similar, except that #1 should be a dimension.

\def\abutleft#1:#2#3{\zzabut{#1}{#2}{#3}{-\yslope}{\zzslidehoriz}{1}}
\def\abutright#1:#2#3{\zzabut{#1}{#2}{#3}{\yslope}{\zzslidehoriz}{1}}
\def\abutbelow#1:#2#3{\zzabut{#1}{#2}{#3}{\xslope}{\zzslidevert}{1}}
\def\abutabove#1:#2#3{\zzabut{#1}{#2}{#3}{-\xslope}{\zzslidevert}{1}}

\def\abutleftd#1#2#3{\zzabut{#1}{#2}{#3}{-\yslope}{\zzslidehoriz}{0}}
\def\abutrightd#1#2#3{\zzabut{#1}{#2}{#3}{\yslope}{\zzslidehoriz}{0}}
\def\abutbelowd#1#2#3{\zzabut{#1}{#2}{#3}{\xslope}{\zzslidevert}{0}}
\def\abutaboved#1#2#3{\zzabut{#1}{#2}{#3}{-\xslope}{\zzslidevert}{0}}

\def\zzabut#1#2#3#4#5#6{\zzcheckedge{ABUT TO}
\zzsetupbox{#2}{#3}\zzcnA=\xslope\zzcnB=\yslope
\relax\ifnum#4<0\relax\zzcnA=-\zzcnA\zzcnB=-\zzcnB\fi
\def\zzprocpoly{\zzclosestpoly}\def\zzprocrorect{\zzprocrorecta}
\zzglobalshadow
\advance\zzdmC by -\zzglobalxcenter\advance\zzdmD by -\zzglobalycenter
\relax\ifnum#6=1\relax\zzmultdiagramunit\zzdmA{#1}\else\zzdmA=#1\fi
\zzdmB=\zzdmA#5\relax\zzissue}

\def\zzslidehoriz{\relax\ifnum\yslope=0\errmessage
{ABUTLEFT OR ABUTRIGHT ATTEMPTED FOR HORIZONTAL EDGE}\fi
\advance\zzdmA by \zzdmD\advance\zzdmA by -\ystart
\multiply\zzdmA by \xslope\divide\zzdmA by \yslope
\advance\zzdmA by \xstart\advance\zzdmA by -\zzdmC}

\def\zzslidevert{\relax\ifnum\xslope=0\errmessage
{ABUTBELOW OR ABUTABOVE ATTEMPTED FOR VERTICAL EDGE}\fi
\advance\zzdmB by \zzdmC\advance\zzdmB by -\xstart
\multiply\zzdmB by \yslope\divide\zzdmB by \xslope
\advance\zzdmB by \ystart\advance\zzdmB by -\zzdmD}

%\zzclosestpoly finds the vertex of a convex polygon that is closest to a
%directed line, x = xs.t+x0, y = ys.t+y0, assuming that the directed line
%is to the left (right) of the polygon if ys is positive (negative)
%and above (below) the polygon if xs is positive (negative).
%It is called by defining \zzprocpoly to be \zzclosestpoly, setting \zzcnA,
%\zzcnB to xs, ys, and executing \zzglobalshadow, which must have been
%defined by a polygon descriptor.  The output is left in \zzdmC, \zzdmD.

\def\zzclosestpoly#1#2#3{\zzdmC=#1\zzdmD=#2\zzcnC=0\zzclosestpolya #3\end}

\def\zzclosestpolya#1{\def\zztesta{#1}\def\zztestb{\end}
\ifx\zztesta\zztestb\let\zznext=\zzclosestpolyb
\else\let\zznext=\zzclosestpolyc\fi\zznext {#1}}

\def\zzclosestpolyb#1{}

\def\zzclosestpolyc#1#2#3{\zzcnD=\zzcnB\multiply\zzcnD by #1\relax
\zzcnE=\zzcnA\multiply\zzcnE by #2\relax\advance\zzcnD by -\zzcnE\relax
\ifnum\zzcnD>0\relax
\ifnum\zzcnC=1\let\zznext=\zzclosestpolyd
\else\zzcnC=0\zzcastpolyd{#1}{#2}{#3}\let\zznext=\zzclosestpolya\fi
\else\zzcnC=1\zzcastpolyd{#1}{#2}{#3}\let\zznext=\zzclosestpolya\fi
\zznext}

\def\zzclosestpolyd#1\end{}

%\setcircle initializes \dcircle, \xcircle, and \ycircle to its first
%three parameters, and sets \zziscircle to 1.

\def\setcircle#1#2#3{\dcircle=#1\xcircle=#2\ycircle=#3\zziscircle=1}

%\shiftcircle#1#2 displaces \xcircle, \ycircle by #1, #2.

\def\shiftcircle#1#2{\zzcheckcircle{SHIFT}
\advance\xcircle by #1\advance\ycircle by #2}

\def\zzcheckcircle#1{\relax\ifnum\zziscircle=0\errmessage
{ATTEMPT TO #1 NONEXISTENT CIRCLE}\fi}

%\drawcircle draws quadrants of the current circle.

\def\drawcircle#1#2#3#4{\zzcheckcircle{DRAW}\zzdmA=\dcircle\divide\zzdmA by 2
\zzmakepicture{\zzcnA=\dcircle\zzcnB=\xcircle\zzcnC=\ycircle
\zztotheight=\ycircle\zztotdepth=\ycircle
\zzrecordwidth\xcircle\xcircle\relax
\zzdrawcirclea{#1}{tr}{\zzdmA}{\zzdmA}\zzdrawcirclea{#2}{br}{\zzdmA}{-\zzdmA}
\zzdrawcirclea{#3}{bl}{-\zzdmA}{-\zzdmA}
\zzdrawcirclea{#4}{tl}{-\zzdmA}{\zzdmA}}}

\def\zzdrawcirclea#1#2#3#4{\zzcheckbool{#1}{}{DRAWCIRCLE}\ifnum#1=1\relax
\put(\zzcnB,\zzcnC){\oval(\zzcnA,\zzcnA)[#2]}
\zzdmB=\xcircle\advance\zzdmB by #3\zzrecordwidth\zzdmB\zzdmB
\zzdmB=\ycircle\advance\zzdmB by #4\zzrecordheight\zzdmB\zzdmB\fi}

%\drawcirclehead issues an arrowhead placed on the current circle.

\def\drawcirclehead#1#2#3{\zzcheckcircle{DRAW ARROWHEAD FOR}
\zzreduceterms{#1}{#2}{0,0 ARE ILLEGAL PARAMETERS FOR DRAWCIRCLEHEAD}
\zzdistance{\zzcnC}{\zzcnD}\zzcheckbool{#3}{THIRD}{DRAWCIRCLEHEAD}
\ifnum#3=1\relax\zzcnA=\zzcnD\zzcnB=-\zzcnC\else\zzcnA=-\zzcnD\zzcnB=\zzcnC\fi
\zzdmA=\dcircle\multiply\zzdmA by 50\divide\zzdmA by \zzglobalcnA
\zzdmB=\circleheaddisp\multiply\zzdmB by 100\divide\zzdmB by \zzglobalcnA
\zzdmE=\zzdmA\multiply\zzdmE by \zzcnC\zzdmC=\xcircle\advance\zzdmC by \zzdmE
\zzdmE=\zzdmB\multiply\zzdmE by \zzcnA\advance\zzdmC by \zzdmE
\zzdmE=\zzdmA\multiply\zzdmE by \zzcnD\zzdmD=\ycircle\advance\zzdmD by \zzdmE
\zzdmE=\zzdmB\multiply\zzdmE by \zzcnB\advance\zzdmD by \zzdmE
\zzcnC=\zzdmC\zzcnD=\zzdmD\zzcheckslope\zzcnA\zzcnB4{ARROWHEAD}
\zzmakepicture{\put(\zzcnC,\zzcnD){\vector(\zzcnA,\zzcnB){0}}
\zzrecordwidth\zzdmC\zzdmC\zztotheight=\zzdmD\zztotdepth=\zzdmD}}

%The next four macros cause an expression to be abutted to the current
%circle.

\def\abutcircleleft#1#2#3{\zzabutcircle{#1}{#2}{#3}{}
\zzslidehoriz{\relax\ifdim\zzdmA>\zzdmH\relax\zzdmH=\zzdmA\fi}}

\def\abutcircleright#1#2#3{\zzabutcircle{#1}{#2}{#3}{\zzrotate\zzrotate}
\zzslidehoriz{\relax\ifdim\zzdmA<\zzdmH\relax\zzdmH=\zzdmA\fi}}

\def\abutcirclebelow#1#2#3{\zzabutcircle{#1}{#2}{#3}{\zzrotate}
\zzslidevert{\relax\ifdim\zzdmB>\zzdmI\relax\zzdmI=\zzdmB\fi}}

\def\abutcircleabove#1#2#3{\zzabutcircle
{#1}{#2}{#3}{\zzrotate\zzrotate\zzrotate}
\zzslidevert{\relax\ifdim\zzdmB<\zzdmI\relax\zzdmI=\zzdmB\fi}}

\def\zzabutcircle#1#2#3#4#5#6{\zzcheckcircle{ABUT TO}
\zzsetupbox{#2}{#3}\def\zzprocpoly{\zzclosestpoly}
\def\zzprocrorect{\zzprocrorecta}
\zzdmF=\dcircle\divide\zzdmF by 2
\zzdmG=\dcircle\multiply\zzdmG by 100\divide\zzdmG by 283
\xstart=-\zzdmF\ystart=0pt\xslope=0\yslope=-1
\zzabutcirclea{#1}{#4}{#5}\zzdmH=\zzdmA\zzdmI=\zzdmB
\xstart=-\zzdmG\ystart=\zzdmG\xslope=-1\yslope=-1
\zzabutcirclea{#1}{#4}{#5}#6\relax
\xstart=-\zzdmG\ystart=-\zzdmG\xslope=1\yslope=-1
\zzabutcirclea{#1}{#4}{#5}#6\relax
\zzdmA=\zzdmH\advance\zzdmA by \xcircle
\zzdmB=\zzdmI\advance\zzdmB by \ycircle
\zzissue}

\def\zzabutcirclea#1#2#3{#2\relax
\zzcnA=\xslope\zzcnB=\yslope
\zzglobalshadow
\advance\zzdmC by -\zzglobalxcenter\advance\zzdmD by -\zzglobalycenter
\zzdmA=#1\zzdmB=\zzdmA #3\relax}

\def\zzrotate{\zzdmE=\xstart\xstart=-\ystart\ystart=\zzdmE
\zzcnC=\xslope\xslope=-\yslope\yslope=\zzcnC}

%MACROS FOR CATEGORY-THEORY DIAGRAMS

%The following control symbols may be redefined by the user:
\def\ctvertexstyle{\displaystyle}
\def\ctabutstyle{\textstyle}
\def\ctvertexborderlr{3pt}
\def\ctvertexbordertb{4pt}
\def\ctloopdiameter{20pt}
\def\ctabutcircledisp{5pt}
\def\ctabutborderlr{2pt}
\def\ctabutbordertb{2pt}
\def\ctabutborderinset{3pt}
\def\ctabutborderinsetdouble{6pt}%Must be twice \ctabutborderinset
\def\ctdoubleedgedisp{2pt}

%The following registers are used locally:
\newdimen\zzdmX\newdimen\zzdmY

%Hidden macros and other defined control symbols:
%    used by \ctdiagram: \diagram\ctsolid\cthead
%    used by \ctv and \ctvg: \vertex\border\rect
%    used by \ctgl: \leftghost
%    used by \ctgr: \rightghost
%    used by \ctlptl, \ctlptlcc, \ctlptr, \ctlptrcc, \ctlpbr, \ctlpbrcc,
%    \ctlpbl, \ctlpblcc: \zzctlp\border\setcircle\shiftcircle\drawcircle
%        \drawcirclehead\abutcircleleft\abutcircleright\zzctabutprog
%        \octagon
%    used by \cten, \ctet, \cteb, \ctel, \cter, \ctetg, \ctebg, \ctelg, \cterg,
%    \ctetb, \ctelr, \ctetbg, \ctelrg: \setedge\zzctxmean\zzctymean
%        \zzmultdiagramunit\zzcte\zzctee\shadeedge\abutaboved\abutbelowd
%        \abutleftd\abutrightd\zzctabutprog\border\octagon\shiftedge
%        \drawsolidedge\zzctdrawdashedge\drawdashedge\zzctdrawdotedge
%        \drawdotedge\zzctdrawedgehead\drawedgehead\zzctnodrawedgehead
%    multiply defined control symbols: \zzctdrawedge\zzctdrawhead
%        \zzctxmeanadj\zzctymeanadj

%\ctdiagram is similar to \diagram, except that it executes \ctsolid and
%\cthead before the expression program #1.

\def\ctdiagram#1{\diagram{\ctsolid\cthead\ctoutermid #1}}

%\ctvg is similar to \vertex except that the expression #3 is printed in
%\ctvertexstyle mode, and the expression program #4 is followed by a
%standard program that borders the current rectangle by \ctvertexborderlr
%on the sides and \ctvertexbordertb on the top and bottom, and then
%creates a rectangular shadow.  \ctv is similar to \ctvg except that the
%expression program is empty (except for the standard program).

\def\ctvg#1,#2:#3#4{\vertex #1,#2:{\ctvertexstyle #3}{#4\relax
\border\ctvertexborderlr\ctvertexbordertb\rect}}

\def\ctv#1,#2:#3{\ctvg #1,#2:{#3}{}}

%\ctgl and \ctgr are similar to \leftghost and \rightghost except that
%the expression #1 is printed in \ctvertexstyle.

\def\ctgl#1{\leftghost{\ctvertexstyle #1}}

\def\ctgr#1{\rightghost{\ctvertexstyle #1}}

%The following eight macros print a three-quarter-circle loop of diameter
%\ctloopdiameter at one of the corners of an expanded current rectangle,
%with an arrowhead at one end of the loop.

\def\ctlptl#1{\zzctlp{\lexpr\texpr}{{0pt}\circleheaddisp}{1011}{101}
{\abutcircleleft\ctabutcircledisp}{#1}}

\def\ctlptlcc#1{\zzctlp{\lexpr\texpr}{{-\circleheaddisp}{0pt}}{1011}{0{-1}0}
{\abutcircleleft\ctabutcircledisp}{#1}}

\def\ctlptr#1{\zzctlp{\rexpr\texpr}{\circleheaddisp{0pt}}{1101}{0{-1}1}
{\abutcircleright\ctabutcircledisp}{#1}}

\def\ctlptrcc#1{\zzctlp{\rexpr\texpr}{{0pt}\circleheaddisp}{1101}{{-1}00}
{\abutcircleright\ctabutcircledisp}{#1}}

\def\ctlpbr#1{\zzctlp{\rexpr\bexpr}{{0pt}{-\circleheaddisp}}{1110}{{-1}01}
{\abutcircleright{-\ctabutcircledisp}}{#1}}

\def\ctlpbrcc#1{\zzctlp{\rexpr\bexpr}{\circleheaddisp{0pt}}{1110}{010}
{\abutcircleright{-\ctabutcircledisp}}{#1}}

\def\ctlpbl#1{\zzctlp{\lexpr\bexpr}{{-\circleheaddisp}{0pt}}{0111}{011}
{\abutcircleleft{-\ctabutcircledisp}}{#1}}

\def\ctlpblcc#1{\zzctlp{\lexpr\bexpr}{{0pt}{-\circleheaddisp}}{0111}{100}
{\abutcircleleft{-\ctabutcircledisp}}{#1}}

\def\zzctlp#1#2#3#4#5#6{\border\ctvertexborderlr\ctvertexbordertb
\setcircle\ctloopdiameter #1
\border{-\ctvertexborderlr}{-\ctvertexbordertb}
\shiftcircle #2\drawcircle #3\drawcirclehead #4
#5{\ctabutstyle #6}\zzctabutprog}

\def\zzctabutprog{\border\ctabutborderlr\ctabutbordertb
\borderto{\ctabutborderinsetdouble}{\ctabutborderinsetdouble}
\octagon\ctabutborderinset}

%\cten#1,#2,#3,#4: draws a shaded edge from #1,#2 to #3,#4, possibly with
%an arrowhead at its end.

\def\cten#1:{\setedge#1:\shadeedge\zzctdrawedge\zzctdrawhead1}

%The following four macros draw a shaded edge, possibly with an arrowhead
%at the end, and abut an expression to the edge at its midpoint.

\def\ctet#1:#2{\setedge#1:\zzctxmean\zzcte\abutaboved{#2}\zzctxmeanadj}

\def\cteb#1:#2{\setedge#1:\zzctxmean\zzcte\abutbelowd{#2}\zzctxmeanadj}

\def\ctel#1:#2{\setedge#1:\zzctymean\zzcte\abutleftd{#2}\zzctymeanadj}

\def\cter#1:#2{\setedge#1:\zzctymean\zzcte\abutrightd{#2}\zzctymeanadj}

\def\zzctxmean{\zzdmX=\xstart\advance\zzdmX by \xend\divide\zzdmX by 2}

\def\zzctymean{\zzdmX=\ystart\advance\zzdmX by \yend\divide\zzdmX by 2}

\def\zzcte#1#2#3{\shadeedge #3\zzctdrawedge\zzctdrawhead1
#1\zzdmX{\ctabutstyle #2}\zzctabutprog}

%The next four macros behave similarly to those above, but abut an
%expression to a specified point on the edge.

\def\ctetg#1;#2:#3{\setedge#1:\zzmultdiagramunit\zzdmX{#2}\zzcte
\abutaboved{#3}\relax}

\def\ctebg#1;#2:#3{\setedge#1:\zzmultdiagramunit\zzdmX{#2}\zzcte
\abutbelowd{#3}\relax}

\def\ctelg#1;#2:#3{\setedge#1:\zzmultdiagramunit\zzdmX{#2}\zzcte
\abutleftd{#3}\relax}

\def\cterg#1;#2:#3{\setedge#1:\zzmultdiagramunit\zzdmX{#2}\zzcte
\abutrightd{#3}\relax}

%\ctetb (\ctelr) draws a pair of shaded edges, with two expressions
%abutted to the top and bottom (left and right) of the midpoint.
%\ctetbg and \ctelrg are similar, but abut to a specified point on the edge.

\def\ctetb#1:#2#3#4#5{\zzcheckbool{#2}{FIFTH}{CTETB}
\zzcheckbool{#3}{SIXTH}{CTETB}
\setedge#1:\zzctxmean\zzctee\xslope\abutaboved\abutbelowd
{#2}{#3}{#4}{#5}\zzctxmeanadj}

\def\ctelr#1:#2#3#4#5{\zzcheckbool{#2}{FIFTH}{CTELR}
\zzcheckbool{#3}{SIXTH}{CTELR}
\setedge#1:\zzctymean\zzctee\yslope\abutleftd\abutrightd
{#2}{#3}{#4}{#5}\zzctymeanadj}

\def\ctetbg#1;#2,#3:#4#5#6#7{\zzcheckbool{#4}{SEVENTH}{CTETBG}
\zzcheckbool{#5}{EIGHTH}{CTETBG}\setedge#1:\zzmultdiagramunit\zzdmX{#2}
\zzctee\xslope
\abutaboved{\zzmultdiagramunit\zzdmX{#3}\abutbelowd}{#4}{#5}{#6}{#7}\relax}

\def\ctelrg#1;#2,#3:#4#5#6#7{\zzcheckbool{#4}{SEVENTH}{CTELRG}
\zzcheckbool{#5}{EIGHTH}{CTELRG}\setedge#1:\zzmultdiagramunit\zzdmX{#2}
\zzctee\yslope
\abutleftd{\zzmultdiagramunit\zzdmX{#3}\abutrightd}{#4}{#5}{#6}{#7}\relax}

\def\zzctee#1#2#3#4#5#6#7#8{
\ifnum#1>0\relax\zzdmY=\ctdoubleedgedisp\else\zzdmY=-\ctdoubleedgedisp\fi
\shiftedge\zzdmY\shadeedge #8\zzctdrawedge\zzctdrawhead{#4}
#2\zzdmX{\ctabutstyle #6}\zzctabutprog
\multiply\zzdmY by -2\relax
\shiftedge\zzdmY\shadeedge #8\zzctdrawedge\zzctdrawhead{#5}
#3\zzdmX{\ctabutstyle #7}\zzctabutprog}

%\ctinnermid defines \zzctxmeanadj and \zzctymeanadj so that \ctet, \cteb,
%\ctel, \cter, \ctetb, and \ctelr recompute the midpoint of the current
%edge after shading.  \ctoutermid defines these control symbols so that
%these routines do not recompute the midpoint.

\def\ctinnermid{\def\zzctxmeanadj{\zzctxmean}\def\zzctymeanadj{\zzctymean}}

\def\ctoutermid{\def\zzctxmeanadj{\relax}\def\zzctymeanadj{\relax}}

%\zzctdrawdashedge draws a dashed edge.

\def\zzctdrawdashedge{\relax
\ifnum\xslope=0\relax\drawdashedge{7pt}{7pt}11
\else\ifnum\yslope=0\relax\drawdashedge{7pt}{7pt}11
\else\drawdashedge{15pt}{7pt}01\fi\fi}

%\zzctdrawdotedge draws a dotted edge.

\def\zzctdrawdotedge{\drawdotedge{8pt}1}

%\ctsolid (\ctdash,\ctdot) defines \zzctdrawedge to be \drawsolidedge
%(\zzctdrawdashedge,\zzctdrawdotedge), so that edges will be solid
%(dashed, dotted).

\def\ctsolid{\def\zzctdrawedge{\drawsolidedge}}

\def\ctdash{\def\zzctdrawedge{\zzctdrawdashedge}}

\def\ctdot{\def\zzctdrawedge{\zzctdrawdotedge}}

%\zzctdrawedgehead places a forward-pointing arrowhead at the end of an edge
%if #1=1 or a backward-pointing arrowhead at the beginning if #1=0.
%\zzctnodrawedgehead is called in the same way but does nothing.

\def\zzctdrawedgehead#1{\relax\ifnum#1=1\relax
\drawedgehead{100}10\else\drawedgehead{0}00\fi}

\def\zzctnodrawedgehead#1{}

%\cthead (\ctnohead) defines \zzctdrawhead to be \zzctdrawedgehead
%(\zzctnodrawedgehead), so that edges will (will not) have arrowheads.

\def\cthead{\def\zzctdrawhead{\zzctdrawedgehead}}

\def\ctnohead{\def\zzctdrawhead{\zzctnodrawedgehead}}
