%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Module:    ZzTeX Register Facilities
%
% Synopsis:  This module provides facilities for defining and manipulating
%            TeX registers.
%
% Author:    Paul C. Anagnostopoulos
% Created:   13 August 1989
%
% Copyright 1989--2020 by Paul C. Anagnostopoulos
% under The MIT License (opensource.org/licenses/MIT)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%                       Register Definition
%                       -------- ----------


% The following constant specifies the number of the first insert register
% set.  Such a set is composed of a count, dimen, skip, and box.

\chardef \zfirstinsert = 245

% Establish counters to keep track of the most recently allocated
% registers.  The registers are initialized to the first available
% register.

\count10 =  20            % Count registers.
\count11 =   0            % Dimen registers.
\count12 =   0            % Skip registers.
\count13 =   0            % Muskip registers.
\count14 =   1            % Box registers (Plain TeX uses box 0).
\count15 =   0            % Toks registers.
\count16 =   0            % Read streams.
\count17 =   0            % Write streams.
\count18 =   0            % Math families.
\count19 = \zfirstinsert  % Insertion register sets.


% There is one macro for each type of register, used to allocate one.

\def \definecount #1#2{%                                {\name}{initial}
  \zalloc{\count}{10}{\zfirstinsert}{\countdef}{#1}%
  #1 = #2}

\def \definedimen #1#2{%                                {\name}{initial}
  \zalloc{\dimen}{11}{\zfirstinsert}{\dimendef}{#1}%
  #1 = #2}

\def \defineskip #1#2{%                                 {\name}{initial}
  \zalloc{\skip}{12}{\zfirstinsert}{\skipdef}{#1}%
  #1 = #2}

\def \definemuskip #1#2{%                               {\name}{initial}
  \zalloc{\muskip}{13}{255}{\muskipdef}{#1}%  Muskip 255 is scratch.
  #1 = #2}

\def \definebox #1{%                                    {\name}
  \zalloc{\box}{14}{\zfirstinsert}{\chardef}{#1}}

\def \definetoks #1{%                                   {\name}
  \zalloc{\toks}{15}{256}{\toksdef}{#1}%
  #1 = {}}

\def \defineread #1{%                                   {\name}
  \zalloc{\read}{16}{16}{\chardef}{#1}}

\def \definewrite #1{%                                  {\name}
  \zalloc{\write}{17}{16}{\chardef}{#1}}

% Families are not defined directly by the user, but via \definetypestyle.

\def \zdeffam #1{%                                      {\name}
  \if \lssp{\count18}{15}%
    \zalloc{\fam}{18}{16}{\chardef}{#1}%
  \else
    \global\chardef #1=\count18
  \fi}

\def \defineinsert #1{%                                 {\name}
  \zalloc{\insert}{19}{255}{\chardef}{#1}}  % Registers 255 are scratch.


\def \zalloc #1#2#3#4#5{%       {type}{counter}{limit}{\def-er}{\name}
  \if \eqlp{\count#2}{#3}%
    \error{noregsleft}{All `\string#1' registers have been allocated}%
  \fi
  \if \definedp{#5}%
    \error{dupreg}{The register `\string#5' is already defined}%
  \fi
  \global#4 #5=\count#2
  \global\increment {\count#2}}

%                       Register Statistics
%                       -------- ----------


{\catcode`\_ = \catactive

\gdef \zregstats {%
  {\def _{\space}%
   \writelog{}%
   \writelog{Register Statistics:}%
   \writelog{}%
   \writelog{Type_____Used / Total}%
   \writelog{----_____------------}%
   \zrsone{Count_}{10}{\zfirstinsert}{}%
   \zrsone{Dimen_}{11}{\zfirstinsert}{}%
   \zrsone{Skip__}{12}{\zfirstinsert}{}%
   \zrsone{Muskip}{13}{255}{}%
   \zrsone{Box___}{14}{\zfirstinsert}{}%
   \zrsone{Toks__}{15}{256}{}%
   \zrsone{Read__}{16}{16}{}%
   \zrsone{Write_}{17}{16}{}%
   \zrsone{Family}{18}{256}{(15 for math)}%
   \zrsone{Insert}{19}{255}{(starts at \the\zfirstinsert)}%
   \writelog{}}}

\gdef \zrsone #1#2#3#4{%                {name}{counter}{ex-limit}{comment}
  \tcounta = #3
  \advance \tcounta by -1
  \writelog{#1___\the\count#2 _/_\the\tcounta_#4}}

} % \catcode

%                       Constant Registers
%                       -------- ---------


\definecount{\maxcount}{2147483647}
\definecount{\mincount}{-\maxcount}
\zremovePlaindef \maxdimen
\definedimen{\maxdimen}{16383.99999pt}
\definedimen{\mindimen}{-\maxdimen}
\definedimen{\naturalwidth}{\mindimen}

\defineskip{\centerindent}{0pt plus 10000pt}
\let \centering = \centerindent

\defineskip{\normalparfillskip}{0pt plus 1fil}

\definebox{\voidbox}    % Permanently void box register.

%                       Temporary Internal Registers
%                       --------- -------- ---------


\definecount{\tcounta}{0}
\definecount{\tcountb}{0}
\definecount{\tcountc}{0}

\definedimen{\tdimena}{0pt}
\definedimen{\tdimenb}{0pt}
\definedimen{\tdimenc}{0pt}
\definedimen{\tdimend}{0pt}

\defineskip{\zskipa}{0pt}
\defineskip{\zskipb}{0pt}
\defineskip{\zskipc}{0pt}

\definemuskip{\zmuskipa}{0mu}
\definemuskip{\zmuskipb}{0mu}
\definemuskip{\zmuskipc}{0mu}

\definebox{\zboxa}
\definebox{\zboxb}
\definebox{\zboxc}

\definetoks{\ztoksa}
\definetoks{\ztoksb}
\definetoks{\ztoksc}

\defineread{\zreada}

\definewrite{\zwritea}

%                       Manipulating Boxes
%                       ------------ -----


\def \shiftboxbaseline #1#2{%                           {\box}{amount}
  {\tdimena = #2%
   \tdimenb = \ht#1%
   \advance \tdimenb by \tdimena
   \ht#1= \tdimenb
   \tdimenb = \dp#1%
   \advance \tdimenb by -\tdimena
   \dp#1= \tdimenb}}

%                       Lists
%                       -----


% These macros support the manipulation of lists in the following format:
%
%   \:{<element-0>}\:{<element-1>}\: ...
%
% All lists are global!

% We need two toks registers, which will be used just for lists.

\definetoks{\zlta}
\definetoks{\zltb}


\def \setlist #1=#2{%                           \listname = {value}
  \zlta = \expandafter{#2}%
  \xdef #1{\the\zlta}}

\long\def \append #1#2{%                        {item}{\list-name}
  {\zlta = {\:{#1}}%
   \zltb = \expandafter{#2}%
   \xdef #2{\the\zltb\the\zlta}}}

\long\def \appendlf #1#2{%                      {\list-name}{item}
  {\zlta = {\:{#2}}%
   \zltb = \expandafter{#1}%
   \xdef #1{\the\zltb\the\zlta}}}

\long\def \prepend #1#2{%                       {item}{\list-name}
  {\zlta = {\:{#1}}%
   \zltb = \expandafter{#2}%
   \xdef #2{\the\zlta\the\zltb}}}

\let \push = \prepend

\def \pop #1#2{%                                {\return-macro}{\list-name}
  \expandafter\zpop #2\zmark#1#2}

\long\def \zpop \:#1#2\zmark#3#4{%
  \def #3{#1}%
  \gdef #4{#2}}

\def \listtop #1#2{%                            {\return-macro}{\list-name}
  \expandafter\zlisttop #2\zmark#1}

\long\def \zlisttop \:#1#2\zmark#3{%
  \def #3{#1}}

\definecount{\zmapldepth}{0}

\def \maplist #1#2{%                            {body}{\listname}
  \increment \zmapldepth
  \withname\let{\zmapl\romannumeral\zmapldepth}=\:%
  \long\def \:##1{#1}%
  #2%
  \expandafter\let
    \expandafter\:\csname zmapl\romannumeral\zmapldepth\endcsname
  \decrement \zmapldepth}

\def \maplistlf #1#2{%                          {\listname}{body}
  \increment \zmapldepth
  \withname\let{\zmapl\romannumeral\zmapldepth}=\:%
  \long\def \:##1{#2}%
  #1%
  \expandafter\let
    \expandafter\:\csname zmapl\romannumeral\zmapldepth\endcsname
  \decrement \zmapldepth}

\def \exitmaplist {\def \:##1{}}

\def \listlength #1#2{%                         {\result-count}{\list}
  #1= 0
  \maplist{\increment #1}{#2}}

\def \member #1#2#3{%                           {\result-flag}{item}{\list}
  \setflag #1= \false
  \maplist{\stringeql{#1}{##1}{#2}\if #1\exitmaplist \fi}
          {#3}}

\def \commalist #1#2{%                          {\result-name}{comma-list}
  \setlist #1={}%
  \expandafter\zcomma #2,\zmark#1}

\def \zcomma #1,#2\zmark#3{%
  \if \notp{\emptyargp{#1}}\append{#1}{#3}\fi
  \if \notp{\emptyargp{#2}}%
    \def \znext {\zcomma #2\zmark#3}%
  \else
    \let \znext = \relax
  \fi
  \znext}

\def \inclusionlist #1#2#3{%            {\result}{item}{inclusion-list}
  \zincla {#1}{#2}#3\zmark}

\let \all = \relax
\let \allbut = \relax
\def \zallname {\all}
\def \zallbutname {\allbut}

\def \zincla #1#2#3#4\zmark{%           {\result}{item}{token1}{tokens...}
  \def \znext {#3}%
  \if \tokeqlp{\znext}{\zallname}%
    \setflag #1= \true
  \else \if \tokeqlp{\znext}{\zallbutname}%
    \commalist{\zincllist}{#4}%
    \member{#1}{#2}{\zincllist}%
    \setflag #1= {\notp{#1}}%
  \else
    \commalist{\zincllist}{#3#4}%
    \member{#1}{#2}{\zincllist}%
  \fi\fi}

%                       New Expression Evaluation
%                       --- ---------- ----------


% These macros provide a rudimentary expression evaluation facility.
% The accumulator can be a count, dimen, or skip register. The expression
% is in the form {operand0,op1,operand1,op2,operand2,...}.
%
% WARNING: The \register cannot be used in the expression except possibly
%          as the first operand.


\definedimen{\zcalct}{0pt}


\def \calculate #1=#2{%                         \register = {expression}
  \let \zaccum = #1%
  \zcalca =,#2,;,,\zmark}

\def \gcalculate #1=#2{%                        \register = {expression}
  \let \zaccum = #1%
  \zcalca =,#2,;,,\zmark
  \global \zaccum = \zaccum}

\def \zcalca #1,#2,#3\zmark{%
  \def \znext{\zcalca #3\zmark}%
       \if #1=\zaccum = #2%                             % Assignment
  \else\if #1+\advance \zaccum #2%                      % Addition
  \else\if #1-\advance \zaccum -#2%                     % Subtraction
  \else\if #1*\multiply \zaccum #2%                     % Multiplication
  \else\if #1/\divide \zaccum #2%                       % Division
  \else\if #1N\if \dimlssp{#2}{\zaccum}\zaccum = #2\fi  % Minimum
  \else\if #1P\divide \zaccum 100                       % Percentage
              \expandafter\zaccum #2\zaccum
  \else\if #1R\zcalct = #2%                             % Ratio
              \divide \zcalct by 4096
              \divide \zaccum by \zcalct
              \multiply \zaccum by 16\relax
  \else\if #1S\zaccum = \thefactor#2\zaccum             % Scale
  \else\if #1X\if \dimgtrp{#2}{\zaccum}\zaccum = #2\fi  % Maximum
  \else\if #1;\let \znext = \relax                      % Done
  \else
    \error{invoper}{Invalid operator `#1' in expression}%
  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
  \znext}

%                       Calculating Values
%                       ----------- ------

%%%\defineskip{\zcaccum}{0pt}
%%%\setflag \zexprpos = \true
%%%
%%%\def \calculate #1=#2{%                         {\reg} = {expression}
%%%  \commalist{\zexprlist}{#2}%
%%%  \zcaccum = 0pt
%%%  \def \zoper {+}%
%%%  \setflag \zexprpos = \true
%%%  \maplist{\zcalc{##1}}{\zexprlist}%
%%%  #1=\zcaccum}
%%%
%%%\def \gcalculate #1=#2{%                        {\reg} = {expression}
%%%  \commalist{\zexprlist}{#2}%
%%%  \zcaccum = 0pt
%%%  \def \zoper {+}%
%%%  \setflag \zexprpos = \true
%%%          \maplist{\zcalc{##1}}{\zexprlist}%
%%%  \global #1=\zcaccum}
%%%
%%%\def \zcalc #1{%                                {token}
%%%  \if \zexprpos
%%%    \zcalcb{#1}%
%%%    \setflag \zexprpos = \false
%%%  \else
%%%    \def \zoper {#1}%
%%%    \setflag \zexprpos = \true
%%%  \fi}
%%%
%%%\def \zcalcb #1{%                               {operand}
%%%  \if \codeeqlp{\zoper}{+}%                     % Add.
%%%    \advance \zcaccum by #1%
%%%  \else\if \codeeqlp{\zoper}{-}%                % Subtract.
%%%    \advance \zcaccum by -#1%
%%%  \else\if \codeeqlp{\zoper}{*}%                % Multiply.
%%%    \multiply \zcaccum by #1%
%%%  \else\if \codeeqlp{\zoper}{/}%                % Divide.
%%%    \divide \zcaccum by #1%
%%%  \else\if \codeeqlp{\zoper}{P}%
%%%     \divide \zcaccum by 100
%%%     \expandafter \zcaccum #1\zcaccum
%%%  \else\if \codeeqlp{\zoper}{R}%                % Compute ratio (in points).
%%%    \zcalct = #1%
%%%    \divide \zcalct by 4096
%%%    \divide \zcaccum by \zcalct
%%%    \multiply \zcaccum by 16\relax
%%%  \else\if \codeeqlp{\zoper}{S}%                % Scale (in points).
%%%    \zcaccum = \thefactor#1\zcaccum
%%%  \else
%%%    \error{invoper}{Invalid operator `\zoper' in expression}%
%%%  \fi\fi\fi\fi\fi\fi\fi}

%                       The Vertical Context
%                       --- -------- -------


% This is in here because we need registers.  Otherwise it would be
% in ZZTEX.TEX


\definecount{\zvcontextdepth}{0}

\definetoks{\zeveryvcontext}
\global\zeveryvcontext = {}


\def \zdeclareeveryvcontext #1{%
  \global\zeveryvcontext = \expandafter{\the\zeveryvcontext #1}}

\def \zpushvcontext {%
  \global\increment \zvcontextdepth
  \withname\gdef{\zvcontextstack\romannumeral\zvcontextdepth}{}%
  \the\zeveryvcontext}

\def \zpopvcontext {%
  {\globaldefs = 1\relax
   \name{\zvcontextstack\romannumeral\zvcontextdepth}}%
  \global\decrement \zvcontextdepth}

\def \zsavevcontext #1{%
  {\ztoksa = \expandafterthrice{%
               \name{\zvcontextstack\romannumeral\zvcontextdepth}}%
   \withname\xdef{\zvcontextstack\romannumeral\zvcontextdepth}%
     {\the\ztoksa #1}}}
