%					
%	dja.tex	
%	Daniel Allcock   (allcock@math.utah.edu)
%	17 December 1996			
%						
%	A simple set of macros for plain tex
%	documentation is in dja.doc, a text file
%
% suppresses duplicate inputs of this file:
\ifx\djatexLoaded\relax\endinput\else\let\djatexLoaded=\relax\fi

% FONTS
%
% calligraphic font
\newfam\calfont
\font\tencal=eusm10 \font\sevencal=eusm7 \font\fivecal=eusm5
\textfont\calfont=\tencal 
\scriptfont\calfont=\sevencal 
\scriptscriptfont\calfont=\fivecal
\def\cal{\fam=\calfont}	
% fraktur
\newfam\frakturfont
\font\tenfrak=eufm10 \font\sevenfrak=eufm7 \font\fivefrak=eufm5
\textfont\frakturfont=\tenfrak 
\scriptfont\frakturfont=\sevenfrak 
\scriptscriptfont\frakturfont=\fivefrak
\def\frak{\fam=\frakturfont}
% blackboard bold (includes many math symbols)
\newfam\bbbfont
\font\tenbbb=msbm10 \font\sevenbbb=msbm7 \font\fivebbb=msbm5	
\textfont\bbbfont=\tenbbb 
\scriptfont\bbbfont=\sevenbbb 
\scriptscriptfont\bbbfont=\fivebbb
\def\bbb{\fam=\bbbfont}
% script and script-script fonts for typewriter font (all in
% 10pt for portability)
\scriptfont\ttfam=\tentt
\scriptscriptfont\ttfam=\tentt

% PROGRAMMING STUFF
% 
% `if not defined' :
\long\def\ifndef#1{\expandafter\ifx\csname#1\endcsname\relax}
% sometimes more convenient, but you can't say
% ``\ifdef...\else...\fi''
\long\def\ifdef#1{\ifndef{#1}\else}
% My warning messages
\def\mywarning#1{\ifnum\warningsoff=0
	\immediate\write16{l.\the\inputlineno: #1}\fi}
\ifndef{warningsoff}\def\warningsoff{0}\fi
\def\givenowarnings{\def\warningsoff{1}}
\def\givewarnings{\def\warningsoff{0}}

% FRONT MATTER
%
% note: \author, \title and \note get redefined when
% \bibliography is called.
\long\def\author#1{\def\Zauthor{#1}}
\long\def\title#1{\def\Ztitle{#1}}
\long\def\address#1{\def\Zaddress{#1}}
\long\def\email#1{\def\Zemail{#1}}
\long\def\homepage#1{\def\Zhomepage{#1}}
\long\def\date#1{\def\Zdate{#1}}
\long\def\subject#1{\def\Zsubject{#1}}
\long\def\note#1{\def\Znote{#1}}
\def\plaintitlepage{{\parindent=0pt
	\ifdef{Ztitle}\par{\bf\Ztitle}\medskip\fi
	\ifdef{Zauthor}\par{\Zauthor}\fi
	\ifdef{Zdate}\par{\Zdate}\fi
	\smallskip
	\ifdef{Zemail}\par{\it \Zemail}\fi
	\ifdef{Zhomepage}\par{web page: \it\Zhomepage}\fi
	\ifdef{Zaddress}\par{\Zaddress}\fi
	\ifdef{Zsubject}\smallskip\par{1991 mathematics subject
		classification: \Zsubject}\fi
	\ifndef{Znote}\else\smallskip\par{\Znote}\fi
	\vskip 0pt plus 10pt}}
\def\abstract{\bigbreak\noindent{\bf Abstract.}\par\noindent} 

% SECTIONS, THEOREMS, ETC.
%
%\let\section=\beginsection
\outer\def\section#1\par{\bigbreak\noindent{\bf #1}\nobreak\medskip\noindent}
% Proclaimed Things (theorems, lemmas, etc.)
%   (modeled on plain tex's ``\proclaim'', but theorems can be more
%   than one paragraph long.)
\outer\def\beginproclaim#1. {\medbreak
	\noindent{\bf #1.\enspace}\begingroup\sl}
\def\endproclaim{\endgroup\par\ifdim\lastskip<\medskipamount 
	\removelastskip\penalty55\medskip\fi}
% Proofs (n.b. \drawbox is defined in `OTHER')
\def\beginproof#1{\smallskip{\it#1\/}}
\def\endproof{\leavevmode\vrule height0pt width0pt
	depth0pt\nobreak\hfill\proofbox\smallskip}
\def\QED{\endproof}
\def\proofbox{\drawbox{1.2ex}{1.2ex}{.1ex}}
% Remarks (n.b. remarks are only one par long)
\def\remark#1#2\par{\ifdim\lastskip<\smallskipamount
	\removelastskip\penalty55\smallskip\fi
	{\it #1\/}#2\smallbreak}

% CROSS-REFERENCES AND BIBLIOGRAPHY
%
% If the tag has not been defined (say by \deftag), then
% \tag warns and inserts question
% marks. If the tag is defined, it
% just produces the tag (symbolic if \userawtags=0, 
% or symbolic:raw, otherwise.) Default is \userawtags=1.
\def\rawtags{\def\userawtags{1}}
\def\symbolictags{\def\userawtags{0}}
\ifndef{userawtags}\def\userawtags{1}\fi
\def\tag#1{\ifndef{#1}\mywarning{tag `#1' 
	undefined.}{\hbox{\bf?`}\tt #1\hbox{\bf ?}}\else
	\ifnum\userawtags=0 \csname #1\endcsname
	\else\csname #1\endcsname{ \tt [#1]}\fi\fi}
\def\Tag#1{\tag{#1}}
\def\eqtag#1{(\tag{#1})}
\def\eqTag#1{\ifnum\userawtags=0
	\eqtag{#1}\else
	\lower12pt\hbox{\eqtag{#1}}\fi}
\def\cite#1{[\tag{#1}]}
\def\ecite#1#2{[\tag{#1}, #2]}
\def\nocite#1{}
% syntax: \deftag{label to insert in text}{label in file}
\def\deftag#1#2{\ifndef{#2}\else\mywarning{tag `#2' defined
	more than once.}\fi 
	\expandafter\def\csname #2\endcsname{#1}}
\def\defcite#1#2{\deftag{#1}{#2}}
% bibliography entries
\def\bibitem#1{\ifnum\userawtags=0
	\item{[{\tag{#1}}]}\else
	\noindent\hangindent=\parindent\hangafter=1
			\cite{#1}\enskip\fi}

% FONT SHORTCUTS 
%
\def\rom#1{({\it\romannumeral#1\/})}
\def\Rom#1{{\rm\uppercase\expandafter{\romannumeral#1}}}
\def\a{\alpha}	\def\b{\beta}	\def\c{\gamma}
\def\d{\delta}  \def\e{\varepsilon}	
\def\cala{{\cal A}} \def\calj{{\cal J}} \def\cals{{\cal S}}
\def\calb{{\cal B}} \def\calk{{\cal K}} \def\calt{{\cal T}}
\def\calc{{\cal C}} \def\call{{\cal L}} \def\calu{{\cal U}}
\def\cald{{\cal D}} \def\calm{{\cal M}} \def\calv{{\cal V}}
\def\cale{{\cal E}} \def\caln{{\cal N}} \def\calw{{\cal W}}
\def\calf{{\cal F}} \def\calo{{\cal O}} \def\calx{{\cal X}}
\def\calg{{\cal G}} \def\calp{{\cal P}} \def\caly{{\cal Y}}
\def\calh{{\cal H}} \def\calq{{\cal Q}} \def\calz{{\cal Z}}
\def\cali{{\cal I}} \def\calr{{\cal R}}

% SHORTCUTS AND MNEMONICS FOR STANDARD MATH SYMBOLS
%
\def\Z{{\bbb Z}} % integers
\def\Q{{\bbb Q}} % rational numbers 
\def\R{{\bbb R}} % real numbers
\def\C{{\bbb C}} % complex numbers
\def\F{{\bbb F}} % A finite field
\let\sset=\subseteq		\let\dimension=\dim%
\let\equivalent=\equiv% 	
\let\congruent=\equiv%
\def\setminus{\mathop{\bbb \char"72}\nolimits}
\def\semidirect{\mathop{\bbb \char"6E}\nolimits}
\let\tensor=\otimes	
\let\del=\partial
\let\isomorphism=\cong		\let\iso=\cong
\let\to=\rightarrow
\def\aut{\mathop{\rm Aut}\nolimits}
\def\hom{\mathop{\rm Hom}\nolimits}
\def\re{\mathop{\rm Re}\nolimits}
\def\im{\mathop{\rm Im}\nolimits}
\def\tr{\mathop{\rm Tr}\nolimits}
\def\mod{\mathop{\rm mod}\nolimits}
% quick parentheses and brackets
\def\({\left(} \def\){\right)}
\def\[{\left[} \def\]{\right]}

% OTHER STUFF	
%
\def\hideboxes{\overfullrule=0pt}
\def\frac#1/#2{\leavevmode\kern.1em\raise.5ex\hbox{\the
	\scriptfont0 #1}\kern-.1em/\kern-.15em\lower.25ex
	\hbox{\the\scriptfont0 #2}} 
% 2x2 matrices for use in text:
\def\smallmatrix#1#2#3#4{%
	\left({#1\atop #3}\;{#2\atop #4}\right)}
% syntax: ``\drawbox{internal width}{internal height}{line
% 	thickness}''; arguments should be dimens.
\def\drawbox#1#2#3{\leavevmode\vbox{\hrule height #3%
	\hbox{\vrule width #3 height #2\kern #1%
	\vrule width #3}\hrule height #3}}

% does an \halign but centers it with a little vertical space
% around it. Uses diplay math mode.
\def\calign#1{$$\vbox{\halign{#1}}$$}

% an \llap that you can use inside formulas without
% tex changing to textstyle inside the \hbox of \llap
\def\mathllap#1{\mathchoice
{\llap{$\displaystyle #1$}}%
{\llap{$\textstyle #1$}}%
{\llap{$\scriptstyle #1$}}%
{\llap{$\scriptscriptstyle #1$}}}
% the set of all #1 such that #2; makes the braces and the
% middle bar big enough to accomodate everything.
\def\set#1#2{\left\{\,#1\mathllap{\phantom{#2}}\mathrel{}\right|\left.#2\mathllap{\phantom{#1}}\,\right\}}

\parskip=0pt

% -----------------end-of-dja.tex-------------------------------
% This is PiCTeX, Version 1.1   9/21/87
 
% CAVEAT: The PiCTeX manual often has a more lucid explanation
%   of any given topic than you will find in the internal documentation
%   of the macros.
 
% PiCTeX's commands can be classified into two groups: (1) public (or
%   external), and (2) private (or internal). The public macros are
%   discussed at length in the manual. The only discussion of the private
%   macros is the internal documentation. The private macros all have
%   names beginning with an exclamation point (!) of category code 11. 
%   Since in normal usage "!" has category code 12, these macros can't
%   be accessed or modified by the general user.
 
% The macros are organized into thematically related groups. For example,
%   the macros dealing with dots & dashes are all in the DASHPATTERN group.
%   The table below shows which macros are in which groups. The table
%   covers all public macros, and many (but not all) of PiCTeX's upper level
%   private macros. Following the table, the various groups are listed
%   in the order in which they appear in the table.
 
% *********************** TABLE OF GROUPS OF MACROS **********************
 
% HACKS:  Utility macros
%    \PiC
%    \PiCTeX
%    \placevalueinpts 
%    \!!loop 
%    \!cfor
%    \!copylist
%    \!ecfor
%    \!etfor
%    \!getnext
%    \!getnextvalueof
%    \!ifempty
%    \!ifnextchar
%    \!leftappend
%    \!listaddon 
%    \!loop
%    \!lop
%    \!mlap
%    \!not
%    \!removept
%    \!rightappend
%    \!tfor 
%    \!vmlap
%    \!wlet
 
% ALLOCATION:  Allocates registers
 
% AREAS: Deals with plot areas 
%    \axis
%    \grid 
%    \invisibleaxes
%    \normalgraphs 
%    \plotheading 
%    \setplotarea
%    \visibleaxes
 
% ARROWS:  Draws arrows
%    \arrow
%    \betweenarrows
 
% BARS:  Draws bars
%    \putbar 
%    \setbars
 
% BOXES:  Draws rectangles
%    \frame
%    \putrectangle 
%    \rectangle
%    \shaderectangleson
%    \shaderectanglesoff
 
% CURVES:  Upper level plot commands
%    \hshade 
%    \plot 
%    \sethistograms
%    \setlinear
%    \setquadratic
%    \vshade
 
% DASHPATTERNS:  Sets up dash patterns
%    \findlength 
%    \setdashes 
%    \setdashesnear
%    \setdashpattern
%    \setdots 
%    \setdotsnear 
%    \setsolid
%    \!dashingoff
%    \!dashingon
 
% DIVISION:  Does long division of dimension registers
%    \Divide 
%    \!divide
 
% ELLIPSES:  Draws ellipses and circles
%    \circulararc 
%    \ellipticalarc 
 
% RULES:  Draws rules, i.e., horizontal & vertical lines
%    \putrule 
%    \!putdashedhline
%    \!putdashedvline
%    \!puthline 
%    \!putsolidhline  
%    \!putsolidvline  
%    \!putvline
 
% LINEAR ARC:  Draws straight lines -- solid and dashed
%    \inboundscheckoff
%    \inboundscheckon
%    \!advancedashing 
%    \!drawlinearsegment
%    \!initinboundscheck
%    \!linearsolid
%    \!lineardashed
%    \!ljoin
%    \!plotifinbounds     
%    \!start 
 
% LOGTEN:  Log_10 function
%    \!logten
 
% PICTURES:  Basic setups for PiCtures; \put commands
%    \accountingoff
%    \accountingon
%    \beginpicture
%    \endpicture    
%    \endpicturesave 
%    \lines
%    \multiput
%    \put 
%    \setcoordinatemode
%    \setcoordinatesystem
%    \setdimensionmode
%    \stack 
%    \Lines
%    \Xdistance
%    \Ydistance
%    \!dimenput
%    \!ifcoordmode
%    \!ifdimenmode
%    \!setcoordmode
%    \!setdimenmode
%    \!setputobject
   
% PLOTTING:  Things to do with plotting
%    \dontsavelinesandcurves
%    \replot
%    \savelinesandcurves 
%    \setplotsymbol
%    \writesavefile 
%    \!plot
 
% PYTHAGORAS:  Euclidean distance function
%    \placehypotenuse 
%    \!Pythag
 
% QUADRATIC ARC:  Draws a quadratic arc
%    \!qjoin 
 
% ROTATIONS:  Handles rotations
%    \startrotation 
%    \stoprotation
%    \!rotateaboutpivot
%    \!rotateonly
 
% SHADING:  Handles shading
%    \setshadegrid 
%    \setshadesymbol
%    \!lshade
%    \!qshade
%    \!starthshade
%    \!startvshade  
 
% TICKS:  Draws ticks on graphs
%    \gridlines
%    \loggedticks
%    \nogridlines
%    \ticksin
%    \ticksout
%    \unloggesticks
 
% ***************** END OF TABLE OF GROUPS OF MACROS ********************


\catcode`!=11 %  ***** THIS MUST NEVER BE OMITTED
% *******************************
% *** HACKS  (Utility macros) ***
% *******************************
 
% ** User commands
% **   \PiC{P\kern-.12em\lower.5ex\hbox{I}\kern-.075emC}
% **   \PiCTeX{\PiC\kern-.11em\TeX}
% **   \placevalueinpts of <DIMENSION REGISTER> in {CONTROL SEQUENCE}
  
% ** Internal commands
% **   \!ifnextchar{CHARACTER}{TRUE ACTION}{FALSE ACTION}
% **   \!tfor NAME := LIST \do {BODY}
% **   \!etfor NAME:= LIST \do {BODY}
% **   \!cfor NAME := LIST \do {BODY}
% **   \!ecfor NAME:= LIST \do {BODY}
% **   \!ifempty{MACRO}{TRUE ACTION}{FALSE ACTION}
% **   \!getnext\\ITEMfrom\LIST
% **   \!getnextvalueof\DIMEN\from\LIST
% **   \!copylist\LISTMACRO_A\to\LISTMACRO_B
% **   \!wlet\CONTROL_SEQUENCE_A=\CONTROL_SEQUENCE_B
% **   \!listaddon ITEM LIST
% **   \!rightappendITEM\withCS\to\LISTMACRO
% **   \!leftappendITEM\withCS\to\LISTMACRO
% **   \!lop\LISTMACRO\to\ITEM
% **   \!loop ... repeat
% **   \!!loop ... repeat
% **   \!removept{DIMENSION REGISTER}{CONTROL SEQUENCE}
% **   \!mlap{...}  
% **   \!vmlap{...}
% **   \!not{TEK if-CONDITION}

% ** First, here are the the PiCTeX logo, and the syllable PiC:
\def\PiC{P\kern-.12em\lower.5ex\hbox{I}\kern-.075emC}
\def\PiCTeX{\PiC\kern-.11em\TeX}

% ** The following macro expands to parameter #2 or parameter #3 according to
% ** whether the next non-blank character following the macro is or is not #1. 
% ** Blanks following the macro are gobbled.
\def\!ifnextchar#1#2#3{%
  \let\!testchar=#1%
  \def\!first{#2}%
  \def\!second{#3}%
  \futurelet\!nextchar\!testnext}
\def\!testnext{%
  \ifx \!nextchar \!spacetoken 
    \let\!next=\!skipspacetestagain
  \else
    \ifx \!nextchar \!testchar
      \let\!next=\!first
    \else 
      \let\!next=\!second 
    \fi 
  \fi
  \!next}
\def\\{\!skipspacetestagain} 
  \expandafter\def\\ {\futurelet\!nextchar\!testnext} 
\def\\{\let\!spacetoken= } \\  %  ** set \spacetoken to a space token
 
 
% ** Borrow the "tfor" macro from Latex:
% **   \!tfor NAME := LIST \do {BODY}
% **   if, before expansion, LIST = T1 ... Tn,  where each  Ti  is a token
% **   or  {...},  then executes  BODY  n  times, with  NAME = Ti  on the
% **   i-th iteration.  Works for  n=0.
\def\!tfor#1:=#2\do#3{%
  \edef\!fortemp{#2}%
  \ifx\!fortemp\!empty 
    \else
    \!tforloop#2\!nil\!nil\!!#1{#3}%
  \fi}
\def\!tforloop#1#2\!!#3#4{%
  \def#3{#1}%
  \ifx #3\!nnil
    \let\!nextwhile=\!fornoop
  \else
    #4\relax
    \let\!nextwhile=\!tforloop
  \fi 
  \!nextwhile#2\!!#3{#4}}
 
 
% **   \!etfor NAME:= LIST\do {BODY}
% **   This is like \!cfor, but LIST is any balanced token list whose complete
% **     expansion has the form  T1 ... Tn
\def\!etfor#1:=#2\do#3{%
  \def\!!tfor{\!tfor#1:=}%
  \edef\!!!tfor{#2}%
  \expandafter\!!tfor\!!!tfor\do{#3}}
 
 
% **   modify the Latex \tfor (token-for) loop to a \cfor (comma-for) loop.
% **   \!cfor NAME := LIST \do {BODY}
% **     if, before expansion, LIST = a1,a2,...an, then executes  BODY n times,
% **     with  NAME = ai  on the i-th iteration.  Works for  n=0.
\def\!cfor#1:=#2\do#3{%
  \edef\!fortemp{#2}%
  \ifx\!fortemp\!empty 
  \else
    \!cforloop#2,\!nil,\!nil\!!#1{#3}%
  \fi}
\def\!cforloop#1,#2\!!#3#4{%
  \def#3{#1}%
  \ifx #3\!nnil
    \let\!nextwhile=\!fornoop 
  \else
    #4\relax
    \let\!nextwhile=\!cforloop
  \fi
  \!nextwhile#2\!!#3{#4}}
 
 
% **   \!ecfor NAME:= LIST\do {BODY}
% **   This is like \!cfor, but LIST is any balanced token list whose complete
% **     expansion has the form  a1,a2,...,an.
\def\!ecfor#1:=#2\do#3{%
  \def\!!cfor{\!cfor#1:=}%
  \edef\!!!cfor{#2}%
  \expandafter\!!cfor\!!!cfor\do{#3}}
 
 
\def\!empty{}
\def\!nnil{\!nil}
\def\!fornoop#1\!!#2#3{}
 
 
% **  \!ifempty{ARG}{TRUE ACTION}{FALSE ACTION}
\def\!ifempty#1#2#3{%
  \edef\!emptyarg{#1}%
  \ifx\!emptyarg\!empty
    #2%
  \else
    #3%
  \fi}
 
% **  \!getnext\\ITEMfrom\LIST
% **    \LIST has the form \\{item1}\\{item2}\\{item3}...\\{itemk}
% **    This routine sets \ITEM to item1, and cycles \LIST to
% **    \\{item2}\\{item3}...\\{itemk}\\{item1}
\def\!getnext#1\from#2{%
  \expandafter\!gnext#2\!#1#2}%
\def\!gnext\\#1#2\!#3#4{%
  \def#3{#1}%
  \def#4{#2\\{#1}}%
  \ignorespaces}
 
 
% ** \!getnextvalueof\DIMEN\from\LIST
% **   Similar to !getnext.  
% **   \LIST has the form \\{dimen1}\\{dimen2}\\{dimen3} ... 
% **   \DIMEN is a dimension register
% **   Works also for counts
%
\def\!getnextvalueof#1\from#2{%
  \expandafter\!gnextv#2\!#1#2}%
\def\!gnextv\\#1#2\!#3#4{%
  #3=#1%
  \def#4{#2\\{#1}}%
  \ignorespaces}
 
 
% ** \!copylist\LISTMACROA\to\LISTMACROB
% **   makes the replacement text of LISTMACRO B identical to that of
% **   list macro A.
\def\!copylist#1\to#2{%
  \expandafter\!!copylist#1\!#2}
\def\!!copylist#1\!#2{%
  \def#2{#1}\ignorespaces}
 
 
% **  \!wlet\CSA=\CSB
% **  lets control sequence \CSB = control sequence \CSA, and writes a
% **    message to that effect in the log file using plain TEK's \wlog
\def\!wlet#1=#2{%
  \let#1=#2 
  \wlog{\string#1=\string#2}}
 
% ** \!listaddon ITEM LIST
% ** LIST <-- LIST \\ ITEM
\def\!listaddon#1#2{%
  \expandafter\!!listaddon#2\!{#1}#2}
\def\!!listaddon#1\!#2#3{%
  \def#3{#1\\#2}}
 
% **  \!rightappendITEM\to\LISTMACRO
% **    \LISTMACRO --> \LISTMACRO\\{ITEM}
%\def\!rightappend#1\to#2{\expandafter\!!rightappend#2\!{#1}#2}
%\def\!!rightappend#1\!#2#3{\def#3{#1\\{#2}}}
 
 
% **  \!rightappendITEM\withCS\to\LISTMACRO
% **    \LISTMACRO --> \LISTMACRO||CS||{ITEM}
\def\!rightappend#1\withCS#2\to#3{\expandafter\!!rightappend#3\!#2{#1}#3}
\def\!!rightappend#1\!#2#3#4{\def#4{#1#2{#3}}}
 
 
% **  \!leftappendITEM\withCS\to\LISTMACRO
% **    \LISTMACRO --> CS||{ITEM}||\LISTMACRO
\def\!leftappend#1\withCS#2\to#3{\expandafter\!!leftappend#3\!#2{#1}#3}
\def\!!leftappend#1\!#2#3#4{\def#4{#2{#3}#1}}
 
 
% **  \!lop\LISTMACRO\to\ITEM
% **    \\{item1}\\{item2}\\{item3} ... --> \\{item2}\\{item3} ...
% **    item1 --> \ITEM
\def\!lop#1\to#2{\expandafter\!!lop#1\!#1#2}
\def\!!lop\\#1#2\!#3#4{\def#4{#1}\def#3{#2}}
 
 
% **  \!placeNUMBER\of\LISTMACRO\in\ITEM
% **    the NUMBERth item of \LISTMACRO --> replacement text of \ITEM
%\def\!place#1\of#2\in#3{\def#3{\outofrange}%
%{\count0=#1\def\\##1{\advance\count0-1 \ifnum\count0=0 \gdef#3{##1}\fi}#2}}
 
 
% **  Following code converts a commalist to a list macro, with all items 
% **    fully expanded.
%\!ecfor\item:=\commalist\do{\expandafter\!rightappend\item\to\list}
 
 
% ** \!loop ... repeat
% ** This is exactly like TEX's \loop ... repeat.  It can be used in nesting
% ** two loops, without puting the inner one inside a group.
\def\!loop#1\repeat{\def\!body{#1}\!iterate}
\def\!iterate{\!body\let\!next=\!iterate\else\let\!next=\relax\fi\!next}
 
% ** \!!loop ... repeat
% ** This is exactly like TEX's \loop ... repeat.  It can be used in nesting
% ** two loops, without puting the inner one inside a group.
\def\!!loop#1\repeat{\def\!!body{#1}\!!iterate}
\def\!!iterate{\!!body\let\!!next=\!!iterate\else\let\!!next=\relax\fi\!!next}
%  (\multiput uses \!!loop)
 
% ** \!removept{DIMENREG}{\CS}
% ** Defines the control sequence CS to be the value (in points) in the
% ** dimension register DIMENREG (but without the "pt" TEK usually adds)
% ** E.g., after  \dimen0=12.3pt \!removept\dimen0\A, \A expands to 12.3
\def\!removept#1#2{\edef#2{\expandafter\!!removePT\the#1}}
{\catcode`p=12 \catcode`t=12 \gdef\!!removePT#1pt{#1}}

% ** \pladevalueinpts of <DIMENSION REGISTER> in {CONTROL SEQUENCE}
\def\placevalueinpts of <#1> in #2 {%
  \!removept{#1}{#2}}
 
% ** \!mlap{...}  \!vmlap{...}
% ** Center  ...  in a box of width 0.
\def\!mlap#1{\hbox to 0pt{\hss#1\hss}}
\def\!vmlap#1{\vbox to 0pt{\vss#1\vss}}
 
% ** \!not{TEK if-CONDITION}
% ** By a TEK if-CONDITION is meant something like 
% **     \ifnum\N<0,   or   \ifdim\A>\B
% ** \!not produces an if-condition which is false if the original condition
% ** is true, and true if the original condition is false.
\def\!not#1{%
  #1\relax
    \!switchfalse
  \else
    \!switchtrue
  \fi
  \if!switch
  \ignorespaces}
 

% *******************
% *** ALLOCATIONS ***
% *******************

% This section allocates all the registers PiCTeX uses. Following
% each allocation is a string of the form  ....N.D...L......... ;
% the various letters show which sections of PiCTeX make explicit
% reference to that register, according to the following code:
 
%   H Hacks
%   A Areas
%   W arroWs
%   B Bars
%   X boXes
%   C Curves
%   D Dashpattterns
%   V diVision
%   E Ellipses
%   U rUles
%   L Linear arc
%   G loGten
%   P Pictures
%   O plOtting
%   Y pYthagoras
%   Q Quadratic arc
%   R Rotations
%   S Shading
%   T Ticks

% Turn off messages from TeX's allocation macros
\let\!!!wlog=\wlog              % "\wlog" is defined in plain TeX
\def\wlog#1{}    

\newdimen\headingtoplotskip     %.A.................
\newdimen\linethickness         %.A..X....U........T
\newdimen\longticklength        %.A................T
\newdimen\plotsymbolspacing     %......D...L....Q...
\newdimen\shortticklength       %.A................T
\newdimen\stackleading          %.A..........P......
\newdimen\tickstovaluesleading  %.A................T
\newdimen\totalarclength        %......D...L....Q...
\newdimen\valuestolabelleading  %.A.................

\newbox\!boxA                   %.AW...............T
\newbox\!boxB                   %..W................
\newbox\!picbox                 %............P......
\newbox\!plotsymbol             %..........L..O.....
\newbox\!putobject              %............PO...S.
\newbox\!shadesymbol            %.................S.

\newcount\!countA               %.A....D..UL....Q.ST
\newcount\!countB               %......D..U.....Q.ST
\newcount\!countC               %...............Q..T
\newcount\!countD               %...................
\newcount\!countE               %.............O....T
\newcount\!countF               %.............O....T
\newcount\!countG               %..................T
\newcount\!fiftypt              %.........U.........
\newcount\!intervalno           %..........L....Q...
\newcount\!npoints              %..........L........
\newcount\!nsegments            %.........U.........
\newcount\!ntemp                %............P......
\newcount\!parity               %.................S.
\newcount\!scalefactor          %..................T
\newcount\!tfs                  %.......V...........
\newcount\!tickcase             %..................T

\newdimen\!Xleft                %............P......
\newdimen\!Xright               %............P......
\newdimen\!Xsave                %.A................T
\newdimen\!Ybot                 %............P......
\newdimen\!Ysave                %.A................T
\newdimen\!Ytop                 %............P......
\newdimen\!angle                %........E..........
\newdimen\!arclength            %..W......UL....Q...
\newdimen\!areabloc             %.A........L........
\newdimen\!arealloc             %.A........L........
\newdimen\!arearloc             %.A........L........
\newdimen\!areatloc             %.A........L........
\newdimen\!bshrinkage           %.................S.
\newdimen\!checkbot             %..........L........
\newdimen\!checkleft            %..........L........
\newdimen\!checkright           %..........L........
\newdimen\!checktop             %..........L........
\newdimen\!dimenA               %.AW.X.DVEUL..OYQRST
\newdimen\!dimenB               %....X.DVEU...O.QRS.
\newdimen\!dimenC               %..W.X.DVEU......RS.
\newdimen\!dimenD               %..W.X.DVEU....Y.RS.
\newdimen\!dimenE               %..W........G..YQ.S.
\newdimen\!dimenF               %...........G..YQ.S.
\newdimen\!dimenG               %...........G..YQ.S.
\newdimen\!dimenH               %...........G..Y..S.
\newdimen\!dimenI               %...BX.........Y....
\newdimen\!distacross           %..........L....Q...
\newdimen\!downlength           %..........L........
\newdimen\!dp                   %.A..X.......P....S.
\newdimen\!dshade               %.................S.
\newdimen\!dxpos                %..W......U..P....S.
\newdimen\!dxprime              %...............Q...
\newdimen\!dypos                %..WB.....U..P......
\newdimen\!dyprime              %...............Q...
\newdimen\!ht                   %.A..X.......P....S.
\newdimen\!leaderlength         %......D..U.........
\newdimen\!lshrinkage           %.................S.
\newdimen\!midarclength         %...............Q...
\newdimen\!offset               %.A................T
\newdimen\!plotheadingoffset    %.A.................
\newdimen\!plotsymbolxshift     %..........L..O.....
\newdimen\!plotsymbolyshift     %..........L..O.....
\newdimen\!plotxorigin          %..........L..O.....
\newdimen\!plotyorigin          %..........L..O.....
\newdimen\!rootten              %...........G.......
\newdimen\!rshrinkage           %.................S.
\newdimen\!shadesymbolxshift    %.................S.
\newdimen\!shadesymbolyshift    %.................S.
\newdimen\!tenAa                %...........G.......
\newdimen\!tenAc                %...........G.......
\newdimen\!tenAe                %...........G.......
\newdimen\!tshrinkage           %.................S.
\newdimen\!uplength             %..........L........
\newdimen\!wd                   %....X.......P....S.
\newdimen\!wmax                 %...............Q...
\newdimen\!wmin                 %...............Q...
\newdimen\!xB                   %...............Q...
\newdimen\!xC                   %...............Q...
\newdimen\!xE                   %..W.....E.L....Q.S.
\newdimen\!xM                   %..W.....E......Q.S.
\newdimen\!xS                   %..W.....E.L....Q.S.
\newdimen\!xaxislength          %.A................T
\newdimen\!xdiff                %..........L........
\newdimen\!xleft                %............P......
\newdimen\!xloc                 %..WB.....U.......S.
\newdimen\!xorigin              %.A........L.P....S.
\newdimen\!xpivot               %................R..
\newdimen\!xpos                 %..........L.P..Q.ST
\newdimen\!xprime               %...............Q...
\newdimen\!xright               %............P......
\newdimen\!xshade               %.................S.
\newdimen\!xshift               %..W.........PO...S.
\newdimen\!xtemp                %............P......
\newdimen\!xunit                %.AWBX...EUL.P..QRS.
\newdimen\!xxE                  %........E..........
\newdimen\!xxM                  %........E..........
\newdimen\!xxS                  %........E..........
\newdimen\!xxloc                %..WB....EU.........
\newdimen\!yB                   %...............Q...
\newdimen\!yC                   %...............Q...
\newdimen\!yE                   %..W.....E.L....Q...
\newdimen\!yM                   %..W.....E......Q...
\newdimen\!yS                   %..W.....E.L....Q...
\newdimen\!yaxislength          %.A................T
\newdimen\!ybot                 %............P......
\newdimen\!ydiff                %..........L........
\newdimen\!yloc                 %..WB.....U.......S.
\newdimen\!yorigin              %.A........L.P....S.
\newdimen\!ypivot               %................R..
\newdimen\!ypos                 %..........L.P..Q.ST
\newdimen\!yprime               %...............Q...
\newdimen\!yshade               %.................S.
\newdimen\!yshift               %..W.........PO...S.
\newdimen\!ytemp                %............P......
\newdimen\!ytop                 %............P......
\newdimen\!yunit                %.AWBX...EUL.P..QRS.
\newdimen\!yyE                  %........E..........
\newdimen\!yyM                  %........E..........
\newdimen\!yyS                  %........E..........
\newdimen\!yyloc                %..WB....EU.........
\newdimen\!zpt                  %.AWBX.DVEULGP.YQ.ST

\newif\if!axisvisible           %.A.................
\newif\if!gridlinestoo          %..................T
\newif\if!keepPO                %...................
\newif\if!placeaxislabel        %.A.................
\newif\if!switch                %H..................
\newif\if!xswitch               %.A................T

\newtoks\!axisLaBeL             %.A.................
\newtoks\!keywordtoks           %.A.................

\newwrite\!replotfile           %.............O.....

\newhelp\!keywordhelp{The keyword mentioned in the error message in unknown. 
Replace NEW KEYWORD in the indicated response by the keyword that 
should have been specified.}    %.A.................

% The following commands assign alternate names to some of the 
% above registers.  "\!wlet"  is defined in  Hacks.
\!wlet\!!origin=\!xM                   %.A................T
\!wlet\!!unit=\!uplength               %.A................T
\!wlet\!Lresiduallength=\!dimenG       %.........U.........
\!wlet\!Rresiduallength=\!dimenF       %.........U.........
\!wlet\!axisLength=\!distacross        %.A................T
\!wlet\!axisend=\!ydiff                %.A................T
\!wlet\!axisstart=\!xdiff              %.A................T
\!wlet\!axisxlevel=\!arclength         %.A................T
\!wlet\!axisylevel=\!downlength        %.A................T
\!wlet\!beta=\!dimenE                  %...............Q...
\!wlet\!gamma=\!dimenF                 %...............Q...
\!wlet\!shadexorigin=\!plotxorigin     %.................S.
\!wlet\!shadeyorigin=\!plotyorigin     %.................S.
\!wlet\!ticklength=\!xS                %..................T
\!wlet\!ticklocation=\!xE              %..................T
\!wlet\!ticklocationincr=\!yE          %..................T
\!wlet\!tickwidth=\!yS                 %..................T
\!wlet\!totalleaderlength=\!dimenE     %.........U.........
\!wlet\!xone=\!xprime                  %....X..............
\!wlet\!xtwo=\!dxprime                 %....X..............
\!wlet\!ySsave=\!yM                    %...................
\!wlet\!ybB=\!yB                       %.................S.
\!wlet\!ybC=\!yC                       %.................S.
\!wlet\!ybE=\!yE                       %.................S.
\!wlet\!ybM=\!yM                       %.................S.
\!wlet\!ybS=\!yS                       %.................S.
\!wlet\!ybpos=\!yyloc                  %.................S.
\!wlet\!yone=\!yprime                  %....X..............
\!wlet\!ytB=\!xB                       %.................S.
\!wlet\!ytC=\!xC                       %.................S.
\!wlet\!ytE=\!downlength               %.................S.
\!wlet\!ytM=\!arclength                %.................S.
\!wlet\!ytS=\!distacross               %.................S.
\!wlet\!ytpos=\!xxloc                  %.................S.
\!wlet\!ytwo=\!dyprime                 %....X..............


% Initial values for registers
\!zpt=0pt                              % static
\!xunit=1pt
\!yunit=1pt
\!arearloc=\!xunit
\!areatloc=\!yunit
\!dshade=5pt
\!leaderlength=24in
\!tfs=256                              % static
\!wmax=5.3pt                           % static
\!wmin=2.7pt                           % static
\!xaxislength=\!xunit
\!xpivot=\!zpt
\!yaxislength=\!yunit 
\!ypivot=\!zpt
\plotsymbolspacing=.4pt
  \!dimenA=50pt \!fiftypt=\!dimenA     % static

\!rootten=3.162278pt                   % static
\!tenAa=8.690286pt                     % static  (A5)
\!tenAc=2.773839pt                     % static  (A3)
\!tenAe=2.543275pt                     % static  (A1)

% Initial values for control sequences
\def\!cosrotationangle{1}      %................R..
\def\!sinrotationangle{0}      %................R..
\def\!xpivotcoord{0}           %................R..
\def\!xref{0}                  %............P......
\def\!xshadesave{0}            %.................S.
\def\!ypivotcoord{0}           %................R..
\def\!yref{0}                  %............P......
\def\!yshadesave{0}            %.................S.
\def\!zero{0}                  %..................T

% Reset TeX to report allocations
\let\wlog=\!!!wlog
%  *************************************
%  ***  AREAS: Deals with plot areas ***
%  *************************************
%
%  ** User commands
%  **   \setplotarea x from LEFT XCOORD to RIGTH XCOORD, y from BOTTOM YCOORD
%  **      to TOP YCOORD
%  **   \axis BOTTOM-LEFT-TOP-RIGHT  [SHIFTEDTO xy=COORD] [VISIBLE-INVISIBLE]
%  **      [LABEL {label}] [TICKS] /
%  **   \visibleaxes
%  **   \invisibleaxes
%  **   \plotheading {HEADING}
%  **   \grid {# of columns} {# of rows}
%  **   \normalgraphs 
  
%  **  \normalgraphs
%  **    Sets defaults for graph setup. See Subsection 3.4 of manual.
\def\normalgraphs{%
  \longticklength=.4\baselineskip
  \shortticklength=.25\baselineskip
  \tickstovaluesleading=.25\baselineskip
  \valuestolabelleading=.8\baselineskip
  \linethickness=.4pt
  \stackleading=.17\baselineskip
  \headingtoplotskip=1.5\baselineskip
  \visibleaxes
  \ticksout
  \nogridlines
  \unloggedticks}
%
% **  \setplotarea x from LEFT XCOORD to RIGTH XCOORD, y from BOTTOM YCOORD
% **    to TOP YCOORD
% **  Reserves space in PICBOX for a rectangular box with the indicated
% **   coordinates.  Must be specified before calls to  \axis, 
% **   \grid, \plotheading.
% **  See Subsection 3.1 of the manual.
\def\setplotarea x from #1 to #2, y from #3 to #4 {%
  \!arealloc=\!M{#1}\!xunit \advance \!arealloc -\!xorigin
  \!areabloc=\!M{#3}\!yunit \advance \!areabloc -\!yorigin
  \!arearloc=\!M{#2}\!xunit \advance \!arearloc -\!xorigin
  \!areatloc=\!M{#4}\!yunit \advance \!areatloc -\!yorigin
  \!initinboundscheck
  \!xaxislength=\!arearloc  \advance\!xaxislength -\!arealloc
  \!yaxislength=\!areatloc  \advance\!yaxislength -\!areabloc
  \!plotheadingoffset=\!zpt
  \!dimenput {{\setbox0=\hbox{}\wd0=\!xaxislength\ht0=\!yaxislength\box0}}
     [bl] (\!arealloc,\!areabloc)}
%
% ** \visibleaxes, \invisibleaxes 
% ** Switches for setting visibility of subsequent axes.
% ** See Subsection 3.2 of the manual.
\def\visibleaxes{%
  \def\!axisvisibility{\!axisvisibletrue}}
\def\invisibleaxes{%
  \def\!axisvisibility{\!axisvisiblefalse}}
%
% ** The next few macros enable the user to fix up an erroneous keyword
% **   in the \axis command.
%  \newhelp is in ALLOCATIONS
%  \newhelp\!keywordhelp{The keyword mentioned in the error message in unknown. 
%  Replace NEW KEYWORD in the indicated response by the keyword that 
%  should have been specified.}

\def\!fixkeyword#1{%
  \errhelp=\!keywordhelp
  \errmessage{Unrecognized keyword `#1': \the\!keywordtoks{NEW KEYWORD}'}}

%  \newtoks\!keywordtoks    In ALLOCATIONS.
\!keywordtoks={enter `i\fixkeyword}

\def\fixkeyword#1{%
  \!nextkeyword#1 }

% ** \axis BOTTOM-LEFT-TOP-RIGHT  [SHIFTEDTO xy=COORD] [VISIBLE-INVISIBLE]
% **   [LABEL {label}] [TICKS] /
% ** Exactly one of the keywords BOTTOM, LEFT, TOP, RIGHT must be
% ** specified. Axis is drawn along the indicated edge of the current
% ** plot area, shifted if the SHIFTEDTO option is used, visible or
% ** invisible according the selected option, with an optional LABEL,
% ** and optional TICKS (see ticks.tex for the options avialabel with
% ** TICKS). The TICKS option must be the last one specified. The \axis
% ** MUST be terminated with a / followed by a space.
% ** See Subsection 3.2 of the manual for more information.

% ** The various options of the \axis command are processed by the
% ** \!nextkeyword macro defined below. For example, 
% ** `\!nextkeyword shiftedto ' expands to `\!axisshiftedto'.
\def\axis {%
  \def\!nextkeyword##1 {%
    \expandafter\ifx\csname !axis##1\endcsname \relax
      \def\!next{\!fixkeyword{##1}}%
    \else
      \def\!next{\csname !axis##1\endcsname}%
    \fi
    \!next}%
  \!offset=\!zpt
  \!axisvisibility
  \!placeaxislabelfalse
  \!nextkeyword}

% ** This and the various macros that follow handle the keyword
% ** specifications on the \axis command
% ** See Subsection 3.2 of the manual.
\def\!axisbottom{%
  \!axisylevel=\!areabloc
  \def\!tickxsign{0}%
  \def\!tickysign{-}%
  \def\!axissetup{\!axisxsetup}%
  \def\!axislabeltbrl{t}%
  \!nextkeyword}

\def\!axistop{%
  \!axisylevel=\!areatloc
  \def\!tickxsign{0}%
  \def\!tickysign{+}%
  \def\!axissetup{\!axisxsetup}%
  \def\!axislabeltbrl{b}%
  \!nextkeyword}

\def\!axisleft{%
  \!axisxlevel=\!arealloc
  \def\!tickxsign{-}%
  \def\!tickysign{0}%
  \def\!axissetup{\!axisysetup}%
  \def\!axislabeltbrl{r}%
  \!nextkeyword}

\def\!axisright{%
  \!axisxlevel=\!arearloc
  \def\!tickxsign{+}%
  \def\!tickysign{0}%
  \def\!axissetup{\!axisysetup}%
  \def\!axislabeltbrl{l}%
  \!nextkeyword}

\def\!axisshiftedto#1=#2 {%
  \if 0\!tickxsign
    \!axisylevel=\!M{#2}\!yunit
    \advance\!axisylevel -\!yorigin
  \else
    \!axisxlevel=\!M{#2}\!xunit
    \advance\!axisxlevel -\!xorigin
  \fi
  \!nextkeyword}

\def\!axisvisible{%
  \!axisvisibletrue  
  \!nextkeyword}

\def\!axisinvisible{%
  \!axisvisiblefalse
  \!nextkeyword}

\def\!axislabel#1 {%
  \!axisLaBeL={#1}%
  \!placeaxislabeltrue
  \!nextkeyword}

\expandafter\def\csname !axis/\endcsname{%
  \!axissetup % This could done already by "ticks"; if so, now \relax
  \if!placeaxislabel
    \!placeaxislabel
  \fi
  \if +\!tickysign %                 ** (A "top" axis)
    \!dimenA=\!axisylevel
    \advance\!dimenA \!offset %      ** dimA = top of the axis structure
    \advance\!dimenA -\!areatloc %   ** dimA = excess over the plot area
    \ifdim \!dimenA>\!plotheadingoffset
      \!plotheadingoffset=\!dimenA % ** Greatest excess over the plot area
    \fi
  \fi}

% ** \grid {c} {r} 
% ** Partitions the plot area into c columns and r rows; see Subsection 3.3
% ** of the manual.
% ** (Other grid patterns can be drawn with the TICKS option of the \axis 
% ** command.
\def\grid #1 #2 {%
  \!countA=#1\advance\!countA 1
  \axis bottom invisible ticks length <\!zpt> andacross quantity {\!countA} /
  \!countA=#2\advance\!countA 1
  \axis left   invisible ticks length <\!zpt> andacross quantity {\!countA} / }

% ** \plotheading{HEADING}
% ** Places HEADING centered above the top of the plotarea (and above
% ** any top axis ticks marks, tick labels, and axis label); see
% ** Subsection 3.3 of the manual.
\def\plotheading#1 {%
  \advance\!plotheadingoffset \headingtoplotskip
  \!dimenput {#1} [B] <.5\!xaxislength,\!plotheadingoffset>
    (\!arealloc,\!areatloc)}

% ** From here on, the routines are internal.
\def\!axisxsetup{%
  \!axisxlevel=\!arealloc
  \!axisstart=\!arealloc
  \!axisend=\!arearloc
  \!axisLength=\!xaxislength
  \!!origin=\!xorigin
  \!!unit=\!xunit
  \!xswitchtrue
  \if!axisvisible 
    \!makeaxis
  \fi}

\def\!axisysetup{%
  \!axisylevel=\!areabloc
  \!axisstart=\!areabloc
  \!axisend=\!areatloc
  \!axisLength=\!yaxislength
  \!!origin=\!yorigin
  \!!unit=\!yunit
  \!xswitchfalse
  \if!axisvisible
    \!makeaxis
  \fi}

\def\!makeaxis{%
  \setbox\!boxA=\hbox{% (Make a pseudo-y[x] tick for an x[y]-axis)
    \beginpicture
      \!setdimenmode
      \setcoordinatesystem point at {\!zpt} {\!zpt}   
      \putrule from {\!zpt} {\!zpt} to
        {\!tickysign\!tickysign\!axisLength} 
        {\!tickxsign\!tickxsign\!axisLength}
    \endpicturesave <\!Xsave,\!Ysave>}%
    \wd\!boxA=\!zpt
    \!placetick\!axisstart}

\def\!placeaxislabel{%
  \advance\!offset \valuestolabelleading
  \if!xswitch
    \!dimenput {\the\!axisLaBeL} [\!axislabeltbrl]
      <.5\!axisLength,\!tickysign\!offset> (\!axisxlevel,\!axisylevel)
    \advance\!offset \!dp  % ** advance offset by the "tallness"
    \advance\!offset \!ht  % ** of the label
  \else
    \!dimenput {\the\!axisLaBeL} [\!axislabeltbrl]
      <\!tickxsign\!offset,.5\!axisLength> (\!axisxlevel,\!axisylevel)
  \fi
  \!axisLaBeL={}}


% *******************************
% *** ARROWS  (Draws arrows)  ***
% *******************************
%
% ** User commands
% **  \arrow <ARROW HEAD LENGTH> [MID FRACTION, BASE FRACTION]
% **    [<XSHIFT,YSHIFT>] from XFROM YFROM to XTO YTO
% **  \betweenarrows {TEXT} [orientation & shift] from XFROM YFROM to XTO YTO

% ** \arrow <ARROW HEAD LENGTH> [MID FRACTION, BASE FRACTION]
% **    [<XSHIFT,YSHIFT>] from XFROM YFROM to XTO YTO
% ** Draws an arrow from (XFROM,YFROM) to (XTO,YTO).  The arrow head
% ** is constructed two quadratic arcs, which extend back a distance
% ** ARROW HEAD LENGTH (a dimension) on both sides of the arrow shaft.
% ** All the way back the arcs are a distance BASE FRACTION*ARROW HEAD
% ** LENGTH apart, while half-way back they are a distance MID FRACTION*
% ** ARROW HEAD LENGTH apart. <XSHIFT,YSHIFT> is optional, and has
% ** its usual interpreation. See Subsection 5.4 of the manual.

\def\arrow <#1> [#2,#3]{%
  \!ifnextchar<{\!arrow{#1}{#2}{#3}}{\!arrow{#1}{#2}{#3}<\!zpt,\!zpt> }}

\def\!arrow#1#2#3<#4,#5> from #6 #7 to #8 #9 {%
%
% ** convert to dimensions
  \!xloc=\!M{#8}\!xunit   
  \!yloc=\!M{#9}\!yunit
  \!dxpos=\!xloc  \!dimenA=\!M{#6}\!xunit  \advance \!dxpos -\!dimenA
  \!dypos=\!yloc  \!dimenA=\!M{#7}\!yunit  \advance \!dypos -\!dimenA
  \let\!MAH=\!M%                         ** save current c/d mode
  \!setdimenmode%                        ** go into dimension mode
%
  \!xshift=#4\relax  \!yshift=#5\relax%  ** pick up shift
  \!reverserotateonly\!xshift\!yshift%   ** back rotate shift
  \advance\!xshift\!xloc  \advance\!yshift\!yloc
%
% **  draw shaft of arrow
  \!xS=-\!dxpos  \advance\!xS\!xshift
  \!yS=-\!dypos  \advance\!yS\!yshift
  \!start (\!xS,\!yS)
  \!ljoin (\!xshift,\!yshift)
%
% ** find 32*cosine and 32*sine of angle of rotation
  \!Pythag\!dxpos\!dypos\!arclength
  \!divide\!dxpos\!arclength\!dxpos  
  \!dxpos=32\!dxpos  \!removept\!dxpos\!!cos
  \!divide\!dypos\!arclength\!dypos  
  \!dypos=32\!dypos  \!removept\!dypos\!!sin
% 
% ** construct arrowhead
  \!halfhead{#1}{#2}{#3}%                ** draw half of arrow head
  \!halfhead{#1}{-#2}{-#3}%              ** draw other half
%
  \let\!M=\!MAH%                         ** restore old c/d mode
  \ignorespaces}
%
% ** draw half of arrow head
  \def\!halfhead#1#2#3{%
    \!dimenC=-#1%                
    \divide \!dimenC 2 %                 ** half way back
    \!dimenD=#2\!dimenC%                 ** half the mid width
    \!rotate(\!dimenC,\!dimenD)by(\!!cos,\!!sin)to(\!xM,\!yM)
    \!dimenC=-#1%                        ** all the way back
    \!dimenD=#3\!dimenC
    \!dimenD=.5\!dimenD%                 ** half the full width
    \!rotate(\!dimenC,\!dimenD)by(\!!cos,\!!sin)to(\!xE,\!yE)
    \!start (\!xshift,\!yshift)
    \advance\!xM\!xshift  \advance\!yM\!yshift
    \advance\!xE\!xshift  \advance\!yE\!yshift
    \!qjoin (\!xM,\!yM) (\!xE,\!yE) 
    \ignorespaces}


% ** \betweenarrows {TEXT} [orientation & shift] from XFROM YFROM to XTO YTO
% **   Makes things like <--- text --->, using arrow heads from TeX's fonts.
% **   See Subsection 5.4 of the manual.
\def\betweenarrows #1#2 from #3 #4 to #5 #6 {%
  \!xloc=\!M{#3}\!xunit  \!xxloc=\!M{#5}\!xunit%   
  \!yloc=\!M{#4}\!yunit  \!yyloc=\!M{#6}\!yunit%           
  \!dxpos=\!xxloc  \advance\!dxpos by -\!xloc
  \!dypos=\!yyloc  \advance\!dypos by -\!yloc
  \advance\!xloc .5\!dxpos
  \advance\!yloc .5\!dypos
%
  \let\!MBA=\!M%           ** save current coord\dimen mode
  \!setdimenmode%          ** express locations in dimens
  \ifdim\!dypos=\!zpt
    \ifdim\!dxpos<\!zpt \!dxpos=-\!dxpos \fi
    \put {\!lrarrows{\!dxpos}{#1}}#2{} at {\!xloc} {\!yloc}
  \else
    \ifdim\!dxpos=\!zpt
      \ifdim\!dypos<\!zpt \!dypos=-\!dypos \fi
      \put {\!udarrows{\!dypos}{#1}}#2{} at {\!xloc} {\!yloc}
    \fi
  \fi
  \let\!M=\!MBA%           ** restore previous c/d mode
  \ignorespaces}

% ** Subroutine for left-right between arrows 
\def\!lrarrows#1#2{% #1=width, #2=text
  {\setbox\!boxA=\hbox{$\mkern-2mu\mathord-\mkern-2mu$}%
   \setbox\!boxB=\hbox{$\leftarrow$}\!dimenE=\ht\!boxB
   \setbox\!boxB=\hbox{}\ht\!boxB=2\!dimenE
   \hbox to #1{$\mathord\leftarrow\mkern-6mu
     \cleaders\copy\!boxA\hfil
     \mkern-6mu\mathord-$%
     \kern.4em $\vcenter{\box\!boxB}$$\vcenter{\hbox{#2}}$\kern.4em
     $\mathord-\mkern-6mu
     \cleaders\copy\!boxA\hfil
     \mkern-6mu\mathord\rightarrow$}}}

% ** Subroutine for up-down between arrows 
\def\!udarrows#1#2{% #1=width, #2=text
  {\setbox\!boxB=\hbox{#2}%
   \setbox\!boxA=\hbox to \wd\!boxB{\hss$\vert$\hss}%
   \!dimenE=\ht\!boxA \advance\!dimenE \dp\!boxA \divide\!dimenE 2
   \vbox to #1{\offinterlineskip
      \vskip .05556\!dimenE
      \hbox to \wd\!boxB{\hss$\mkern.4mu\uparrow$\hss}\vskip-\!dimenE
      \cleaders\copy\!boxA\vfil
      \vskip-\!dimenE\copy\!boxA
      \vskip\!dimenE\copy\!boxB\vskip.4em
      \copy\!boxA\vskip-\!dimenE
      \cleaders\copy\!boxA\vfil
      \vskip-\!dimenE \hbox to \wd\!boxB{\hss$\mkern.4mu\downarrow$\hss}
      \vskip .05556\!dimenE}}}


% ***************************
% *** BARS  (Draws bars)  ***
% ***************************
%
% ** User commands:
% ** \putbar [<XSHIFT,YSHIFT>] breadth <BREADTH> from XSTART YSTART
% **   to XEND YEND
% ** \setbars [<XSHIFT,YSHIFT>] breadth <BREADTH> baseline at XY = COORD
% **   [baselabels ([B_ORIENTATION_x,B_ORIENTATION_y] <B_XSHIFT,B_YSHIFT>)]
% **   [endlabels  ([E_ORIENTATION_x,E_ORIENTATION_y] <E_XSHIFT,E_YSHIFT>)]


% ** \putbar [<XSHIFT,YSHIFT>] breadth <BREADTH> from XSTART YSTART
% **   to XEND YEND
% ** Either XSTART=XEND or YSTART=YEND. Draws a rectangle between
% **   (XSTART,YSTART) & (XEND,YEND). The "depth" of the rectangle
% **   is determined by those two plot positions; its other
% **   dimension "breadth" is specified by the dimension BREADTH.
% ** See Subsection 4.2 of the manual.
\def\putbar#1breadth <#2> from #3 #4 to #5 #6 {%
  \!xloc=\!M{#3}\!xunit  \!xxloc=\!M{#5}\!xunit%   
  \!yloc=\!M{#4}\!yunit  \!yyloc=\!M{#6}\!yunit%           
  \!dypos=\!yyloc  \advance\!dypos by -\!yloc
  \!dimenI=#2  
% 
  \ifdim \!dimenI=\!zpt %            ** If 0 breadth
    \putrule#1from {#3} {#4} to {#5} {#6} % ** Then draw line
  \else %                            ** Else, put in a rectangle
    \let\!MBar=\!M%                  ** save current c/d mode
    \!setdimenmode %                 ** go into dimension mode
    \divide\!dimenI 2
    \ifdim \!dypos=\!zpt             
      \advance \!yloc -\!dimenI %    ** Equal y coordinates
      \advance \!yyloc \!dimenI
    \else
      \advance \!xloc -\!dimenI %    ** Equal x coordinates
      \advance \!xxloc \!dimenI
    \fi
    \putrectangle#1corners at {\!xloc} {\!yloc} and {\!xxloc} {\!yyloc}
    \let\!M=\!MBar %                 ** restore c/d mode
  \fi
  \ignorespaces}


% ** \setbars [<XSHIFT,YSHIFT>] breadth <BREADTH> baseline at XY = COORD
% **   [baselabels ([B_ORIENTATION_x,B_ORIENTATION_y] <B_XSHIFT,B_YSHIFT>)]
% **   [endlabels  ([E_ORIENTATION_x,E_ORIENTATION_y] <E_XSHIFT,E_YSHIFT>)]
% ** This command puts PiCTeX into the bar graph drawing mode described
% **   in Subsection 4.4 of the manual.
\def\setbars#1breadth <#2> baseline at #3 = #4 {%
  \edef\!barshift{#1}%
  \edef\!barbreadth{#2}%
  \edef\!barorientation{#3}%
  \edef\!barbaseline{#4}%
  \def\!bardobaselabel{\!bardoendlabel}%
  \def\!bardoendlabel{\!barfinish}%
  \let\!drawcurve=\!barcurve
  \!setbars}
\def\!setbars{%
  \futurelet\!nextchar\!!setbars}
\def\!!setbars{%
  \if b\!nextchar
    \def\!!!setbars{\!setbarsbget}%
  \else 
    \if e\!nextchar
      \def\!!!setbars{\!setbarseget}%
    \else
      \def\!!!setbars{\relax}%
    \fi
  \fi
  \!!!setbars}
\def\!setbarsbget baselabels (#1) {%
  \def\!barbaselabelorientation{#1}%
  \def\!bardobaselabel{\!!bardobaselabel}%
  \!setbars}
\def\!setbarseget endlabels (#1) {%
  \edef\!barendlabelorientation{#1}%
  \def\!bardoendlabel{\!!bardoendlabel}%
  \!setbars}

% ** \!barcurve
% ** Draws a bargraph with preset values of barshift, barbreadth,
% ** barorientation (x or y) and barbaseline (coordinate)
\def\!barcurve #1 #2 {%
  \if y\!barorientation
    \def\!basexarg{#1}%
    \def\!baseyarg{\!barbaseline}%
  \else
    \def\!basexarg{\!barbaseline}%
    \def\!baseyarg{#2}%
  \fi
  \expandafter\putbar\!barshift breadth <\!barbreadth> from {\!basexarg}
    {\!baseyarg} to {#1} {#2}
  \def\!endxarg{#1}%
  \def\!endyarg{#2}%
  \!bardobaselabel}

\def\!!bardobaselabel "#1" {%
  \put {#1}\!barbaselabelorientation{} at {\!basexarg} {\!baseyarg}
  \!bardoendlabel}
 
\def\!!bardoendlabel "#1" {%
  \put {#1}\!barendlabelorientation{} at {\!endxarg} {\!endyarg}
  \!barfinish}

\def\!barfinish{%
  \!ifnextchar/{\!finish}{\!barcurve}}
 

% ********************************
% *** BOXES (Draws rectangles) ***
% ********************************
%
% ** User commands:
% **   \putrectangle [<XSHIFT,YSHIFT>] corners at  XCOORD1 YCOORD1
% **     and  XCOORD2 YCOORD2 
% **   \shaderectangleson
% **   \shaderectanglesoff
% **   \frame [<SEPARATION>] {TEXT}
% **   \rectangle <WIDTH> <HEIGHT>
%
%
% **  \putrectangle [<XSHIFT,YSHIFT>] corners at XCOORD1 YCOORD1 
% **    and  XCOORD2 YCOORD2 
% **  Draws a rectangle with corners at (X1,Y1), (X2,Y1), (X1,Y2), (X2,Y2)
% **  Lines have thickness \linethickness, and overlap at the corners.
% **  The optional field  <XSHIFT,YSHIFT>  functions as with a \put command.
% **  See Subsection 4.2 of the manual.
\def\putrectangle{%
  \!ifnextchar<{\!putrectangle}{\!putrectangle<\!zpt,\!zpt> }}
\def\!putrectangle<#1,#2> corners at #3 #4 and #5 #6 {%
%
% ** get locations
  \!xone=\!M{#3}\!xunit  \!xtwo=\!M{#5}\!xunit%   
  \!yone=\!M{#4}\!yunit  \!ytwo=\!M{#6}\!yunit%           
  \ifdim \!xtwo<\!xone
    \!dimenI=\!xone  \!xone=\!xtwo  \!xtwo=\!dimenI
  \fi
  \ifdim \!ytwo<\!yone
    \!dimenI=\!yone  \!yone=\!ytwo  \!ytwo=\!dimenI
  \fi
  \!dimenI=#1\relax  \advance\!xone\!dimenI  \advance\!xtwo\!dimenI
  \!dimenI=#2\relax  \advance\!yone\!dimenI  \advance\!ytwo\!dimenI
  \let\!MRect=\!M%                  ** save current coord/dimen mode
  \!setdimenmode
%
% ** shade rectangle if appropriate
  \!shaderectangle
%
% ** draw horizontal edges
  \!dimenI=.5\linethickness
  \advance \!xone  -\!dimenI%       ** adjust x-location to overlap corners
  \advance \!xtwo   \!dimenI%       ** ditto
  \putrule from {\!xone} {\!yone} to {\!xtwo} {\!yone} 
  \putrule from {\!xone} {\!ytwo} to {\!xtwo} {\!ytwo} 
%
% ** draw vertical edges
  \advance \!xone   \!dimenI%       ** restore original x-values
  \advance \!xtwo  -\!dimenI% 
  \advance \!yone  -\!dimenI%       ** adjust y-location to overlap corners
  \advance \!ytwo   \!dimenI%       ** ditto
  \putrule from {\!xone} {\!yone} to {\!xone} {\!ytwo} 
  \putrule from {\!xtwo} {\!yone} to {\!xtwo} {\!ytwo} 
%
  \let\!M=\!MRect%                  ** restore coord/dimen mode
  \ignorespaces}
 
% ** \shaderectangleson 
% **   Subsequent rectangles will be shaded according to 
% **   the current shading pattern.  Affects \putrectangle, \putbar,
% **   \frame, \sethistograms, and \setbars. See Subsection 7.5 of the manual.
\def\shaderectangleson{%     
  \def\!shaderectangle{\!!shaderectangle}%
  \ignorespaces}
% ** \shaderectanglesoff 
% **    Suppresses  \shaderectangleson.  The default.
\def\shaderectanglesoff{%
  \def\!shaderectangle{}%
  \ignorespaces}

\shaderectanglesoff
 
% ** The following internal routine shades the current rectangle, when
% **   \!shaderectangle = \!!shaderectangle . 
\def\!!shaderectangle{%
  \!dimenA=\!xtwo  \advance \!dimenA -\!xone
  \!dimenB=\!ytwo  \advance \!dimenB -\!yone
  \ifdim \!dimenA<\!dimenB
    \!startvshade (\!xone,\!yone,\!ytwo)
    \!lshade      (\!xtwo,\!yone,\!ytwo)
  \else
    \!starthshade (\!yone,\!xone,\!xtwo)
    \!lshade      (\!ytwo,\!xone,\!xtwo)
  \fi
  \ignorespaces}
  
% ** \frame [<SEPARATION>] {TEXT}
% ** Draws a frame of thickness linethickness about the box enclosing
% **   TEXT; the frame is separated from the box by a distance of
% **   SEPARATION.  The result is an hbox with the same baseline as TEXT.
% **   If <SEPARATION> is omitted, you get the effect of <0pt>.
% ** See Subsection 4.2 of the manual.
\def\frame{%
  \!ifnextchar<{\!frame}{\!frame<\!zpt> }}
\long\def\!frame<#1> #2{%
  \beginpicture
    \setcoordinatesystem units <1pt,1pt> point at 0 0 
    \put {#2} [Bl] at 0 0 
    \!dimenA=#1\relax
    \!dimenB=\!wd \advance \!dimenB \!dimenA
    \!dimenC=\!ht \advance \!dimenC \!dimenA
    \!dimenD=\!dp \advance \!dimenD \!dimenA
    \let\!MFr=\!M
    \!setdimenmode
    \putrectangle corners at {-\!dimenA} {-\!dimenD} and {\!dimenB} {\!dimenC}
    \!setcoordmode
    \let\!M=\!MFr
  \endpicture
  \ignorespaces}
 
% ** \rectangle <WIDTH> <HEIGHT>
% ** Constructs a rectangle of width WIDTH and heigth HEIGHT. 
% ** See Subsection 4.2 of the manual.
\def\rectangle <#1> <#2> {%
  \setbox0=\hbox{}\wd0=#1\ht0=#2\frame {\box0}}


% *********************************************
% ***  CURVES  (Upper level \plot commands) ***
% *********************************************
%
% ** User commands
% **   \plot  DATA  /
% **   \plot  "FILE NAME"
% **   \setquadratic
% **   \setlinear
% **   \sethistograms
% **   \vshade  ...
% **   \hshade  ...

% \plot: multi-purpose command. Draws histograms, bar graphs, piecewise-linear
% or piecewise quadratic curves, depending on the setting of \!drawcurve.
% See Subsections 4.3-4.5, 5.1, 5.2 of the manual.
\def\plot{%
  \!ifnextchar"{\!plotfromfile}{\!drawcurve}}
\def\!plotfromfile"#1"{%
  \expandafter\!drawcurve \input #1 /}

% Command to set piecewise quadratic mode
% See Subsections 5.1, 7.3, and 7.4 of the manual.
\def\setquadratic{%
  \let\!drawcurve=\!qcurve
  \let\!!Shade=\!!qShade
  \let\!!!Shade=\!!!qShade}

% Command to set piecewise linear mode
% See Subsections 5.1, 7.3, and 7.4 of the manual.
\def\setlinear{%
  \let\!drawcurve=\!lcurve
  \let\!!Shade=\!!lShade
  \let\!!!Shade=\!!!lShade}

% Command to set histogram mode
% See Subsection 4.3 of the manual.
\def\sethistograms{%
  \let\!drawcurve=\!hcurve}

% Commands to cycle through list of coordinates in piecewise quadratic 
% interpolation mode
\def\!qcurve #1 #2 {%
  \!start (#1,#2)
  \!Qjoin}
\def\!Qjoin#1 #2 #3 #4 {%
  \!qjoin (#1,#2) (#3,#4)             % \!qjoin  is defined in QUADRATIC
  \!ifnextchar/{\!finish}{\!Qjoin}}

% Commands to cycle through list of coordinates in piecewise linear 
% interpolation mode
\def\!lcurve #1 #2 {%
  \!start (#1,#2)
  \!Ljoin}
\def\!Ljoin#1 #2 {%
  \!ljoin (#1,#2)                    % \!ljoin  is defined in LINEAR
  \!ifnextchar/{\!finish}{\!Ljoin}}

\def\!finish/{\ignorespaces}

% Command to cycle through list of coordinates in histogram mode
\def\!hcurve #1 #2 {%
  \edef\!hxS{#1}%
  \edef\!hyS{#2}%
  \!hjoin}
\def\!hjoin#1 #2 {%
  \putrectangle corners at {\!hxS} {\!hyS} and {#1} {#2}
  \edef\!hxS{#1}%
  \!ifnextchar/{\!finish}{\!hjoin}}


% \vshade: See Subsection 7.3 of the manual.
\def\vshade #1 #2 #3 {%
  \!startvshade (#1,#2,#3)
  \!Shadewhat}

% \hshade: See Subsection 7.4 of the manual.
\def\hshade #1 #2 #3 {%
  \!starthshade (#1,#2,#3)
  \!Shadewhat}

% Commands to cycle through coordinates and optional "edge effect"
% fields while shading.
\def\!Shadewhat{%
  \futurelet\!nextchar\!Shade}
\def\!Shade{%
  \if <\!nextchar
    \def\!nextShade{\!!Shade}%
  \else
    \if /\!nextchar
      \def\!nextShade{\!finish}%
    \else
      \def\!nextShade{\!!!Shade}%
    \fi
  \fi
  \!nextShade}
\def\!!lShade<#1> #2 #3 #4 {%
  \!lshade <#1> (#2,#3,#4)                 % \!lshade is defined in SHADING
  \!Shadewhat}
\def\!!!lShade#1 #2 #3 {%
  \!lshade (#1,#2,#3)
  \!Shadewhat} 
\def\!!qShade<#1> #2 #3 #4 #5 #6 #7 {%
  \!qshade <#1> (#2,#3,#4) (#5,#6,#7)      % \!qshade is defined in SHADING
  \!Shadewhat}
\def\!!!qShade#1 #2 #3 #4 #5 #6 {%
  \!qshade (#1,#2,#3) (#4,#5,#6)
  \!Shadewhat} 

% ** Set default interpolation mode
\setlinear


%  ********************************************
%  *** DASHPATTERNS (Sets up dash patterns) ***
%  ********************************************

%  **  User commands:
%  **    \setdashpattern <DIMEN1,DIMEN2,DIMEN3,...>
%  **    \setdots <INTRADOT_DISTANCE>
%  **    \setdotsnear <INTRADOT_DISTANCE> for <ARC LENGTH>
%  **    \setdashes <DASH/SKIP_DISTANCE>
%  **    \setdashesnear <DASH/SKIP_DISTANCE> for <ARC LENGTH>
%  **    \setsolid
%  **    \findlength {CURVE CMDS}
 
%  **  Internal commands:
%  **    \!dashingon
%  **    \!dashingoff
 
%  **  Dash patterns are specified by a balanced token list whose complete
%  **    expansion has the form: DIMEN1,DIMEN2,DIMEN3,DIMEN4,... ; this produces
%  **    an arc of length DIMEN1, a skip of length DIMEN2, an arc of length
%  **    DIMEN3, a skip of length DIMEN4, ... .  Any number of DIMEN values may
%  **    be given. The pattern is repeated as many times (perhaps fractional)
%  **    as necessary to draw the curve. 
%  **  A dash pattern remains in effect until it is overridden by a call to
%  **    \setdashpattern, or to \setdots, \setdotsnear ... , \setdashes, 
%  **    \setdashesnear ... , or \setsolid.
%  **  Solid lines are the default.
 
 
%  **  \def\setdashpattern <DIMEN1,DIMEN2,DIMEN3,...>
%  **  The following routine converts a balanced list of tokens whose
%  **  complete expansion has the form  DIMEN1,DIMEN2, ... , DIMENk  into 
%  **  three list macros that are used in drawing dashed rules and curves:
%  **    !Flist:   \!Rule{DIMEN1}\!Skip{DIMEN2}\!Rule{DIMEN3}\!Skip{DIMEN4} ...
%  **    !Blist:   ...\!Skip{DIMEN4}\!Rule{DIMEN3}\!Skip{DIMEN2}\!Rule{DIMEN1}
%  **    !UDlist:  \\{DIMEN1}\\{DIMEN2}\\{DIMEN3}\\{DIMEN4} ...;
%  **  calculates \!leaderlength := DIMEN1 + ... + DIMENk; and
%  **  sets the curve drawing routines to dash mode.
%  **  Those lists are used by the curve drawing routines.
%  **  Dimenj ... may be given as an explicit dimension (e.g., 5pt), or
%  **  as an expression involving a dimension register (e.g., -2.5\dimen0).
%  **  See Subsection 6.2 of the manual
\def\setdashpattern <#1>{%
  \def\!Flist{}\def\!Blist{}\def\!UDlist{}%
  \!countA=0
  \!ecfor\!item:=#1\do{%
    \!dimenA=\!item\relax
    \expandafter\!rightappend\the\!dimenA\withCS{\\}\to\!UDlist%
    \advance\!countA  1
    \ifodd\!countA
      \expandafter\!rightappend\the\!dimenA\withCS{\!Rule}\to\!Flist%
      \expandafter\!leftappend\the\!dimenA\withCS{\!Rule}\to\!Blist%
    \else 
      \expandafter\!rightappend\the\!dimenA\withCS{\!Skip}\to\!Flist%
      \expandafter\!leftappend\the\!dimenA\withCS{\!Skip}\to\!Blist%
    \fi}%
  \!leaderlength=\!zpt
  \def\!Rule##1{\advance\!leaderlength  ##1}%
  \def\!Skip##1{\advance\!leaderlength  ##1}%
  \!Flist%
  \ifdim\!leaderlength>\!zpt 
  \else
    \def\!Flist{\!Skip{24in}}\def\!Blist{\!Skip{24in}}\ignorespaces
    \def\!UDlist{\\{\!zpt}\\{24in}}\ignorespaces
    \!leaderlength=24in
  \fi
  \!dashingon}   
 
 
%  **  \!dashingon  -- puts the curve drawing routines into dash mode
%  **  \!dashingoff -- puts the curve drawing routines into solid mode
%  **  These are internal commands, invoked by \setdashpattern and \setsolid
\def\!dashingon{%
  \def\!advancedashing{\!!advancedashing}%
  \def\!drawlinearsegment{\!lineardashed}%
  \def\!puthline{\!putdashedhline}%
  \def\!putvline{\!putdashedvline}%
%  \def\!putsline{\!putdashedsline}%
  \ignorespaces}% 
\def\!dashingoff{%
  \def\!advancedashing{\relax}%
  \def\!drawlinearsegment{\!linearsolid}%
  \def\!puthline{\!putsolidhline}%
  \def\!putvline{\!putsolidvline}%
%  \def\!putsline{\!putsolidsline}%
  \ignorespaces}
 
 
%  **  \setdots <LENGTH>  --  sets up a dot/skip pattern where dot (actually
%  **    the current plotsymbol) is plunked down once for every LENGTH 
%  **    traveled along the curve.  LENGTH defaults to 5pt.
%  **    See Subsection 6.1 of the manual.
\def\setdots{%
  \!ifnextchar<{\!setdots}{\!setdots<5pt>}}
\def\!setdots<#1>{%
  \!dimenB=#1\advance\!dimenB -\plotsymbolspacing
  \ifdim\!dimenB<\!zpt
    \!dimenB=\!zpt
  \fi
\setdashpattern <\plotsymbolspacing,\!dimenB>}
 
% ** \setdotsnear <LENGTH> for <ARC LENGTH>
% ** sets up a dot pattern where the dots are approximately LENGTH apart,
% ** the total length of the pattern is ARC LENGTH, and the pattern
% ** begins and ends with a dot. See Subsection 6.3 of the manual.
\def\setdotsnear <#1> for <#2>{%
  \!dimenB=#2\relax  \advance\!dimenB -.05pt  
  \!dimenC=#1\relax  \!countA=\!dimenC 
  \!dimenD=\!dimenB  \advance\!dimenD .5\!dimenC  \!countB=\!dimenD
  \divide \!countB  \!countA
  \ifnum 1>\!countB 
    \!countB=1
  \fi
  \divide\!dimenB  \!countB
  \setdots <\!dimenB>}
 
%  **  \setdashes <LENGTH>  --  sets up a dash/skip pattern where the dash
%  **    and the skip are each of length LENGTH (the dash is formed by
%  **    plunking down the current plotsymbol over an arc of length LENGTH
%  **    and so may actually be longer than LENGTH.  LENGTH defaults to 5pt.
%  **    See Subsection 6.1 of the manual.
\def\setdashes{%
  \!ifnextchar<{\!setdashes}{\!setdashes<5pt>}}
\def\!setdashes<#1>{\setdashpattern <#1,#1>}
 
% ** \setdashesnear ...
% ** Like \setdotsnear; the pattern begins and ends with a dash.
% ** See Subsection 6.3 of the manual.
\def\setdashesnear <#1> for <#2>{%
  \!dimenB=#2\relax  
  \!dimenC=#1\relax  \!countA=\!dimenC 
  \!dimenD=\!dimenB  \advance\!dimenD .5\!dimenC  \!countB=\!dimenD
  \divide \!countB  \!countA
  \ifodd \!countB 
  \else 
    \advance \!countB  1
  \fi
  \divide\!dimenB  \!countB
  \setdashes <\!dimenB>}
 
%  **  \setsolid  --  puts the curve drawing routines in "solid line" mode,
%  **    the default mode.  See Subsection 6.1 of the manual.
\def\setsolid{%
  \def\!Flist{\!Rule{24in}}\def\!Blist{\!Rule{24in}}%  
  \def\!UDlist{\\{24in}\\{\!zpt}}%
  \!dashingoff}  
\setsolid

%  **  \findlength {CURVE CMDS}
%  **  PiCTeX executes the \start, \ljoin, and \qjoin cmds comprising
%  **  CURVE CMDS without plotting anything, but stashes the length
%  **  of the phantom curve away in \totalarclength.
%  **  See Subsection 6.3 of the manual.
\def\findlength#1{%
  \begingroup
    \setdashpattern <0pt, \maxdimen>
    \setplotsymbol ({})  
    \dontsavelinesandcurves
    #1%
  \endgroup
  \ignorespaces}


% *************************************************************
% *** DIVISION  (Does long division of dimension registers) ***
% *************************************************************
 
% ** User command:
% **   \Divide {DIVIDEND} by {DIVISOR} forming {RESULT}
  
% ** Internal command
% **   \!divide{DIVIDEND}{DIVISOR}{RESULT}
 
% **  \!divide DIVIDEND [by] DIVISOR [to get] ANSWER
% **  Divides the dimension DIVIDEND by the dimension DIVISOR, placing the 
% **  quotient in the dimension register ANSWER.  Values are understood to 
% **  be in points.  E.g.  12.5pt/1.4pt=8.92857pt.
% **  Quotient is accurate to 1/65536pt=2**[-16]pt
% **  |DIVISOR| should be < 2048pt (about 28 inches).
\def\!divide#1#2#3{%
  \!dimenB=#1%                      **  dimB  holds current remainder (r)
  \!dimenC=#2%                      **  dimC  holds divisor (d)
  \!dimenD=\!dimenB%                **  dimD  holds quotient q=r/d for this 
  \divide \!dimenD \!dimenC%        **    step, in units of scaled pts
  \!dimenA=\!dimenD%                **  dimA  eventually holds answer (a)
  \multiply\!dimenD \!dimenC%       **  r <-- r - dq
  \advance\!dimenB -\!dimenD%       **  First step complete. Have integer part
%                                   **  of a, and corresponding remainder.
  \!dimenD=\!dimenC%                **  Temporarily use dimD to hold |d|
    \ifdim\!dimenD<\!zpt \!dimenD=-\!dimenD 
  \fi
  \ifdim\!dimenD<64pt%              **  Branch on the magnitude of |d|
    \!divstep[\!tfs]\!divstep[\!tfs]%
  \else 
    \!!divide
  \fi
  #3=\!dimenA\ignorespaces}

% **  The following code handles divisors  d  with 
% **    (1)  .88in =  64pt <= d <  256pt =  3.54in
% **    (2) 3.54in = 256pt <= d < 2048pt = 28.34in
% **  Anything bigger than that may result in an overflow condition.
% **  For our purposes, we should never even see case (2).
\def\!!divide{%
  \ifdim\!dimenD<256pt
    \!divstep[64]\!divstep[32]\!divstep[32]%
  \else 
    \!divstep[8]\!divstep[8]\!divstep[8]\!divstep[8]\!divstep[8]%
    \!dimenA=2\!dimenA
  \fi}
 
 
% **  The following macro does the real long division work.
\def\!divstep[#1]{%                 **  #1 = "B"
  \!dimenB=#1\!dimenB%              **  r <-- B*r
  \!dimenD=\!dimenB%                **  dimD  holds quotient q=r/d for this 
    \divide \!dimenD by \!dimenC%   **    step, in units of scaled pts
  \!dimenA=#1\!dimenA%              **  a <-- B*a + q
    \advance\!dimenA by \!dimenD%
  \multiply\!dimenD by \!dimenC%    **  r <-- r - dq
    \advance\!dimenB by -\!dimenD}
 
% **  \Divide:  See Subsection 9.3 of the manual.
\def\Divide <#1> by <#2> forming <#3> {%
  \!divide{#1}{#2}{#3}}


% *********************************************
% *** ELLIPSES (Draws ellipses and circles) ***
% *********************************************
 
% ** User commands
% **   \ellipticalarc  axes ratio A:B  DEGREES degrees from XSTART YSTART 
% **      center at XCENTER YCENTER 
% **   \circulararc DEGREES degrees from XSTART YSTART 
% **      center at XCENTER YCENTER 
 
% ** Internal command
% **   \!sinandcos{32*ANGLE in radians}{32*SIN}{32*COS}
 
 
% **   \ellipticalarc  axes ratio A:B  DEGREES degrees from XSTART YSTART 
% **      center at XCENTER YCENTER 
% **    Draws a elliptical arc starting at the coordinate point (XSTART,YSTART).
% **    The center of the ellipse of which the arc is a segment is at 
% **      (XCENTER,YCENTER).
% **    The arc extends through an angle of DEGREES degrees (may be + or -).
% **    A:B is the ratio of the length of the xaxis to the length of
% **      the yaxis of the ellipse
% **    Sqrt{[(XSTART-XCENTER)/A]**2 + [(YSTART-YCENTER)/B]**2}
% **      must be < 512pt (about 7in).
% **    Doesn't modify the dimensions (ht, dp, wd) of the PiCture under
% **      construction.
 
% ** \circulararc  --  See Subsection 5.3 of the manual.
\def\circulararc{%
  \ellipticalarc axes ratio 1:1 }

% ** \ellipticalarc  --  See Subsection 5.3 of the manual.
\def\ellipticalarc axes ratio #1:#2 #3 degrees from #4 #5 center at #6 #7 {%
  \!angle=#3pt\relax%                    ** get angle
  \ifdim\!angle>\!zpt 
    \def\!sign{}%                        ** counterclockwise
  \else 
    \def\!sign{-}\!angle=-\!angle%       ** clockwise
  \fi
  \!xxloc=\!M{#6}\!xunit%                ** convert CENTER to dimension
  \!yyloc=\!M{#7}\!yunit     
  \!xxS=\!M{#4}\!xunit%                  ** get STARTing point on rim of ellipse
  \!yyS=\!M{#5}\!yunit
  \advance\!xxS -\!xxloc%                ** make center of ellipse (0,0)
  \advance\!yyS -\!yyloc
  \!divide\!xxS{#1pt}\!xxS %             ** scale point on ellipse to point on 
  \!divide\!yyS{#2pt}\!yyS %                 corresponding circle
%
  \let\!MC=\!M%                          ** save current c/d mode
  \!setdimenmode%                        ** go into dimension mode
%
  \!xS=#1\!xxS  \advance\!xS\!xxloc
  \!yS=#2\!yyS  \advance\!yS\!yyloc
  \!start (\!xS,\!yS)%
  \!loop\ifdim\!angle>14.9999pt%         ** draw in major portion of ellipse 
    \!rotate(\!xxS,\!yyS)by(\!cos,\!sign\!sin)to(\!xxM,\!yyM) 
    \!rotate(\!xxM,\!yyM)by(\!cos,\!sign\!sin)to(\!xxE,\!yyE)
    \!xM=#1\!xxM  \advance\!xM\!xxloc  \!yM=#2\!yyM  \advance\!yM\!yyloc
    \!xE=#1\!xxE  \advance\!xE\!xxloc  \!yE=#2\!yyE  \advance\!yE\!yyloc
    \!qjoin (\!xM,\!yM) (\!xE,\!yE)
    \!xxS=\!xxE  \!yyS=\!yyE 
    \advance \!angle -15pt
  \repeat
  \ifdim\!angle>\!zpt%                   ** complete remaining arc, if any
    \!angle=100.53096\!angle%            ** convert angle to radians, divide
    \divide \!angle 360 %                **   by 2, and multiply by 32
    \!sinandcos\!angle\!!sin\!!cos%      ** get 32*sin & 32*cos
    \!rotate(\!xxS,\!yyS)by(\!!cos,\!sign\!!sin)to(\!xxM,\!yyM) 
    \!rotate(\!xxM,\!yyM)by(\!!cos,\!sign\!!sin)to(\!xxE,\!yyE)
    \!xM=#1\!xxM  \advance\!xM\!xxloc  \!yM=#2\!yyM  \advance\!yM\!yyloc
    \!xE=#1\!xxE  \advance\!xE\!xxloc  \!yE=#2\!yyE  \advance\!yE\!yyloc
    \!qjoin (\!xM,\!yM) (\!xE,\!yE)
  \fi
%
  \let\!M=\!MC%                          ** restore c/d mode
  \ignorespaces}%                        **   if appropriate
 
 
%  ** \!rotate(XREG,YREG)by(32cos,32sin)to(XXREG,YYREG)
%  ** rotates (XREG,YREG) by angle with specfied scaled cos & sin to
%  ** (XXREG,YYREG).  Uses \!dimenA & \!dimenB as scratch registers.
\def\!rotate(#1,#2)by(#3,#4)to(#5,#6){% 
  \!dimenA=#3#1\advance \!dimenA -#4#2%   ** Rcos(x+t)=Rcosx*cost - Rsinx*sint
  \!dimenB=#3#2\advance \!dimenB  #4#1%   ** Rsin(x+t)=Rsinx*cost + Rcosx*sint
  \divide \!dimenA 32  \divide \!dimenB 32 
  #5=\!dimenA  #6=\!dimenB
  \ignorespaces}
\def\!sin{4.17684}%                       ** 32*sin(pi/24) (pi/24=7.5deg)
\def\!cos{31.72624}%                      ** 32*cos(pi/24)
 
 
%  ** \!sinandcos{32*ANGLE in radians}{\SINCS}{\COSCS}
%  **   Computes the 32*sine and 32*cosine of a small ANGLE expressed in 
%  **   radians/32 and puts these values in the replacement texts of 
%  **   \SINCS and \COSCS
\def\!sinandcos#1#2#3{%
 \!dimenD=#1%                **  angle is expressed in radians/32: 1pt = 1/32rad
 \!dimenA=\!dimenD%          **  dimA will eventually contain 32sin(angle)in pts
 \!dimenB=32pt%              **  dimB will eventually contain 32cos(angle)in pts
 \!removept\!dimenD\!value%  **  get value of 32*angle, without "pt"
 \!dimenC=\!dimenD%          **  holds 32*angle**i/i! in pts
 \!dimenC=\!value\!dimenC \divide\!dimenC by 64 %   ** now 32*angle**2/2
 \advance\!dimenB by -\!dimenC%                     ** 32-32*angle**2/2
 \!dimenC=\!value\!dimenC \divide\!dimenC by 96 %   ** now 32*angle**3/3!
 \advance\!dimenA by -\!dimenC%                     ** now 32*(angle-angle**3/6)
 \!dimenC=\!value\!dimenC \divide\!dimenC by 128 %  ** now 32*angle**4/4!
 \advance\!dimenB by \!dimenC%
 \!removept\!dimenA#2%                              ** set 32*sin(angle)
 \!removept\!dimenB#3%                              ** set 32*cos(angle)
 \ignorespaces}


% *****************************************************************
% ***  RULES  (Draws rules, i.e., horizontal & vertical lines)  ***
% *****************************************************************

% **  User command:
% **    \putrule [<XDIMEN,YDIMEN>] from  XCOORD1 YCOORD1 
% **      to  XCOORD2 YCOORD2 

% **  Internal commands:
% **    \!puthline [<XDIMEN,YDIMEN>]    (h = horizontal)
% **      Set by dashpat to either: \!putsolidhline  or \!putdashedhline
% **    \!putvline [<XDIMEN,YDIMEN>]    (v = vertical)
% **      Either:  \!putsolidvline  or  \!putdashedvline


% **  \putrule [<XDIMEN,YDIMEN>] from XCOORD1 YCOORD1
% **    to XCOORD2 YCOORD2
% **  Draws a rule -- dashed or solid depending on the current dash pattern --
% **    from (X1,Y1) to (X2,Y2).  Uses TEK's  \hrule & \vrule & \leaders  
% **    constructions to handle horizontal & vertical lines efficiently both
% **    in terms of execution time and space in the DVI file.  
% **  See Subsection 4.1 of the manual.
\def\putrule#1from #2 #3 to #4 #5 {%
  \!xloc=\!M{#2}\!xunit  \!xxloc=\!M{#4}\!xunit%   
  \!yloc=\!M{#3}\!yunit  \!yyloc=\!M{#5}\!yunit%           
  \!dxpos=\!xxloc  \advance\!dxpos by -\!xloc
  \!dypos=\!yyloc  \advance\!dypos by -\!yloc
%
  \ifdim\!dypos=\!zpt
    \def\!!Line{\!puthline{#1}}\ignorespaces
  \else
    \ifdim\!dxpos=\!zpt
      \def\!!Line{\!putvline{#1}}\ignorespaces
    \else 
       \def\!!Line{}
    \fi
  \fi
  \let\!ML=\!M%           ** save current coord\dimen mode
  \!setdimenmode%         ** express locations in dimens
  \!!Line%
  \let\!M=\!ML%           ** restore previous c/d mode
  \ignorespaces}
 
 
% **  \!putsolidhline [<XDIMEN,YDIMEN>]
% **  Place horizontal solid line
\def\!putsolidhline#1{%
  \ifdim\!dxpos>\!zpt 
    \put{\!hline\!dxpos}#1[l] at {\!xloc} {\!yloc}
  \else 
    \put{\!hline{-\!dxpos}}#1[l] at {\!xxloc} {\!yyloc}
  \fi
  \ignorespaces}
 
% **  \!putsolidvline [shifted <XDIMEN,YDIMEN>]
% **  Place vertical solid line
\def\!putsolidvline#1{%
  \ifdim\!dypos>\!zpt 
    \put{\!vline\!dypos}#1[b] at {\!xloc} {\!yloc}
  \else 
    \put{\!vline{-\!dypos}}#1[b] at {\!xxloc} {\!yyloc}
  \fi
  \ignorespaces}
 
\def\!hline#1{\hbox to #1{\leaders \hrule height\linethickness\hfill}}
\def\!vline#1{\vbox to #1{\leaders \vrule width\linethickness\vfill}}
 
 
% **  \!putdashedhline [<XDIMEN,YDIMEN>]
% **  Place dashed horizontal line
\def\!putdashedhline#1{%
  \ifdim\!dxpos>\!zpt 
    \!DLsetup\!Flist\!dxpos
    \put{\hbox to \!totalleaderlength{\!hleaders}\!hpartialpattern\!Rtrunc}
      #1[l] at {\!xloc} {\!yloc} 
  \else 
    \!DLsetup\!Blist{-\!dxpos}
    \put{\!hpartialpattern\!Ltrunc\hbox to \!totalleaderlength{\!hleaders}}
      #1[r] at {\!xloc} {\!yloc} 
  \fi
  \ignorespaces}
 
% **  \!putdashedhline [<XDIMEN,YDIMEN>]
% **  Place dashed vertical line
\def\!putdashedvline#1{%
  \!dypos=-\!dypos%            ** vertical leaders go from top to bottom
  \ifdim\!dypos>\!zpt 
    \!DLsetup\!Flist\!dypos 
    \put{\vbox{\vbox to \!totalleaderlength{\!vleaders}
      \!vpartialpattern\!Rtrunc}}#1[t] at {\!xloc} {\!yloc} 
  \else 
    \!DLsetup\!Blist{-\!dypos}
    \put{\vbox{\!vpartialpattern\!Ltrunc
      \vbox to \!totalleaderlength{\!vleaders}}}#1[b] at {\!xloc} {\!yloc} 
  \fi
  \ignorespaces}
 

% **  The rest of the macros in this section are subroutines used by 
% **  \!putdashedhline and \!putdashedvline.
\def\!DLsetup#1#2{%            ** Dashed-Line set up
  \let\!RSlist=#1%             ** set !Rule-Skip list
  \!countB=#2%                 ** convert rule length to integer (number of sps)
  \!countA=\!leaderlength%     ** ditto, leaderlength
  \divide\!countB by \!countA% ** number of complete leader units
  \!totalleaderlength=\!countB\!leaderlength
  \!Rresiduallength=#2%
  \advance \!Rresiduallength by -\!totalleaderlength%  \** excess length
  \!Lresiduallength=\!leaderlength
  \advance \!Lresiduallength by -\!Rresiduallength
  \ignorespaces}
 
\def\!hleaders{%
  \def\!Rule##1{\vrule height\linethickness width##1}%
  \def\!Skip##1{\hskip##1}%
  \leaders\hbox{\!RSlist}\hfill}
 
\def\!hpartialpattern#1{%
  \!dimenA=\!zpt \!dimenB=\!zpt 
  \def\!Rule##1{#1{##1}\vrule height\linethickness width\!dimenD}%
  \def\!Skip##1{#1{##1}\hskip\!dimenD}%
  \!RSlist}
 
\def\!vleaders{%
  \def\!Rule##1{\hrule width\linethickness height##1}%
  \def\!Skip##1{\vskip##1}%
  \leaders\vbox{\!RSlist}\vfill}
 
\def\!vpartialpattern#1{%
  \!dimenA=\!zpt \!dimenB=\!zpt 
  \def\!Rule##1{#1{##1}\hrule width\linethickness height\!dimenD}%
  \def\!Skip##1{#1{##1}\vskip\!dimenD}%
  \!RSlist}
 
\def\!Rtrunc#1{\!trunc{#1}>\!Rresiduallength}
\def\!Ltrunc#1{\!trunc{#1}<\!Lresiduallength}
 
\def\!trunc#1#2#3{%          
  \!dimenA=\!dimenB         
  \advance\!dimenB by #1%
  \!dimenD=\!dimenB  \ifdim\!dimenD#2#3\!dimenD=#3\fi
  \!dimenC=\!dimenA  \ifdim\!dimenC#2#3\!dimenC=#3\fi
  \advance \!dimenD by -\!dimenC}
 

%  ****************************************************************
%  ***  LINEAR ARC  (Draws straight lines -- solid and dashed)  ***
%  ****************************************************************
 
%  **  User commands
%  **    \inboundscheckoff
%  **    \inboundscheckon
   
%  **  Internal commands 
%  **    \!start (XCOORD,YCOORD)
%  **    \!ljoin (XCOORD,YCOORD)
%  **    \!drawlinearsegment  --  set by \dashpat to either
%  **      \!linearsolid  or  \!lineardashed
%  **    \!advancedashing     --  set by \dashpat to either
%  **       \relax  or  \!!advancedashing
%  **    \!plotifinbounds     --  set by \inboundscheck off/on to either
%  **       \!plot  or  \!!plotifinbounds
%  **    \!initinboundscheck  --  set by \inboundscheck off/on to either
%  **       \relax  or  \!!initinboundscheck
 
 
%  \plotsymbolspacing  ** distance between consecutive plot positions
%  \!xS                ** starting x
%  \!yS                ** starting y
%  \!xE                ** ending   x
%  \!yE                ** ending   y
%  \!xdiff             ** x_end - x_start
%  \!ydiff             ** y_end - y_start
%  \!distacross        ** how far along curve next point to be plotted is
%  \!arclength         ** approximate length of arc for current interval
%  \!downlength        ** remaining length for "pen" to be down    
%  \!uplength          ** length for "pen" to be down    
%  \!intervalno        ** counts segments to curve
%  \totalarclength     ** cumulative distance along curve
%  \!npoints           ** approximately  (arc length / plotsymbolspacing)
 
%  **  Calls -- \!Pythag, \!divide, \!plot
 
 
%  **  \!start (XCOORD,YCOORD)
%  **  Sets initial point for linearly (or quadratically) interpolated curve
\def\!start (#1,#2){%
  \!plotxorigin=\!xorigin  \advance \!plotxorigin by \!plotsymbolxshift
  \!plotyorigin=\!yorigin  \advance \!plotyorigin by \!plotsymbolyshift
  \!xS=\!M{#1}\!xunit \!yS=\!M{#2}\!yunit
  \!rotateaboutpivot\!xS\!yS
  \!copylist\!UDlist\to\!!UDlist% **\!UDlist has the form \\{dimen1}\\{dimen2}..
%                                 ** Routine will draw dashed line with pen
%                                 ** down for dimen1, up for dimen2, ...
  \!getnextvalueof\!downlength\from\!!UDlist
  \!distacross=\!zpt%             ** 1st point goes at start of curve
  \!intervalno=0 %                ** initialize interval counter
  \global\totalarclength=\!zpt%   ** initialize distance traveled along curve
  \ignorespaces}
 

%  **  \!ljoin (XCOORD,YCOORD) 
%  **  Draws a straight line starting at the last point specified
%  **    by the most recent \!start, \!ljoin, or \!qjoin, and
%  **    ending at (XCOORD,YCOORD).
\def\!ljoin (#1,#2){%
  \advance\!intervalno by 1
  \!xE=\!M{#1}\!xunit \!yE=\!M{#2}\!yunit
  \!rotateaboutpivot\!xE\!yE
  \!xdiff=\!xE \advance \!xdiff by -\!xS%**  xdiff = xE - xS
  \!ydiff=\!yE \advance \!ydiff by -\!yS%**  ydiff = yE - yS
  \!Pythag\!xdiff\!ydiff\!arclength%     **  arclength = sqrt(xdiff**2+ydiff**2) 
  \global\advance \totalarclength by \!arclength%
  \!drawlinearsegment%   ** set by dashpat to \!linearsolid or \!lineardashed
  \!xS=\!xE \!yS=\!yE%   ** shift ending points to starting points
  \ignorespaces}
 
 
% **  The following routine is used to draw a "solid" line between (xS,yS)
% **  and (xE,yE).  Points are spaced nearly every  \plotsymbolspacing length
% **  along the line.  
\def\!linearsolid{%
  \!npoints=\!arclength
  \!countA=\plotsymbolspacing
  \divide\!npoints by \!countA%      ** now #pts =. arclength/plotsymbolspacing
  \ifnum \!npoints<1 
    \!npoints=1 
  \fi
  \divide\!xdiff by \!npoints
  \divide\!ydiff by \!npoints
  \!xpos=\!xS \!ypos=\!yS
%
  \loop\ifnum\!npoints>-1
    \!plotifinbounds
    \advance \!xpos by \!xdiff
    \advance \!ypos by \!ydiff
    \advance \!npoints by -1
  \repeat
  \ignorespaces}
 
 
% ** The following routine is used to draw a dashed line between (xS,yS)
% ** and (xE,yE). The dash pattern continues from the previous segment.
\def\!lineardashed{%
% **
  \ifdim\!distacross>\!arclength
    \advance \!distacross by -\!arclength  %nothing to plot in this interval
%
  \else
%
    \loop\ifdim\!distacross<\!arclength
%     ** plot point, interpolating linearly in x and y
      \!divide\!distacross\!arclength\!dimenA%  ** dimA = across/arclength
      \!removept\!dimenA\!t%  ** \!t holds value in dimA, without the "pt"
      \!xpos=\!t\!xdiff \advance \!xpos by \!xS
      \!ypos=\!t\!ydiff \advance \!ypos by \!yS
      \!plotifinbounds
      \advance\!distacross by \plotsymbolspacing
      \!advancedashing
    \repeat  
%
    \advance \!distacross by -\!arclength%    ** prepare for next interval 
  \fi
  \ignorespaces}
 
 
\def\!!advancedashing{%
  \advance\!downlength by -\plotsymbolspacing
  \ifdim \!downlength>\!zpt
  \else
    \advance\!distacross by \!downlength
    \!getnextvalueof\!uplength\from\!!UDlist
    \advance\!distacross by \!uplength
    \!getnextvalueof\!downlength\from\!!UDlist
  \fi}
 
 
% ** \inboundscheckoff & \inboundscheckon: See Subsection 5.5 of the manual.
\def\inboundscheckoff{%
  \def\!plotifinbounds{\!plot(\!xpos,\!ypos)}%
  \def\!initinboundscheck{\relax}\ignorespaces}
\def\inboundscheckon{%
  \def\!plotifinbounds{\!!plotifinbounds}%
  \def\!initinboundscheck{\!!initinboundscheck}%
  \!initinboundscheck\ignorespaces} 
\inboundscheckoff
 
% ** The following code plots the current point only if it falls in the
% ** current plotarea.  It doesn't matter if the coordinate system has
% ** changed since the plotarea was set up.  However, shifts of the plot
% ** are ignored (how the plotsymbol stands relative to its plot position is
% ** unknown anyway).
\def\!!plotifinbounds{%
  \ifdim \!xpos<\!checkleft
  \else
    \ifdim \!xpos>\!checkright
    \else
      \ifdim \!ypos<\!checkbot
      \else
         \ifdim \!ypos>\!checktop
         \else
           \!plot(\!xpos,\!ypos)
         \fi 
      \fi
    \fi
  \fi}
 
 
\def\!!initinboundscheck{%
  \!checkleft=\!arealloc     \advance\!checkleft by \!xorigin
  \!checkright=\!arearloc    \advance\!checkright by \!xorigin
  \!checkbot=\!areabloc      \advance\!checkbot by \!yorigin
  \!checktop=\!areatloc      \advance\!checktop by \!yorigin}
 

% *********************************
% *** LOGTEN  (Log_10 function) ***
% *********************************
%
% ** \!logten{X}
% ** Calculates log_10 of X.  X and LOG10(X) are in fixed point notation.
% **  X must be positive; it may have an optional `+' sign; any number
% **  of digits may be specified for X.  The absolute error in LOG10(X) is
% **  less than .0001 (probably < .00006).  That's about as good as you
% **  hope for, since TEX only operates to 5 figures after the decimal
% **  point anyway.

%  \!rootten=3.162278pt       **** These are values are set in ALLOCATIONS
%  \!tenAe=2.543275pt  (=A5)
%  \!tenAc=2.773839pt  (=A3)
%  \!tenAa=8.690286pt  (=A1)

\def\!logten#1#2{%
  \expandafter\!!logten#1\!nil
  \!removept\!dimenF#2%
  \ignorespaces}

\def\!!logten#1#2\!nil{%
  \if -#1%
    \!dimenF=\!zpt
    \def\!next{\ignorespaces}%
  \else
    \if +#1%
      \def\!next{\!!logten#2\!nil}%
    \else
      \if .#1%
        \def\!next{\!!logten0.#2\!nil}%
      \else
        \def\!next{\!!!logten#1#2..\!nil}%
      \fi
    \fi
  \fi
  \!next}

\def\!!!logten#1#2.#3.#4\!nil{%
  \!dimenF=1pt %                 ** DimF holds log10 original argument
  \if 0#1%                      
    \!!logshift#3pt %            ** Argument < 1
  \else %                        ** Argument >= 1
    \!logshift#2/%               ** Shift decimal pt as many places
    \!dimenE=#1.#2#3pt %         **   as there are figures in #2
  \fi %                          ** Now dimE holds revised X want log10 of
  \ifdim \!dimenE<\!rootten%          ** Transform X to XX between sqrt(10) 
    \multiply \!dimenE 10 %           **   and 10*sqrt(10)
    \advance  \!dimenF -1pt
  \fi
  \!dimenG=\!dimenE%                  ** dimG <- (XX + 10)
    \advance\!dimenG 10pt
  \advance\!dimenE -10pt %            ** dimE <- (XX - 10)
  \multiply\!dimenE 10 %              ** dimE = 10*(XX-10)
  \!divide\!dimenE\!dimenG\!dimenE%   ** Now dimE=10t==10*(XX-10)/(XX+10)
  \!removept\!dimenE\!t%              ** !t=10t, with "pt" removed
  \!dimenG=\!t\!dimenE%               ** dimG=100t**2
  \!removept\!dimenG\!tt%             ** !tt=100t**2, with "pt" removed
  \!dimenH=\!tt\!tenAe%               ** dimH=10*a5*(10t)**2 /100
    \divide\!dimenH 100
  \advance\!dimenH \!tenAc%           ** ditto + 10*a3
  \!dimenH=\!tt\!dimenH%              ** ditto * (10t)**2 /100
    \divide\!dimenH 100   
  \advance\!dimenH \!tenAa%           ** ditto + 10*a1
  \!dimenH=\!t\!dimenH%               ** ditto * 10t / 100
    \divide\!dimenH 100 %             ** Now dimH = log10(XX) - 1
  \advance\!dimenF \!dimenH}%         ** dimF = log10(X)

\def\!logshift#1{%
  \if #1/%
    \def\!next{\ignorespaces}%
  \else
    \advance\!dimenF 1pt 
    \def\!next{\!logshift}%
  \fi 
  \!next}
 
 \def\!!logshift#1{%
   \advance\!dimenF -1pt
   \if 0#1%
     \def\!next{\!!logshift}%
   \else
     \if p#1%
       \!dimenF=1pt
       \def\!next{\!dimenE=1p}%
     \else
       \def\!next{\!dimenE=#1.}%
     \fi
   \fi
   \!next}


% ***********************************************************
% *** PICTURES (Basic setups for PiCtures; \put commands) ***
% ***********************************************************
 
% **  User Commands:
% **    \beginpicture
% **    \endpicture    
% **    \endpicturesave <XREG,YREG>
% **    \setcoordinatesystem units <XUNIT,YUNIT> point at XREF YREF
% **    \put {OBJECT} [ORIENTATION] <XSHIFT,YSHIFT> at XCOORD YCOORD
% **    \multiput {OJBECT} [ORIENTATION] <XSHIFT,YSHIFT>) at
% **      XCOORD YCOORD
% **      *NUMBER_OF_TIMES DXCOORD DYCOORD  /
% **    \accountingon
% **    \accountingoff
% **    \stack [ORIENTATION] <LEADING> {LIST OF ITEMS}
% **    \lines [ORIENTATION] {LINES}
% **    \Lines [ORIENTATION] {LINES}
% **    \setdimensionmode
% **    \setcoordinatemode
% **    \Xdistance
% **    \Ydistance
  
% **  Internal commands:
% **    \!setputobject{OBJECT}{[ORIENTATION]<XSHIFT,YSHIFT>}
% **    \!dimenput{OBJECT}[ORIENTATION]<XSHIFT,YSHIFT>(XDIMEN,YDIMEN)
% **    \!setdimenmode
% **    \!setcoordmode
% **    \!ifdimenmode
% **    \!ifcoordmode
  
 
% **  \beginpicture
% **  \endpicture
% **  \endpicturesave <XREG,YREG>
% **    \beginpicture ... \endpicture  creates an hbox.  Objects are
% **    placed in this box using the \put command and the like (see below).
% **    The location of an object is specified in terms of coordinate system(s)
% **    established by \setcoordinatesystem.  Each coordinate system (there
% **    might be just one) specifies the length of 1 horizontal unit, the length
% **    of 1 vertical unit, and the coordinates of a "reference point".  The
% **    reference points of various coordinate systems will be in the same
% **    physical location.  The macros keep track of the size of the objects
% **    and their locations. The resulting hbox is the smallest hbox which
% **    encloses all the objects, and whose TEK reference point is the point
% **    on the left edge of the box closest vertically to the PICTEX reference
% **    point. Using \endpicturesave, you can (globally) save the distance TEK's
% **    reference point is to the right (respectively, up from) PICTEX's 
% **    reference point in the dimension register \XREG (respectively \YREG). 
% **    You can then \put the picture OBJECT into a larger picture so that its
% **    reference point is at (XCOORD,YCOORD) with the command
% **      \put {picture OBJECT} [Bl] <\XREG, \YREG> at  XCOORD YCOORD 

% **  \beginpicture : See Subsection 1.1 of the manual.
\def\beginpicture{%
  \setbox\!picbox=\hbox\bgroup%
  \!xleft=\maxdimen  
  \!xright=-\maxdimen
  \!ybot=\maxdimen
  \!ytop=-\maxdimen}
 
% **  \endpicture : See Subsection 1.1 of the manual.
\def\endpicture{%
  \ifdim\!xleft=\maxdimen%  ** check if nothing was put in picbox
    \!xleft=\!zpt \!xright=\!zpt \!ybot=\!zpt \!ytop=\!zpt 
  \fi
  \global\!Xleft=\!xleft \global\!Xright=\!xright
  \global\!Ybot=\!ybot \global\!Ytop=\!ytop
  \egroup%
  \ht\!picbox=\!Ytop  \dp\!picbox=-\!Ybot
  \ifdim\!Ybot>\!zpt
  \else 
    \ifdim\!Ytop<\!zpt
      \!Ybot=\!Ytop
    \else
      \!Ybot=\!zpt
    \fi
  \fi
  \hbox{\kern-\!Xleft\lower\!Ybot\box\!picbox\kern\!Xright}}
 
% **  \endpicturesave : See Subsection 8.4 of the manual.
\def\endpicturesave <#1,#2>{%
  \endpicture \global #1=\!Xleft \global #2=\!Ybot \ignorespaces}
 
 
% **   \setcoordinatesystem units <XUNIT,YUNIT> 
% **     point at XREF YREF  
% **   Each of `units <XUNIT,YUNIT>' and `point at XREF YREF' 
% **     are optional.
% **   Unit lengths must be given in dimensions (e.g., <10pt,1in>).
% **     Default unit lengths are 1pt, 1pt, or previous unit lengths.
% **   Reference point is specified in current units (e.g., 3 5 ). 
% **     Default reference point is 0 0 , or previous reference point.
% **   Unit lengths and reference points obey TEX's scoping rules.
% **   See Subsection 1.2 of the manual.
\def\setcoordinatesystem{%
  \!ifnextchar{u}{\!getlengths }
    {\!getlengths units <\!xunit,\!yunit>}}
\def\!getlengths units <#1,#2>{%
  \!xunit=#1\relax
  \!yunit=#2\relax
  \!ifcoordmode 
    \let\!SCnext=\!SCccheckforRP
  \else
    \let\!SCnext=\!SCdcheckforRP
  \fi
  \!SCnext}
\def\!SCccheckforRP{%
  \!ifnextchar{p}{\!cgetreference }
    {\!cgetreference point at {\!xref} {\!yref} }}
\def\!cgetreference point at #1 #2 {%
  \edef\!xref{#1}\edef\!yref{#2}%
  \!xorigin=\!xref\!xunit  \!yorigin=\!yref\!yunit  
  \!initinboundscheck % ** See linear.tex
  \ignorespaces}
\def\!SCdcheckforRP{%
  \!ifnextchar{p}{\!dgetreference}%
    {\ignorespaces}}
\def\!dgetreference point at #1 #2 {%
  \!xorigin=#1\relax  \!yorigin=#2\relax
  \ignorespaces}

 
%  ** \put {OBJECT} [XY] <XDIMEN,YDIMEN> at (XCOORD,YCOORD)
%  **   `[XY]' and `<XDIMEN,YDIMEN>' are optional.
%  **   First OBJECT is placed in an hbox (the "objectbox") and then a
%  **     "reference point" is assigned to the objectbox as follows:
%  **     [1] first, the reference point is taken to be the center of the box;
%  **     [2] next, centering is overridden by the specifications
%  **           X=l -- reference point along the left edge of the objectbox
%  **           X=r -- reference point along the right edge of the objectbox
%  **           Y=b -- reference point along the bottom edge of the objectbox
%  **           Y=B -- reference point along the Baseline of the objectbox
%  **           Y=t -- reference point along the top edge of the objectbox;
%  **     [3] finally the reference point is shifted left by XDIMEN, down
%  **           by YDIMEN  (both default to 0pt).
%  **   The objectbox is placed within PICBOX with its reference point at  
%  **     (XCOORD,YCOORD). 
%  **   If OBJECT is a saved box, say  box0, you have to write
%  **     \put{\box0}...   or  \put{\copy0}...
%  **   The objectbox is void after the put.
%  **   See Subsection 2.1 of the manual.
\long\def\put#1#2 at #3 #4 {%
  \!setputobject{#1}{#2}%
  \!xpos=\!M{#3}\!xunit  \!ypos=\!M{#4}\!yunit  
  \!rotateaboutpivot\!xpos\!ypos%
  \advance\!xpos -\!xorigin  \advance\!xpos -\!xshift
  \advance\!ypos -\!yorigin  \advance\!ypos -\!yshift
  \kern\!xpos\raise\!ypos\box\!putobject\kern-\!xpos%
  \!doaccounting\ignorespaces}
 
%  **   \multiput etc.  Like  \put.  The objectbox is not voided until the
%  **     termininating /, and is placed repeatedly with:
%  **     XCOORD YCOORD -- the objectbox is put down with its reference point
%  **       at (XCOORD,YCOORD);
%  **     *N DXCOORD DYCOORD -- each of N times the current
%  **       (xcoord,ycoord) is incremented by (DXCOORD,DYCOORD), and the
%  **       objectbox is put down with its reference point at (xcoord,ycoord)
%  **       (This specification has to follow an XCOORD YCOORD pair)
%  **     See Subsection 2.2 of the manual.
\long\def\multiput #1#2 at {%
  \!setputobject{#1}{#2}%
  \!ifnextchar"{\!putfromfile}{\!multiput}}
\def\!putfromfile"#1"{%
  \expandafter\!multiput \input #1 /}
\def\!multiput{%
  \futurelet\!nextchar\!!multiput}
\def\!!multiput{%
  \if *\!nextchar
    \def\!nextput{\!alsoby}%
  \else
    \if /\!nextchar
      \def\!nextput{\!finishmultiput}%
    \else
      \def\!nextput{\!alsoat}%
    \fi
  \fi
  \!nextput}
\def\!finishmultiput/{%
  \setbox\!putobject=\hbox{}%
  \ignorespaces}
 
%  **   \!alsoat XCOORD YCOORD 
%  **     The objectbox is put down with reference point at XCOORD,YCOORD
\def\!alsoat#1 #2 {%
  \!xpos=\!M{#1}\!xunit  \!ypos=\!M{#2}\!yunit  
  \!rotateaboutpivot\!xpos\!ypos%
  \advance\!xpos -\!xorigin  \advance\!xpos -\!xshift
  \advance\!ypos -\!yorigin  \advance\!ypos -\!yshift
  \kern\!xpos\raise\!ypos\copy\!putobject\kern-\!xpos%
  \!doaccounting
  \!multiput}
 
% **   \!alsoby*N DXCOORD DYCOORD
% **     N times, the current (XCOORD,YCOORD) is advanced by (DXCOORD,DYCOORD),
% **     and the current (shifted, oriented) OBJECT is put down.
\def\!alsoby*#1 #2 #3 {%
  \!dxpos=\!M{#2}\!xunit \!dypos=\!M{#3}\!yunit 
  \!rotateonly\!dxpos\!dypos
  \!ntemp=#1%
  \!!loop\ifnum\!ntemp>0
    \advance\!xpos by \!dxpos  \advance\!ypos by \!dypos
    \kern\!xpos\raise\!ypos\copy\!putobject\kern-\!xpos%
    \advance\!ntemp by -1
  \repeat
  \!doaccounting 
  \!multiput}
 
% **  \accountingoff : Suspends PiCTeX's accounting of the aggregate
% **    size of the picture box.
% **  \accounting on : Reinstates accounting.
% **  See Subsection 8.2 of the manual.
\def\accountingon{\def\!doaccounting{\!!doaccounting}\ignorespaces}
\def\accountingoff{\def\!doaccounting{}\ignorespaces}
\accountingon
\def\!!doaccounting{%
  \!xtemp=\!xpos  
  \!ytemp=\!ypos
  \ifdim\!xtemp<\!xleft 
     \!xleft=\!xtemp 
  \fi
  \advance\!xtemp by  \!wd 
  \ifdim\!xright<\!xtemp 
    \!xright=\!xtemp
  \fi
  \advance\!ytemp by -\!dp
  \ifdim\!ytemp<\!ybot  
    \!ybot=\!ytemp
  \fi
  \advance\!ytemp by  \!dp
  \advance\!ytemp by  \!ht 
  \ifdim\!ytemp>\!ytop  
    \!ytop=\!ytemp  
  \fi}
 
\long\def\!setputobject#1#2{%
  \setbox\!putobject=\hbox{#1}%
  \!ht=\ht\!putobject  \!dp=\dp\!putobject  \!wd=\wd\!putobject
  \wd\!putobject=\!zpt
  \!xshift=.5\!wd   \!yshift=.5\!ht   \advance\!yshift by -.5\!dp
  \edef\!putorientation{#2}%
  \expandafter\!SPOreadA\!putorientation[]\!nil%
  \expandafter\!SPOreadB\!putorientation<\!zpt,\!zpt>\!nil\ignorespaces}
 
\def\!SPOreadA#1[#2]#3\!nil{\!etfor\!orientation:=#2\do\!SPOreviseshift}
 
\def\!SPOreadB#1<#2,#3>#4\!nil{\advance\!xshift by -#2\advance\!yshift by -#3}
 
\def\!SPOreviseshift{%
  \if l\!orientation 
    \!xshift=\!zpt
  \else 
    \if r\!orientation 
      \!xshift=\!wd
    \else 
      \if b\!orientation
        \!yshift=-\!dp
      \else 
        \if B\!orientation 
          \!yshift=\!zpt
        \else 
          \if t\!orientation 
            \!yshift=\!ht
          \fi 
        \fi
      \fi
    \fi
  \fi}
 
 
%  **  \!dimenput{OBJECT} <XDIMEN,YDIMEN> [XY] (XLOC,YLOC)
%  **    This is an internal put routine, similar to \put, except that
%  **    XLOC=distance right from reference point, YLOC=distance up from
%  **    reference point. XLOC and YLOC are dimensions, so this routine
%  **    is completely independent of the current coordinate system. 
%  **    This routine does NOT do ROTATIONS.
\long\def\!dimenput#1#2(#3,#4){%
  \!setputobject{#1}{#2}%
  \!xpos=#3\advance\!xpos by -\!xshift
  \!ypos=#4\advance\!ypos by -\!yshift
  \kern\!xpos\raise\!ypos\box\!putobject\kern-\!xpos%
  \!doaccounting\ignorespaces}
 
 
%  ** The following macros permit the picture drawing routines to be used 
%  ** either in the default "coordinate mode", or in "dimension mode".
%  **   In coordinate mode  \!M(1.5,\!xunit)    expands to  1.5\!xunit
%  **   In dimension  mode  \!M(1.5pt,\!xunit)  expands to  1.5pt
%  ** Dimension mode is useful in coding macros.
%  ** Any special purpose picture macro that sets dimension mode should 
%  ** reset coordinate mode before completion.
%  ** See Subsection 9.2 of the manual.
\def\!setdimenmode{%
  \let\!M=\!M!!\ignorespaces}
\def\!setcoordmode{%
  \let\!M=\!M!\ignorespaces}
\def\!ifcoordmode{%
  \ifx \!M \!M!}
\def\!ifdimenmode{%
  \ifx \!M \!M!!}
\def\!M!#1#2{#1#2} 
\def\!M!!#1#2{#1}
\!setcoordmode
\let\setdimensionmode=\!setdimenmode
\let\setcoordinatemode=\!setcoordmode

%  ** \Xdistance{XCOORD}, \Ydistance{YCOORD}  are the horizontal and
%  **   vertical distances from the origin (0,0) to the point
%  **   (XCOORD,YCOORD)  in the current coordinate system.
%  ** See Subsection 9.2 of the manual.
\def\Xdistance#1{%
  \!M{#1}\!xunit
  \ignorespaces}
\def\Ydistance#1{%
  \!M{#1}\!yunit
  \ignorespaces}

% ** The following macros -- \stack, \line, and \Lines -- are useful for
% **   annotating PiCtures. They can be used outside the \beginpicture ...
% **   \endpicture environment.

% ** \stack [POSITIONING] <LEADING> {VALUESLIST}
% ** Builds a vertical stack of the values in VALUESLIST. Values in
% ** VALUESLIST are separated by commas.  In the resulting stack, values are
% ** centered by default, and positioned flush left (right) if 
% ** POSITIONING = l (r).  Values are separated vertically by LEADING,
% ** which defaults to \stackleading.
% ** See Subsection 2.3 of the manual.
\def\stack{%
  \!ifnextchar[{\!stack}{\!stack[c]}}
\def\!stack[#1]{%
  \let\!lglue=\hfill \let\!rglue=\hfill
  \expandafter\let\csname !#1glue\endcsname=\relax
  \!ifnextchar<{\!!stack}{\!!stack<\stackleading>}}
\def\!!stack<#1>#2{%
  \vbox{\def\!valueslist{}\!ecfor\!value:=#2\do{%
    \expandafter\!rightappend\!value\withCS{\\}\to\!valueslist}%
    \!lop\!valueslist\to\!value
    \let\\=\cr\lineskiplimit=\maxdimen\lineskip=#1%
    \baselineskip=-1000pt\halign{\!lglue##\!rglue\cr \!value\!valueslist\cr}}%
  \ignorespaces}

% ** \lines [POSITIONING] {LINES}
% ** Builds a vertical array of the lines in LINES. Each line in LINES
% ** is terminated by a \cr.  In the resulting array, lines are
% ** centered by default, and positioned flush left (right) if 
% ** POSITIONING = l (r).  The lines in the array are subject to TeX's
% ** usual spacing rules: in particular the baselines are ordinarily an equal
% ** distance apart. The baseline of the array is the baseline of the
% ** the bottom line.
% ** See Subsection 2.3 of the manual.
\def\lines{%
  \!ifnextchar[{\!lines}{\!lines[c]}}
\def\!lines[#1]#2{%
  \let\!lglue=\hfill \let\!rglue=\hfill
  \expandafter\let\csname !#1glue\endcsname=\relax
  \vbox{\halign{\!lglue##\!rglue\cr #2\crcr}}%
  \ignorespaces}

% ** \Lines [POSITIONING] {LINES}
% ** Like \lines, but the baseline of the array is the baseline of the
% ** top line.  See Subsection 2.3 of the manual.
\def\Lines{%
  \!ifnextchar[{\!Lines}{\!Lines[c]}}
\def\!Lines[#1]#2{%
  \let\!lglue=\hfill \let\!rglue=\hfill
  \expandafter\let\csname !#1glue\endcsname=\relax
  \vtop{\halign{\!lglue##\!rglue\cr #2\crcr}}%
  \ignorespaces}


% *********************************************
% *** PLOTTING (Things to do with plotting) ***
% *********************************************
 
% **  User commands
% **    \setplotsymbol ({PLOTSYMBOL} [ORIENTATION] <XSHIFT,YSHIFT>)
% **    \savelinesandcurves on "FILE_NAME"
% **    \dontsavelinesandcurves
% **    \writesavefile {MESSAGE}
% **    \replot {FILE_NAME}
 
% **  Internal command
% **    \!plot(XDIMEN,YDIMEN)
 
% **  \setplotsymbol ({PLOTSYMBOL} [ ] < , >)
% **  Save PLOTSYMBOL away in an hbox for use with curve plotting routines
% **  See Subsection 5.2 of the manual.
\def\setplotsymbol(#1#2){%
  \!setputobject{#1}{#2}
  \setbox\!plotsymbol=\box\!putobject%
  \!plotsymbolxshift=\!xshift 
  \!plotsymbolyshift=\!yshift 
  \ignorespaces}
 
\setplotsymbol({\fiverm .})%       ** initialize plotsymbol
 
 
% **  \!plot is either \!!plot (when no lines and curves are being saved) or
% **                   \!!!plot (when   lines and curves are being saved)
 
% **  \!!plot(XDIMEN,YDIMEN)
% **  Places the current plotsymbol a horizontal distance=XDIMEN-xorigin 
% **    and a vertical distance=YDIMEN-yorigin from the current
% **    reference point.  
\def\!!plot(#1,#2){%
  \!dimenA=-\!plotxorigin \advance \!dimenA by #1%    ** over
  \!dimenB=-\!plotyorigin \advance \!dimenB by #2%    ** up
  \kern\!dimenA\raise\!dimenB\copy\!plotsymbol\kern-\!dimenA%
  \ignorespaces}
 
% **  \!!!plot(XDIMEN,YDIMEN)
% **  Like \!!plot, but also saves the plot location in units of 
% **    scaled point, on file `replotfile'
\def\!!!plot(#1,#2){%
  \!dimenA=-\!plotxorigin \advance \!dimenA by #1%    ** over
  \!dimenB=-\!plotyorigin \advance \!dimenB by #2%    ** up
  \kern\!dimenA\raise\!dimenB\copy\!plotsymbol\kern-\!dimenA%
  \!countE=\!dimenA
  \!countF=\!dimenB
  \immediate\write\!replotfile{\the\!countE,\the\!countF.}%
  \ignorespaces}


% ** \savelinesandcurves on "FILE_NAME"
% **   Switch to save locations used for plotting lines and curves
% **   (No advantage in saving locations for solid lines; however
% **   replotting curve locations speeds things up by a factor of about 4. 
% ** \dontsavelinesandcurves
% **   Terminates \savelinesandcurves. The default.
% ** See Subsection 5.6 of the manual.
\def\savelinesandcurves on "#1" {%
  \immediate\closeout\!replotfile
  \immediate\openout\!replotfile=#1%
  \let\!plot=\!!!plot}

\def\dontsavelinesandcurves {%
  \let\!plot=\!!plot}
\dontsavelinesandcurves

% ** \writesavefile {MESSAGE}
% ** The message is preceded by a "%", so that it won't interfere
% ** with replotting.
% ** See Subsection 5.6 of the manual.
{\catcode`\%=11\xdef\!Commentsignal{%}}
\def\writesavefile#1 {%
  \immediate\write\!replotfile{\!Commentsignal #1}%
  \ignorespaces}

% ** \replot "FILE_NAME"
% **   Replots the locations saved earlier under \savelinesandcurves
% **   on "FILE_NAME"
% ** See Subsection 5.6 of the manual.
\def\replot"#1" {%
  \expandafter\!replot\input #1 /}
\def\!replot#1,#2. {%
  \!dimenA=#1sp
  \kern\!dimenA\raise#2sp\copy\!plotsymbol\kern-\!dimenA
  \futurelet\!nextchar\!!replot}
\def\!!replot{%
  \if /\!nextchar 
    \def\!next{\!finish}%
  \else
    \def\!next{\!replot}%
  \fi
  \!next}
% **************************************************
% ***  PYTHAGORAS  (Euclidean distance function) ***
% **************************************************

% ** User command:
% **   \placehypotenuse for <dimension1> and <dimension2> in <register> 

% ** Internal command:
% **   \!Pythag{X}{Y}{Z}
% **     Input X,Y are dimensions, or dimension registers.
% **     Output Z == sqrt(X**2+Y**2) must be a dimension register.
% **     Assumes that |X|+|Y| < 2048pt (about 28in).
 
% ** Without loss of generality, suppose  x>0, y>0.  Put s = x+y,
% **   z = sqrt(x**2+y**2). Then  z = s*f,  where  f = sqrt(t**2 + (1-t)**2)
% **   = sqrt((1+tau**2)/2), where  t = x/s  and  tau = 2(t-1/2) .
 
% ** Uses the \!divide macro (which uses registers \!dimenA--\!dimenD.
% ** Uses the \!removept macro   (e.g., 123.45pt --> 123.45)
% ** Uses registers \!dimenE--\!dimenI.
\def\!Pythag#1#2#3{%
  \!dimenE=#1\relax                                     
  \ifdim\!dimenE<\!zpt 
    \!dimenE=-\!dimenE 
  \fi%                                            ** dimE = |x|
  \!dimenF=#2\relax
  \ifdim\!dimenF<\!zpt 
    \!dimenF=-\!dimenF 
  \fi%                                            ** dimF = |y|
  \advance \!dimenF by \!dimenE%                  ** dimF = s = |x|+|y|
  \ifdim\!dimenF=\!zpt 
    \!dimenG=\!zpt%                               ** dimG = z = sqrt(x**2+y**2)
  \else 
    \!divide{8\!dimenE}\!dimenF\!dimenE%          ** now dimE = 8t = (8|x|)/s
    \advance\!dimenE by -4pt%                     ** 8tau = (8t-4)*2
      \!dimenE=2\!dimenE%                         **   (tau = 2*t - 1)
    \!removept\!dimenE\!!t%                       ** 8tau, without "pt"
    \!dimenE=\!!t\!dimenE%                        ** (8tau)**2, in pts
    \advance\!dimenE by 64pt%                     ** u = [64 + (8tau)**2]/2
    \divide \!dimenE by 2%                        **   [u = (8f)**2]
    \!dimenH=7pt%                                 ** initial guess g at sqrt(u)
    \!!Pythag\!!Pythag\!!Pythag%                  ** 3 iterations give sqrt(u)
    \!removept\!dimenH\!!t%                       ** 8f=sqrt(u), without "pt"
    \!dimenG=\!!t\!dimenF%                        ** z = (8f)*s/8
    \divide\!dimenG by 8
  \fi
  #3=\!dimenG
  \ignorespaces}

\def\!!Pythag{%                                   ** Newton-Raphson for sqrt
  \!divide\!dimenE\!dimenH\!dimenI%               ** v = u/g
  \advance\!dimenH by \!dimenI%                   ** g <-- (g + u/g)/2
    \divide\!dimenH by 2}

% **  \placehypotenuse for <XI> and <ETA> in <ZETA>
% **  See Subsection 9.3 of the manual.
\def\placehypotenuse for <#1> and <#2> in <#3> {%
  \!Pythag{#1}{#2}{#3}}


% **********************************************
% *** QUADRATIC ARC  (Draws a quadratic arc) ***
% **********************************************
 
% **  Internal command
% **    \!qjoin (XCOORD1,YCOORD1) (XCOORD2,YCOORD2)
 
% **  \!qjoin (XCOORD1,YCOORD1) (XCOORD2,YCOORD2)
% **  Draws an arc starting at the (last) point specified by the most recent
% **  \!qjoin, or \!ljoin, or \!start  and passing through (X_1,Y_1), (X_2,Y_2).
% **  Uses quadratic interpolation in both  x  and  y:  
% **    x(t), 0 <= t <= 1, interpolates  x_0, x_1, x_2  at  t=0, .5, 1
% **    y(t), 0 <= t <= 1, interpolates  y_0, y_1, y_2  at  t=0, .5, 1
 
\def\!qjoin (#1,#2) (#3,#4){%
  \advance\!intervalno by 1
  \!ifcoordmode
    \edef\!xmidpt{#1}\edef\!ymidpt{#2}%
  \else
    \!dimenA=#1\relax \edef\!xmidpt{\the\!dimenA}%
    \!dimenA=#2\relax \edef\!xmidpt{\the\!dimenA}%
  \fi
  \!xM=\!M{#1}\!xunit  \!yM=\!M{#2}\!yunit   \!rotateaboutpivot\!xM\!yM
  \!xE=\!M{#3}\!xunit  \!yE=\!M{#4}\!yunit   \!rotateaboutpivot\!xE\!yE
%
% ** Find coefficients for x(t)=a_x + b_x*t + c_x*t**2
  \!dimenA=\!xM  \advance \!dimenA by -\!xS%   ** dimA = I = xM - xS
  \!dimenB=\!xE  \advance \!dimenB by -\!xM%   ** dimB = II = xE-xM
  \!xB=3\!dimenA \advance \!xB by -\!dimenB%   ** b=3I-II
  \!xC=2\!dimenB \advance \!xC by -2\!dimenA%  ** c=2(II-I)
%
% ** Find coefficients for y(t)=y_x + b_y*t + c_y*t**2
  \!dimenA=\!yM  \advance \!dimenA by -\!yS%   
  \!dimenB=\!yE  \advance \!dimenB by -\!yM%  
  \!yB=3\!dimenA \advance \!yB by -\!dimenB%  
  \!yC=2\!dimenB \advance \!yC by -2\!dimenA% 
%
% ** Use Simpson's rule to calculate arc length over [0,1/2]:
% **   arc length = 1/2[1/6 f(0) + 4/6 f(1/4) + 1/6 f(1/2)]
% ** with f(t) = sqrt(x'(t)**2 + y'(t)**2).
  \!xprime=\!xB  \!yprime=\!yB%          ** x'(t) = b + 2ct
  \!dxprime=.5\!xC  \!dyprime=.5\!yC%    ** dt=1/4 ==> dx'(t) = c/2
  \!getf \!midarclength=\!dimenA
  \!getf \advance \!midarclength by 4\!dimenA
  \!getf \advance \!midarclength by \!dimenA
  \divide \!midarclength by 12
%
% ** Get arc length over [0,1].
  \!arclength=\!dimenA
  \!getf \advance \!arclength by 4\!dimenA
  \!getf \advance \!arclength by \!dimenA
  \divide \!arclength by 12%             ** Now have arc length over [1/2,1]
  \advance \!arclength by \!midarclength
  \global\advance \totalarclength by \!arclength
%
%
% ** Check to see if there's anything to plot in this interval
  \ifdim\!distacross>\!arclength 
    \advance \!distacross by -\!arclength%   ** nothing 
%
  \else
    \!initinverseinterp%  ** initialize for inverse interpolation on arc length
    \loop\ifdim\!distacross<\!arclength%     ** loop over points on arc 
      \!inverseinterp%    ** find  t  such that arc length[0,t] = distacross,
%                         **   using inverse quadratic interpolation
%                         ** now evaluate x(t)=(c*t + b)*t + a
      \!xpos=\!t\!xC \advance\!xpos by \!xB
        \!xpos=\!t\!xpos \advance \!xpos by \!xS
%                                             ** evaluate y(t)
      \!ypos=\!t\!yC \advance\!ypos by \!yB
        \!ypos=\!t\!ypos \advance \!ypos by \!yS
      \!plotifinbounds%                       ** plot point if in bounds
      \advance\!distacross \plotsymbolspacing%** advance arc length for next pt
      \!advancedashing%                       ** see "linear"
    \repeat  
%
    \advance \!distacross by -\!arclength%    ** prepare for next interval 
  \fi
%
  \!xS=\!xE%              ** shift ending points to starting points
  \!yS=\!yE
  \ignorespaces}
 
 
% ** \!getf -- Calculates sqrt(x'(t)**2 + y'(t)**2) and advances
% **   x'(t) and y'(t)
\def\!getf{\!Pythag\!xprime\!yprime\!dimenA%
  \advance\!xprime by \!dxprime
  \advance\!yprime by \!dyprime}
 
 
% ** \!initinverseinterp -- initializes for inverse quadratic interpolation
% ** of arc length provided  1/3 < midarclength/arclength < 2/3; otherwise
% ** initializes for inverse linear interpolation.
\def\!initinverseinterp{%
  \ifdim\!arclength>\!zpt
    \!divide{8\!midarclength}\!arclength\!dimenE% ** dimE=8w=8r/s, where  r 
%                                               **  = midarclength, s=arclength
% **  Test for  w  out of range:  w<1/3  or w>2/3
    \ifdim\!dimenE<\!wmin \!setinverselinear
    \else 
      \ifdim\!dimenE>\!wmax \!setinverselinear
      \else%                                    ** w  in range: initialize
        \def\!inverseinterp{\!inversequad}\ignorespaces
%
% **     Calculate the coefficients  \!beta  and  \!gamma  of the quadratic
% **                    t = \!beta*v + \!gamma*v**2
% **     taking the values  t=0, 1/2, 1  at  v=0, w==r/s, 1  respectively:
% **        \!beta = (1/2 - w**2)/[w(1-w)] 
% **        \!gamma = 1 - beta.
%
         \!removept\!dimenE\!Ew%           **  8w, without "pt"
         \!dimenF=-\!Ew\!dimenE%           **  -(8w)**2
         \advance\!dimenF by 32pt%         **  32 - (8w)**2
         \!dimenG=8pt 
         \advance\!dimenG by -\!dimenE%    **  8 - 8w
         \!dimenG=\!Ew\!dimenG%            **  (8w)*(8-8w)
         \!divide\!dimenF\!dimenG\!beta%   **  beta = (32-(8w)**2)/(8w(8-8w))
%                                          **       = (1/2 - w**2)/(w(1-w))
         \!gamma=1pt
         \advance \!gamma by -\!beta%      **  gamma = 1-beta
      \fi%       ** end of the \ifdim\!dimenE>\!wmax
    \fi%         ** end of the \ifdim\!dimenE<\!wmin
  \fi%           ** end of the \ifdim\!arclength>\!zpt
  \ignorespaces}
 
 
% ** For 0 <= t <= 1, let AL(t) = arclength[0,t]/arclength[0,1]; note
% ** AL(0)=0, AL(1/2)=midarclength/arclength, AL(1)=1.  This routine
% ** calculates an approximation to AL^{-1}(distance across/arclength),
% ** using the assumption that AL^{-1} is quadratic.  Specifically, 
% ** it finds  t  such that
% **    AL^{-1}(v) =. t = v*(\!beta + \!gamma*v)
% ** where  \!beta  and  \!gamma  are set by \!initinv, and where
% ** v=distance across/arclength
\def\!inversequad{%
  \!divide\!distacross\!arclength\!dimenG%   ** dimG = v = distacross/arclength
  \!removept\!dimenG\!v%                     ** v, without "pt"
  \!dimenG=\!v\!gamma%                       ** gamma*v
  \advance\!dimenG by \!beta%                ** beta + gamma*v
  \!dimenG=\!v\!dimenG%                      ** t = v*(beta + gamma*v)
  \!removept\!dimenG\!t}%                    ** t, without "pt"
 
 
% ** When  w <= 1/3  or  w >= 2/3, the following routine writes (using
% ** plain TEK's \wlog command) a warning message on the user's log file,
% ** and initializes for inverse linear interpolation on arc length.
\def\!setinverselinear{%
  \def\!inverseinterp{\!inverselinear}%
  \divide\!dimenE by 8 \!removept\!dimenE\!t
  \!countC=\!intervalno \multiply \!countC 2
  \!countB=\!countC     \advance \!countB -1
  \!countA=\!countB     \advance \!countA -1
  \wlog{\the\!countB th point (\!xmidpt,\!ymidpt) being plotted 
    doesn't lie in the}%
  \wlog{ middle third of the arc between the \the\!countA th 
    and \the\!countC th points:}%
  \wlog{ [arc length \the\!countA\space to \the\!countB]/[arc length 
    \the \!countA\space to \the\!countC]=\!t.}%
  \ignorespaces}
 
% **  Inverse linear interpolation
\def\!inverselinear{% 
  \!divide\!distacross\!arclength\!dimenG
  \!removept\!dimenG\!t}


% **************************************
% **  ROTATIONS  (Handles rotations) ***
% **************************************
 
% ** User commands
% **   \startrotation [by COS_OF_ANGLE SIN_OF_ANGLE] [about XPIVOT YPIVOT]
% **   \stoprotation

% **   \startrotation [by COS_OF_ANGLE SIN_OF_ANGLE] [about XPIVOT YPIVOT]
% ** Future (XCOORD,YCOORD)'s will be rotated about (XPIVOT,YPIVOT) 
% ** by the angle with the give COS and SIN. Both fields are optional.
% ** [COS,SIN] defaults to previous value, or (1,0).
% ** (XPIVOT,YPIVOT) defaults to previous value, or (0,0)
% ** You can't change the coordinate system in the scope of a rotation.
% ** See Subsection 9.1 of the manual.
\def\startrotation{%
  \let\!rotateaboutpivot=\!!rotateaboutpivot
  \let\!rotateonly=\!!rotateonly
  \!ifnextchar{b}{\!getsincos }%
    {\!getsincos by {\!cosrotationangle} {\!sinrotationangle} }}
\def\!getsincos by #1 #2 {%
  \edef\!cosrotationangle{#1}%
  \edef\!sinrotationangle{#2}%
  \!ifcoordmode 
    \let\!ROnext=\!ccheckforpivot
  \else
    \let\!ROnext=\!dcheckforpivot
  \fi
  \!ROnext}
\def\!ccheckforpivot{%
  \!ifnextchar{a}{\!cgetpivot}%
    {\!cgetpivot about {\!xpivotcoord} {\!ypivotcoord} }}
\def\!cgetpivot about #1 #2 {%
  \edef\!xpivotcoord{#1}%
  \edef\!ypivotcoord{#2}%
  \!xpivot=#1\!xunit  \!ypivot=#2\!yunit
  \ignorespaces}
\def\!dcheckforpivot{%
  \!ifnextchar{a}{\!dgetpivot}{\ignorespaces}}
\def\!dgetpivot about #1 #2 {%
  \!xpivot=#1\relax  \!ypivot=#2\relax
  \ignorespaces}
  

% ** Following terminates rotation.
% ** See Subsection 9.1 of the manual.
\def\stoprotation{%
  \let\!rotateaboutpivot=\!!!rotateaboutpivot
  \let\!rotateonly=\!!!rotateonly
  \ignorespaces}
 
% ** !!rotateaboutpivot{XREG}{YREG}
% ** XREG <-- xpvt + cos(angle)*(XREG-xpvt) - sin(angle)*(YREG-ypvt)
% ** YREG <-- ypvt + cos(angle)*(YREG-ypvt) + sin(angle)*(XREG-xpvt)
% ** XREG,YREG are dimension registers. Can't be \!dimenA to \!dimenD
\def\!!rotateaboutpivot#1#2{%
  \!dimenA=#1\relax  \advance\!dimenA -\!xpivot
  \!dimenB=#2\relax  \advance\!dimenB -\!ypivot
  \!dimenC=\!cosrotationangle\!dimenA
    \advance \!dimenC -\!sinrotationangle\!dimenB
  \!dimenD=\!cosrotationangle\!dimenB
    \advance \!dimenD  \!sinrotationangle\!dimenA
  \advance\!dimenC \!xpivot  \advance\!dimenD \!ypivot
  #1=\!dimenC  #2=\!dimenD
  \ignorespaces}

% ** \!!rotateonly{XREG}{YREG}
% ** Like \!!rotateaboutpivot, but with a pivot of  (0,0)
\def\!!rotateonly#1#2{%
  \!dimenA=#1\relax  \!dimenB=#2\relax 
  \!dimenC=\!cosrotationangle\!dimenA
    \advance \!dimenC -\!rotsign\!sinrotationangle\!dimenB
  \!dimenD=\!cosrotationangle\!dimenB
    \advance \!dimenD  \!rotsign\!sinrotationangle\!dimenA
  #1=\!dimenC  #2=\!dimenD
  \ignorespaces}
\def\!rotsign{}
\def\!!!rotateaboutpivot#1#2{\relax}
\def\!!!rotateonly#1#2{\relax}
\stoprotation

\def\!reverserotateonly#1#2{%
  \def\!rotsign{-}%
  \!rotateonly{#1}{#2}%
  \def\!rotsign{}%
  \ignorespaces}


% **********************************
% *** SHADING  (Handles shading) ***
% **********************************

% **  User commands
% **    \setshadegrid [span <SPAN>] [point at XSHADE YSHADE] 
% **    \setshadesymbol [<LS, RS, BS, TS>] ({SHADESYMBOL}
% **      <XDIMEN,YDIMEN> [ORIENTATION])

% **  Internal commands:
% **    \!startvshade  (xS,ybS,ytS)
% **    \!starthshade  (yS,xlS,xrS)
% **    \!lshade [<LS,RS,BS,TS>]
% **       ** when shading vertically:
% **       [the region from (xS,ybS,ytS) to] (xE,ybE,ytE)
% **       ** when shading horizontally:
% **       [the region from (yS,xlS,xrS) to] (yE,xlE,xrE)
% **    \!qshade [<LS,RS,BS,TS>]
% **       ** when shading vertically:
% **       [the region from (xS,ybS,ytS) to] (xM,ybM,ytM)  (xE,ybE,ytE)
% **       ** when shading horizontally:
% **       [the region from (yS,xlS,xrS) to] (yM,xlM,xrM)  (yE,xlE,xrE)
% **    \!lattice{ANCHOR}{SPAN}{LOCATION}{INDEX}{LATTICE LOCATION}
% **    \!override{NOMINAL DIMEN}{REPLACEMENT DIMEN}{DIMEN}


% **  The shading routine can operate either in a "vertical mode" or a
% **  "horizontal mode".  In vertical mode, the region to be shaded is specified
% **  in the form
% **                 {(x,y): xl <= x <= xr  &  yb(x) <= y <= yt(x)}
% **  where  yb  and  yt  are functions of  x.  In horizontal mode, the region
% **  is specified in the form
% **                 {(x,y): yb <= y <= yt  &  xl(y) <= x <= xr(y)}.
% **  The functions  yb  and  yt  may be either both linear or both quadratic;
% **  similarly for  xl  and  xr.  A region with say, piecewise quadratic bottom
% **  and top boundaries, can be shaded by consecutive (vertical) \!qshades,
% **  proceeding from left to right.  Similarly, a region with piecewise     
% **  quadratic left and right boundaries can be shaded by consecutive
% **  (horizontal) \!qshades, proceeding from bottom to top.  More complex
% **  regions can be shaded by partitioning them into appropriate subregions,
% **  and shading those.
    
% **  Shading is accomplished by placing a user-selected shading symbol at
% **  those points of a regular grid which fall within the region to be
% **  shaded.  This region can be "shrunk" so that a largish shading symbol
% **  will not extend outside it.  Shrinking is accomplished by specifying
% **  shrinkages for the left, right, bottom, and top boundaries, in a manner
% **  discussed further below.

% **  \shades and \!joins MUST NOT be intermingled.  Finish drawing a curve
% **  before starting to shade a region, and finish shading a region before
% **  starting to draw a curve.


% **  \setshadegrid [span <SPAN>] [point at XSHADE YSHADE] 
% **  The shading symbol is placed down on the points of a grid centered
% **  at the coordinate point (XSHADE,YSHADE).  The grid points are of the
% **  form (j*SPAN,k*SPAN), with  j+k  even.  SPAN is specified
% **  as a dimension.
% **  (XSHADE,YSHADE) defaults to previous (XSHADE,YSHADE) (or (0,0) if none)
% **  SPAN defaults to previous span (or 5pt if none)
% **  See Subsection 7.2 of the manual.
\def\setshadegrid{%
  \!ifnextchar{s}{\!getspan }
    {\!getspan span <\!dshade>}}
\def\!getspan span <#1>{%
  \!dshade=#1\relax
  \!ifcoordmode 
    \let\!GRnext=\!GRccheckforAP
  \else
    \let\!GRnext=\!GRdcheckforAP
  \fi
  \!GRnext}
\def\!GRccheckforAP{%
  \!ifnextchar{p}{\!cgetanchor }
    {\!cgetanchor point at {\!xshadesave} {\!yshadesave} }}
\def\!cgetanchor point at #1 #2 {%
  \edef\!xshadesave{#1}\edef\!yshadesave{#2}%
  \!xshade=\!xshadesave\!xunit  \!yshade=\!yshadesave\!yunit
  \ignorespaces}
\def\!GRdcheckforAP{%
  \!ifnextchar{p}{\!dgetanchor}%
    {\ignorespaces}}
\def\!dgetanchor point at #1 #2 {%
  \!xshade=#1\relax  \!yshade=#2\relax
  \ignorespaces}

% **  \setshadesymbol  [<LS, RS, BS, TS>] ({SHADESYMBOL}
% **    <XDIMEN,YDIMEN> [ORIENTATION])
% **  Saves SHADESYMBOL away in an hbox for use with shading routines.
% **  A shade symbol will not be plotted if its plot position comes within
% **    distance LS of the left boundary,  RS of the right boundary,  TS of the
% **    top boundary,  BS of the bottom boundary.  These parameters have 
% **    default values that should work in most cases (see below).
% **    To override a default value, specify the replacement value
% **    in the appropriate subfield of the shrinkages field.
% **    0pt may be coded as  "z" (without the quotes).  To accept a
% **    default value, leave the field empty.  Thus
% **      [,z,,5pt]  sets  LS=default, RS=0pt, BS=default, TS=5pt .
% **    Skipping the shrinkages field accepts all the defaults.
% **  See Subsection 7.1 of the manual.
\def\setshadesymbol{%
  \!ifnextchar<{\!setshadesymbol}{\!setshadesymbol<,,,> }}

\def\!setshadesymbol <#1,#2,#3,#4> (#5#6){%
% **  set the shadesymbol
  \!setputobject{#5}{#6}%                        
  \setbox\!shadesymbol=\box\!putobject%
  \!shadesymbolxshift=\!xshift \!shadesymbolyshift=\!yshift
%
% **  set the shrinkages
  \!dimenA=\!xshift \advance\!dimenA \!smidge% ** default LS = xshift - smidge
  \!override\!dimenA{#1}\!lshrinkage%         
  \!dimenA=\!wd \advance \!dimenA -\!xshift%   ** default RS = width - xshift
    \advance\!dimenA \!smidge%                                  - smidge
    \!override\!dimenA{#2}\!rshrinkage
  \!dimenA=\!dp \advance \!dimenA \!yshift%    ** default BS = depth + yshift
    \advance\!dimenA \!smidge%                                  - smidge
    \!override\!dimenA{#3}\!bshrinkage
  \!dimenA=\!ht \advance \!dimenA -\!yshift%   ** default TS = height - yshift
    \advance\!dimenA \!smidge%                                  - smidge
    \!override\!dimenA{#4}\!tshrinkage
  \ignorespaces}
\def\!smidge{-.2pt}%

% ** \!override{NOMINAL DIMEN}{REPLACEMENT DIMEN}{DIMEN}
% ** Overrides the NOMINAL DIMEN by the REPLACEMENT DIMEN to produce DIMEN,
% ** according to the following rules:
% **   REPLACEMENT DIMEN empty: DIMEN <-- NOMINAL DIMEN
% **   REPLACEMENT DIMEN z:     DIMEN <-- 0pt
% **   otherwise:               DIMEN <-- REPLACEMENT DIMEN
% ** DIMEN must be a dimension register
\def\!override#1#2#3{%
  \edef\!!override{#2}% 
  \ifx \!!override\empty
    #3=#1\relax
  \else
    \if z\!!override
      #3=\!zpt
    \else
      \ifx \!!override\!blankz
        #3=\!zpt
      \else
        #3=#2\relax
      \fi
    \fi
  \fi
  \ignorespaces}
\def\!blankz{ z}

\setshadesymbol ({\fiverm .})%       ** initialize plotsymbol
%                                    ** \fivesy ^^B  is a small cross


% ** \!startvshade [at] (xS,ybS,ytS)
% ** Initiates vertical shading mode
\def\!startvshade#1(#2,#3,#4){%
  \let\!!xunit=\!xunit%
  \let\!!yunit=\!yunit%
  \let\!!xshade=\!xshade%
  \let\!!yshade=\!yshade%
  \def\!getshrinkages{\!vgetshrinkages}%
  \let\!setshadelocation=\!vsetshadelocation%
  \!xS=\!M{#2}\!!xunit
  \!ybS=\!M{#3}\!!yunit
  \!ytS=\!M{#4}\!!yunit
  \!shadexorigin=\!xorigin  \advance \!shadexorigin \!shadesymbolxshift
  \!shadeyorigin=\!yorigin  \advance \!shadeyorigin \!shadesymbolyshift
  \ignorespaces}
 
% ** \!starthshade [at] (yS,xlS,xrS)
% ** Initiates horizontal shading mode
\def\!starthshade#1(#2,#3,#4){%
  \let\!!xunit=\!yunit%
  \let\!!yunit=\!xunit%
  \let\!!xshade=\!yshade%
  \let\!!yshade=\!xshade%
  \def\!getshrinkages{\!hgetshrinkages}%
  \let\!setshadelocation=\!hsetshadelocation%
  \!xS=\!M{#2}\!!xunit
  \!ybS=\!M{#3}\!!yunit
  \!ytS=\!M{#4}\!!yunit
  \!shadexorigin=\!xorigin  \advance \!shadexorigin \!shadesymbolxshift
  \!shadeyorigin=\!yorigin  \advance \!shadeyorigin \!shadesymbolyshift
  \ignorespaces}


% **  \!lattice{ANCHOR}{SPAN}{LOCATION}{INDEX}{LATTICE LOCATION}
% **  Consider the lattice with points  ANCHOR + j*SPAN. This routine determines
% **  the index  k  of the smallest lattice point >= LOCATION, and sets
% **  LATTICE LOCATION = ANCHOR + k*SPAN.
% **  INDEX is assumed to be a count register, LATTICE LOCATION a dimen reg.
\def\!lattice#1#2#3#4#5{%
  \!dimenA=#1%                        ** dimA = ANCHOR
  \!dimenB=#2%                        ** dimB = SPAN  (assumed > 0pt)
  \!countB=\!dimenB%                  ** ctB  = SPAN, as a count
%
% ** Determine index of smallest lattice point >= LOCATION
  \!dimenC=#3%                        ** dimC = LOCATION
  \advance\!dimenC -\!dimenA%         ** now dimC = LOCATION-ANCHOR
  \!countA=\!dimenC%                  ** ctA = above, as a count
  \divide\!countA \!countB%           ** now ctA = desired index, if dimC <= 0
  \ifdim\!dimenC>\!zpt
    \!dimenD=\!countA\!dimenB%        ** (tentative k)*span
    \ifdim\!dimenD<\!dimenC%          ** if this is false, ctA = desired index
      \advance\!countA 1 %            ** if true, have to add 1
    \fi
  \fi
%
  \!dimenC=\!countA\!dimenB%          ** lattice location = anchor + ctA*span
    \advance\!dimenC \!dimenA
  #4=\!countA%                        ** the desired index
  #5=\!dimenC%                        ** corresponding lattice location
  \ignorespaces}


% ** \!qshade [with shrinkages] [[LS,RS,BS,TS]]
% ***** during vertical shading:
% **    [the region from (xS,ybS,ytS) to] (xM,ybM,ytM) [and] (xE,ybE,ytE)
% ** Shades the region {(x,y): xS <= x <= xE, yb(x) <= y <= yt(x)}, where 
% **   yb is the quadratic thru (xS,ybS) & (xM,ybM) & (xE,ybE)
% **   yt is the quadratic thru (xS,ytS) & (xM,ybM) & (xE,ytE)
% ** xS,ybS,ytS are either given by \!startvshade or carried over
% **   as the ending values of the immediately preceding \!qshade.
% ** For the interpretation of LS, RS, BS, & TS, see \setshadesymbol. The
% **   values set there can be overridden, for the course of this \!qshade
% **   only, in the same manner as overrides are specified for
% **   \setshadesymbol.
% ***** during horizontal shading:
% **    [the region from (yS,xlS,xrS) to] (yM,xlM,xrM) [and] (yE,xlE,xrE)
\def\!qshade#1(#2,#3,#4)#5(#6,#7,#8){%
  \!xM=\!M{#2}\!!xunit
  \!ybM=\!M{#3}\!!yunit
  \!ytM=\!M{#4}\!!yunit
  \!xE=\!M{#6}\!!xunit
  \!ybE=\!M{#7}\!!yunit
  \!ytE=\!M{#8}\!!yunit
  \!getcoeffs\!xS\!ybS\!xM\!ybM\!xE\!ybE\!ybB\!ybC%**Get coefficients B & C for
  \!getcoeffs\!xS\!ytS\!xM\!ytM\!xE\!ytE\!ytB\!ytC%**y=y0 + B(x-X0) + C(x-X0)**2
  \def\!getylimits{\!qgetylimits}%
  \!shade{#1}\ignorespaces}
 
% ** \!lshade ... (xE,ybE,ytE)
% ** This is like \!qshade, but the top and bottom boundaries are linear,
% ** rather than quadratic.
\def\!lshade#1(#2,#3,#4){%
  \!xE=\!M{#2}\!!xunit
  \!ybE=\!M{#3}\!!yunit
  \!ytE=\!M{#4}\!!yunit
  \!dimenE=\!xE  \advance \!dimenE -\!xS%   ** xE-xS
  \!dimenC=\!ytE \advance \!dimenC -\!ytS%  ** ytE-ytS
  \!divide\!dimenC\!dimenE\!ytB%            ** ytB = (ytE-ytS)/(xE-xS)
  \!dimenC=\!ybE \advance \!dimenC -\!ybS%  ** ybE-ybS
  \!divide\!dimenC\!dimenE\!ybB%            ** ybB = (ybE-ybS)/(xE-xS)
  \def\!getylimits{\!lgetylimits}%
  \!shade{#1}\ignorespaces}
 
% **  \!getcoeffs{X0}{Y0}{X1}{Y1}{X2}{Y2}{B}{C}
% **  Finds  B  and  C  such that the quadratic  y = Y0 + B(x-X0) + C(x-X0)**2
% **  passes through (X1,Y1) and (X2,Y2):  when X0=0=Y0, the formulas are:
% **                   B = S1 - X1*C,   C = (S2-S1)/X2
% **  with
% **                 S1 = Y1/X1,   S2 = (Y2-Y1)/(X2-X1).
\def\!getcoeffs#1#2#3#4#5#6#7#8{% 
  \!dimenC=#4\advance \!dimenC -#2%            ** dimC=Y1-Y0
  \!dimenE=#3\advance \!dimenE -#1%            ** dimE=X1-X0
  \!divide\!dimenC\!dimenE\!dimenF%            ** dimF=S1
  \!dimenC=#6\advance \!dimenC -#4%            ** dimC=Y2-Y1
  \!dimenH=#5\advance \!dimenH -#3%            ** dimH=X2-X1
  \!divide\!dimenC\!dimenH\!dimenG%            ** dimG=S2
  \advance\!dimenG -\!dimenF%                  ** dimG=S2-S1
  \advance \!dimenH \!dimenE%                  ** dimH=X2-X0
  \!divide\!dimenG\!dimenH#8%                  ** C=(S2-S1)/(X2-X0)
  \!removept#8\!t%                             ** C, without "pt"
  #7=-\!t\!dimenE%                             ** -C*(X1-X0)
  \advance #7\!dimenF%                         ** B=S1-C*(X1-X0)
  \ignorespaces}
 
 
\def\!shade#1{%
% ** Get LS,RS,BS,TS for this panel
  \!getshrinkages#1<,,,>\!nil% %       ** now effective LS=dimE, RS=dimF,
%                                      **   BS=dimG, TS=dimH
  \advance \!dimenE \!xS%              ** now dimE=xS+LS
  \!lattice\!!xshade\!dshade\!dimenE%  ** set parity=index of left-mst x-lattice
    \!parity\!xpos%                    **   point >= xS+LS, xpos=its location
  \!dimenF=-\!dimenF%                  ** set dimF=xE-RS
    \advance\!dimenF \!xE
%               
  \!loop\!not{\ifdim\!xpos>\!dimenF}%  ** loop over x-lattice points <= xE-RS
    \!shadecolumn%                 
    \advance\!xpos \!dshade%           ** move over to next column
    \advance\!parity 1%                ** increase index of x-point
  \repeat
%
  \!xS=\!xE%                           ** shift ending values to starting values
  \!ybS=\!ybE
  \!ytS=\!ytE
  \ignorespaces}
 
 
\def\!vgetshrinkages#1<#2,#3,#4,#5>#6\!nil{%
  \!override\!lshrinkage{#2}\!dimenE
  \!override\!rshrinkage{#3}\!dimenF
  \!override\!bshrinkage{#4}\!dimenG
  \!override\!tshrinkage{#5}\!dimenH
  \ignorespaces}
\def\!hgetshrinkages#1<#2,#3,#4,#5>#6\!nil{%
  \!override\!lshrinkage{#2}\!dimenG
  \!override\!rshrinkage{#3}\!dimenH
  \!override\!bshrinkage{#4}\!dimenE
  \!override\!tshrinkage{#5}\!dimenF
  \ignorespaces}
 
 
\def\!shadecolumn{%
  \!dxpos=\!xpos
  \advance\!dxpos -\!xS%            ** dx = x - xS
  \!removept\!dxpos\!dx%            ** ditto, without "pt"
  \!getylimits%                     ** get top and bottom y-values
  \advance\!ytpos -\!dimenH%        ** less TS
  \advance\!ybpos \!dimenG%         ** plus BS
  \!yloc=\!!yshade%                 ** get anchor point for this column
  \ifodd\!parity 
     \advance\!yloc \!dshade
  \fi
  \!lattice\!yloc{2\!dshade}\!ybpos%
    \!countA\!ypos%                 ** ypos=smallest y point for this column
  \!dimenA=-\!shadexorigin \advance \!dimenA \!xpos%      ** over
  \loop\!not{\ifdim\!ypos>\!ytpos}% ** loop over ypos <= yt(t)
    \!setshadelocation%             ** vmode: xloc=xpos, yloc=ypos 
%                                   ** hmode: xloc=ypos, yloc=xpos 
    \!rotateaboutpivot\!xloc\!yloc%
    \!dimenA=-\!shadexorigin \advance \!dimenA \!xloc%    ** over
    \!dimenB=-\!shadeyorigin \advance \!dimenB \!yloc%    ** up
    \kern\!dimenA \raise\!dimenB\copy\!shadesymbol \kern-\!dimenA
    \advance\!ypos 2\!dshade
  \repeat
  \ignorespaces}
 
\def\!qgetylimits{%
  \!dimenA=\!dx\!ytC              
  \advance\!dimenA \!ytB%         ** yt(t)=ytS + dx*(Bt + dx*Ct)
  \!ytpos=\!dx\!dimenA
  \advance\!ytpos \!ytS
  \!dimenA=\!dx\!ybC              
  \advance\!dimenA \!ybB%         ** yb(t)=ybS + dx*(Bb + dx*Cb)
  \!ybpos=\!dx\!dimenA
  \advance\!ybpos \!ybS}
 
\def\!lgetylimits{%
  \!ytpos=\!dx\!ytB%              ** yt(t)=ytS + dx*Bt
  \advance\!ytpos \!ytS
  \!ybpos=\!dx\!ybB%              ** yb(t)=ybS + dx*Bb
  \advance\!ybpos \!ybS}
 
\def\!vsetshadelocation{%         ** vmode: xloc=xpos, yloc=ypos 
  \!xloc=\!xpos
  \!yloc=\!ypos}
\def\!hsetshadelocation{%         ** hmode: xloc=ypos, yloc=xpos 
  \!xloc=\!ypos
  \!yloc=\!xpos}


% **************************************
% *** TICKS  (Draws ticks on graphs) ***
% **************************************

% ** User commands
% **   \ticksout
% **   \ticksin
% **   \gridlines
% **   \nogridlines
% **   \loggedticks
% **   \unloggesticks
% ** See Subsection 3.4 of the manual

% ** The following is an option of the \axis command
% **   ticks 
% **     [in] [out] 
% **     [long] [short] [length <LENGTH>] 
% **     [width <WIDTH>]
% **     [andacross] [butnotacross] 
% **     [logged] [unlogged] 
% **     [unlabeled] [numbered] [withvalues VALUE1 VALUE2 ... VALUEk / ]
% **     [quantity Q] [at LOC1 LOC2 ... LOCk / ] [from LOC1 to LOC2 by
% **       LOC_INCREMENT]
% ** See Subsection 3.2 of the manual for the rules.

% ** The various options of the  tick  field are processed by the
% ** \!nextkeyword  command defined below.
% ** For example, `\!nextkeyword short '  expands to  `\!ticksshort',
% ** while `\!nextkeyword withvalues' expands to `\!tickswithvalues'.

\def\!axisticks {%
  \def\!nextkeyword##1 {%
    \expandafter\ifx\csname !ticks##1\endcsname \relax
      \def\!next{\!fixkeyword{##1}}%
    \else
      \def\!next{\csname !ticks##1\endcsname}%
    \fi
    \!next}%
  \!axissetup
    \def\!axissetup{\relax}%
  \edef\!ticksinoutsign{\!ticksinoutSign}%
  \!ticklength=\longticklength
  \!tickwidth=\linethickness
  \!gridlinestatus
  \!setticktransform
  \!maketick
  \!tickcase=0
  \def\!LTlist{}%
  \!nextkeyword}

\def\ticksout{%
  \def\!ticksinoutSign{+}}
\def\ticksin{%
  \def\!ticksinoutSign{-}}
\ticksout

\def\gridlines{%
  \def\!gridlinestatus{\!gridlinestootrue}}
\def\nogridlines{%
  \def\!gridlinestatus{\!gridlinestoofalse}}
\nogridlines

\def\loggedticks{%
  \def\!setticktransform{\let\!ticktransform=\!logten}}
\def\unloggedticks{%
  \def\!setticktransform{\let\!ticktransform=\!donothing}}
\def\!donothing#1#2{\def#2{#1}}
\unloggedticks

% ** \!ticks/ : terminates read of tick options
\expandafter\def\csname !ticks/\endcsname{%
  \!not {\ifx \!LTlist\empty}
    \!placetickvalues
  \fi
  \def\!tickvalueslist{}%
  \def\!LTlist{}%
  \expandafter\csname !axis/\endcsname}

\def\!maketick{%
  \setbox\!boxA=\hbox{%
    \beginpicture
      \!setdimenmode
      \setcoordinatesystem point at {\!zpt} {\!zpt}   
      \linethickness=\!tickwidth
      \ifdim\!ticklength>\!zpt
        \putrule from {\!zpt} {\!zpt} to
          {\!ticksinoutsign\!tickxsign\!ticklength}
          {\!ticksinoutsign\!tickysign\!ticklength}
      \fi
      \if!gridlinestoo
        \putrule from {\!zpt} {\!zpt} to
          {-\!tickxsign\!xaxislength} {-\!tickysign\!yaxislength}
      \fi
    \endpicturesave <\!Xsave,\!Ysave>}%
    \wd\!boxA=\!zpt}
  
\def\!ticksin{%
  \def\!ticksinoutsign{-}%
  \!maketick
  \!nextkeyword}

\def\!ticksout{%
  \def\!ticksinoutsign{+}%
  \!maketick
  \!nextkeyword}

\def\!tickslength<#1> {%
  \!ticklength=#1\relax
  \!maketick
  \!nextkeyword}

\def\!tickslong{%
  \!tickslength<\longticklength> }

\def\!ticksshort{%
  \!tickslength<\shortticklength> }

\def\!tickswidth<#1> {%
  \!tickwidth=#1\relax
  \!maketick
  \!nextkeyword}

\def\!ticksandacross{%
  \!gridlinestootrue
  \!maketick
  \!nextkeyword}

\def\!ticksbutnotacross{%
  \!gridlinestoofalse
  \!maketick
  \!nextkeyword}

\def\!tickslogged{%
  \let\!ticktransform=\!logten
  \!nextkeyword}

\def\!ticksunlogged{%
  \let\!ticktransform=\!donothing
  \!nextkeyword}

\def\!ticksunlabeled{%
  \!tickcase=0
  \!nextkeyword}

\def\!ticksnumbered{%
  \!tickcase=1
  \!nextkeyword}

\def\!tickswithvalues#1/ {%
  \edef\!tickvalueslist{#1! /}%
  \!tickcase=2
  \!nextkeyword}

\def\!ticksquantity#1 {%
  \ifnum #1>1
    \!updatetickoffset
    \!countA=#1\relax
    \advance \!countA -1
    \!ticklocationincr=\!axisLength
      \divide \!ticklocationincr \!countA
    \!ticklocation=\!axisstart
    \loop \!not{\ifdim \!ticklocation>\!axisend}
      \!placetick\!ticklocation
      \ifcase\!tickcase
          \relax %  Case 0: no labels
        \or
          \relax %  Case 1: numbered -- not available here
        \or
          \expandafter\!gettickvaluefrom\!tickvalueslist
          \edef\!tickfield{{\the\!ticklocation}{\!value}}%
          \expandafter\!listaddon\expandafter{\!tickfield}\!LTlist%
      \fi
      \advance \!ticklocation \!ticklocationincr
    \repeat
  \fi
  \!nextkeyword}

\def\!ticksat#1 {%
  \!updatetickoffset
  \edef\!Loc{#1}%
  \if /\!Loc
    \def\next{\!nextkeyword}%
  \else
    \!ticksincommon
    \def\next{\!ticksat}%
  \fi
  \next}    
      
\def\!ticksfrom#1 to #2 by #3 {%
  \!updatetickoffset
  \edef\!arg{#3}%
  \expandafter\!separate\!arg\!nil
  \!scalefactor=1
  \expandafter\!countfigures\!arg/
  \edef\!arg{#1}%
  \!scaleup\!arg by\!scalefactor to\!countE
  \edef\!arg{#2}%
  \!scaleup\!arg by\!scalefactor to\!countF
  \edef\!arg{#3}%
  \!scaleup\!arg by\!scalefactor to\!countG
  \loop \!not{\ifnum\!countE>\!countF}
    \ifnum\!scalefactor=1
      \edef\!Loc{\the\!countE}%
    \else
      \!scaledown\!countE by\!scalefactor to\!Loc
    \fi
    \!ticksincommon
    \advance \!countE \!countG
  \repeat
  \!nextkeyword}

\def\!updatetickoffset{%
  \!dimenA=\!ticksinoutsign\!ticklength
  \ifdim \!dimenA>\!offset
    \!offset=\!dimenA
  \fi}

\def\!placetick#1{%
  \if!xswitch
    \!xpos=#1\relax
    \!ypos=\!axisylevel
  \else
    \!xpos=\!axisxlevel
    \!ypos=#1\relax
  \fi
  \advance\!xpos \!Xsave
  \advance\!ypos \!Ysave
  \kern\!xpos\raise\!ypos\copy\!boxA\kern-\!xpos
  \ignorespaces}

\def\!gettickvaluefrom#1 #2 /{%
  \edef\!value{#1}%
  \edef\!tickvalueslist{#2 /}%
  \ifx \!tickvalueslist\!endtickvaluelist
    \!tickcase=0
  \fi}
\def\!endtickvaluelist{! /}

\def\!ticksincommon{%
  \!ticktransform\!Loc\!t
  \!ticklocation=\!t\!!unit
  \advance\!ticklocation -\!!origin
  \!placetick\!ticklocation
  \ifcase\!tickcase
    \relax % Case 0: no labels
  \or %      Case 1: numbered
    \ifdim\!ticklocation<-\!!origin
      \edef\!Loc{$\!Loc$}%
    \fi
    \edef\!tickfield{{\the\!ticklocation}{\!Loc}}%
    \expandafter\!listaddon\expandafter{\!tickfield}\!LTlist%
  \or %      Case 2: labeled
    \expandafter\!gettickvaluefrom\!tickvalueslist
    \edef\!tickfield{{\the\!ticklocation}{\!value}}%
    \expandafter\!listaddon\expandafter{\!tickfield}\!LTlist%
  \fi}

\def\!separate#1\!nil{%
  \!ifnextchar{-}{\!!separate}{\!!!separate}#1\!nil}
\def\!!separate-#1\!nil{%
  \def\!sign{-}%
  \!!!!separate#1..\!nil}
\def\!!!separate#1\!nil{%
  \def\!sign{+}%
  \!!!!separate#1..\!nil}
\def\!!!!separate#1.#2.#3\!nil{%
  \def\!arg{#1}%
  \ifx\!arg\!empty
    \!countA=0
  \else
    \!countA=\!arg
  \fi
  \def\!arg{#2}%
  \ifx\!arg\!empty
    \!countB=0
  \else
    \!countB=\!arg
  \fi}
 
\def\!countfigures#1{%
  \if #1/%
    \def\!next{\ignorespaces}%
  \else
    \multiply\!scalefactor 10
    \def\!next{\!countfigures}%
  \fi
  \!next}

\def\!scaleup#1by#2to#3{%
  \expandafter\!separate#1\!nil
  \multiply\!countA #2\relax
  \advance\!countA \!countB
  \if -\!sign
    \!countA=-\!countA
  \fi
  #3=\!countA
  \ignorespaces}

\def\!scaledown#1by#2to#3{%
  \!countA=#1\relax%                          ** get original #
  \ifnum \!countA<0 %                         ** take abs value,
    \def\!sign{-}%                            **   remember sign
    \!countA=-\!countA
  \else
    \def\!sign{}%
  \fi
  \!countB=\!countA%                          ** copy |#|
  \divide\!countB #2\relax%                   ** integer part (|#|/sf)
  \!countC=\!countB%                          ** get sf * (|#|/sf)
    \multiply\!countC #2\relax
  \advance \!countA -\!countC%                ** ctA is now remainder
  \edef#3{\!sign\the\!countB.}%               ** +- integerpart.
  \!countC=\!countA %                         ** Tack on proper number
  \ifnum\!countC=0 %                          **   of zeros after .
    \!countC=1
  \fi
  \multiply\!countC 10
  \!loop \ifnum #2>\!countC
    \edef#3{#3\!zero}%
    \multiply\!countC 10
  \repeat
  \edef#3{#3\the\!countA}%                    ** Add on rest of remainder
  \ignorespaces}

\def\!placetickvalues{%
  \advance\!offset \tickstovaluesleading
  \if!xswitch
    \setbox\!boxA=\hbox{%
      \def\\##1##2{%
        \!dimenput {##2} [B] (##1,\!axisylevel)}%
      \beginpicture 
        \!LTlist
      \endpicturesave <\!Xsave,\!Ysave>}%
    \!dimenA=\!axisylevel
      \advance\!dimenA -\!Ysave
      \advance\!dimenA \!tickysign\!offset
      \if -\!tickysign
        \advance\!dimenA -\ht\!boxA
      \else
        \advance\!dimenA  \dp\!boxA
      \fi
    \advance\!offset \ht\!boxA 
      \advance\!offset \dp\!boxA
    \!dimenput {\box\!boxA} [Bl] <\!Xsave,\!Ysave> (\!zpt,\!dimenA)
  \else
    \setbox\!boxA=\hbox{%
      \def\\##1##2{%
        \!dimenput {##2} [r] (\!axisxlevel,##1)}%
      \beginpicture 
        \!LTlist
      \endpicturesave <\!Xsave,\!Ysave>}%
    \!dimenA=\!axisxlevel
      \advance\!dimenA -\!Xsave
      \advance\!dimenA \!tickxsign\!offset
      \if -\!tickxsign
        \advance\!dimenA -\wd\!boxA
      \fi
    \advance\!offset \wd\!boxA
    \!dimenput {\box\!boxA} [Bl] <\!Xsave,\!Ysave> (\!dimenA,\!zpt)
  \fi}


\normalgraphs
\catcode`!=12 %  *****  THIS MUST NEVER BE OMITTED
% This is a plain TeX file:
% 
% Reflection Groups on the Octave Hyperbolic Plane
%
% Daniel Allcock


% load the style file
%
%----------Options-(comment-or-uncomment-as-desired)-------------
%
\magnification=1100
%
%    for doublespacing
%\baselineskip=24pt
%
%    eliminates the nasty overfullbox bars:
\hideboxes 
%
%    uses your symbols rather than tag names for
%    cross-referencing: 
\symbolictags
%
%    suppresses djatex warnings (e.g., undefined tags)
%\givenowarnings

%
%-----------------Cross-References-and-Citations------------------
%
% Do not tamper with the following line.
% ---begin deftags---
\deftag{1}{sec-octave-reflec-intro}
\deftag{1.1}{eq-the-forms}
\deftag{2}{sec-octaves}
\deftag{2.1}{eq-moup}
\deftag{2.2}{eq-rexyz}
\deftag{2.3}{eq-moup2}
\deftag{2.4}{eq-moup3}
\deftag{2.5}{eq-moup4}
\deftag{3}{sec-geometry-oh2}
\deftag{4}{sec-jordan-alg}
\deftag{4.1}{eq-def-of-X}
\deftag{4.2}{eq-bilform}
\deftag{4.1}{tab-multiplication-table}
\deftag{4.3}{eq-def-of-R}
\deftag{4.4}{eq-def-of-S}
\deftag{4.5}{eq-def-of-T}
\deftag{4.6}{eq-def-of-R'}
\deftag{4.1}{thm-are-automorphisms}
\deftag{4.2}{thm-inner-products}
\deftag{4.7}{eq-Heisenberg-1}
\deftag{4.8}{eq-Heisenberg-2}
\deftag{4.9}{eq-Heisenberg-3}
\deftag{4.10}{eq-Heisenberg-4}
\deftag{4.3}{thm-all-potents}
\deftag{4.11}{eq-nil1}
\deftag{4.12}{eq-nil2}
\deftag{4.13}{eq-nil3}
\deftag{4.14}{eq-nil4}
\deftag{4.15}{eq-nil5}
\deftag{4.16}{eq-nil6}
\deftag{4.4}{thm-they-generate-group}
\deftag{4.17}{eq-isotopy-rule}
\deftag{4.18}{eq-formula-for-phi1}
\deftag{4.19}{eq-formula-for-phi2}
\deftag{4.20}{eq-isotopy2}
\deftag{4.5}{thm-semisimple}
\deftag{5}{sec-examples}
\deftag{5.1}{thm-properties-of-K}
\deftag{5.2}{thm-got-translations}
\deftag{5.3}{thm-hyperbolic-cell-group}
\deftag{5.1}{fig-reflection-group}
\deftag{5.4}{thm-finite-covol}
\deftag{6}{sec-determinant}
\deftag{6.1}{thm-det}
\deftag{6.1}{eq-det}
\deftag{6.2}{thm-they-are-automorphisms}
\deftag{6.2}{eq-det-for-J-I}
\deftag{7}{sec-interpretation}
\deftag{7.1}{thm-lattice-interp}
% ---end deftags---
% Do not tamper with the previous line or the following line.
% ---begin defcites---
\defcite{1}{dja:complex-reflection-groups}
\defcite{2}{dja:op2}
\defcite{3}{asl:op2}
\defcite{4}{reb:lzl}
\defcite{5}{borel:arithmetic-groups}
\defcite{6}{jhc:26dim}
\defcite{7}{ATLAS}
\defcite{8}{splag}
\defcite{9}{cox:integer-octaves}
\defcite{10}{elkies:cone}
\defcite{11}{freud:oktav-eben}
\defcite{12}{freud:oktav-geometry}
\defcite{13}{gross:groups-over-Z}
\defcite{14}{jordan:op2}
\defcite{15}{mostow:strong-rigidity}
\defcite{16}{tits:op2}
\defcite{17}{tits:e6-and-e7}
\defcite{18}{vin:groups-of-quad-forms}
\defcite{19}{vin:unimodular-quad-forms}
\defcite{20}{vin:I18.1-and-I19.1}
% ---end defcites---
% Do not tamper with the preceding line.

%-----------------Beginning-of-Document---------------------------
%

\title{Reflection Groups on the Octave Hyperbolic Plane}

\author{Daniel Allcock\footnote*{Supported by an NSF
postdoctoral fellowship.}} 

\date{4 August 1997}
%\note	{commenced August 1996; Initial version commenced 20
%February 1996} 

\address{Department of Mathematics\par 
University of Utah\par
Salt Lake City, UT 84112.}
\email	{allcock@math.utah.edu}
\subject{22E40; secondary: 11F06, 17C40, 53C35.}
\plaintitlepage

\def\w{\omega}
\def\quaternionic{{\bbb H}}
\def\H{\quaternionic}
\def\height{\mathop{\rm ht}}
\def\diag{\mathop{\rm diag}}
\def\spanof#1{\langle#1\rangle}
\def\kpnp{\K P^{n+1}}
\def\khnp{\K H^{n+1}}
\def\dkhnp{\partial\khnp}
\def\hyperbolic{H}
\def\projective{P}
%\def\rh {\R\hyperbolic^}	
\def\rh {\hyperbolic^}
\def\ch {\C\hyperbolic^}	
\def\qh{\quaternionic\hyperbolic^} 
\def\rp{\R\projective^}
\def\rpn{\rp n}
\def\qhn{\qh n}
\def\chn{\ch n}
\def\rhn{\rh n}
\def\chnp {\ch{n+1}} 
\def\drh {\del\rh}
\def\I#1,#2{{\rm I}_{#1,#2}}
\def\II#1,#2{{\rm I\kern-.1emI}_{#1,#2}}
\def\ip#1#2{\langle#1{|}#2\rangle}
%\def\eisI#1,#2{\I#1,#2^\eisen}   % Lorentzian lattices of
				 % various types
%\def\eisII#1,#2{\II#1,#2^\eisen}
%\def\qcI#1,#2{\I#1,#2^\Hurwitz}
%\def\qcII#1,#2{\II#1,#2^\Hurwitz}
%\def\gauI#1,#2{\I#1,#2^\gauss}
%\def\gauII#1,#2{\II#1,#2^\gauss}
%\def\zI#1,#2{\I#1,#2^\Z}
%\def\rI#1,#2{\I#1,#2^\calr}
%\def\rII#1,#2{\II#1,#2^\calr}

\def\ubar{\bar{u}}

\def\wolog{w.l.o.g.}
\def\vec#1#2{(#1_1,\ldots,#1_{#2})}
\def\vector#1#2{\vec{#1}{#2}}
\def\generate#1{\langle #1 \rangle}
% Octave stuff
\def\O{{\bbb O}}
\def\ocint{\calk}
\def\op{\O\projective^}
\def\oh{\O\hyperbolic^}
\def\doh{\partial\O\hyperbolic^}
%\def\oip#1#2#3{\langle#1|#2\rangle_{#3}}
\def\bilform#1#2{\langle#1|#2\rangle}
\def\trilform#1#2#3{\langle#1|#2|#3\rangle}
\def\cuform{\calc}
\def\spin#1{{\rm Spin}_{#1}}
\def\goodo{{\O^3_0}}
\def\goodoo{{\O^3_{00}}}
\def\gint{H_I^\ocint}
\def\hd{\mathop{\rm h.d.}}
\def\Heisenberg{\calh}

%
%  a Coxeter Diagram
%
\newbox\coxboxone
\setbox\coxboxone=\hbox{
\beginpicture
\def\coxHalfedgelength{12pt}
\newdimen\coxEdgelength
\coxEdgelength=\coxHalfedgelength
\multiply\coxEdgelength by 2
\linethickness=.5pt
\setcoordinatesystem units
	<\coxHalfedgelength,\coxHalfedgelength>
	point at 0 .7
\setplotsymbol ({\vrule width \linethickness height \linethickness})
\plotsymbolspacing=\linethickness
\put {\phantom{$\bullet$}} at 0 0
\put {\phantom{$\bullet$}} at 14 2
\plot 0 0 14 0 /
\plot 6 0 6 2 /
\setdots <\coxEdgelength>
\setplotsymbol ({$\bullet$})
\plot 0 0 15 0 /
\plot 6 2 6 1 /
\endpicture}

%
%  same Coxeter Diagram, with a couple letters added
%
\newbox\coxboxtwo
\setbox\coxboxtwo=\hbox{
\beginpicture
\def\coxHalfedgelength{12pt}
\newdimen\coxEdgelength
\coxEdgelength=\coxHalfedgelength
\multiply\coxEdgelength by 2
\linethickness=.5pt
\setcoordinatesystem units
	<\coxHalfedgelength,\coxHalfedgelength>
	point at 0 .7
\setplotsymbol ({\vrule width \linethickness height \linethickness})
\plotsymbolspacing=\linethickness
\put {\phantom{$\bullet$}} at 0 0
\put {\phantom{$\bullet$}} at 14 2
\plot 0 0 14 0 /
\plot 6 0 6 2 /
\setdots <\coxEdgelength>
\setplotsymbol ({$\bullet$})
\plot 0 0 15 0 /
\plot 6 2 6 1 /
\put {$\a$} [b] <0pt,4pt> at 12 0
\put {$\b$} [b] <0pt,2pt> at 14 0
\endpicture}

\abstract
For two different integral forms $K$ of  the exceptional Jordan
algebra we show that $\aut K$ is generated by
octave reflections. These provide `geometric' examples of
discrete reflection 
groups acting with finite  covolume
on the octave (or Cayley)
hyperbolic plane $\oh2$, the exceptional rank one
symmetric space. (The isometry group of the plane is the
exceptional Lie group  $F_{4(-20)}$.)
Our groups are defined in terms of  
Coxeter's discrete subring $\ocint$ of the nonassociative 
division algebra
$\O$ and we interpret them as the symmetry
groups of ``Lorentzian lattices'' over $\ocint$. 
We also show that the  reflection group of the ``hyperbolic cell''
over $\ocint$  is the rotation subgroup
of a particular {\it real} reflection group acting on $\rh8\cong\oh1$.
Part of our approach is the treatment of the
Jordan algebra of matrices that are Hermitian with respect to any real
symmetric matrix. 

\section{\Tag{sec-octave-reflec-intro} Introduction}

The octave hyperbolic plane $\oh2$ is the exceptional rank one
symmetric space; it is very similar to the more familiar real
(and complex and quaternionic) hyperbolic spaces. There is a
natural notion of a hyperplane in $\oh2$, and of a reflection in
such a hyperplane (the mirror of the reflection). In this paper
we explain this and construct two discrete groups of isometries
of $\oh2$ that are generated by reflections. One can also define
the `octave hyperbolic line' $\oh1$, and it turns out to be
isometric with the real hyperbolic space $\rh8$. In this
setting, a hyperplane is just a point and the octave reflection
therein is just central inversion. We will construct a discrete
group of isometries of $\oh1$ generated by octave reflections
and show that it is the rotation subgroup of a certain group of
isometries of $\rh8$ generated by {\it real} reflections. This
real reflection group is easy to describe; it has a simplex for
its fundamental domain, with Coxeter diagram
$$
\box\coxboxone.
$$

We construct all three of our groups by defining them
arithmetically and then finding sufficiently many reflections to
generate them. It turns out that in each case one can easily
write down a large set of
reflections preserving the relevant algebraic structure, and
that the mirrors of these reflections are arranged in a pattern
governed by the combinatorics of the $E_7$ and $E_8$ root
lattices. We use facts about the geometry of these lattices to
prove that these ``known'' reflections actually generate the
entire group. To the author's knowledge this is the first
geometric construction of discrete groups of isometries of
$\oh2$. 

To discuss $\oh2$ in any depth one must introduce various spaces
of $3\times3$ matrices with entries in the nonassociative field
$\O$ of octaves, such as the space $J_I$ of Hermitian matrices; such
matrices should informally regarded as ``Hermitian forms'' on
the nonexistent vector space $\O^3$. One can define the determinant of such a
``Hermitian form''
in terms of a certain cubic form.
The
group $\gint$ of linear transformations of $J_I$ that preserve
the determinant form  is isomorphic to the exceptional Lie group
$E_{6(-26)}$. This representation of $H_I$ is very closely
analogous to the action of $PGL_3\C$ on the space of $3\times3$
complex Hermitian matrices given for $g\in GL_3\C$ by
$\Phi\mapsto g\Phi g^*$. (One can replace $\C$ here with any
associative ring.) One should think of $H_I$ as ``$PGL_3\O$'',
and the stabilizer in $H_I$ of some matrix $\Phi$ as the unitary
group preserving the ``Hermitian form on $\O^3$'' with inner
product matrix $\Phi$. For appropriate choice of $\Phi$ this
stabilizer is isomorphic to $G=F_{4(-20)}=\aut\oh2$, and one can
use this to construct $\oh2$.

To construct discrete subgroups of $G$ one can simply take
$\Phi$ to be integral and consider  the stabilizer of $\Phi$ in
the subgroup $\gint$ of $H_I$ that
preserves  the set of integral Hermitian
matrices. By an integral matrix we mean one whose entries lie
the natural discrete subring $\ocint$ of $\O$ discovered by
Coxeter \cite{cox:integer-octaves}. In light of the above
interpretation of $G$ and 
$H_I$, we may think of this discrete subgroup as being essentially
the isometry group of the ``lattice'' over $\ocint$ with inner
product matrix $\Phi$. Our main result is that if $\Phi$ is 
$$
\pmatrix{
1 & 0 & 0 \cr
0 & 0 & 1 \cr
0 & 1 & 0 
\cr}
\quad\hbox{or}\quad
\pmatrix{
1 & 0 & 0 \cr
0 & 0 & 2 \cr
0 & 2 & 0 
\cr}
\eqno\eqTag{eq-the-forms}
$$
then the discrete group is generated by octave reflections. We
note that the first of these groups has been studied by Gross
\cite{gross:groups-over-Z} in a different guise (see section~\tag{sec-interpretation}). If one uses
$2\times2$ matrices over $\O$ then one  finds that the
analogous discrete subgroup defined by the matrix
$\smallmatrix0110$ is also generated by octave reflections, and that
is has
index 2 in the real reflection group with Coxeter diagram given
above. We note in passing that the finite simple group
$^3D_4(2)$ described in the ATLAS \cite{ATLAS} as an octave
reflection group in the compact  form of $F_4$ has been
realized by Elkies and Gross \cite{elkies:cone} as the
stabilizer in $\gint$ of a
certain positive definite matrix with entries in $\ocint$ and
unit determinant.

Our construction of reflection groups in $F_{4(-20)}$ reveals
the source of the ideas for this investigation. In \cite{dja:complex-reflection-groups} we
constructed a large number of discrete reflection groups acting
with finite covolume on complex and quaternionic hyperbolic
spaces $\chn$ and $\qhn$. These groups were defined as the
automorphism groups of certain Lorentzian lattices over discrete
subrings of $\C$ and $\H$. (A Lorentzian lattice is a free
module equipped with a Hermitian form of signature
$-++\cdots+$. See \cite{dja:complex-reflection-groups} for details.) This work was in turn
inspired by the work of Vinberg \cite{vin:groups-of-quad-forms}
\cite{vin:unimodular-quad-forms}, Vinberg and Kaplinskaja
\cite{vin:I18.1-and-I19.1}, Conway \cite{jhc:26dim}, and
Borcherds \cite{reb:lzl} on real hyperbolic 
reflection groups defined in terms of Lorentzian lattices over
$\Z$. 

The basic idea of \cite{dja:complex-reflection-groups} was that
the symmetry group of a Lorentzian
lattice sometimes contains a collection of reflections whose
mirrors are arranged in the pattern of some positive-definite
lattice. If this lattice is ``good enough'' (e.g., has small
covering radius) then these reflections generate a group with
finite covolume. We use the same idea here, although all of the
arguments of \cite{dja:complex-reflection-groups} must be changed because $\O$ and $\ocint$
are not associative and so modules and lattices over them do not
make sense. We are pleased that the geometric ideas
continue to work in the nonassociative case even though all of
the formalism must be rewritten. In this paper, the relevant 
positive-definite ``lattice'' over $\ocint$ is just $\ocint$ itself, 
which is a scaled copy of the $E_8$ root lattice.

Above, we described $F_{4(-20)}$ as the stabilizer in
$E_{6(-26)}$ of any of
various Hermitian matrices. However, for computations it is more
convenient to describe the group as the automorphism group of a
Jordan algebra $J$, and to describe $\oh2$ in terms of the
idempotent elements  of $J$. For any $3\times3$ nondegenerate real
symmetric 
matrix $\Phi$ we define $J_\Phi$ to be the set of $3\times3$
octave matrices that are Hermitian with respect to
$\Phi$. This vector space is closed under the Jordan multiplication
$X*Y=(XY+YX)/2$, and the automorphism group of $J_\Phi$ turns
out to be a real form of $F_4$. In particular, if $\Phi$ is
indefinite then the group is $F_{4(-20)}=\aut\oh2$. For most of
the paper we will work with $J_\Psi$ and various integral forms
thereof, where
$$
\Psi=
\pmatrix{
1 & 0 & 0 \cr
0 & 0 & 1 \cr
0 & 1 & 0 
\cr}.
$$
This is a convenient setting for computations and for the
construction of the reflection groups. A little bit more effort
is then required to identify the groups as the stabilizers in
$\gint$ of
the Hermitian forms \eqtag{eq-the-forms}. This extra work
essentially consists of the
introduction of the determinant forms on $J_I$ and $J_\Psi$ and
an identification of these  cubic forms with each other.

Existing treatments of $\oh2$ are either elegant and abstract
but not suited for computation (see \cite{tits:op2}) or else are defined
in terms of the Jordan algebra $J_{\diag[-1,+1,+1]}$ (see
\cite{mostow:strong-rigidity}). To prove our results we need to
introduce and name 
various transformations of $J_\Psi$ and make other detailed
constructions. In light of this,
our treatment of  $J$ is almost entirely self-contained.
In section~\tag{sec-octaves} we describe $\O$ and list some identities
useful for computing therein. We then heuristically describe in
section~\tag{sec-geometry-oh2} the geometry of $\oh2$, to guide the reader
through the algebraic thicket of section~\tag{sec-jordan-alg}, which
formally defines the Jordan algebras and $\oh2$, provides useful
coordinates for them, and 
establishes key properties of their symmetry groups. Section~\tag{sec-examples}
is the heart of the paper and constructs the advertised
reflection groups. The proof that the groups 
are the entire symmetry 
groups of two integral forms of $J$ requires the rather involved
theorem~\tag{thm-hyperbolic-cell-group}, but we indicate how to
obtain the weaker result 
that they have finite covolume without using this theorem.
In section~\tag{sec-determinant} we describe
the determinant form on $J$, a cubic form which makes the
inclusion $F_{4(-20)}\sset E_{6(-26)}\cong\aut\op2$
visible. Finally in section~\tag{sec-interpretation}  we use the
determinant form to realize
our reflection groups  
as the stabilizers  of the integral Hermitian forms 
\eqtag{eq-the-forms}.

\section{\Tag{sec-octaves} The Nonassociative Field $\O$}

The algebra $\O$ of octaves 
is the algebra over $\R$ with basis $e_{\infty}=1,e_0,\dots,e_6$ with
$e_a^2=-1$ for $a\neq\infty$, 
any two elements lying in an associative algebra,  
and $e_ae_{a+1}=e_{a+3}$ for each $a\neq\infty$, with indices read modulo
$7$. The algebra of octaves is noncommutative and
nonassociative, but otherwise a 
division algebra. 
For computational purposes it is useful to know that for
distinct $a,b,c\neq\infty$ we have 
$e_ae_b=-e_be_a$ and
$e_a(e_be_c)=\pm(e_ae_b)e_c$,  with associativity holding just
if 
$$
\{a,b,c\}=\{d,d+1,d+3\}\pmod{7}\hbox{ for some }d=0,\dots,6.
$$
Sometimes it is convenient to write $i$,
$j$, $k$ and $\ell$ for $e_0$, $e_1$, $e_3$ and $e_2$,
respectively.
Then $i$ generates a copy of
$\C$ and $i,j,k$ generate a copy of the quaternions $\H$.
The real part $\re x$ of $x=\sum x_ae_a$ is $x_\infty$, and the
conjugate $\bar x$ of $x$ is $\bar x=-x+2\re x$; we have
$\re x=\re\bar x$.
We say that $x$ is imaginary if
$\re x=0$ and we write  $\im\O$ for the set of imaginary octaves.
The norm $|x|^2$ of $x$ is
defined to be $x\bar x=\sum x_a^2$, and $x$ is called a unit if
it has  norm one. 
The identities
$\overline{xy}=\bar y\bar x$ and $|xy|^2=|x|^2|y|^2$ hold universally. 

Since $\O$ is nonassociative, computations with it are sometimes
complicated. 
Five identities we will use are
$$\displaylines{
z(xy)z=(zx)(yz),
	\hfil\llap{\eqTag{eq-moup}}\hfilneg\cr
\re((xy)z)=\re(x(yz))=\re((yz)x)
	\hfil\llap{\eqTag{eq-rexyz}}\hfilneg\cr
(\mu x\bar\mu)(\mu y)=\mu(xy),
	\hfil\llap{\eqTag{eq-moup2}}\hfilneg\cr
(x\mu)(\bar\mu y\mu)=(xy)\mu,\rlap{\qquad\qquad and}
	\hfil\llap{\eqTag{eq-moup3}}\hfilneg\cr
xy+yx=(x\bar\mu)(\mu y) + (y\bar\mu)(\mu x)
	\hfil\llap{\eqTag{eq-moup4}}\hfilneg\cr
}$$
which hold for all $x,y,z\in\O$ and all imaginary units $\mu$.
We write $\re(xyz)$ for any of the three expressions in
\eqtag{eq-rexyz}. 
Identities such as these are  easily proven if one takes
advantage of the automorphism group of $\O$. This group is the
compact form of the exceptional Lie group 
$G_2$, and the stabilizer of a subalgebra
isomorphic to $\R$, $\C$ or $\H$ acts transitively on the units
orthogonal to the algebra. (See \cite{freud:oktav-geometry} for a proof.) 
For example, we prove \eqtag{eq-moup2}. After applying an
automorphism of $\O$ we may take $\mu=i$, $x=x_0+\a j$ and
$y=y_0+\b \ell$ with $\a,\b\in\R$, $x_0\in\C$ and $y_0\in\H$. After expanding
both sides of \eqtag{eq-moup2} we find that almost all terms
cancel by associativity in $\H$. All that remains is to check 
$$
(ij\bar\imath)(i\ell)=i(j\ell),
$$
which one does using the definition of multiplication. Note that
\eqtag{eq-moup3} may be obtained from 
\eqtag{eq-moup2} by taking conjugates.

We observe
that  if $A_1,\dots,A_n$ are matrices
over $\O$, with all except
at most two of them having all real entries, then the product
$A_1\cdots A_n$ is independent of the manner
in which the terms are grouped by parentheses.
The same result holds if all the entries of all the
matrices lie in some associative subalgebra of $\O$.

Finally, we will  use the fact that the group of transformations of
$\O$ generated by the left multiplications by imaginary units acts
transitively on the unit sphere $S^7$ in $\O$. (Proof: every orbit
contains an equator $S^6$ of $S^7\sset\O$, so any two orbits meet.)

\section{\Tag{sec-geometry-oh2} The Geometry of $\oh2$}

This section is not logically necessary for those following it. Its
purpose is to tell the reader in advance what some of the answers are,
to serve as a guide to the rather heavy algebra of
section~\tag{sec-jordan-alg}. We discovered the 
reflection groups mostly by geometric visualization using the
model described here, together with analogies with the
constructions of \cite{dja:complex-reflection-groups}. 
The geometry of $\oh2$ and $\op2$ can
be developed entirely synthetically, along the lines of
\cite{tits:op2} and \cite{tits:e6-and-e7}, 
but we will use the Jordan algebra approach in
order to be able to use a theorem of Borel and Harish-Chandra at
a crucial point in the proof of theorem~\tag{thm-finite-covol}.
Some of the basic ideas described here are implicit in \cite{jordan:op2} and
they are all developed in \cite{freud:oktav-eben} and
\cite{freud:oktav-geometry}. 

Even though $\O$ is not associative and so modules over it can't
reasonably be defined, one should imagine the fictional vector
space $\O^3$. If it were equipped with a nondegenerate Hermitian
form $\Phi$ then we could consider the set $J_\Phi$ of $\O$-linear
transformations of $\O^3$ that were self-adjoint with respect to
$\Phi$. Among these would lie the orthogonal projections onto
subspaces of $\O^3$; these would be the identity and zero
operators together with two continuous families corresponding to
the 1- and 2-dimensional subspaces. In particular, thinking of
elements of $\O^3$ as row vectors with matrices acting on the
right we could consider $X=v^*v$ where $v^*$ would be the
$\Phi$-adjoint of $v\in\O^3$. Up to a multiplicative constant
this would be the projection onto the span of $v$. It would turn out that
such transformations could be essentially characterized by the
conditions that $X^2=\lambda X$ and $\tr X=\lambda$ where
$\lambda\in\R$ would be the (square) norm of $v$. This would allow one to
recover the 1-dimensional subspaces of $\O^3$ and the norms of
vectors in them. Furthermore, if $X$ and $Y$ lay in $J_\Phi$
then their Jordan product $X*Y=(XY+YX)/2$ would also lie in
$J_\Phi$, so that $J_\Phi$ would be a Jordan algebra. Finally,
one could recover the notion of orthogonality of subspaces of
$\O^3$ because if $X$ and $Y$ were projections to two subspaces
then these subspaces would be orthogonal just if $X*Y=0$.

The reason one goes to these lengths is that $\O^3$ is not
in any sense a vector space over $\O$, so the above account is
purely fictional. However, the Jordan algebra $J_\Phi$ {\it
does} exist (although we will only treat the case of
real $\Phi$).  This allows us to recover much of the structure
that $\O^3$ would have provided if it existed. In particular, if
$\Phi$ has signature $-++$ then we can recover the set of
``1-dimensional subspaces of $\O^3$ on which $\Phi$ is negative
definite''. By analogy with hyperbolic geometry over $\R$, $\C$
and $\H$ we define $\oh2$ to be this set. The group $\aut
J_\Phi$ turns out to be $F_{4(-20)}$, which acts transitively
and with compact stabilizer on $\oh2$. 

The geometry of $\oh2$ is very similar to more conventional
hyperbolic 
geometry. In particular, $\oh2$ is an open 16-ball and it has a
natural boundary $\doh2$ ``at infinity'', a sphere $S^{15}$
which arises from the
nilpotents of $J_\Phi$ (or, heuristically, from the norm 0
elements of $\O^3$). Furthermore, by considering idempotents
of $J_\Phi$ one obtains points ``outside'' $\doh2$ which
together with $\oh2$ and $\doh2$ form the octave projective
plane $\op2$.
This
is essentially the same as  the realization of real hyperbolic
space $\rhn$ as an open
ball in $\rpn$, as the image of the  elements of
$\R^{n+1}$ of negative norm with respect to a quadratic form of
signature $-++\cdots+$. The only difference is that there is no
vector space $\O^3$ associated with $\op2$.

We will show that $\aut\oh2$ acts transitively on $\doh2$, and
the stabilizer of a null point will be very important in our
work. 
There is an  upper-half-space model for $\oh2$ which is ideal
for studying the stabilizer of a null point.
In this model, the points of $\op2$ (except those on the line at
infinity) are described by pairs $(x,z)$ with $x,z\in\O$. The
points of  $\oh2$
are the pairs with $\re z>0$, and the points of $\doh2$ are
the pairs with $\re z=0$, together with one extra point called
$\infty$, which lies on the line at infinity. There are
``translations'' stabilizing $\infty$, which have the form 
$$
(x,z)\mapsto(x+\xi,z-\im(x\bar\xi)+\eta)
$$
with $\xi\in\O$ and $\eta\in\im\O$. The translations turn out to
form a $15$-dimensional Lie group $\Heisenberg^{15}$ which (obviously) acts
transitively on $\doh2\setminus\{\infty\}$. The group of
translations is
closely analogous to the translations of Euclidean space
$\R^n$
which act on $\rh{n+1}$ in the usual upper-half-space model. 
The only substantial difference from the real case is that
$\Heisenberg^{15}$ is nonabelian, 
being a sort of octave version of the Heisenberg group.

We mentioned above that one can define the notion of
orthogonality of  ``subspaces of $\O^3$'' purely in terms of the
Jordan algebra. This
leads to the concept of a hyperplane in $\oh2$; usually we will call a
hyperplane a line.
A reflection is a nontrivial transformation  of
$\oh2$ that fixes a line pointwise. We will see
that there is a unique reflection across each line; the line is
called the mirror of the reflection. The map
$R':(x,z)\mapsto(-x,z)$ is an example of a reflection which fixes
$\infty$ and  there is
another reflection $R$ which
exchanges $\infty$ with $(0,0)$. The coordinate expression for
$R$ is complicated, but $R$
is analogous to a real reflection of $\rhn$ whose mirror
appears as a hemisphere resting on $\drh n$ in the upper-half-space model.
The two
reflection groups we will build will be generated by conjugates
of $R$ and $R'$ by translations satisfying appropriate integrality
conditions on $\xi$ and $\eta$. The mirrors of the conjugates of
$R'$ will be arranged in the pattern of a discrete subgroup of
$\Heisenberg^{15}$, and if this subgroup is ``dense enough''
then the mirrors will ``cover'' $\doh2\setminus\{\infty\}$. This
will allow us to prove that the groups have finite covolume in
$\aut\oh2$. 

\section{\Tag{sec-jordan-alg} Jordan Algebras}

%If there were a group $GL_3\O$ then an element $g$ thereof  would
%act on $M_3\O$ by $X\mapsto gXg^*$. Such an action would
%permute the various Hermitian matrices in $M_3\O$, and since
%such matrices would represent Hermitian forms on a
%3-dimensional vector space over $\O$, we could consider the
%subgroup of $GL_3\O$ stabilizing such a form $\Phi$. For
%appropriately chosen $\Phi$, the stabilizing group could be called
%$U(3;\O)$ or $U(2,1;\O)$. In truth, this  is almost what
%happens (though we will
%deal only with real $\Phi$). 
%To make it work we need to use Jordan algebras.
% 
We will use
angle-brackets `$\langle\rangle$' to denote the values of
various multilinear forms, and also to denote ``the linear span
of'' or ``the group generated by''. One of the latter 
possibilities applies when there are no vertical bars `$|$'
between the brackets. In this case,  the meaning will be clear from the
sort of objects lying between the brackets.

If $\Phi$ is a symmetric matrix in $M_3\R$, then
we will regard it informally as a ``Hermitian
form on $\O^3$''; we will restrict our attention to
invertible $\Phi$, corresponding to nondegenerate
forms. 
We write $M_3\O$ for the real vector space of $3\times3$
matrices with entries in $\O$, and denote by $X^*$ the conjugate-transpose
of a matrix $X$. 
The vector space $J_\Phi=\{X\in M_3\O\;|\;X\Phi=\Phi X^*\}$ is
closed under the Jordan multiplication $X*Y=(XY+YX)/2$, 
and we call it the Jordan
algebra associated to $\Phi$.
We say that $X,Y\in J_\Phi$ Jordan-commute if $X*Y=0$.
We informally regard elements of $J_\Phi$ as ``transformations
of $\O^3$'' that are Hermitian with respect to $\Phi$.
In most treatments (e.g., \cite{freud:oktav-eben} and
\cite{freud:oktav-geometry}), $\op2$ is
described in terms of $J_I$, whose automorphism group
is the compact form of $F_4$. We will study  $\Phi\neq I$ because we
are interested in $\oh2$ and because it is useful to formulate  the theory
for more general $\Phi$---this clarifies the various roles of the
matrices involved. For $g\in GL_3\R$ we define the
transformation $C_g$ of  $M_3\O$ given 
by $C_g:X\mapsto g^{-1}Xg$.   A quick computation shows that
$C_g$ 
carries $J_{g\Phi g^*}$ to $J_\Phi$ and preserves matrix
multiplication on $M_3\O$. 
We write
$O(\Phi)$ for the subgroup of $GL_3\R$ whose elements  are
unitary with respect to $\Phi$ (that is, $g\in O(\Phi)$ if
$g\Phi g^*=\Phi$). Scalars in $O(\Phi)$ act trivially on
$J_\Phi$, so we have $PO(\Phi)=O(\Phi)/\{\pm I\} \sset\aut J_\Phi$. 
When $\Phi$ is indefinite, $PO(\Phi)$ may be identified with the
subgroup $O^+(\Phi)$ of $O(\Phi)$ that preserves each of the two
null cones defined in $\R^3$ by
$\Phi$. We will assume this identification henceforth. 
%(When
%$\Phi$ is definite  $PO(\Phi)$ may be identified with
%$SO(\Phi)\sset O(\Phi)$.) 
For any $X\in M_3\O$ we define $\chi(X)=\re\tr(X)$ and the
symmetric inner product 
$\bilform{X}{Y}=\chi(X*Y)$. We call $\bilform{X}{X}$ the norm of
$X$.
It is easy to see that
$\tr(XY)=\tr(YX)$ if either $X$ or $Y$ is real, so 
$GL_3\R$ preserves the trace form on $M_3\O$. Since
$\bilform{X}{Y}$ 
is defined in terms of the trace and the
multiplication, $GL_3\R$ also preserves norms and inner products.
We
will see that all of $\aut J_{\Phi}$ preserves the restrictions
of these forms to $J_\Phi$, and that
the trace of any element of $J_{\Phi}$ is real. 

We will mainly be concerned with the indefinite form
$$
\Psi=\pmatrix{1&0&0\cr
		0&0&1\cr
		0&1&0\cr}
$$
and we write $J$ for $J_\Psi$. 
For any $\Phi$, there exists $g\in GL_3\R$ such that $g\Phi
g^*\in\{\pm I,\pm \Psi\}$, so $J_\Phi$ is isomorphic to $J_I$ or
to $J_\Psi$.
An element of $J$ has the form
$$
X=(a,b,c,u,v,w)=
\pmatrix{	a	&w	&\bar v\cr
		v	&\bar{u}&b	\cr
		\bar w	&c	&u\cr}
\eqno\eqTag{eq-def-of-X}
$$
with $a,b,c\in\R$ and $u,v,w\in\O$. It is obvious that $X$ has real trace. 
Since
all elements of $J_I$ also have real trace (proof: compute, or
see \cite{freud:oktav-eben})
and the maps $C_g$
preserve traces,  the trace of any element of any
$J_\Phi$ is real.  
Computation reveals
$$
\bilform{X}{X}=a^2+2bc+2\re(u^2)+4\re(vw),
$$
and so by polarization we obtain
$$
\bilform{X_1}{X_2}=a_1a_2+b_1c_2+b_2c_1+2\re(u_1u_2) +
	2\re(v_1w_2+v_2w_1).
\eqno\eqTag{eq-bilform}
$$

In order to work with the reflection groups of section~\tag{sec-examples}
we will need  detailed information about $J$ and its
automorphism group. We begin by giving
the multiplication table for $J$. For each $x\in\O$ we define
$$\displaylines{
A=
\pmatrix{
1 & 0 & 0 \cr
0 & 0 & 0 \cr
0 & 0 & 0
\cr}
,\quad 
B=
\pmatrix{
0 & 0 & 0 \cr
0 & 0 & 1 \cr
0 & 0 & 0 \cr}
,\quad
C=
\pmatrix{
0 & 0 & 0 \cr
0 & 0 & 0 \cr
0 & 1 & 0
\cr}
,\cr
U_x=
\pmatrix{
0 & 0 & 0 \cr
0 & \bar{x} & 0 \cr
0 & 0 & x
\cr}
,\quad
V_x=
\pmatrix{
0 & 0 & \bar{x} \cr
x & 0 & 0 \cr
0 & 0 & 0
\cr}
\hbox{,\quad and }
W_x=
\pmatrix{
0 & x & 0 \cr
0 & 0 & 0 \cr
\bar{x} & 0 & 0
\cr}.
\cr}$$
With respect to this spanning set, the Jordan multiplication
is given in table~\tag{tab-multiplication-table}.
Note that entries denote {\it twice} the Jordan product.
We write $U$ (resp. $V$, $W$) for the span of  the $U_x$
(resp. $V_x$, $W_x$). We write $\im U$ for the span of those $U_x$
with $x\in\im\O$. The nilpotent  $B$ will be very important in
our analysis, and we 
define the height of $X\in J$ to be $\height(X)=\bilform{X}{B}$. For
$X$ given by \eqtag{eq-def-of-X}, we see by \eqtag{eq-bilform} that 
$\height(X)=c$.

\topinsert
$$
\vbox{\offinterlineskip
\halign{\vrule#&\quad\hfil\strut$#$\hfil&\quad\vrule#&\quad\hfil$#$\hfil&\quad\hfil$#$\hfil&\quad\hfil$#$\hfil&\quad\vrule#&\quad\hfil$#$\hfil&\quad\hfil$#$\hfil&\qquad\hfil$#$\hfil&\quad\vrule#\cr
\noalign{\hrule}
height2pt&\omit&&&&&&&&&\cr
&&&A&B&C &&U_y &V_y &W_y&\cr
height2pt&\omit&&&&&&&&&\cr
\noalign{\hrule}
height2pt&\omit&&&&&&&&&\cr
&A&&2A &0 &0 &&0 &V_y &W_y&\cr
&B&& &0 &U_1 &&2\re(y)\cdot{B} &0 &V_{\bar{y}}&\cr
&C && & & 0 && 2\re(y)\cdot{C} & W_{\bar{y}} & 0&\cr
height2pt&\omit&&&&&&&&&\cr
\noalign{\hrule}
height2pt&\omit&&&&&&&&&\cr
&U_x&& & & && U_{xy+yx} & V_{\bar{x}y} & W_{y\bar{x}}&\cr
&V_x&& & & && & 2\re(x\bar{y})\cdot{B} & 2\re(xy)\cdot{A}+U_{\bar{y}\bar{x}}&\cr
&W_x&& & & && & & 2\re(x\bar{y})\cdot{C}&\cr
height2pt&\omit&&&&&&&&&\cr
\noalign{\hrule}
}}$$
\centerline{{\bf Table \Tag{tab-multiplication-table}}
Entries denote {\it twice} the Jordan product in $J$.}
\endinsert

We now introduce several transformations which will turn out to
be automorphisms of $J$.
We already know that  $O^+(\Psi)\sset\aut J$.
For $t\in\R$ and $\mu$ any imaginary unit of $\O$ we define the
transformations 
$$\displaylines{
R:(a,b,c,u,v,w)\mapsto
	(a,c,b,\bar{u},-\bar w,-\bar v) 
	\hfil\llap{\eqTag{eq-def-of-R}}\hfilneg\cr
S_{\mu}:(a,b,c,u,v,w)\mapsto
	(a,b,c,\mu u\bar\mu,\mu v,w\bar\mu)
	\hfil\llap{\eqTag{eq-def-of-S}}\hfilneg\cr
T_{t,0}=C_{g_t}\hbox{ for }
g_t=
\pmatrix{
1 & 0 & -t \cr
t & 1 & -t^2/2 \cr
0 & 0 & 1
\cr}
\hbox{ (Note $g_t^{-1}=g_{-t}$.)}
\hfil\llap{\eqTag{eq-def-of-T}}\hfilneg
\cr}$$
We will soon introduce a more convenient notation for certain
elements of $J$, which will greatly simplify these expressions.
We write $G$ for the group generated by $R$, the $S_\mu$
and the $T_{t,0}$. 
Later in this section we will see that $G=\aut J$, that  the
$S_{\mu}$  generate 
a group isomorphic to $\spin7\R$, and that the $T_{t,0}$
together with their conjugates under this $\spin7\R$ generate
a $15$-dimensional nilpotent Lie group.
An important element of $G$ is the map
$$
R':(a,b,c,u,v,w)\mapsto(a,b,c,u,-v,-w),
\eqno\eqTag{eq-def-of-R'}
$$
which is the square of any $S_\mu$. We will define octave
reflections in section~\tag{sec-examples} and see that $R'$ is one.

\beginproclaim Lemma
\Tag{thm-are-automorphisms}. 
The transformations $R$, $S_\mu$ and $T_{t,0}$ are
automorphisms of $J$ and preserve the trace and norm forms. We have
$\langle R,T_{t,0}\rangle=O^+(\Psi)$, and it follows that $O^+(\Psi)\sset G$.
\endproclaim

\beginproof{Proof:}
We first observe that $R$ and $T_{t,0}$ lie in $O^+(\Psi)$:
$T_{t,0}$ obviously does 
and $R=C_g$ for
$$
g=\pmatrix{
1 & 0 & 0 \cr
0 & 0 & -1 \cr
0 & -1 & 0
\cr}.
$$
Verification that each $S_\mu$ is an
automorphism relies on several pages of computation, using the identities
\eqtag{eq-moup}--\eqtag{eq-moup4} and the fact that
$\bar\mu=-\mu$. 
It is obvious that  $S_\mu$
preserves traces and since it preserves the Jordan
multiplication  it
also preserve norms. 
Alternately, section~\tag{sec-determinant} defines the
determinant form on $J$ and
theorem~\tag{thm-they-are-automorphisms} uses it to prove  
that $S_\mu\in\aut J$.
This approach
reduces considerably the amount of work required but is much more
circuitous.

We write elements of $\R^3$ as row vectors; $O^+(\Psi)$ acting
on such vectors (by multiplication on the right) preserves the
quadratic form $\Psi$. The $T_{t,0}$ are the parabolic
transformations stabilizing $(0,0,1)$
and their conjugates by $R$ are those stabilizing $(0,1,0)$. It
is obvious that these one-parameter subgroups generate
$SO^+(\Psi)\cong SO^+(2,1;\R)$. Since 
$R\in O^+(\Psi)\setminus SO^+(\Psi)$ we see that
$\generate{R,T_{t,0}}=O^+(\Psi)$.
\endproof

Working with $3\times3$ matrices is tedious and there is a
better notation, which closely resembles the use of ``vectors in
$\O^3$''. We write elements of the real vector space $\O^3$ as
row vectors, and define
$$\displaylines{
\goodo=\set{(x,y,z)\in\O^3}{x,y,z \hbox{ all lie in some associative
algebra}}\cr
\goodoo=\set{(x,y,z)\in\O^3}{y\in\R}\sset\goodo.
\cr}$$
Suppose $\Phi$ is given. We define the map $\pi_\Phi:\goodo\to
M_3\O$ by $\pi_\Phi(v)=\Phi v^*v$; we write $\pi$ for
$\pi_{\Psi}$. One checks immediately that
$\pi_\Phi(\goodo)\sset J_\Phi$.
We say that an element of
$J_\Phi$ is ``good'' (with respect to $\Phi$) if it is nonzero
and lies in the
image of $\pi_\Phi$. One checks that if
$(x,y,z)\in\goodo$  and
$\a\in\O\setminus\{0\}$ such that $x,y,z$ and $\a$ all lie in some  
associative algebra
then $\pi_\Phi(x,y,z)=\pi_\Phi(\a x,\a y,\a z)$, so 
any good element of $J_\Phi$ lies in the image of $\goodoo$.
It is easy to see that  the span of
$\pi(\goodo)$ is all of $J$. 
The elements  of $\goodo$ and $\goodoo$ have been dubbed ``restricted
homogeneous coordinates'' by Aslaksen \cite{asl:op2}. (Actually,
Aslaksen considered a slightly different version of the special
case $\Phi=I$. A very similar idea appears in \cite{jordan:op2}.
The relation between these ideas is explained in \cite{dja:op2}.) 
For reference, we record that
the ordered pair $(x,z)$ of
section~\tag{sec-geometry-oh2} represents
$$
\pi(x,1,z-|x|^2/2)\in J.
$$
(We will not need this identification.)

The following theorem a very useful computational tool.

\beginproclaim Theorem
\Tag{thm-inner-products}.
If $v,w\in\goodo$ and all six entries of $v$ and $w$ lie in some
associative subalgebra of $\O$, then for any $\Phi$ we have
$$
\bilform{\pi_\Phi(v)}{\pi_\Phi(w)}=|v\Phi w^*|^2.
$$
\endproclaim

\beginproof{Proof:}
Let $\a$ denote the single entry of $v\Phi w^*$. We write
$v=(v_a)$, $w=(w_b)$ and $\Phi=(\phi_{ab})$ for
$a,b=1,2,3$. In the derivation below we have associated terms
freely and used identity \eqtag{eq-rexyz} and the symmetry and
reality of $\Phi$.
$$\eqalign{
\bilform{\pi_\Phi(v)}{\pi_\Phi(w)} & =
	\tr(\pi_\Phi(v)*\pi_\Phi(w)) \cr
&= \tr(\Phi v^*v\Phi w^*w + \Phi w^*w\Phi v^*v)/2 \cr
&= \tr(\Phi v^*\a w + \Phi w^*\bar\a v)/2 \cr
&= \sum_{a,b}(\phi_{ab}\bar{v}_b\a w_a + \phi_{ab}\bar{w}_b\bar\a v_a)/2\cr
&= \sum_{a,b}\re(\phi_{ab}\bar{v}_b\a w_a) \cr
&= \re\(\a\sum_{a,b}w_a\phi_{ab}\bar{v}_b\) \cr
&= \re(\a\bar\a)=|\a|^2.
\cr}$$
\endproof

For
$\lambda\in\R$, we say that $X\in J_\Phi$ is $\lambda$-potent
(or is a
$\lambda$-potent) if $X^2=\lambda X$. We refer to $1$-, $0$-, and
$(-1)$-potents as idempotents, nilpotents and negpotents,
respectively. We say that $X$ is potent if it is
$\lambda$-potent for some $\lambda$. 
One reason we use $\goodo$ and $\goodoo$ 
is that the good elements turn out to be  the most important
elements of  $J_\Phi$. In particular, every good element is potent.
We will see that the
geometry of $\oh2$ may be described in terms of the good potents
of $J_\Phi$. We define the norm $|v|^2_\Phi$ of $v\in\goodo$ to be
the single entry of $v\Phi v^*$, which is automatically
real. Then we have
$$
\pi_\Phi(v)\pi_\Phi(v)
=\Phi v^*v\Phi v^*v=\Phi v^*|v|_{\Phi}^2v=
|v|_{\Phi}^2\pi_\Phi(v), 
$$
so we see  that $\pi_\Phi(v)$ is
$|v|^2_\Phi$-potent. ({\it Warning:} The norm $|v|^2_\Phi$ of
$v\in\goodo$ is not the same as the norm in $J_\Phi$ of
$\pi_\Phi(v)$---by theorem~\tag{thm-inner-products} the latter norm is the square of the
former. This is an unavoidable inconvenience.)
 
We also have
$$
\tr(\pi_\Phi(v))=
\re\tr(\pi_\Phi(v))=
\re\sum_{a,b}\phi_{ab}\bar{v}_bv_a=
\re\sum_{a,b}v_a\phi_{ab}\bar{v}_b=
|v|_{\Phi}^2.
$$
Therefore a necessary condition
for $X\in J_\Phi$ to be good is that there be $\lambda\in\R$ such
that $X$ is $\lambda$-potent and has trace $\lambda$.
In theorem~\tag{thm-all-potents} we will see that this is nearly a sufficient
condition. 

We can define an action of $O(\Phi)$ on $\goodo$ that is
$\pi_\Phi$-equivariant with its action on
$J_\Phi$. Namely,  $g\in O(\Phi)$ acts by 
$v\mapsto vg$; observe that this preserves $\goodo$. 
Taking $\Phi=\Psi$ we observe  that the
maps $T_{t,0}$ also preserve $\goodoo$, and we define an action
of the $S_\mu$ on 
$\goodoo$ by
$$
S_\mu:(x,y,z)\mapsto(\mu x,y,\mu z\bar\mu).
$$
With the aid of \eqtag{eq-moup2} and the fact that $y\in\R$
one can check that this action and the action of $S_\mu$ on $J$ are
$\pi$-equivariant.

An important set of transformations of $\goodoo$
are the ``translations''
$$
T_{\xi,\eta}:(x,y,z)\mapsto
(x+\xi y, y, z-x\bar\xi-|\xi|^2y/2+y\eta)
$$
with $\xi\in\O$ and $\eta\in\im\O$.
Those with $\xi=0$ are called central translations.
If $\xi=t\in\R$ and $\eta=0$ then this definition agrees with
the action of $T_{t,0}$ on $\goodoo$, defined in the previous paragraph by
virtue of $T_{t,0}\in O(\Phi)$.
All of the translations fix $(0,0,1)$, which is useful because
$B=\pi(0,0,1)$ is a useful nilpotent of $J$.
Using \eqtag{eq-moup} and the fact that $\bar\mu=-\mu$ one may show
$$\displaylines{
S_\mu\circ T_{\xi,\eta}\circ S_{\bar\mu} =
	T_{\mu\xi,\mu\eta\bar\mu} 
	\hfil\llap{\eqTag{eq-Heisenberg-1}}\hfilneg\cr
T_{\xi_1,\eta_1}\circ T_{\xi_2,\eta_2} =
	T_{\xi_1+\xi_2,\eta_1+\eta_2+\im(\xi_1\bar{\xi}_2)} 
	\hfil\llap{\eqTag{eq-Heisenberg-2}}\hfilneg\cr
T_{\xi,\eta}^{-1}=T_{-\xi,-\eta} 
	\hfil\llap{\eqTag{eq-Heisenberg-3}}\hfilneg\cr
[T_{\xi_1,\eta_1},T_{\xi_2,\eta_2}]=
	T_{\xi_1,\eta_1}^{-1}\circ T_{\xi_2,\eta_2}^{-1}\circ 
	T_{\xi_1,\eta_1}\circ T_{\xi_2,\eta_2} =
	T_{0,2\im(\xi_1\bar{\xi}_2)}\quad.
	\hfil\llap{\eqTag{eq-Heisenberg-4}}\hfilneg\cr
}$$
These show that the translations form a $15$-dimensional Lie
group (which we call $\Heisenberg^{15}$) which
is nilpotent of class $2$ and normalized by the $S_\mu$. 
Its derived subgroup coincides
with its center and consists of the central
translations---$\Heisenberg^{15}$ is a sort of  octave version of
the Heisenberg group. 
Because $\pi(\goodoo)$ spans $J$, the $\pi$-equivariance
properties of the $T_{t,0}$ and $S_\mu$ show that the action on
$J$ of any elements of $\generate{T_{t,0},S_\mu}$ is completely determined
by its action on $\goodoo$. 
The relations above  show that all of
$\Heisenberg^{15}\semidirect\generate{S_\mu}$ is generated by
the $S_\mu$ and those $T_{t,0}$ with $t\in\R$, so all of 
$\Heisenberg^{15}\semidirect\generate{S_\mu}$ maps into
$G\sset\aut J$. One reason for introducing the restricted
homogeneous coordinates is that the expression for the action
of $T_{\xi,\eta}$  on $J$ is horrible for general $\xi$
and $\eta$.
We now show that the natural map from the group
$\generate{T_{t,0},S_\mu}$ acting on $\goodoo$ to the 
group 
$\generate{T_{t,0},S_\mu}\sset G$ acting on $J$ is an
isomorphism.
The action of  $s\in\generate{S_\mu}$  on $\goodoo$
is determined by its actions on the first and third coordinates
of elements of $\goodoo$. These actions coincide with those of the image
of $s$ in $G$ on the subspaces $V$ and $U$ of $J$. Thus
$\generate{S_\mu}\sset\aut\goodoo$ acts faithfully on $J$. If
$h\in\Heisenberg^{15}\setminus{1}$ and $s\in\generate{S_\mu}$
then by considering the action of $hs$ on $(0,1,0)\in\goodoo$
we see that the corresponding action of $hs$ on $J$ is
nontrivial. Therefore all of 
$\generate{T_{t,0},S_\mu}\sset\aut\goodoo$
acts faithfully on $J$. This establishes an isomorphism between
the group $\generate{T_{t,0},S_\mu}$ acting on $\goodoo$ and 
the group $\generate{T_{t,0},S_\mu}$ acting on $J$. We will henceforth
assume this identification.

The following theorem provides the justification for our
assertion that the good elements of $J$ are the most important
ones, and provides the link between $\goodo$ and the structure
of the Jordan
algebra.

\beginproclaim Theorem
\Tag{thm-all-potents}.
Suppose $X\in J\setminus\{0\}$ is $\lambda$-potent and has trace
$\lambda$. Then
\rom1 under $G$, $X$ is equivalent to a real matrix, and 
\rom2 precisely one of $X$ and $-X$ is good. Furthermore,
\rom3 every nilpotent of $J$ has trace $0$.
\endproclaim

\beginproof{Proof:}
By the $\pi$-equivariance of the actions of $O(\Psi)$ on
$J$ and $\goodo$ and those of $\generate{\Heisenberg^{15},S_\mu}$ on
$J$ and $\goodoo$, the image under $G$ of a good element is good.
Therefore
it suffices to find a transform $X'$ of $X$ under $G$ such that
just one 
of $\pm X'$ is good.
Suppose $X=(a,b,c,u,v,w)$. If $X$ Jordan-commutes with both 
$B$ and $C$ then by table~\tag{tab-multiplication-table} we have
$X=\lambda A$ for some $\lambda\in\R$,
obviously a real matrix.  If 
$\lambda>0$ then $X=\pi(\sqrt \lambda,0,0)$ is good but
$-X$ is not. If $\lambda<0$ then the reverse applies. Since
$X\neq0$ we do not need to consider the case $\lambda =0$.

If $X$ fails to Jordan-commute with at least one of $B$ and $C$
then we may (if necessary applying $R\in G$ to exchange $B$ with
$C$) 
suppose that $X$ fails to Jordan-commute with
$B$. If $c=0$ then in order for $X$ to be  $\lambda$-potent we
must have
$|w|^2=\lambda c=0$, so $w=0$. Even if $c\neq0$, we may
suppose without loss of generality that $w=0$, as follows. After applying
suitable $S_\mu$'s to $X$ we may take $w\in\R$.  Computation
shows that $T_{t,0}$ (for $t\in\R$) acts on $J$ by
$$
T_{t,0}:(a,b,c,u,v,w)\mapsto(?,?,?,?,?,w+ct)
$$
where the question marks indicate irrelevant coordinates.
Applying $T_{-w/c,0}$ we may take $w=0$.  
Since the $S_\mu$ and
$T_{t,0}$ fix $B$,  we know that $X$ still fails to
Jordan-commute with $B$.

Squaring the matrix $X$ we find that when $w=0$ the conditions for 
$X$ to be $\lambda$-potent 
are
$$\displaylines{
a(a-\lambda)=0	
\hfil\llap{\eqTag{eq-nil1}}\hfilneg\cr
(a+\bar{u}-\lambda)v=0 
\hfil\llap{\eqTag{eq-nil2}}\hfilneg\cr
vc=0 
\hfil\llap{\eqTag{eq-nil3}}\hfilneg\cr
u(u-\lambda)+bc=0
\hfil\llap{\eqTag{eq-nil4}}\hfilneg\cr
c(2\re u-\lambda)=0 
\hfil\llap{\eqTag{eq-nil5}}\hfilneg\cr
|v|^2+b(2\re u-\lambda)=0.
\hfil\llap{\eqTag{eq-nil6}}\hfilneg\cr
}$$
If $c=0$ then by \eqtag{eq-nil4}, 
$u=0$ or $u=\lambda$, the latter condition being
impossible by the trace condition on $X$. Therefore $u=0$, which
together with $w=c=0$ shows that $X$ Jordan-commutes with
$B$. This contradicts our hypothesis on $X$, so $c=0$ is
impossible.

Since $c\neq0$,  \eqtag{eq-nil3} and \eqtag{eq-nil5} 
show  $v=0$ and $\re u=\lambda/2$. The trace condition implies that $a=0$,
and we solve for $b$ by finding
$b=-u(u-\lambda)/c=u\bar u/c$. We conclude 
$$
X=
\pmatrix{
0 & 0 & 0\cr
0 & \bar u & u\bar{u}/c \cr
0 & c & u
\cr}.
$$
If $c>0$ then $X=\pi(0,\sqrt c,u/\sqrt c)$ and $-X$ is not
good. 
If $c<0$ then $-X=\pi(0,\sqrt{-c},u/\sqrt{-c})$ and $X$ is not
good. This proves \rom2.  
To prove \rom1, we suppose without loss of generality that $c>0$
and simply apply $T_{0,-\im u/c}$, 
which carries $X$ to 
$\pi(0,\sqrt c,\re u/\sqrt c)$,  a real matrix.

To prove \rom3  we suppose that $X$ is nilpotent but make no
assumption about 
its trace. As above, we may suppose  $w=0$, so that
\eqtag{eq-nil1}--\eqtag{eq-nil6} hold with $\lambda=0$. Then \eqtag{eq-nil1}
implies   $a=0$. If $bc=0$
then by \eqtag{eq-nil4} we have $u=0$, so $\tr X=0$. If $bc\neq0$ then
by \eqtag{eq-nil3} we find $v=0$ and then by \eqtag{eq-nil6} we
find $\re u=0$, which again shows $\tr X=0$.
\endproof
 
One consequence of theorem~\tag{thm-all-potents} is that if $X\in J$ is
nilpotent then we know that $X=\pm\pi(v)$ for some $v\in\goodoo$
with $|v|_\Psi^2=0$. It is easy to 
enumerate all possibilities for $v$, and we find that either $X$
is a multiple of $B=\pi(0,0,1)$ or else
$$
X=h\cdot\pi(x,1,-|x|^2/2+z)
$$
for some $h\in\R$, $x\in\O$ and $z\in\im\O$.

\beginproclaim Theorem
\Tag{thm-they-generate-group}. 
\item{\rom1} 
For each $\lambda\in\R$, $G$ acts transitively on the
good $\lambda$-potents of $J$.
\item{\rom2}
Two orthogonal nilpotents are proportional.
\item{\rom3}
The subgroup $\Heisenberg^{15}$ of $G$ stabilizes $B$ and 
acts simply transitively on the 
nilpotents of  any given nonzero height.
\item{\rom4} The groups $G$ and $\aut J$ coincide. 
\item{\rom5}
The
subgroup fixing  $B$ and also $C$ 
is isomorphic to $\spin7\R$, is generated by the $S_\mu$, has
center $\{1,R'\}$, and acts on $\generate{B,C,U}$ as $SO(7)$.
\item{\rom6}
The stabilizer of $B$ is the
semidirect product $\Heisenberg^{15}\semidirect\spin7\R$.
\endproclaim

\remark
Recall that the transformation $R'$ is defined in \eqtag{eq-def-of-R'}, as
$$
R':(a,b,c,u,v,w)\mapsto(a,b,c,u,-v,-w).
$$

\beginproof{Proof:}
\rom1 Suppose that $X$ and $Y$ are good $\lambda$-potents and
thus nonzero. 
By theorem~\tag{thm-all-potents}, after applying elements of $G$, we may
suppose that $X=\pi(v_x)$ and $Y=\pi(v_y)$ with
$v_x,v_y\in\R^3\setminus\{0\}\sset\goodo$.
The norm map $v\mapsto|v|^2_{\Psi}$ is the usual norm on
$\R^3$ associated to the quadratic form $\Psi$. We know that
$|v_x|^2_{\Psi}=|v_y|^2_{\Psi}=\lambda$ and that
$O(\Psi)=O^+(\Psi)\times\{\pm I\}$ acts
transitively on the nonzero vectors in $\R^3$ with any given
norm. Applying an element of $O^+(\Psi)\sset G$ we may take 
$v_x=\pm v_y$ and so $X=\pi(v_x)=\pi(v_y)=Y$.

\rom2,\rom3 For any nilpotent $X$, either $X$ or $-X$ is
good (this follows from theorem~\tag{thm-all-potents}).
In order to have inner product $h\neq0$ with $B$, we must have
$X=h\cdot\pi(x,1,-|x|^2/2+z)$ with $x\in\O$,
$z\in\im\O$. For $h\neq0$ these are obviously permuted 
simply transitively by $\Heisenberg^{15}$, proving
{\rom3}. 
If $h=0$ then $X$ must
have the form $\pm\pi(x,0,z)$, but then since
$0=|(x,0,z)|^2_\Psi=|x|^2$, we must have
$x=0$ and so $X$ is a multiple of $B$. This proves {\rom2}.

\rom4
Suppose  $\phi\in\aut J$. By \rom1 and
theorem~\tag{thm-all-potents} {\rom2},{\rom3}, 
after multiplying $\phi$ by an element of
$G$ we may suppose $\phi(B)=\e B$, with $\e=\pm1$.  By \rom2 and
\rom3 we may then take $\phi(C)=\e' C$ for some $\e'\in\R$. Since
$2\phi(B)*\phi(C)=\phi(U_1)$ must be idempotent 
we see $\e'=1/\e=\e$. We will eventually learn
that $\e=+1$. The rest of the proof is mostly  a  chase
through the  multiplication table of $J$
(table~\tag{tab-multiplication-table}).  

By considering
$B*C$ we find that $\phi(U_1)=U_1$. We know that $\phi$ fixes the
identity matrix $I$ (since $I$ is the identity element of $J$),
so it also fixes $I-U_1=A$. We know that
$\phi$ fixes each of the spaces $\spanof{B,C,U}$, $\spanof{A,B,\im U,V}$ and
$\spanof{A,C,\im U,W}$, as these are the subspaces of $J$ which
Jordan-commute with $A$, $B$ and $C$, respectively. Taking their
intersection we see that $\phi$ stabilizes $\im U$. If $x\in\im\O$
then $x^2\in\R$ and $U_x*U_x=x^2\cdot U_1$, 
which shows that $\phi$ preserves
the norm $U_x\mapsto |x|^2$ on $\im U$. Next, for any $x\in\O$, 
$V_x$ Jordan-commutes with
$B$, so we see that $\phi(V_x)=\a A+\b B+ V_{x'} +U_{y'}$ for some
$\a,\b\in\R$, $x'\in\O$ and $y'\in\im\O$. Consideration of $U_1*V_x$ shows
that $\a=\b=y'=0$ and therefore $\phi$ preserves $V\sset J$. Considering
$V_x*V_x$ shows that $\phi$ preserves the norm $V_x\mapsto|x|^2$
on $V$. Similar reasoning proves that $\phi$ preserves $W\sset J$ and
the norm $W_x\mapsto|x|^2$ thereon. We conclude that
$$
\phi(a,b,c,u,v,w)=(a,\e b,\e c,\phi_1(u),\phi_2(v),\phi_3(w)),
$$
with $\e=\pm1$, each $\phi_m$ an orthogonal transformation, and
$\phi_1(1)=1$. 

The transformation $S_\mu$ acts on $\im U$ by the product of
central inversion and reflection in $\mu$. Therefore $\generate{S_\mu}$
(the group generated by all the $S_\mu$) acts on $\im U$ as $SO(7)$ and we may 
suppose that $\phi_1$ is either the identity or the
conjugation map. (We will see soon that the latter is
impossible.) 

Considering $V_x*W_y$ and using the fact that
$\phi_1(\bar{x})=\overline{\phi_1(x)}$, we find
$$
\phi_1(xy)=\phi_2(x)\phi_3(y)\qquad\forall x,y\in\O.
\eqno\eqTag{eq-isotopy-rule}
$$
Taking $x=y=1$ shows that $\phi_2(1)=\overline{\phi_3(1)}$ and
we write $\xi$ for this common value. Taking $x=1$ in \eqtag{eq-isotopy-rule}
yields 
$$
\phi_3(y)=\bar\xi\phi_1(y)
\eqno\eqTag{eq-formula-for-phi1}
$$
and taking $y=1$ in \eqtag{eq-isotopy-rule} yields
$$
\phi_2(x)=\phi_1(x)\xi.
\eqno\eqTag{eq-formula-for-phi2}
$$
Plugging these into \eqtag{eq-isotopy-rule} we obtain
$$
\phi_1(xy)=\phi_1(x)\xi\cdot\bar\xi\phi_1(y)\qquad\forall x,y\in\O.
\eqno\eqTag{eq-isotopy2}
$$
If $\phi_1$ is the conjugation map then we derive 
$$
\bar y\bar x=\bar x\xi\cdot\bar\xi\bar y\qquad\forall x,y\in\O,
$$
which is impossible since there are noncommuting $x$ and $y$
in some associative algebra containing $\xi$. Therefore $\phi_1=I$
and so by \eqtag{eq-isotopy2} we find
$$
xy=x\xi\cdot\bar\xi y\qquad\forall x,y\in\O.
$$
Taking $z\in\O$ and $x=\bar\xi z$ we may apply the identity
\eqtag{eq-moup2} to deduce $(\bar\xi z)y=\bar\xi(zy)$ for all
$y,z\in\O$. Therefore $\bar\xi\in\R$, so $\xi=\pm1$ and thus
by \eqtag{eq-formula-for-phi1} and \eqtag{eq-formula-for-phi2} we have $\phi_2=\phi_3=\pm I$. After
applying the square $R'$ of any $S_\mu$ 
we may suppose that $\phi_2=\phi_3=I$. Finally, consideration of
$B*W_x$ shows that $\phi_3=\e\phi_2$, so $\e=+1$. Therefore
$\phi$ is the identity, establishing \rom4.

We have just seen that the group stabilizing each of
$B$ and $C$ is generated by the $S_\mu$, and that this group
maps to its action $SO(7)$ on $\generate{B,C,U}$ with 
kernel equal to $\{1,R'\}$ and central in $\generate{S_\mu}$. 
Therefore $\generate{S_\mu}$ is
isomorphic to either $\spin7\R$ or $SO(7)\times(\Z/2)$. 
The latter is impossible
because the nontrivial central element $R'$ is a square. 
This establishes \rom5, and 
\rom6 follows immediately from \rom3 and \rom5.
\endproof

We now define the octave hyperbolic plane $\oh2$. It is is
the image in $\rp{26}=\projective J$ of the good
negpotents of $J$. The ``boundary''
of $\oh2$ is denoted $\doh2$ and is defined as the image of the
good nilpotents. A line in $\oh2$ is the set of good negpotents that
Jordan-commute with some fixed good
idempotent. The line associated to a given idempotent $X$ is
said to be polar to $X$. 
One may define the octave projective plane  $\op2$ as the image
in $PJ$ of all the good 
elements of $J$. Then $\doh2$ turns out to be the topological
boundary of $\oh2$ in $\op2$, and each line of $\oh2$
(equivalently, each good idempotent of $J$) is
associated with a point of $\op2\setminus\overline{\oh2}$. We
will not need this description of $\op2$.

Theorem~\tag{thm-they-generate-group} shows that
$G$ acts transitively on the points and lines of $\oh2$ and
$2$-transitively on the points of $\doh2$.
The remark following theorem~\tag{thm-all-potents}
shows that  $\doh2$ consists of the images
of the elements $(0,0,1)$ and $(x,1,z-|x|^2/2)$ of $\goodoo$,
where $x\in\O$ and $z\in\im\O$. This realizes $\doh2$
topologically as the
one-point compactification $S^{15}$ of $\R^{15}$.
Similarly, $\oh2$ may be identified with the image of the set 
$\{(x,1,z-|x|^2/2)\sset\goodoo|\re z>0\}$. This realizes $\oh2$ as a
16-ball bounded by $S^{15}=\doh2$. 

%Theorem~\tag{thm-semisimple}, which
%identifies 
%the Lie group $G$, requires the following lemma.

%\beginproclaim Lemma
%\oldTag{thm-faithful-on-doh2}. 
%$G$ acts faithfully on $\doh2$.
%\endproclaim

%\beginproof{Proof:}
%Since $G=\aut J$ contains no scalars, it acts faithfully on
%$PJ$. Therefore it suffices to show that the action of $g\in G$
%is governed by its action on the set of nilpotents of
%$J$. Suppose $g$ fixes every nilpotent. Then in particular, it
%fixes $B$ and $C$ and so also fixes $U_1=2B*C$. Furthermore, for each
%$z\in\im\O$, $g$ must fix $\pi(0,1,z)=C+|z|^2B+U_z$. We conclude
%that $g$ fixes all of
%$\generate{B,C,U}$.
%Theorem~\tag{thm-they-generate-group}{\rom5}  
%implies that $g$ is either the identity or $g=R'$.  Since $R'$
%carries the nilpotent $\pi(2,1,-2)$ to $\pi(-2,1,-2)$, 
%$g\neq R'$ and so $g$ is the identity.
%\endproof

\beginproclaim Theorem
\Tag{thm-semisimple}. 
$G$ is isomorphic to the group $F_{4(-20)}$, 
a  connected simply connected simple
Lie group of $52$ dimensions.
\endproclaim

\beginproof{Proof:}
The orbit of good nilpotents in $J$ is connected because it fibers
over its  image $S^{15}$
in $\oh2$ with the fibers being half-lines.
The stabilizer of a nilpotent is $\Heisenberg^{15}\semidirect\spin7\R$.
This realizes $\aut\oh2$ as an iterated fibration of
connected simply connected spaces and therefore it has these
properties itself. 
We have $\dimension G=15+1+15+\dimension \spin7\R=52$. We may also
compute the homological dimension ($\hd$) 
of $G$, as
$$
\hd(G)=\hd(S^{15})+\hd(\R^+)+\hd(\Heisenberg^{15})+\hd(\spin7\R)=36.
$$

To show that $G$ is semisimple and centerless it
suffices  to show that it
has no nontrivial normal solvable subgroups. Suppose $N$ were
such a subgroup. It is easy to
verify that the nilpotents of $J$ span $J$ and 
this easily implies that $N$ acts faithfully on $\doh2$.
As a normal subgroup of the $2$-transitive group $G$,
it acts transitively on $\doh2$. (This holds because $G$ permutes the orbits of
$N$; if there were more than one then this would contradict the
2-transitivity of $G$.) This  realizes $S^{15}$ as the
coset space $N/M$ for some subgroup $M$ of $N$ which of  course
is also solvable. Since solvable groups are aspherical, the long exact
homotopy sequence  shows that $S^{15}$ is aspherical, which
is absurd.

As a connected centerless semisimple Lie group, $G$ 
is a direct product of simple Lie
groups. Each factor, being normal, must act transitively on
$S^{15}$. If $M$ and $N$ are distinct (thus commuting) 
factors then since $N$ is
transitive, the action of $g\in M$ on $S^{15}$ is completely determined by
its action on any point thereof. This implies that $M$ acts
simply-transitively on $S^{15}$ and hence is compact. If there
were more than one factor than this
argument would apply to each, exhibiting $G$ as a product of
compact groups. Since $G$ is noncompact, this is
absurd. Thus there is only one factor,
and $G$ is simple. 

The dimension of $G$ and its simplicity show that it has
type $F_4$.  The dimension of any
maximal compact subgroup equals $\hd (G)$, so we see that $G$ is
isomorphic to $F_{4(52-2\cdot36)}=F_{4(-20)}$.
\endproof

\remark
One can see that the stabilizer $G_0$ of a point of $\oh2$ is
compact, as follows. We know 
$\dimension G_0+\dimension\oh2=\dimension G$, so 
$\dimension G_0=36$. We also know that $\hd(G_0)+\hd(\oh2)=\hd(G)$,
so $\hd(G_0)=36$. The compactness of $G_0$ follows from the
equality  $\dimension G_0=\hd(G_0)$. One can also show (see
\cite{tits:op2}) that $G_0\cong\spin9\R$. We 
will not need either of these facts.

\section{\Tag{sec-examples} The Reflection Groups}

A reflection
is a  nontrivial transformation that
fixes a line (its mirror) 
of $\oh2$ pointwise.  For
each line $L$ there is a unique reflection across $L$.
One can see this by considering the line $L$ polar to
$A=\pi(1,0,0)$, which contains $B=\pi(0,0,1)$ and
$C=\pi(0,1,0)$. 
By part \rom5 of
theorem~\tag{thm-they-generate-group}, the stabilizer of $B$ and $C$
is $\spin7\R$,  which acts
on $\spanof{B,C,U}$ and hence on 
$L$ as $SO(7)$. Therefore the central element of $\spin7\R$ is
the only reflection across $L$.
Explicitly, this reflection $R'$ acts on $J$ by
$$
R':(a,b,c,u,v,w)\mapsto(a,b,c,u,-v,-w),
$$
which may be written more simply on $\goodo$ as
$$
R':(x,y,z)\mapsto(-x,y,z).
$$
Since $G$ acts transitively on lines of $\oh2$, the reflections
form a conjugacy class in $G$.

Coxeter \cite{cox:integer-octaves} discovered a natural
discrete subring $\ocint$ of $\O$. One description
\ecite{ATLAS}{p. 14} of $\ocint$ is as the set of vectors $\sum x_ae_a$
with all $x_a\in{1\over2}\Z$ such that  the set of $a$ for which
$x_a\in\Z+{1\over2}$ coincides with one of the  sets 
$$\displaylines{
\{0\,1\,2\,4\},\{0\,2\,3\,5\},\{0\,1\,5\,6\},\{0\,3\,4\,6\},
\{\infty\,0\,1\,3\}, \cr
\{\infty\,0\,2\,6\},\{\infty\,0\,4\,5\},\{\infty\,0\,1\,2\,3\,4\,5\,6\},
\cr}$$
or one of their complements. 
We summarize here the  properties of $\ocint$ that we
will need. 

\beginproclaim Lemma
\Tag{thm-properties-of-K}.
\par

\item{\rom1}
As a lattice, $\ocint$ is isometric to a scaled copy of the
$E_8$ root lattice, with
minimal norm $1$, covering radius $1/\sqrt2$ and $240$ units.
\item{\rom2}
The elements of $\ocint$  of even norm span $\ocint$.
\item{\rom3}
The deep holes of $\ocint$ nearest $0$ are the halves of
the elements of $\ocint$ of  norm $2$.
\item{\rom4}
As a lattice, $\im\ocint$ is a scaled copy of the $E_7$ root
lattice, with minimal 
norm $1$, covering radius $\sqrt{3/4}$, and $126$ units.
\item{\rom5}
All deep holes of the  $E_7$ lattice are equivalent under translations by
elements of $E_7$.
\item{\rom6}
The group of transformations of $\im\ocint$ generated by the
maps  $x\mapsto\mu x\bar\mu$ with $\mu$ a unit of $\im\ocint$ is
the full rotation group of the $\Z$-lattice $\im\ocint$.
\item{\rom7}
The group of transformation of $\ocint$ generated by the maps
$x\mapsto\mu x$, with $\mu$ a unit of $\im\ocint$, is isomorphic to
$\spin7(\F_2)$ and acts transitively on the elements of $\ocint$ of
each norm $1$ and $2$.
\par
\endproclaim

\remark{Remarks:}
\rom6 and \rom7 identify the action of
$\generate{\{S_\mu:\mu\hbox{ is a unit of }\im\ocint\}}$
on $\goodoo$ and hence on $J$. 
We will write $\spin7(2)$ for $\spin7(\F_2)$ and
otherwise use ATLAS notation \cite{ATLAS} for finite groups.
The deep holes of a lattice $L$ in a Euclidean space
are the points of the space furthest from $L$; the
distance from any one of these to $L$ is called the
covering radius of $L$.

\beginproof{Proof:}
Background information on $E_7$ and $E_8$ sufficient to prove
\rom1, \rom3, \rom4, and \rom5 is provided in \ecite{splag}{ch. 4}. It is
a simple exercise to prove \rom2; in fact $\ocint$ is the
integral span of its
elements of norm $2$. The map in \rom6 acts on $\im\ocint$ by
the product of the real reflection in $\mu$ and the central
involution. Therefore these maps generate the rotation subgroup
of the $E_7$ Weyl group, which is the full rotation group of
the lattice $E_7$ and hence of $\im\ocint$. This establishes \rom6.

The central quotient of the 
group generated by the transformations of \rom7 is
identified on p. 85 of \cite{ATLAS} as the simple group
$O_7(2)$, and since the central 
involution is a square (the square of any $S_\mu$), 
the group must be the (unique) nontrivial central
extension $\spin7(2)$ of $O_7(2)$. To prove transitivity on the
240 units of
$\ocint$,  observe that each orbit
must have at least $126$ members, so any two orbits meet. Now we
show transitivity on norm $2$ vectors. Every
norm $2$ element of $\ocint$ has the form $a+b$ 
where $a$ and $b$ are orthogonal units of $\ocint$. By
transitivity on units we may take
$a=1$. Then since $\spin7(2)$ contains
$G_2(2)=\aut\ocint$ (see \ecite{ATLAS}{p. 14}), which fixes $a=1$ and 
acts transitively on the units of
$\im\ocint$, we may take $b=i$ (say). This establishes \rom7.
\endproof
 
For $n$ a positive integer we define $K_n$ as the integral
span in  $J$ of $nA$, $B$, $C$, and those $U_x$, $\sqrt{n}\,V_x$ and
$\sqrt{n}\,W_x$ with $x\in\ocint$. It is easy to see the $\ocint$
is the integral span of the image under $\pi$ of
$$
\{(x,y,z)\in\goodo|{x\in\sqrt{n}\cdot\ocint}, y,z\in\ocint\},
$$
or of
$$
\{(x,y,z)\in\goodoo|{x\in\sqrt{n}\cdot\ocint}, y\in\Z,
z\in\ocint\}.
$$
In particular, we can show that an element of $G$ preserves
$K_n$ if it preserves either of these subsets of $\goodo$. 
It is easy to check that $K_n$ is closed under Jordan
multiplication and thus is an integral form of $J$. We write
$R_n$ for the subgroup of $\aut K_n$ generated by 
reflections. We will see that $R_n=\aut K_n$ if $n=1$ or $n=2$.
 
Recall that $R'$ acts on $\goodo$ by
$R':(x,y,z)\mapsto(-x,y,z)$, so we see that
$R'\in R_n$ for all $n$. The transformation $R$ of \eqtag{eq-def-of-R}
acts on  $\goodo$ by
$R:(x,y,z)\mapsto(x,-z,-y)$, fixing
$\{(x,y,-y)\}$  pointwise. Since this subset of $\goodo$
corresponds to  the line polar to
$\pi(0,1,1)$, we see that $R$ is a reflection. It is
also clear that $R\in R_n$ for all $n$. The reflection groups we
construct will be generated by conjugates by
certain translations of $R$ and $R'$. We first show that $R_n$
contains a generous supply of translations.

\beginproclaim Theorem
\Tag{thm-got-translations}.
For every $n$ and every $\xi\in\ocint$, there exists
$\eta\in\im\O$ such that $R_n$ contains the transformation
$T_{\xi\sqrt n,\eta}$.
\endproclaim

\beginproof{Proof:}
By \eqtag{eq-Heisenberg-2} and
lemma~\tag{thm-properties-of-K}{\rom2} 
it suffices to prove the theorem when
$|\xi|^2$ is even. Computations in $\goodoo$ show that
$T_{-\xi\sqrt n/2,0}\circ R'\circ T_{\xi\sqrt n/2,0}$ preserves
$K_n$. (This holds even though $T_{\xi\sqrt{n}/2,0}$ might 
not preserve $K_n$.)
Since it is a reflection, it lies in $R_n$. Therefore 
$$
T_{\xi\sqrt n,0}=R'\circ T_{-\xi\sqrt n/2,0}\circ R'\circ T_{\xi\sqrt n/2,0}
$$
lies in $R_n$.
\endproof

\remark{Remark:}
The geometric picture behind this proof is that $R'$ and its
conjugate by $T_{\xi\sqrt{n}/2,0}$ are reflections whose mirrors
are parallel at infinity (that is, their mirrors do not meet in
$\oh2$, but both contain the point $\pi(0,0,1)$ of
$\doh2$). Naturally, the product of reflections in 
parallel mirrors in a translation.

We now study the  stabilizer in $R_n$ of $nA$.
Let $J_-$ denote the subspace of $J$ whose elements
Jordan-commute with $nA$. We have $J_-=\spanof{B,C,U}$, a Jordan
subalgebra of $J$. Since $R_n$ contains $1$ and $R'$, which are
the only elements of $G$ 
that fix $J_-$ pointwise, the stabilizer in
$R_n$ of $nA$ is completely determined by its
action on $J_-$. In particular, it must stabilize 
$J_-\cap K_n$, which is independent of $n$ and which we denote
by $K_-$. We write $R_-$ for the subgroup of $R_n$ 
generated by all the reflections 
$T_{0,\eta}\circ R\circ T_{0,-\eta}$ with $\eta\in\im\ocint$.
(For each $n$, we have $\generate{R,T_{0,\eta}}\sset\aut K_n$, so
$R_-\sset R_n$.) Since $R$ and each $T_{0,\eta}$ fixes $A$,
we see that $R_-$ acts on $J_-$ and $K_-$.

We write $\oh1$ (resp. $\doh1$) for the intersection of $\oh2$
(resp. $\doh2$) with the image of $J_-$ in $PJ=\rp{26}$. One can
identify $\oh1$ with the real hyperbolic space $\rh8$ and show
that its stabilizer in $G$ is $\spin{}(8,1)$, acting on each of
$J_-$ and $\rh8$ as
$SO(8,1)$. (In fact this falls out of the proof of theorem 
\tag{thm-hyperbolic-cell-group} below.) Therefore $R_-$ will
acts 
as a group of isometries of $\rh8$. We can describe this
group explicitly:

\beginproclaim Theorem
\Tag{thm-hyperbolic-cell-group}.
\par

\item{\rom1}
The action of $R_-$ on $\oh1\cong\rh8$ is that of the
rotation subgroup of the 
real hyperbolic reflection group with Coxeter diagram 
$$
\Delta = \box\coxboxtwo
$$
\item{\rom2}
For each $\eta\in\im\ocint$ and
each unit $\mu$ of $\im\ocint$, 
the transformations $T_{0,\eta}$ and $S_\mu$ lie in $R_-$ (and
hence in
$R_n$, for all $n$).
\item{\rom3}
For each $n$, the stabilizers of $B$ in $R_n$ and in $\aut K_n$
coincide. 
\endproclaim

\remark{Remarks:}
The labels $\a$ and $\b$ on the diagram are for reference by the
proof. This proof is lengthy and the reader may be satisfied
with a weaker version of theorem~\tag{thm-finite-covol}, for which this theorem
is not needed. Namely, knowing only that the stabilizer of $B$
in $R_n$ has finite index in its stabilizer in $\aut K_n$, one
can slightly modify the proof of theorem~\tag{thm-finite-covol} to prove that
$R_n$ has finite index in $\aut K_n$. (We indicate there how to
make these modifications.)
To prove this property of the stabilizers of $B$ one need only
take the 
translations of theorem~\tag{thm-got-translations} together with
those central 
translations which are their commutators. 

\beginproof{Proof:}
The elements of $\doh1$ are the images in projective space $PJ$ of
$\pi(0,0,1)$ and those $\pi(0,1,z)$ with $z\in\im\O$. We denote
these points by $\infty$ and $z$ respectively, and we will
describe symmetries of $\oh1$ by their actions on
$\im\O\cup\{\infty\}$. We know that $\aut J_-$ contains the
transformations $R$ and $T_{0,\eta}$ ($\eta\in\im\O$). 
These act as follows:
$$\displaylines{
R:z\mapsto 1/z \cr
T_{0,\eta}:z\mapsto z+\eta.
\cr}$$
These formulas also describe the action of $R$ and $T_{0,\eta}$
on $\oh1=\set{z\in\O}{\re z>0}$. Note that the octave reflection
$R$ acts as the central inversion about $1\in\oh1$. These
transformations generate the conformal group of $\doh1=S^7$, so
this identifies $\oh1$ with $\rh8$, as claimed
above. We henceforth use the symbol $\eta$ (resp. $\mu$)
exclusively  to
denote  elements (resp. units) of $\im\ocint$.
By a real reflection
we will mean a nontrivial transformation of $\rh8$ that fixes a
real hyperplane pointwise. This is the usual definition  of
reflections, and opposed to octave reflections, which act on
$\rh8$ by central inversion in points of $\rh8$.

By definition,
$R_-=\generate{P_\eta}$
where $P_\eta=T_{0,\eta}\circ R\circ T_{0,-\eta}$ and $\eta$
varies over $\im\ocint$. This group is
normalized by the involution $q:z\mapsto\bar z$, since $q$
commutes with $R$ and conjugates $T_{0,\eta}$ to
$T_{0,-\eta}$. The reader is 
cautioned that $q$ is not the restriction to $J_-$ of any
element of $\aut J$. Since $q$
reverses orientation on $\doh1$, $\generate{P_\eta}$ is the
rotation subgroup of $\generate{P_\eta,q}$. We write $Q_\eta$ for
$T_{0,\eta}\circ R\circ q\circ T_{0,-\eta}$. One can check
that $Q_0$ is a real reflection which
acts on $\doh1$ by inversion in the unit sphere
centered at $0$, so $Q_\eta$ acts by inversion in the unit
sphere centered at $\eta$. The next few constructions are much more
easily carried out by geometric rather than symbolic 
computation. Figure~\tag{fig-reflection-group} should assist the reader. 
%
\topinsert
\beginpicture
\def\magfactor{4}
\newdimen\hstep
\hstep=17.13 pt
\newdimen\vstep
\vstep=20 pt
\multiply\vstep by\magfactor
\multiply\hstep by\magfactor
\setcoordinatesystem units <\hstep,\vstep>
\put {$\bullet$} at -1   .5 
\put {$\bullet$} at -1  -.5
\put {$\bullet$} at  0    1
\put {$\bullet$} at  0    0
\put {$\bullet$} at  0   -1
\put {$\bullet$} at  1  1.5
\put {$\bullet$} at  1   .5
\put {$\bullet$} at  1  -.5
\put {$\bullet$} at  1 -1.5
\put {$\bullet$} at  2    1
\put {$\bullet$} at  2    0
\put {$\bullet$} at  2   -1
\put {$0$} [tr]  <-4pt,-1pt> at 0 0
\put {$\eta_1$} [bl] <1pt,1pt> at 1 .5
\put {$\eta_2$} [tl] <1pt,-2pt> at 1 -.5
\put {$2\eta_1$} [bl] <1pt,1pt> at 2 1
\put {$2\eta_2$} [tl] <1pt,-2pt> at 2 -1
\put {$Q_0$} [r]  at -1.2 0
% the equation of the unit circle, in these coordinates, is 
% x^2/.75 + y^2 =1.
\put {$P_{\eta_1}$} [br]  at .5 1.401
\put {$P_{\eta_2}$} [tr]  at .5 -1.401
\put {$P_{\eta_1}Q_0P_{\eta_1}$} [b] <0pt,1pt> at .75 1.875
\put {$P_{\eta_2}Q_0P_{\eta_2}$} [t] <0pt,-2pt> at .75 -1.875
\put
{$(P_{\eta_1}Q_0P_{\eta_1})(P_{\eta_2}Q_0P_{\eta_2})(P_{\eta_1}Q_0P_{\eta_1})$}
[bl] at 2.5 0
\circulararc 360 degrees from 1 .5 center at 0 0
\plot -.5 0 4 0 /
\plot .75 1.875 2.5 -.75 /
\plot .75 -1.875 2.5 .75 /
\setdashes
\circulararc 360 degrees from 0 0 center at 1  .5
\circulararc 360 degrees from 0 0 center at 1 -.5
\endpicture
\medskip
\noindent
{\bf Figure \Tag{fig-reflection-group}.}
A $2$-dimensional section of $\im\O$ (see the proof of
theorem~\tag{thm-hyperbolic-cell-group}). 
Dots indicate elements of $\im\ocint$. 
The real reflection $Q_0$ acts by inversion in the solid
circle. The lines are the mirrors of certain other real reflections, whose
names are next to them. Each dashed circle is carried to itself by
the octave reflection whose name is next to it; this reflection
carries each point on the dashed circle to its antipode on the
same dashed circle. The octave reflection also exchanges the
center of the dashed circle with $\infty$.
\endinsert
%
We know that $Q_0\in\generate{P_\eta,q}$. If $|\eta|^2=1$ then
$P_\eta\circ Q_0\circ P_\eta$ is the (real)
reflection across the $P_\eta$-translate of the mirror of
$Q_0$---namely, the perpendicular bisector of the segment
joining $\eta$ and $2\eta$. If $\eta_1$ and $\eta_2$ are two
units of $\im\ocint$ with mutual distance $1$, then
$$
(P_{\eta_1}\circ Q_0\circ P_{\eta_1})
(P_{\eta_2}\circ Q_0\circ P_{\eta_2})
(P_{\eta_1}\circ Q_0\circ P_{\eta_1})
$$
is reflection across the hyperplane of $\im\O$ that passes
through $0$ and is orthogonal to $\pm(\eta_1-\eta_2)$. These
reflections generate the $E_7$ Weyl group, the group of all
isometries of $\im\ocint$ fixing 
$0$, and so $\generate{P_\eta}$ contains all of the rotations
of $\im\ocint$ fixing $0$. Since $\generate{P_\eta}$ is normalized by the
$T_{0,\eta}$, it contains all the rotations about all of the
points of $\im\ocint$. These rotations generate an (infinite)
group of transformations of $\im\O$ containing all rotations and
translations preserving $\im\ocint$.
We are now in a position to prove {\rom2}. We know that
$R_-$ contains transformations acting on $J_-$ in the same
manner as do the $T_{0,\eta}$ and $S_\mu$. Fixing $\mu$ for the
moment, we see that $R_-$ contains either $S_\mu$ or 
$R'\circ S_\mu$. The square of either of these is $R'$, so
$R'\in R_-$. Now, for any $\mu$ (resp. $\eta$), $R_-$ contains
either $S_\mu$ or $R'\circ S_\mu$ (resp. $T_{0,\eta}$ or 
$R'\circ T_{0,\eta}$) and hence contains both. This proves {\rom2}.

We now finish the proof of {\rom1}. We know that
$\generate{P_\eta,q}$ contains $Q_0$ and all the translations 
$z\mapsto z+\eta$, and so it contains all the $Q_\eta$. Since
$q=Q_0\circ P_0$, we see that
$\generate{P_\eta,q}=\generate{P_\eta,Q_\eta}$ and so 
$\generate{P_\eta}$ is the rotation subgroup of
$\generate{P_\eta,Q_\eta}$.  We now  show 
$\generate{P_\eta}\sset\generate{Q_\eta}$. 
If $\eta_1$ and $\eta_2$ are
neighbors in $\im\ocint$ then 
$Q_{\eta_1}\circ Q_{\eta_2}\circ Q_{\eta_1}$ is the real
reflection in the perpendicular bisector of the segment joining
$\eta_1$ and $\eta_2$. These real reflections generate the
group of all isometries of $\im\O$ that preserve $\im\ocint$.
(This is just the affine $E_7$ Weyl group; note that the diagram
automorphism of the Coxeter group does not preserve the
lattice---it exchanges it with the set of its deep holes.) 
In particular, it  contains the map
$q:z\mapsto-z$. Since $P_0=q\circ Q_0$, we see that
$P_0\in\generate{Q_\eta}$. Since $\generate{Q_\eta}$
is normalized by all $T_{0,\eta}$, we see  that
$\generate{P_\eta}\sset\generate{Q_\eta}$.  This identifies
$\generate{P_\eta}$ as the rotation subgroup of $\generate{Q_\eta}$.

It remains to identify $\generate{Q_\eta}$. We take the nodes of
$\Delta$ other than $\a$ and $\b$ to represent standard
generators of the $E_7$ Weyl group, acting on $\im\ocint$ by
isometries that preserve $0$.
We take the node $\a$ to represent the reflection in the
perpendicular bisector of the segment joining $0$ and $\eta_0$,
for some unit $\eta_0$ of $\ocint$. The group generated by these
$8$ reflections is the affine $E_7$ Weyl group and contains all
translations of $\im\ocint$. Taking the node $\b$ to represent
the reflection $Q_0$, we see that the group generated by 
these $9$ reflections contains all the $Q_\eta$ and thus equals
$\generate{Q_\eta}$. It is easy to see that the angles between
pairs of
these $9$ mirrors are $\pi/3$ (resp. $\pi/2$) when the
corresponding  nodes of $\Delta$ are joined
(resp. unjoined). Since these are integral submultiples of
$\pi$, the region in $\rh8$ bounded by the mirrors is a
fundamental domain for the group generated by the $9$
reflections. This identifies $\generate{Q_\eta}$ as the Coxeter
group with diagram $\Delta$, proving {\rom1}.

Now we prove {\rom3}. Suppose $\phi\in\aut K_n$ with
$\phi(B)=B$. Then $\phi(C)$ is  a good nilpotent of height $1$,
so $\phi(C)=\pi(x,1,z)$ for some $x,z\in\O$. Since
$\phi(C)\in K_n$ we must have $x\in\sqrt{n}\cdot\ocint$. After
applying a translation of $R_n$, 
courtesy of theorem~\tag{thm-got-translations}, we may take $x=0$. Then since
$\phi(C)$ is nilpotent and in $K_n$ we must have
$z\in\im\ocint$. After applying $T_{0,-z}$, which lies in $R_n$
by {\rom2}, we may take $z=0$. That is, we may suppose
$\phi(C)=C$. By mimicking the proof of {\rom4} of
theorem~\tag{thm-hyperbolic-cell-group} and using {\rom6} of
lemma~\tag{thm-properties-of-K} we see 
that that the simultaneous stabilizer of $B$ and $C$ is generated
by the $S_\mu$, which  by {\rom2} also lie in $R_n$. This
establishes {\rom3}.
\endproof

\remark{Remarks:}
The proof of {\rom2} shows that $R_-$ is a nontrivial
central extension by $\Z/2$ of its image in $\aut J_-$; 
the nontrivial central
element is $R'$.
One can show that $\Delta$ is the Coxeter diagram of the
reflection group of the real Lorentzian lattice
$E_7\oplus\smallmatrix0110$. This is
not surprising since this form is the negative of
the restriction to the traceless elements of $K_-$ of the norm
form on $J$. 

Here is the main result of the paper: the existence of finite
covolume reflection groups acting on $\oh2$.

\beginproclaim Theorem
\Tag{thm-finite-covol}.
\item{\rom1}
Under the action of $R_1$, there is a single orbit
of primitive good nilpotents of $K_1$.
\item{\rom2}
Under the action of $R_2$, there are precisely two orbits of primitive good
nilpotents of $K_2$. Elements of one orbit are characterized as
such by
being orthogonal to idempotents of $K_2$.
\item{\rom3}
For $n=1$ or $2$, $R_n$ coincides with $\aut K_n$ and has finite
covolume in $G\cong F_{4(-20)}$.
%
% Needed in the large commented-out region below
%
%\item{\rom4}
%The stabilizer of $B$ in $R_2$ acts transitively on the
%primitive good nilpotents of $K_2$ that have height $2$ and do
%not lie in the $R_2$-orbit of $B$.
\endproclaim

\beginproof{Proof:}
Let $n=1$ or $2$.
Suppose $X\in K_n$ is a primitive  good nilpotent and has
minimal height $h$ among its images under $R_n$. Then either $X=B$
or $X=h\cdot\pi(x,1,-|x|^2/2+z)$ with $h>0$, $x\in\O$ and
$z\in\im\O$. After applying a translation in $R_n$ (of which
there are plenty by theorem~\tag{thm-got-translations}), we may suppose that $x$ is
at least as close to $0$ as it is to any other element of
$\sqrt{n}\cdot\ocint$. By theorem~\tag{thm-hyperbolic-cell-group}, $R_n$ also contains
all central translations, so we may also suppose that $z$ is at
least as close to $0$ as it is to any other point of
$\im\ocint$. By lemma~\tag{thm-properties-of-K} {\rom1}, {\rom2},
these conditions imply $|x|^2\leq n/2$ and $|z|^2\leq3/4$. 
Observe that
$$
R(X)=h\cdot(x,|x|^2/2-z,-1)
$$
has height $h(|z|^2+|x|^4/4)$. 
If $n=1$ then this is smaller than
the height $h$ of $X$, contrary to our hypothesis on $X$.
Thus we have proven {\rom1}. 

If $n=2$ then either $\height(R(X))<\height(X)$, contrary to our
hypothesis on $X$, or we have $|x|^2=1$ and $|z|^2=3/4$. Since $x$
is at
least as close to $0$ as to any other element of
$\sqrt2\cdot\ocint$, we see that $x$
is a deep hole of $\sqrt2\cdot\ocint$.
Similarly, since $z$
is at
least as close to $0$ as to any other element of
$\im\ocint$, we see that $z$
is a deep hole of $\im\ocint$.
By lemma~\tag{thm-properties-of-K}{\rom3}, {\rom7}
and theorem~\tag{thm-hyperbolic-cell-group}{\rom2},
after applying an element of $\spin7(2)\sset R_2$ we may take
$x$ to be any particular deep hole of $\sqrt{2}\cdot\ocint$ 
nearest $0$, say $x=(1+i)/\sqrt2$. By
lemma~\tag{thm-properties-of-K}{\rom5} and
theorem~\tag{thm-hyperbolic-cell-group}{\rom2}
we may apply some $T_{0,\eta}\in R_2$ and take $z$ to be 
any particular deep hole of $\im\ocint$, say
$z=(i+j+k)/2$. This determines $X$ up to the scale factor $h$,
which is determined by the requirement that $X$ be a primitive 
good element of $K_2$. Therefore
$$
X=X_0=\pi(1+i,\sqrt2,\w\sqrt2),
$$
where $\w=(-1+i+j+k)/2$.
This proves that $K_2$ has at most two orbits of primitive good
nilpotents under $R_2$. It is obvious that $X_0$ is orthogonal
to the idempotent 
$E=\pi(0,1,-\bar\w)\in K_2$ and one can show $B$ is not orthogonal to any
idempotent of $K_2$. (The key is that
$A\not\in K_2$.) This proves that there are exactly two orbits
under $R_2$ and under $\aut K_2$; in particular, {\rom2}
holds. 
%We have also proven 
%{\rom4}, since we have shown that any primitive good
%nilpotent of height $2$ either can be reduced in height by a
%reflection (so that it is equivalent to $B$), or else is
%equivalent to $X_0$ by the product of a translation of $R_2$ and
%an element of $\generate{S_\mu}$.

Now suppose $\phi\in\aut K_n$. By using \rom1 or \rom2, after
multiplying by an element of $R_n$ we may
suppose that $\phi(B)=B$. 
But then by theorem~\tag{thm-hyperbolic-cell-group}
{\rom3} we see that
$\phi\in R_n$. This 
establishes the equality $R_n=\aut K_n$ for $n=1,2$.
Because $\aut K_n$ is an arithmetically defined subgroup of a
real semisimple Lie group, a theorem of Borel and Harish-Chandra
\cite{borel:arithmetic-groups} shows that it has finite
covolume in $G$, which establishes {\rom3}.
\endproof

\remark{Remarks:}
If we do not assume the full results of
theorem~\tag{thm-hyperbolic-cell-group}, as described in the remark
there, the proof above can be modified to obtain the weaker
result  that $R_1$ and
$R_2$ have finite covolume in $G$. The proof above shows that if
$X$ is a primitive good nilpotent of $K_1$ of positive height then
there is a reflection in $R_1$ reducing the height of $X$ (namely,
the conjugate of $R$ by the translations used in the first
paragraph of the proof). Therefore we can conclude that $X$ is
equivalent to $B$ under $R_1$. Similarly, if $X$ is a primitive
good nilpotent in $K_2$ then after a sequence of reflections in
$R_2$, $X$ may be taken to have height~$\leq1$. Since the
stabilizer of $B$ in $\aut K_2$ acts with one orbit on the primitive
good nilpotents of height $1$, and by hypothesis the stabilizer
of $B$ in  $R_-\sset R_2$ has finite index of that in $\aut
K_2$, we see that there are only finitely many $R_2$-orbits of
primitive good nilpotents in $K_2$. The finiteness
of the  index of $R_i$ in $\aut K_i$ for each $i=1,2$
follows from this and from the assumption that $B$'s stabilizer  in
$R_i$ has finite index in its stabilizer in $\aut K_i$.

%
% Commented out 1 July 1997; it explains an alternate and
% not-so-nice interpretation of the groups as stabilizers of
% Hermitian forms.
% 
%We now interpret $R_1$ and $R_2$ as the symmetry groups  of
%certain ``Lorentzian lattices'' over $\ocint$, meaning that
%they are the automorphism groups of the Jordan rings of
%$\Phi$-Hermitian elements of $M_3\ocint$ for suitable
%integral matrices $\Phi$. Interpreted in this way,
%theorem~\tag{thm-lattice-stabilizers} 
%below shows that
%$R_1$ is the stabilizer of the ``$\ocint$-lattice'' with  ``inner
%product matrix'' $\Psi$, and that $R_2$ is (isomorphic to) the
%stabilizer ``over $\ocint$'' of 
%$$
%\Psi'=
%\pmatrix{
%2 & 0 & 0 \cr
%0 & 0 & 1 \cr
%0 & 1 & 0
%\cr}.
%$$
%In section~\tag{sec-determinant} we will give a different (and more natural)
%interpretation of $\aut K_n$, as the stabilizer of an integral
%form in the group $\gint$,  a certain
%integral form of the Lie group $E_6$.

%\beginproclaim Theorem
%\goofTag{thm-lattice-stabilizers}.
%$R_1=\aut(J\cap M_3\ocint)$ and $R_2\cong\aut(J_{\Psi'}\cap M_3\ocint)$.
%\endproclaim

%\beginproof{Proof:}
%\def\moreK{\tilde{K}_2}
%The claim for $R_1$ is trivial since $K_1=J\cap M_3\ocint$.
%We write $\moreK$ for the integral span of $K_2\cup\{A\}$. (Recall that
%$2A\in K_2$ but $A\not\in K_2$.) The map $C_g$, for
%$$
%g=
%\pmatrix{
%1/\sqrt2 & 0 & 0 \cr
%0 & 1 & 0 \cr
%0 & 0 & 1
%\cr},
%$$
%identifies the Jordan rings $\moreK$ and $J_{\Psi'}\cap M_3\ocint$, so it suffices to
%show that $R_2=\aut\moreK$. To show that $R_2\sset\aut\moreK$ it
%suffices to show that every element of $R_2$ carries $A$ into
%$\moreK$. $R_2$ is generated by its maps $R$, $R'$, $S_\mu$,
%$T_{0,\eta}$ and $T_{\xi\sqrt2,0}$, with $\mu$ a unit of
%$\im\ocint$,
%$\eta\in\im\ocint$ and $\xi$ an element of
%$\ocint$ of even norm. All but the last fix $A$, and the last
%carries it to
%$$
%T_{\xi\sqrt2,0}(A)=\pi(1,0,-\bar{\xi}\sqrt2) =
%\pmatrix{
%1 & 0 & -\bar{\xi}\sqrt2 \cr
%-\xi\sqrt2 & 0 & 2|\xi|^2 \cr
%0 & 0 & 0
%\cr}
%\in\moreK.
%$$

%We claim that every nilpotent of $\moreK$ is actually in
%$K_2$. If $X\in\moreK$ is nilpotent then $2X\in K_2$ and so
%theorem~\tag{thm-finite-covol}{\rom2} shows that $X$
%must be equivalent under $R_2$ to a multiple of one of $B$ and 
%$X_0=\pi(1+i,\sqrt2,\w\sqrt2)$ where $\w=(-1+i+j+k)/2$. Since
%each of these is primitive in $\moreK$ as well as in $K_2$, we
%see that $X$ must lie in $K_2$.
%We write $R_2(B)$ and $R_2(X_0)$ for the $R_2$-orbits of $B$ and
%$X_0$, respectively.

%Next we claim that the stabilizers of $B$ in $R_2$ and
%$\aut\moreK$ coincide. To see this, observe that any good
%nilpotent of height $1$ in $\moreK$ actually lies in $K_2$. (If
%$Y$ is good and has height $1$ then $Y=\pi(x,1,z)$ for some
%$x,z\in\O$. Since $Y\in\moreK$ we must have
%$x\in\sqrt2\cdot\ocint$ and $z\in\ocint$. But these conditions
%imply  $Y\in K_2$.) Then the proof of
%theorem~\tag{thm-hyperbolic-cell-group}{\rom3} can be
%applied to show that the
%stabilizers of $B$ in $R_2$ and $\aut\moreK$ coincide.

%Suppose $g\in\aut\moreK\setminus R_2$. If $g$ carried any
%element of $R_2(B)$ to any other element of $R_2(B)$, then there
%would exist $g',g''\in R_2$ such that $g''\circ g\circ g'(B)=B$.
%By the second claim, $g''\circ g\circ g'\in R_2$, so $g\in R_2$,
%contrary to hypothesis on $g$. Therefore $g(X)=B$ for some 
%$X\in R_2(X_0)$. By precomposing $g$ with some element of $R_2$
%we may suppose that $g(X_0)=B$.
%Since $\bilform{X_0}{B}=2$, $g(B)$ is a
%height $2$ nilpotent which we know does not lie in $R_2(B)$. By
%theorem~\tag{thm-finite-covol}{\rom4}, after applying another element of
%$R_n$ we may take $g(B)=X_0$. In short, we may suppose that
%$g$ exchanges $B$ and $X_0$. 

%Recall that $X_0$ is orthogonal to the good idempotent
%$E=\pi(0,1,-\bar\w)$ of height $1$. Consider $g(E)$. Since
%$\bilform{g(E)}{B}=0$ and $g(E)$ is idempotent we see
%that $g(E)=\pi(1,0,z)$ for some $z\in\O$. Since
%$g(E)\in\moreK$, we must have $z\in\sqrt2\cdot\ocint$. Then
%$$
%g(E)=
%\pmatrix{
%1 & 0 & z \cr
%\bar{z} & 0 & |z|^2 \cr
%0 & 0 & 0
%\cr}
%$$
%and so
%$$
%\bilform{g(E)}{X_0}=2+2|z|^2+2\re(z(1-i)\sqrt2)
%$$
%is even. (Note that we must compute the inner product using
%\eqtag{eq-bilform} 
%rather than theorem~\tag{thm-inner-products} because we do not
%know whether $z$ and the ``coordinates'' $(1+i,\sqrt2,\w\sqrt2)$
%of $X_0$ lie in an associative algebra.) 
%This contradicts the fact that
%$\bilform{g(E)}{g(B)}=1$. We see that $g$ cannot exist
%and so $R_2=\aut\moreK$.
%\endproof

%Finally, we may  regard the group $R_-$ (modulo its central
%element $R'$) as the reflection group of
%the ``Lorentzian lattice'' over $\ocint$ with inner product matrix
%the standard hyperbolic pairing
%$$
%\pmatrix{
%0 & 1 \cr
%1 & 0
%\cr}. 
%$$

\section{\Tag{sec-determinant} The Determinant}

In section~\tag{sec-examples} we constructed the promised octave hyperbolic
reflection groups and proved that they have finite covolume. In
the following section we will interpret our groups as the
stabilizers of certain Hermitian forms over $\ocint$. To do this
we must work in a context wider than that of our treatment so
far. Namely, we must introduce the determinant form on $J$ and
its automorphism group $H$. By explicit construction we will show
that the determinant form on $J$ is equivalent to the one on
$J_I$ studied in \cite{freud:oktav-eben}, which will prove that $H\isomorphism
E_{6(-26)}$. The action of $E_{6(-26)}$ on $J_I$ is similar to
the action of $PGL_3\C$ on the space of Hermitian forms on
$\C^3$, so this is the natural setting for discussions of the
stabilizers of Hermitian forms. We will pursue this further in
the next section.

Recall that we defined $\chi(X)=\re\tr(X)$ for any
$X\in M_3\O$. 
(Of course, we are only
interested in elements of Jordan algebras $J_\Phi$, which have
real traces.)
Pages 30-31 of \cite{freud:oktav-geometry} show that if $X,Y,Z\in M_3\O$ then
$\bilform{X}{YZ}=\bilform{XY}{Z}=\bilform{ZX}{Y}$, and it
follows therefrom that $\chi(X^2X)=\chi(XX^2)$ and that 
$\trilform{X}{Y}{Z}=\bilform{X*Y}{Z}$ is a symmetric trilinear form.
Since it is  symmetric,  it is obtained by
polarization from the cubic form $\cuform$ given by 
$$
\cuform(X)=\trilform{X}{X}{X}=\chi(X^3).
$$
Following \cite{freud:oktav-eben} 
we define the cubic ``determinant'' form  by
$$
\det(X)={1\over6}[2\chi(X^3)-3\chi(X^2)\chi(X)+\chi(X)^3].
$$
The name of this cubic form derives from analogies between it
and the usual determinant form on (say) $M_3\R$. In particular,
in the
special cases will consider below the formulas \eqtag{eq-det} and
\eqtag{eq-det-for-J-I} for the
determinant 
closely resemble the
usual expression.
For any $\Phi$ we write $H_\Phi$ for the group of all linear
transformations  preserving the determinant form on 
the real vector space underlying $J_\Phi$. We write $H$ for
$H_\Psi$. 

\beginproclaim Theorem
\Tag{thm-det}.
For $X\in J$ given by \eqtag{eq-def-of-X},
$$
\det(X)=a|u|^2+b|w|^2+c|v|^2-abc-2\re(uvw).\eqno\eqTag{eq-det}
$$
\endproclaim

\beginproof{Proof:}
The computation is long and tedious
but mostly straightforward.
One should begin by writing
$2\tr(X^3)=\tr(X^2X)+\tr(XX^2)$ before multiplying together the matrices.
This device makes the resulting sum simplify because many of its
terms appear in $\O$-conjugate pairs. 
Manipulations involving no special
$\O$ identities except obvious applications of \eqtag{eq-rexyz} show
$$
\det(X)=a|u|^2+b|w|^2+c|v|^2-abc+[2\re(\bar uvw)-4\re(vw)\re(u)],
$$
and so we examine the term in brackets. Further use of \eqtag{eq-rexyz}
allows the derivation
$$\eqalign{
2\re(\bar uvw)-4\re(vw)\re(u) 
&=	\re[2\re(\bar uvw)-4\re(vw)\re(u)] \cr
&=	\re[\bar{u}vw +
		\bar{w}\bar{v}u-(vw+\bar{w}\bar{v})(u+\bar{u})] \cr
&=	\re[\bar{u}vw + \bar{w}\bar{v}u - vw\bar{u} - vwu -
		\bar{w}\bar{v}\bar{u} - \bar{w}\bar{v}u] \cr
&=	\re[u\bar{w}\bar{v}+u\bar{w}\bar{v}-u\bar{w}\bar{v}-uvw
		-uvw-u\bar{w}\bar{v}] \cr
&=	-2\re(uvw),
\cr}$$
which completes the proof. 
\endproof

Since the action of $GL_3\R$ on $M_3\O$ preserves matrix
multiplication and traces, it also preserves the determinant
form. In light of this, the next theorem assures us that $G\sset
H$; it also provides a quick 
alternate proof of most of theorem~\tag{thm-are-automorphisms}.

\beginproclaim Theorem
\Tag{thm-they-are-automorphisms}.
The transformations $S_\mu$  preserve
the restriction  to $J$ of the  determinant form and act as
automorphisms
of the Jordan algebra $J$.
\endproclaim

\beginproof{Proof:}
To show that $\det S_\mu(X)=\det X$ for $X\in J$ given by
\eqtag{eq-def-of-X} it suffices to show
$$
\re(\mu u\bar{\mu}\cdot\mu v\cdot w\bar{\mu})=
\re(uvw).
$$
This follows from the derivation 
$$\eqalign{
\re(\mu u\bar{\mu}\cdot\mu v\cdot w\bar{\mu})
&= 	-\re(\mu u\bar\mu\cdot\mu v\cdot w\mu) \cr
&=	-\re(\mu u\bar\mu\cdot\mu(vw)\mu) \cr
&=	-\re(\mu u\bar\mu\cdot\mu\cdot(vw)\mu) \cr
&=	-\re(\mu u\bar\mu\mu\cdot(vw)\mu) \cr
&=	-\re(\mu u\cdot(vw)\cdot\mu) \cr
&=	-\re(\mu\cdot\mu u\cdot vw) \cr
&=	\re(uvw),
\cr}$$
where we have used \eqtag{eq-moup}, \eqtag{eq-rexyz}, and the
fact that $\mu=-\bar\mu$.
(This is in spirit the same argument as that applied to $J_I$
in   \ecite{freud:oktav-eben}{\S6}.)

Combined with the fact that the $S_\mu$ preserve traces and
norms, this proves that $S_\mu\in\aut J$. This follows because
the Jordan multiplication can be defined in terms of the trace,
norm and determinant forms: 
Suppose $X,Y\in J$. Supposing that we  know the trace, norm and
determinant forms, we know the value of $\cuform$ for
every element of $J$. Therefore  we know the value of the symmetric
trilinear form $\trilform{X}{Y}{Z}$ obtained from $\cuform$ by
polarization, for every $Z\in J$. By the definition of
$\trilform{X}{Y}{Z}$, we know the value of $\bilform{X*Y}{Z}$ for every
$Z$, and since the inner product is nondegenerate
this uniquely determines $X*Y$.
\endproof

There are elements of $H$ that do not lie
in $G$, for example if $r\in\R\setminus\{0\}$ then $H$
contains the map
$$
F_r:(a,b,c,u,v,w)\mapsto(ar^4,br^{-2},cr^{-2},ur^{-2},vr,wr).
$$
We may identify $H$ with the real Lie group $E_{6(-26)}$ as
follows. In \cite{freud:oktav-eben} Freudenthal calculated 
the determinant form on $J_I$. An element of $J_I$ has the form
$$
\pmatrix{
d & z & \bar y \cr
\bar z & e & x \cr
y & \bar x & f
\cr},
$$
for $d,e,f\in\R$ and $x,y,z\in\O$. We denote this element of
$J_I$ by $[d,e,f,x,y,z]$. It is shown in \cite{freud:oktav-eben} that 
$$
\det[d,e,f,x,y,z]=de\kern-.12emf+2\re(xyz)-d|x|^2-e|y|^2-f|z|^2,
\eqno\eqTag{eq-det-for-J-I}
$$
and that the $H_I$ is isomorphic to $E_{6(-26)}$. To
identify $H$ with this Lie group we need only observe that
the map $g:J\to J_I$ defined by
$$
g:(a,b,c,u,v,w)\mapsto[-a,-c,-b,-u,-v,-w]
$$
identifies the determinant forms.

So far in this paper we have informally regarded elements of a
Jordan algebra $J_\Phi$ as ``transformations of $\O^3$'' that
are Hermitian with respect to $\Phi$. When one considers just
the determinant form on $J_I$ and not the Jordan algebra
structure, 
it is more appropriate to regard elements of $J_I$ as
``Hermitian forms on $\O^3$''. One should also regard their
determinants as those of Hermitian forms rather than of 
``linear transformations''.

\section{\Tag{sec-interpretation}
Stabilizers of Integral Hermitian Forms}

We write $\gint$ for the stabilizer in $H_I$ of $J_I\cap
M_3\ocint$. Given the material we have developed so far, it is
quite easy to 
realize the groups $R_n$ as stabilizers in $\gint$ of
appropriate forms over $\ocint$.


One can show that $F_{4(-20)}$ is a maximal (proper) subgroup of
$E_{6(-26)}$, and since not all of $H$ fixes the identity
matrix $I$, we see that we may define $G$ as the subgroup of
$H$ fixing $I\in J$. Recall that for $n$ a positive integer
we defined
$$
K_n=\{(na,b,c,u,v\sqrt{n},w\sqrt{n})|a,b,c\in\Z,u,v,w\in\ocint\}.
$$
From the above, we conclude that $\aut K_n$ is the subgroup of
$H$ that preserves $K_n$ as a set and fixes $nI\in K_n$.

We define the map $h_n:J\to J_I$ by
$h_n(X)=-n^{-1/3}\cdot(g\circ F_r)(X)$ where
$r=n^{-1/6}$ and $F_r$ and $g$ are as defined in
section~\tag{sec-determinant}. Computation reveals that $h_n$
carries $K_n$ 
bijectively to $J_I\cap M_3\ocint$ and multiplies determinants
by $-1/n$. This shows that $\aut K_n$ is isomorphic to the
subgroup of $\gint$ that fixes
$h_n(nI)$. Computing $h_n(nI)$ we obtain:

\beginproclaim Theorem
\Tag{thm-lattice-interp}.
For any $n$, $\aut K_n$ is isomorphic to the subgroup of
$\gint$ that preserves the matrix (or ``Hermitian form'')
$$
\pmatrix{
1 & 0 & 0 \cr
0 & 0 & n \cr
0 & n & 0
\cr}.
$$
\endproclaim

An 
element $g\in PGL_3\Z$ acts on $3$-dimensional integral
quadratic forms $\Phi$ by $\Phi\mapsto g\Phi g^*$, and obviously
acts on $J_I\cap M_3\ocint$ in the same way, realizing $PGL_3\Z$ as a
subgroup of $\gint$. 
The stabilizer in $PGL_3\Z$ of an integral
quadratic form $\Phi$ is (the central quotient of) the
automorphism group of the lattice with inner product matrix
$\Phi$. 
This makes it  natural and satisfying to regard the groups $\aut K_n$ (and
in particular the reflection groups $R_1$ and $R_2$) as the
symmetry groups of ``Lorentzian lattices'' over $\ocint$ with the ``inner 
product matrices'' given in theorem~\tag{thm-lattice-interp}. 

Gross \cite{gross:groups-over-Z} has investigated integral forms of various
semisimple algebraic groups, and observed that the stabilizer
in $\gint$ of the matrix
$$
\Psi'=
\pmatrix{
0 & 0 & -1\cr
0 & -1 & 0 \cr
-1 & 0 & 0
\cr}
$$
is a model over $\Z$ for the unique form of $F_4$ over $\Q$
which is split at each prime $p$ and has rank $1$ over
$\R$. Since $\Psi'$ is 
equivalent  to $-\Psi$ under $PGL_3\Z\sset\gint$ we see that
this integral form of $F_4$ is the reflection group $R_1$.
In particular, one of our groups has been investigated before
and found to be a natural integral form of $F_4$.

Finally, one can consider 2-dimensional versions of our
constructions. As introduced in section~\tag{sec-examples}, $J_-$ is the
space of $2\times2$ octave matrices which are Hermitian with
respect to $\smallmatrix0110$. (We actually introduced $J_-$ in
a slightly different but equivalent way.) That is, $J_-$ consists of the
matrices $\pmatrix{\ubar&b\cr c& u\cr}$ with $b,c\in\R$ and
$u\in\O$. There are no subtleties involved in the definition of
the determinant  
$|u|^2-bc$ of  an element of $J_-$, and the group preserving
just the vector space structure and determinant form on $J_-$ is
obviously the real orthogonal group $O(9,1)$. The group of
Jordan algebra automorphisms is the stabilizer $O(8,1)$ therein
of the identity matrix. We also defined $K_-$ as $M_2\ocint\cap
J_-$. 

The determinant form on the space 
$$
J_-'=\set{\pmatrix{e&x\cr \bar{x}&f\cr}}{e,f\in\R, x\in\O}
$$
of matrices in $M_2\O$ that are Hermitian with respect to 
$\smallmatrix1001$ is given by $ef-|x|^2$. Thus the
map $J_-\to J_-'$ given by
$$ 
\pmatrix{\ubar&b\cr c& u\cr}
\mapsto
\pmatrix{b&u\cr\ubar&c\cr}
$$
negates determinants, carries $\smallmatrix1001$ to
$\smallmatrix0110$,  and identifies $K_-$ with $M_2\ocint\cap
J_-'$. Therefore, as in the 3-dimensional case, we may interpret
the group $\aut K_-$ as begin essentially the automorphism group
of the ``Lorentzian lattice'' with inner product matrix
$\smallmatrix0110$. 

In section~\tag{sec-examples} we defined a group $R_-$ which acted on $K_-$;
it turned out that $R_-$ was a central extension by $\Z/2$ of
the rotation subgroup of a certain Coxeter group. The map
$R_-\to\aut K_-$ is almost an isomorphism. The kernel of the map
is the central $\Z/2$ of $R_-$ and the image has index two in $\aut
K_-$, which is the entire Coxeter group. (The real reflections
of $\rh8=\oh1$ arise from automorphisms of $J_-$ that do not
extend to $J$.)  This description of $\aut K_-$ follows from the
fact that  the central involution of
$K_-$ does not lie in $\aut K_-$, together with the remark
concerning the lattice $E_7\oplus\smallmatrix0110$
following the proof of theorem~\tag{thm-hyperbolic-cell-group}. 

\section{References}

% Do not tamper with the following line
% ---begin bibentries---

\bibitem{dja:complex-reflection-groups}
D.~J. Allcock.
 New complex and quaternion-hyperbolic reflection groups.
 Preprint, 1996.

\bibitem{dja:op2}
D.~J. Allcock.
 Identifying models of the octave projective plane.
 {\it Geometriae Dedicata}, 65:215--217, 1997.

\bibitem{asl:op2}
H.~Aslaksen.
 Restricted homogeneous coordinates for the {C}ayley projective plane.
 {\it Geometriae Dedicata}, 40:245--50, 1991.

\bibitem{reb:lzl}
R.~E. Borcherds.
 Automorphism groups of {L}orentzian lattices.
 {\it Journal of Algebra}, 111:133--53, 1987.

\bibitem{borel:arithmetic-groups}
A.~Borel and Harish-Chandra.
 Arithmetic subgroups of algebraic groups.
 {\it Annals of Mathematics}, 75:485--535, 1962.

\bibitem{jhc:26dim}
J.~H. Conway.
 The automorphism group of the 26-dimensional even unimodular
  {L}orentzian lattice.
 {\it Journal of Algebra}, 80:159--163, 1983.
 Reprinted in \cite{splag}.

\bibitem{ATLAS}
J.~H. Conway, R.~T. Curtis, S.~P. Norton, R.~A. Parker, and R.~A. Wilson.
 {\it {A}{T}{L}{A}{S} of Finite Groups}.
 Oxford, 1985.

\bibitem{splag}
J.~H. Conway and N.~J.~A. Sloane.
 {\it Sphere Packings, Lattices, and Groups}.
 Springer-Verlag, 1988.

\bibitem{cox:integer-octaves}
H.~S.~M. Coxeter.
 Integral {C}ayley numbers.
 {\it Duke Math. J.}, 13:561--78, 1946.

\bibitem{elkies:cone}
N.~Elkies and B.~H. Gross.
 The exceptional cone and the {L}eech lattice.
 Preprint 1996.

\bibitem{freud:oktav-eben}
H.~Freudenthal.
 Zur ebenen {O}ktavengeometrie.
 {\it Neder. Akad. wan Wetenschappen}, 56:195--200, 1953.

\bibitem{freud:oktav-geometry}
H.~Freudenthal.
 {O}ktaven, {A}usnahmegruppen, und {O}ktavengeometrie.
 {\it Geometriae Dedicata}, 19:1--73, 1985.
 (Mimeographed, Utrecht 1951).

\bibitem{gross:groups-over-Z}
B.~H. Gross.
 Groups over $\bf {Z}$.
 {\it Invent. Math.}, 124:263--279, 1996.

\bibitem{jordan:op2}
P.~Jordan.
 {\"{U}}ber eine nicht-{D}esaurguessche ebene projective {G}eometrie.
 {\it Abhandlungen Mathematischen Seminar der Universit{\"{a}}t
  Hamburg}, 16:74--6, 1949.

\bibitem{mostow:strong-rigidity}
G.~D. Mostow.
 {\it Strong rigidity of locally symmetric spaces}.
 Number~78 in Annals of Mathematics Studies. Princeton University
  Press, 1973.

\bibitem{tits:op2}
J.~Tits.
 Le plan projectif des octaves et les groups de {L}ie exceptionnels.
 {\it Bull. Acad. Roy. Belg., Classe des Sciences}, 39:300--29, 1953.

\bibitem{tits:e6-and-e7}
J.~Tits.
 Le plan projectif des octaves et les groupes exceptionnels ${E}_6$ et
  ${E}_7$.
 {\it Bull. Acad. Roy. Belg., Classe des Sciences}, 40:29--40, 1954.

\bibitem{vin:groups-of-quad-forms}
E.~B. Vinberg.
 On the groups of units of certain quadratic forms.
 {\it Mathematics of the USSR---Sbornik}, 16(1):17--35, 1972.

\bibitem{vin:unimodular-quad-forms}
E.~B. Vinberg.
 On unimodular integral quadratic forms.
 {\it Funct. Anal. Applic.}, 6:105--11, 1972.

\bibitem{vin:I18.1-and-I19.1}
E.~B. Vinberg and I.~M. Kaplinskaja.
 On the groups ${O}_{18,1}({Z})$ and ${O}_{19,1}({Z})$.
 {\it Soviet Mathematics, Doklady}, 19(1):194--197, 1978.

% ---end bibentries---
% Do not tamper with the previous line.
\bye
