diff options
Diffstat (limited to 'macros')
39 files changed, 1120 insertions, 385 deletions
diff --git a/macros/latex/contrib/counterz/README b/macros/latex/contrib/counterz/README.txt index b7376e3a0b..00daf51728 100644 --- a/macros/latex/contrib/counterz/README +++ b/macros/latex/contrib/counterz/README.txt @@ -1,7 +1,7 @@ ---------------------------------------------------------------------- -`counterz' --- 2023/05/19 Version 1.0.0 +`counterz' --- 2023/05/30 Version 1.1.0 Copyright (C) 2023 by Christopher McClain E-mail: christopher.mcclain@mail.wvu.edu diff --git a/macros/latex/contrib/counterz/counterz.dtx b/macros/latex/contrib/counterz/counterz.dtx index 82857faf4c..2efbfa056d 100644 --- a/macros/latex/contrib/counterz/counterz.dtx +++ b/macros/latex/contrib/counterz/counterz.dtx @@ -7,7 +7,7 @@ %<*readme> ---------------------------------------------------------------------- -`counterz' --- 2023/05/19 Version 1.0.0 +`counterz' --- 2023/05/30 Version 1.1.0 Copyright (C) 2023 by Christopher McClain E-mail: christopher.mcclain@mail.wvu.edu @@ -38,7 +38,7 @@ random counters and save such counter values for future typesetting. \preamble ---------------------------------------------------------------------- -`counterz' --- 2023/05/19 Version 1.0.0 Additional tools for counters +`counterz' --- 2023/05/30 Version 1.1.0 Additional tools for counters E-mail: christopher.mcclain@mail.wvu.edu Released under the LaTeX Project Public License v1.3c or later See https://www.latex-project.org/lppl.txt @@ -110,11 +110,12 @@ and the derived files counterz.ins, \newcounter,\newbool,\newwrite,\numexpr,\openout,\pdfuniformdeviate, \providecounter,\ProvidesPackage,\relax,\renewcommand, \RequirePackage,\Roman,\roman,\setcounter,\the,\unexpanded,\value, - \whileboolexpr,\write} + \whileboolexpr,\write,\@ifnextchar,\ifltxcounter,\InputIfFileExists, + \m@k@gobbleendoptarg,\MessageBreak,\PackageError,\protect,\space, + \typein,\@typein} \CodelineIndex \RecordChanges \begin{document} - \newenvironment{codeoutput}{\newline\newline}{\newline\newline} \DocInput{\jobname.dtx} \end{document} %</driver> @@ -136,6 +137,7 @@ and the derived files counterz.ins, %\maketitle % %\changes{v1.0.0}{2023/05/19}{First public release} +%\changes{v1.1.0}{2023/05/30}{New and revised commands and error reports} % % %\begin{abstract} @@ -188,11 +190,12 @@ and the derived files counterz.ins, % %\subsection{Installation} % -% This package may be installed by copying the file \textsf{counterz.sty} to -% your local texmf directory. The code and documentation may also be generated -% from \textsf{\jobname.dtx}. Typesetting the documentation requires -% the package \textsf{hypdoc} which is included in \TeX\ distributions and at -% \href{http://www.ctan.org}{The Comprehensive TeX Archive Network}. +% Run (pdf)\TeX{} on counterz.dtx to generate the file counterz.sty, and copy it +% to your local texmf directory. To generate both the package file counterz.sty +% and the documentation counterz.pdf, run (pdf)\LaTeX{} on counterz.dtx. +% Typesetting the documentation requires the package \textsf{hypdoc} which is +% included in \TeX\ distributions and at \href{http://www.ctan.org} +% {The Comprehensive TeX Archive Network}. % %\section{User Guide} % @@ -202,57 +205,69 @@ and the derived files counterz.ins, %\begin{verbatim} %\usepackage{counterz} %\end{verbatim} +% +% \noindent % The package \textsf{counterz} loads the packages \textsf{etoolbox} and % \textsf{makecmds}, both of which are included in \TeX\ distributions % and at \href{http://www.ctan.org}{The Comprehensive TeX Archive Network}. % %\subsection{Counter Prefixes} % -%\DescribeMacro{\setcounterprefix} -%\DescribeMacro{\clearcounterprefix} % Counter prefixes are stored in an internal macro whose default value is an -% empty string. The command \cs{setcounterprefix}\marg{prefix} is used to change -% this value. For example, to change the prefix to \textit{PurpleMonkey}, use +% empty string. +%\DescribeMacro{\setcounterprefix} +% The command \cs{setcounterprefix}\marg{prefix} is used to change this value. +% For example, to change the prefix to \textit{PurpleMonkey}, use +% %\begin{verbatim} %\setcounterprefix{PurpleMonkey} %\end{verbatim} +% +% \noindent % and to change it from \textit{PurpleMonkey} to \textit{Dishwasher}, use +% %\begin{verbatim} %\setcounterprefix{Dishwasher} %\end{verbatim} -% The command \cs{clearcounterprefix} returns the prefix to its empty default: -%\begin{verbatim} -%\clearcounterprefix -%\end{verbatim} +% +% \noindent +%\DescribeMacro{\clearcounterprefix} +% The command \cs{clearcounterprefix} returns the prefix to its empty default. % %\subsection{Manipulating Counters} % %\DescribeMacro{\xnewcounter} -%\DescribeMacro{\xprovidecounter} -%\DescribeMacro{\xsetcounter} -%\DescribeMacro{\xaddtocounter} -%\DescribeMacro{\xvalue} % The command \cs{xnewcounter}\marg{countername} creates a counter with a -% prefixed name. The command \cs{xsetcounter}\marg{countername}\marg{integer} -% assigns the specified value to the counter with the prefixed name. For -% example, suppose that the file \textsf{BoringFile1.tex} contains the -% following: +% prefixed name. +%\DescribeMacro{\xsetcounter} +% The command \cs{xsetcounter}\marg{countername}\marg{integer} assigns the +% specified value to the counter with the prefixed name. For example, suppose +% that the file \textsf{BoringFile1.tex} contains the following: +% %\begin{verbatim} %\xnewcounter{bestcounterever} %\xsetcounter{bestcounterever}{100} %\end{verbatim} +% +% \noindent % and suppose that the file \textsf{BoringFile2.tex} contains the following: +% %\begin{verbatim} %\xnewcounter{bestcounterever} %\xsetcounter{bestcounterever}{-29} %\end{verbatim} +% +% \noindent % and, finally, suppose that the file Main.tex contains (in part) the following: +% %\begin{verbatim} %\setcounterprefix{PurpleMonkey} %\input{BoringFile1} %\setcounterprefix{Dishwasher} %\input{BoringFile2} %\end{verbatim} +% +% \noindent % Then typesetting \textsf{Main.tex} will create a counter % \textit{PurpleMonkeybestcounterever} with the value 100 and a counter % \textit{Dishwasherbestcounterever} with the value $-29$. By using commands @@ -263,9 +278,12 @@ and the derived files counterz.ins, % in the file \textsf{Main.tex}. By changing prefixes, \textsf{Main.tex} can % even input the same file multiple times without conflict. % +%\DescribeMacro{\xprovidecounter} % The commands \cs{xprovidecounter}, \cs{xaddtocounter}, and \cs{xvalue} are % likewise prefix versions of commands \cs{providecounter}, \cs{addtocounter}, +%\DescribeMacro{\xaddtocounter} % and \cs{value}, respectively. When the prefix is empty, the commands expand +%\DescribeMacro{\xvalue} % like their standard counterparts. (Note: \cs{providecounter} defines a counter % if it has not already been defined. See the documentation for the package % \textsf{makecmds} for details.) @@ -273,15 +291,17 @@ and the derived files counterz.ins, %\subsection{Conditional Statements} % %\DescribeMacro{\ifctrequal} -%\DescribeMacro{\ifctrless} -%\DescribeMacro{\ifctrmore} % The command \cs{ifctrequal}\marg{counter1}\marg{counter2}\marg{foo}\marg{bar} % uses the command \cs{xvalue} to compare the values of the (prefixed) counters % and then executes \meta{foo} if the values are equal and otherwise executes -% \meta{bar}. The commands \cs{ifctrless} and \cs{ifctrmore} work analogously, -% based on whether the value of prefixed \meta{counter1} is less than that of -% of prefixed \meta{counter2} or more than that of prefixed \meta{counter2}, -% respectively. Consider the example code +% \meta{bar}. The commands +%\DescribeMacro{\ifctrless} +% \cs{ifctrless} and \cs{ifctrmore} work analogously, based on whether the value +% of prefixed \meta{counter1} is less than that of of prefixed \meta{counter2} +%\DescribeMacro{\ifctrmore} +% or more than that of prefixed \meta{counter2}, respectively. Consider the +% example code +% %\begin{verbatim} %\setcounterprefix{TigerTiger} %\xnewcounter{Small} @@ -292,8 +312,11 @@ and the derived files counterz.ins, %\ifctrless{Small}{Large}{March}{April} %\ifctrmore{Small}{Large}{May}{June} %\end{verbatim} +% +% \noindent % which produces the output -% \begin{codeoutput} +% +% \bigskip %\setcounterprefix{TigerTiger} %\xnewcounter{Small} %\xsetcounter{Small}{7} @@ -302,18 +325,21 @@ and the derived files counterz.ins, %\ifctrequal{Small}{Large}{January}{February} %\ifctrless{Small}{Large}{March}{April} %\ifctrmore{Small}{Large}{May}{June} -% \end{codeoutput} +% +% \bigskip +% \noindent % because the value of the counter \textit{TigerTigerSmall} is 7 which is less % than 11, the value of the counter \textit{TigerTigerLarge}. % %\DescribeMacro{\ifctrzero} -%\DescribeMacro{\ifctrneg} -%\DescribeMacro{\ifctrpos} % The command \cs{ifctrzero}\marg{counter}\marg{foo}\marg{bar} executes % \meta{foo} if the value of the (prefixed) counter is zero and otherwise -% executes \meta{bar}. The commands \cs{ifctrneg} and \cs{ifctrpos} work -% analogously based on whether the value is negative or positive, respectively. -% The example code +% executes \meta{bar}. The commands +%\DescribeMacro{\ifctrneg} +% \cs{ifctrneg} and \cs{ifctrpos} work analogously based on whether the value +%\DescribeMacro{\ifctrpos} +% is negative or positive, respectively. The example code +% %\begin{verbatim} %\setcounterprefix{TigerTiger} %\xprovidecounter{Small} @@ -322,54 +348,72 @@ and the derived files counterz.ins, %\ifctrneg{Small}{March}{April} %\ifctrpos{Small}{May}{June} %\end{verbatim} +% +% \noindent % produces the output -% \begin{codeoutput} +% +% \bigskip %\setcounterprefix{TigerTiger} %\xprovidecounter{Small} %\xsetcounter{Small}{7} %\ifctrzero{Small}{January}{February} %\ifctrneg{Small}{March}{April} %\ifctrpos{Small}{May}{June} -% \end{codeoutput} +% +% \bigskip +% \noindent % because the value of the counter \textit{TigerTigerSmall} is 7 which is % positive (and thus nonzero, as well). % %\subsection{Displaying Counters} % %\DescribeMacro{\xarabic} +% The command \cs{xarabic}\marg{counter} is simply a prefix version of the +% standard display command \cs{arabic}. %\DescribeMacro{\xroman} +% The commands \cs{xroman}, \cs{xRoman}, \cs{xalph}, \cs{xAlph}, and %\DescribeMacro{\xRoman} +% \cs{xfnsymbol} are likewise prefix versions of the standard display commands %\DescribeMacro{\xalph} +% \cs{roman}, \cs{Roman}, \cs{alph}, \cs{Alph}, and \cs{fnsymbol}, inheriting +% the restrictions of their %\DescribeMacro{\xAlph} +% parent commands. +% %\DescribeMacro{\xfnsymbol} -% The command \cs{xarabic}\marg{counter} is simply a prefix version of the -% standard display command \cs{arabic}. The commands \cs{xroman}, \cs{xRoman}, -% \cs{xalph}, \cs{xAlph}, and \cs{xfnsymbol} are likewise prefix versions of -% the standard display commands \cs{roman}, \cs{Roman}, \cs{alph}, \cs{Alph}, -% and \cs{fnsymbol}, inheriting the restrictions of their parent commands. % Note that the code +% %\begin{verbatim} %\setcounterprefix{Sneaky} %\xprovidecounter{Pete} %\xsetcounter{Pete}{42} %\arabic{Pete} %\end{verbatim} +% +% \noindent % produces an error because the counter \textit{Pete} is not defined, but the % code +% %\begin{verbatim} %\setcounterprefix{Sneaky} %\xprovidecounter{Pete} %\xsetcounter{Pete}{42} %\xarabic{Pete} %\end{verbatim} +% +% \noindent % produces the output -% \begin{codeoutput} +% +% \bigskip %\setcounterprefix{Sneaky} %\xprovidecounter{Pete} %\xsetcounter{Pete}{42} %\xarabic{Pete} -% \end{codeoutput} +% +% \bigskip +% \noindent % which is the value of the counter \textit{SneakyPete}. The code +% %\begin{verbatim} %\setcounterprefix{Sneaky} %\xprovidecounter{Pete} @@ -377,6 +421,8 @@ and the derived files counterz.ins, %\clearcounterprefix %\xarabic{Pete} %\end{verbatim} +% +% \noindent % also generates error because the final line is trying to use the % undefined counter \textit{Pete} after the prefix was returned to its % default value. @@ -385,6 +431,7 @@ and the derived files counterz.ins, % \textsf{counterz} defines some variants of \cs{xarabic} that are useful in % the display of mathematical expressions. For example, consider the following % code: +% %\begin{verbatim} %\xprovidecounter{a} %\xsetcounter{a}{5} @@ -394,8 +441,11 @@ and the derived files counterz.ins, %\xsetcounter{c}{-7} %$\xarabic{a}+\xarabic{b}+\xarabic{c}$ %\end{verbatim} +% +% \noindent % which produces -% \begin{codeoutput} +% +% \bigskip %\xprovidecounter{a} %\xsetcounter{a}{5} %\xprovidecounter{b} @@ -403,81 +453,109 @@ and the derived files counterz.ins, %\xprovidecounter{c} %\xsetcounter{c}{-7} %$\xarabic{a}+\xarabic{b}+\xarabic{c}$ -% \end{codeoutput} -% Using \cs{arabicx} causes the expression to -% contain the consecutive pair $+-$. The command +% +% \bigskip +% \noindent +% Using \cs{arabicx} causes the expression to contain the consecutive pair $+-$. +% The command \cs{xsigned}\marg{counter} is like \cs{xarabic} except that %\DescribeMacro{\xsigned} -% \cs{xsigned}\marg{counter} is like \cs{xarabic} except that nonnegative values -% are preceded by a plus sign ``+''. The code +% nonnegative values are preceded by a plus sign ``+''. The code +% %\begin{verbatim} %$\xarabic{a}\xsigned{b}\xsigned{c}$ %\end{verbatim} +% +% \noindent % produces -% \begin{codeoutput} +% +% \bigskip %$\xarabic{a}\xsigned{b}\xsigned{c}$ -% \end{codeoutput} +% +% \bigskip +% \noindent % If we wish to suppress the $0$, we can instead use the command %\DescribeMacro{\xsignednz} -%\DescribeMacro{\xarabicnz} % \cs{xsignednz}\marg{counter} which is a nonzero version of \cs{xsigned} and, % if desired or necessary, the command \cs{xarabicnz}\marg{counter} which is a -% nonzero version of \cs{xarabic}. -% The code +%\DescribeMacro{\xarabicnz} +% nonzero version of \cs{xarabic}. The code +% %\begin{verbatim} %$\xarabicnz{a}\xsignednz{b}\xsignednz{c}$ %\end{verbatim} +% +% \noindent % produces -% \begin{codeoutput} +% +% \bigskip %$\xarabicnz{a}\xsignednz{b}\xsignednz{c}$ -% \end{codeoutput} +% +% \bigskip +% \noindent %\DescribeMacro{\xnegof} +% The command \cs{xnegof}\marg{counter} displays the negative of \meta{counter}. +% The command \cs{xnegofnz} does the same except that it suppresses the number %\DescribeMacro{\xnegofnz} +% zero. The command \cs{xnegsigned} includes the appropriate signs of plus ``+'' %\DescribeMacro{\xnegsigned} +% and minus ``-'' (assigning a minus to zero in this case). %\DescribeMacro{\xnegsignednz} -% The package also contains variants of these commands for displaying the -% negatives of counters, as demonstrated by the following code: +% Finally, the command \cs{xnegsignednz} does the same except that it suppresses +% the number zero., as demonstrated by the following code: +% %\begin{verbatim} %\xprovidecounter{d} %\xsetcounter{d}{-2} -%$\xarabic{a}\xsigned{b}\xsigned{c}=\xarabic{d}$\\ -%$\xnegof{d}=\xnegof{a}\xnegsigned{b}\xnegsigned{c}$\\ +% +%$\xarabic{a}\xsigned{b}\xsigned{c}=\xarabic{d}$ +% +%$\xnegof{d}=\xnegof{a}\xnegsigned{b}\xnegsigned{c}$ +% %$\xnegofnz{d}=\xnegofnz{a}\xnegsignednz{b}\xnegsignednz{c}$ %\end{verbatim} +% +% \noindent % which produces -%\begin{codeoutput} +% +% \bigskip %\xprovidecounter{d} %\xsetcounter{d}{-2} -%$\xarabic{a}\xsigned{b}\xsigned{c}=\xarabic{d}$\\ -%$\xnegof{d}=\xnegof{a}\xnegsigned{b}\xnegsigned{c}$\\ +% +%$\xarabic{a}\xsigned{b}\xsigned{c}=\xarabic{d}$ +% +%$\xnegof{d}=\xnegof{a}\xnegsigned{b}\xnegsigned{c}$ +% %$\xnegofnz{d}=\xnegofnz{a}\xnegsignednz{b}\xnegsignednz{c}$ -%\end{codeoutput} +% +% \bigskip % The preceding commands for displaying values related to counters were created % by using some other commands that we make available in case they prove useful. %\DescribeMacro{\xabsof} -%\DescribeMacro{\xsignof} -%\DescribeMacro{\xnegsignof} % The command \cs{xabsof}\marg{counter} prints the absolute value of % \meta{counter}. The command \cs{xsignof}\marg{counter} prints a minus sign +%\DescribeMacro{\xsignof} % ``-'' if \meta{counter} is negative and otherwise prints a plus sign ``+''. % (Note that the latter case includes the value zero.) The command +%\DescribeMacro{\xnegsignof} % \cs{xnegsignof}\marg{counter} prints a plus sign ``+'' if \meta{counter} is -% negative and otherwise prints a minus sign ``-''. (Note that the former case +% negative and otherwise prints a minus sign ``-''. (Note that the latter case % includes the value zero.) % % Additional variants of these commands suppress certain output, as is % conventional when using integers as coefficients in algebraic expressions. -%\DescribeMacro{\xabsofcoef} -%\DescribeMacro{\xsignofcoef} -%\DescribeMacro{\xnegsignofcoef} % The command \cs{xabsofcoef}\marg{counter} prints the absolute value of +%\DescribeMacro{\xabsofcoef} % \meta{counter} except that it suppresses the values of 1 and 0. The command +%\DescribeMacro{\xsignofcoef} % \cs{xsignofcoef}\marg{counter} prints the sign of \meta{counter} if the value % of \meta{counter} is nonzero. The command \cs{xnegsignofcoef}\marg{counter} +%\DescribeMacro{\xnegsignofcoef} % prints the opposite sign of \meta{counter} if the value of \meta{counter} is % nonzero. These commands are used to build versions of \cs{xarabic} and % \cs{xsigned} specific to typesetting coefficients, as we now illustrate. % % Consider the following code +% %\begin{verbatim} %\xprovidecounter{a0} %\xsetcounter{a0}{-10} @@ -492,10 +570,13 @@ and the derived files counterz.ins, %\xprovidecounter{a5} %\xsetcounter{a5}{11} %$\xarabic{a5}x^5 + \xarabic{a4}x^4 + \xarabic{a3}x^3 + \xarabic{a2}x^2 -% + \xarabic{a1}x + \xarabic{a0} = 42$ +% + \xarabic{a1}x + \xarabic{a0} = 42$ %\end{verbatim} +% +% \noindent % and its output -%\begin{codeoutput} +% +% \bigskip %\xprovidecounter{a0} %\xsetcounter{a0}{-10} %\xprovidecounter{a1} @@ -509,57 +590,95 @@ and the derived files counterz.ins, %\xprovidecounter{a5} %\xsetcounter{a5}{11} %$\xarabic{a5}x^5 + \xarabic{a4}x^4 + \xarabic{a3}x^3 + \xarabic{a2}x^2 -% + \xarabic{a1}x + \xarabic{a0} = 42$ -%\end{codeoutput} -% We seek a better way to handle the coefficients, especially $1$ and $-1$. +% + \xarabic{a1}x + \xarabic{a0} = 42$ +% +% \bigskip +% \noindent +% We seek a better way to handle the coefficients, especially $1$ and $-1$. The +% command \cs{xcoef}\marg{counter} prints the value of \meta{counter} except %\DescribeMacro{\xcoef} -%\DescribeMacro{\xsignedcoef} -% The command \cs{xcoef}\marg{counter} prints the value of \meta{counter} except % that it suppresses the values of 1, 0, and -1, printing a minus sign ``-'' in % the latter case. The command \cs{xsignedcoef}\marg{counter} is like \cs{xcoef} +%\DescribeMacro{\xsignedcoef} % except that positive values are preceded by a plus sign ``+''. We use these to % write the code +% %\begin{verbatim} %$\xarabic{a5}x^5 + \xarabic{a4}x^4 + \xarabic{a3}x^3 + \xarabic{a2}x^2 -% + \xarabic{a1}x + \xarabic{a0} = 42$\\ -%$\xcoef{a5}\ifctrzero{a5}{}{x^5} \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} -% \xsignedcoef{a3}\ifctrzero{a3}{}{x^3} \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} -% \xsignedcoef{a1}\ifctrzero{a1}{}{x} \xsignednz{a0} = 42$ +% + \xarabic{a1}x + \xarabic{a0} = 42$ +% +%$\xcoef{a5}\ifctrzero{a5}{}{x^5} +% \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} +% \xsignedcoef{a3}\ifctrzero{a3}{}{x^3} +% \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} +% \xsignedcoef{a1}\ifctrzero{a1}{}{x} +% \xsignednz{a0} +% = 42$ %\end{verbatim} +% +% \noindent % whose output is -%\begin{codeoutput} +% +% \bigskip %$\xarabic{a5}x^5 + \xarabic{a4}x^4 + \xarabic{a3}x^3 + \xarabic{a2}x^2 -% + \xarabic{a1}x + \xarabic{a0} = 42$\\ -%$\xcoef{a5}\ifctrzero{a5}{}{x^5} \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} -% \xsignedcoef{a3}\ifctrzero{a3}{}{x^3} \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} -% \xsignedcoef{a1}\ifctrzero{a1}{}{x} \xsignednz{a0} = 42$ -%\end{codeoutput} +% + \xarabic{a1}x + \xarabic{a0} = 42$ +% +%$\xcoef{a5}\ifctrzero{a5}{}{x^5} +% \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} +% \xsignedcoef{a3}\ifctrzero{a3}{}{x^3} +% \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} +% \xsignedcoef{a1}\ifctrzero{a1}{}{x} +% \xsignednz{a0} +% = 42$ +% +% \bigskip %\DescribeMacro{\xnegcoef} -%\DescribeMacro{\xnegsignedcoef} % The command \cs{xnegcoef}\marg{counter} prints the negative of the value of % \meta{counter} except that it suppresses the values of 1, 0, and -1, printing % a ``-'' in the latter case. The command \cs{xnegsignedcoef}\marg{counter} is +%\DescribeMacro{\xnegsignedcoef} % like \cs{xnegcoef} except that positive values are preceded by a plus sign % ``+''. We use these to write the code +% %\begin{verbatim} -%$\xcoef{a5}\ifctrzero{a5}{}{x^5} \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} -% \xsignedcoef{a3}\ifctrzero{a3}{}{x^3} \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} -% \xsignedcoef{a1}\ifctrzero{a1}{}{x} \xsignednz{a0} = 42$\\ -%$\xcoef{a5}\ifctrzero{a5}{}{x^5} \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} -% \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} \xsignednz{a0} -% = \xnegcoef{a3}\ifctrzero{a3}{}{x^3} \xnegsignedcoef{a1}\ifctrzero{a1}{}{x} +%$\xcoef{a5}\ifctrzero{a5}{}{x^5} +% \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} +% \xsignedcoef{a3}\ifctrzero{a3}{}{x^3} +% \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} +% \xsignedcoef{a1}\ifctrzero{a1}{}{x} +% \xsignednz{a0} +% = 42$ +% +%$\xcoef{a5}\ifctrzero{a5}{}{x^5} +% \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} +% \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} +% \xsignednz{a0} +% = \xnegcoef{a3}\ifctrzero{a3}{}{x^3} +% \xnegsignedcoef{a1}\ifctrzero{a1}{}{x} % +42$ %\end{verbatim} +% +% \noindent % whose output is -%\begin{codeoutput} -%$\xcoef{a5}\ifctrzero{a5}{}{x^5} \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} -% \xsignedcoef{a3}\ifctrzero{a3}{}{x^3} \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} -% \xsignedcoef{a1}\ifctrzero{a1}{}{x} \xsignednz{a0} = 42$\\ -%$\xcoef{a5}\ifctrzero{a5}{}{x^5} \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} -% \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} \xsignednz{a0} -% = \xnegcoef{a3}\ifctrzero{a3}{}{x^3} \xnegsignedcoef{a1}\ifctrzero{a1}{}{x} +% +% \bigskip +%$\xcoef{a5}\ifctrzero{a5}{}{x^5} +% \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} +% \xsignedcoef{a3}\ifctrzero{a3}{}{x^3} +% \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} +% \xsignedcoef{a1}\ifctrzero{a1}{}{x} +% \xsignednz{a0} +% = 42$ +% +%$\xcoef{a5}\ifctrzero{a5}{}{x^5} +% \xsignedcoef{a4}\ifctrzero{a4}{}{x^4} +% \xsignedcoef{a2}\ifctrzero{a2}{}{x^2} +% \xsignednz{a0} +% = \xnegcoef{a3}\ifctrzero{a3}{}{x^3} +% \xnegsignedcoef{a1}\ifctrzero{a1}{}{x} % +42$ -%\end{codeoutput} +% +% \bigskip % As the reader has probably already observed in the code above, these display % commands appear to be less efficient than a manual adjustment of signs and % numbers. For fixed, known values of counters, this assessment is correct. The @@ -568,16 +687,21 @@ and the derived files counterz.ins, % %\subsection{Random Counters} % +% We first define random versions of \cs{setcounter} and \cs{addtocounter}. +% The command \cs{randsetcounter}\marg{counter}\marg{min}\marg{max} assigns +%\DescribeMacro{\randsetcounter} +% to \meta{counter} a random integer value between \meta{min} and \meta{max}. %\DescribeMacro{\xrandsetcounter} +% The command \cs{xrandsetcounter} is a prefix version of \cs{randsetcounter}. +% Analogously, we define the command +%\DescribeMacro{\randaddtocounter} +% \cs{randaddtocounter}\marg{counter}\marg{min}\marg{max} which adds to +% \meta{counter} a random integer value between \meta{min} and \meta{max}. %\DescribeMacro{\xrandaddtocounter} -% We first define random versions of \cs{xsetcounter} and \cs{xaddtocounter}. -% The command \cs{xrandsetcounter}\marg{counter}\marg{min}\marg{max} assigns -% to the prefixed \meta{counter} a random integer value between \meta{min} and -% \meta{max}. Analogously, the command -% \cs{xrandaddtocounter}\marg{counter}\marg{min}\marg{max} adds to the prefixed -% \meta{counter} a random integer value between \meta{min} and \meta{max}. The -% following code may be used to produce an expression in the form $ax+b$, where -% $a$ and $b$ are random integers between $-10$ and $10$: +% \cs{xrandaddtocounter} is a prefix version of \cs{randaddtocounter}. +% The following code may be used to produce an expression in the form +% $ax+b$, where $a$ and $b$ are random integers between $-10$ and $10$: +% %\begin{verbatim} %\xprovidecounter{a} %\xprovidecounter{b} @@ -585,9 +709,13 @@ and the derived files counterz.ins, %\xrandsetcounter{b}{-10}{10} %$\xcoef{a}\ifctrzero{a}{\xarabic{b}}{x \xsignednz{b}}$ %\end{verbatim} +% +% \noindent % Organized in the following table are sixty instances of output that are % randomly generated by the typesetting of this document: -%\begin{codeoutput} +% +% \bigskip +%\noindent %\xprovidecounter{a} %\xprovidecounter{b} %\begin{tabular}{rrrrrr} @@ -772,57 +900,130 @@ and the derived files counterz.ins, %\xrandsetcounter{b}{-10}{10} %$\xcoef{a}\ifctrzero{a}{\xarabic{b}}{x \xsignednz{b}}$ \\ %\end{tabular} -%\end{codeoutput} +% +% \bigskip +% In order to effectively manage the options of randomizing counter values or +%\DescribeMacro{\randomizectr} +% reusing counter values, the commands \cs{randomizectr} and \cs{norandomizectr} +% are used to toggle an internal boolean variable. The internal boolean +%\DescribeMacro{\norandomizectr} +% is initialized as TRUE when the \textsf{counterz} package is loaded. A +% conditional command \cs{ifrandomizectr}\marg{foo}\marg{bar} executes +%\DescribeMacro{\ifrandomizectr} +% \meta{foo} when the boolean is true and otherwise executes \meta{bar}. % % If our document contains randomly generated counters, but we wish to typeset % the document again without changing those values, then we need a way to save -% those values. The \textsf{counterz} package offers the following solution: a -% file \textsf{\meta{jobname}.counters.tex} may be created during the -% typesetting process to store the necessary information. For example, if the -% document is named \textsf{Yellowdog.tex}, then the previously generated -% counters and their assigned values will be stored the file -% \textsf{Yellowdog.counters.tex}. %\DescribeMacro{\opencountersfile} -% The command \cs{opencountersfile} creates and opens the write stream to this -% file. The author only has to include this command once, prior to any commands -% used to save the counter values. -% +% them. The command \cs{opencountersfile} creates and opens the write stream to +% the file \textsf{\meta{jobname}.counters.tex} to store the necessary +% information. For example, if the document is +% named \textsf{Yellowdog.tex}, then the previously generated counters and their +% assigned values will be stored the file \textsf{Yellowdog.counters.tex}. +% The author only has to include this command once, prior to any commands used +% to save the counter values. Additional instances of \cs{opencountersfile} will +% report an error, as will trying to use the command when the document is set to +% not randomize (e.g. \cs{norandomizectr}). These error reports are designed to +% prevent the accidental overwriting of \textsf{\meta{jobname}.counters.tex}. +% +% After opening the write stream to \textsf{\meta{jobname}.counters.tex}, the +% command \cs{savecounter}\marg{counter} may be used to ``save'' the value of +%\DescribeMacro{\savecounter} +% \textit{counter} by writing to the file the relevant \cs{providecounter} and +% \cs{setcounter} commands. The command \cs{xsavecounter} is a prefix version of %\DescribeMacro{\xsavecounter} -% The command \cs{xsavecounter}\marg{counter} ``saves'' the value of -% \textit{counter} by writing to the file \textsf{\meta{jobname}.counters.tex} -% the relevant \cs{providecounter} and \cs{setcounter} commands. The commands -% written to the file explicitly include the necessary counter prefixes, and -% consequently an author can, if necessary, manually find in the file the -% specific assignment for any counter. The counters file may then be inputted -% near the beginning of a subsequent typesetting to preassign all of the -% values. +% \cs{savecounter}. When using \cs{xsavecounter}, the commands that are written +% to the file include the necessary counter prefixes. Consequently, an author +% can, if necessary or desired, manually search the file for the value assigned +% to any randomly generated counter. +% +% Once we have generated a file for storing counters, we need a way to recover +% those values during a subsequent typesetting. The command +%\DescribeMacro{\inputcountersfile} +% \cs{inputcountersfile} will input the necessary file, if it exists, and report +% an error if it does not. Keep in mind that inputting the file will override +% any previous assignments of those counters, so it is probably best to invoke +% this command near the beginning of a document. For example, after including an +% instance of either \cs{randomizectr} or \cs{norandomizectr}, a document named +% \textsf{Yellowdog.tex} might include the code % -%\DescribeMacro{\randomizectr} -%\DescribeMacro{\norandomizectr} -%\DescribeMacro{\ifrandomizectr} -% In order to effectively manage the options of randomizing counter values or -% reusing counter values, the \textsf{counterz} package offers the commands -% \cs{randomizectr} and \cs{norandomizectr} that toggle an internal boolean -% variable, and a conditional \cs{ifrandomizectr}\marg{foo}\marg{bar} that -% executes \meta{foo} when the boolean is true and otherwise executes -% \meta{bar}. For example, a document named \textsf{Yellowdog.tex} might -% include the code %\begin{verbatim} -%\ifrandomizectr{}{input{Yellowdog.counters}} +%\ifrandomizectr{\opencountersfile}{\inputcountersfile} %\end{verbatim} -% to determine whether to preload previously stored counter values. % -%\DescribeMacro{\xrandprovidecounter} -%\DescribeMacro{\xrandprovidecounternz} -% The command \cs{xrandprovidecounter}\marg{counter}\marg{min}\marg{max} -% combines the four commands \cs{xprovidecounter}, \cs{ifrandomizectr}, -% \cs{xrandsetcounter}, and \cs{xsavecounter} to define a command that creates -% \meta{counter} if it has not already been defined and, if the document is +% \noindent +% to determine whether to preload previously stored counter values or open the +% write stream in anticipation of randomly generating new counter values. +% +%\DescribeMacro{\promptrandomizectr} +% The command \cs{promptrandomizectr}\oarg{macro}\marg{message}\marg{string} +% offers an alternative to manually switching between the commands +% \cs{randomizectr} and \cs{norandomizectr} for different typesettings. The +% contents of \meta{message} are displayed in the terminal, awaiting a response +% from the user at the prompt \meta{macro}. If the optional argument is not used +% then the default prompt is \cs{@typein}. If the optional argument is given, it +% must be a macro name that includes the backslash. The user's response is +% stored as a string in \meta{macro} and compared to \meta{string}. If they are +% equal, then the command \cs{randomizectr} is executed. If they are not equal +% then \cs{norandomizectr} is executed. +% +% Consider the following example code: +% +%\begin{verbatim} +%\promptrandomizectr[\EnterResponse]{% +% ^^J Enter 1 to randomize. +% ^^J Enter 2 to not randomize. +%}{% +% 1% +%}% +%\ifrandomizectr{% +% \opencountersfile +%}{% +% \inputcountersfile +%}% +%\end{verbatim} +% +% \noindent +% which displays the following in the terminal: +% +%\begin{verbatim} +% Enter 1 to randomize. +% Enter 2 to not randomize. +% +%\EnterResponse= +%\end{verbatim} +% +% \noindent +% Notice that the first (optional) argument \cs{EnterResponse} begins with a +% backslash and is displayed with an equals sign ``='' at the prompt. Also note +% that the second argument contains two instances of the text \texttt{\^{}\^{}J} +% which is used to produce a line break in the terminal output. Next, note that +% the third argument \texttt{1} is immediately followed by a percent symbol +% \texttt{\%} to prevent extra space being included in the string. (If the +% \texttt{1} was immediately followed by a closing brace instead of a line break +% in the code, the percent symbol would not be used.) Finally, note that if the +% user types a \texttt{1} in the terminal and presses Enter, then the commands +% \cs{randomizectr} and \cs{opencountersfile} will be executed. If the user +% enters \textit{any other text} or simply presses Enter with no text, then the +% commands \cs{norandomizectr} and \cs{inputcountersfile} will be executed, +% despite the instructions to enter a \texttt{2} to achieve this outcome. +% +%\DescribeMacro{\randprovidecounter} +% The command \cs{randprovidecounter}\marg{counter}\marg{min}\marg{max} +% combines the four commands \cs{providecounter}, \cs{ifrandomizectr}, +% \cs{randsetcounter}, and \cs{savecounter}. The command creates \meta{counter} +% if it has not already been defined and, if the document is % randomized, assigns to \meta{counter} a random integer value between % \meta{min} and \meta{max} and saves this value to the counters file. The -% command \cs{xrandprovidecounternz} is like \cs{xrandprovidecounter} except -% that the generated value is nonzero. Suppose that \textsf{Neverending.tex} +%\DescribeMacro{\randprovidecounternz} +% command \cs{randprovidecounternz} is like \cs{randprovidecounter} except +%\DescribeMacro{\xrandprovidecounter} +% that the generated value is nonzero. The commands \cs{xrandprovidecounter} and +%\DescribeMacro{\xrandprovidecounternz} +% \cs{xrandprovidecounternz} are prefix versions of \cs{randprovidecounter} and +% \cs{randprovidecounternz}, respectively. Suppose that \textsf{Neverending.tex} % contains the code +% %\begin{verbatim} %\randomizectr %\ifrandomizectr{\opencountersfile}{} @@ -830,21 +1031,27 @@ and the derived files counterz.ins, %\xrandprovidecounternz{Oracle}{-10}{10} %\xcoef{Oracle}x+42 %\end{verbatim} +% +% \noindent % After typesetting once, the resulting document might display an expression % such as $-9x+42$ and print to \textsf{Neverending.counters.tex} the line +% %\begin{verbatim} %\providecounter {SouthernOracle} \setcounter {SouthernOracle}{-9} %\end{verbatim} +% +% \noindent % After typesetting a second time, the resulting document might display $4x+42$ % and print to \textsf{Neverending.counters.tex} the line +% %\begin{verbatim} %\providecounter {SouthernOracle} \setcounter {SouthernOracle}{4} %\end{verbatim} +% +% \noindent % If, however, the command \cs{randomizectr} is replaced by \cs{norandomizectr}, % then a third typesetting will leave both the displayed text and the counters -% file unchanged. (Tip: Users who are concerned about accidental randomization -% might create a terminal prompt with the commands \cs{typeout} and \cs{typein} -% to input the randomization preference, as an added layer of security.) +% file unchanged. % %\StopEventually{^^A % \PrintChanges @@ -859,7 +1066,9 @@ and the derived files counterz.ins, % % \begin{macrocode} %<*package> -\ProvidesPackage{counterz}[2023/05/19 v1.0.0 Additional tools for counters] +\ProvidesPackage{counterz}[% + 2023/05/30 v1.1.0 Additional tools for counters +]% \RequirePackage{etoolbox,makecmds} % \end{macrocode} % @@ -874,8 +1083,12 @@ and the derived files counterz.ins, % % \begin{macrocode} \newcommand{\@counterz@counterprefix}{} -\newcommand{\setcounterprefix}[1]{\renewcommand{\@counterz@counterprefix}{#1}} -\newcommand{\clearcounterprefix}{\setcounterprefix{}} +\newcommand{\setcounterprefix}[1]{% + \renewcommand{\@counterz@counterprefix}{#1} +}% +\newcommand{\clearcounterprefix}{% + \setcounterprefix{} +}% % \end{macrocode} % %\end{macro} @@ -898,11 +1111,21 @@ and the derived files counterz.ins, % counterparts. % % \begin{macrocode} -\newcommand{\xnewcounter}[1]{\newcounter{\@counterz@counterprefix #1}} -\newcommand{\xprovidecounter}[1]{\providecounter{\@counterz@counterprefix #1}} -\newcommand{\xsetcounter}[2]{\setcounter{\@counterz@counterprefix #1}{#2}} -\newcommand{\xaddtocounter}[2]{\addtocounter{\@counterz@counterprefix #1}{#2}} -\newcommand{\xvalue}[1]{\value{\@counterz@counterprefix #1}} +\newcommand{\xnewcounter}[1]{% + \newcounter{\@counterz@counterprefix #1} +}% +\newcommand{\xprovidecounter}[1]{% + \providecounter{\@counterz@counterprefix #1} +}% +\newcommand{\xsetcounter}[2]{% + \setcounter{\@counterz@counterprefix #1}{#2} +}% +\newcommand{\xaddtocounter}[2]{% + \addtocounter{\@counterz@counterprefix #1}{#2} +}% +\newcommand{\xvalue}[1]{% + \value{\@counterz@counterprefix #1} +}% % \end{macrocode} % %\end{macro} @@ -926,7 +1149,9 @@ and the derived files counterz.ins, % \meta{counter2} and otherwise executes \meta{bar}. % % \begin{macrocode} -\newcommand{\ifctrequal}[4]{\ifnumequal{\xvalue{#1}}{\xvalue{#2}}{#3}{#4}} +\newcommand{\ifctrequal}[4]{% + \ifnumequal{\xvalue{#1}}{\xvalue{#2}}{#3}{#4} +}% % \end{macrocode} % %\end{macro} @@ -938,7 +1163,9 @@ and the derived files counterz.ins, % \meta{counter2} and otherwise executes \meta{bar}. % % \begin{macrocode} -\newcommand{\ifctrless}[4]{\ifnumless{\xvalue{#1}}{\xvalue{#2}}{#3}{#4}} +\newcommand{\ifctrless}[4]{% + \ifnumless{\xvalue{#1}}{\xvalue{#2}}{#3}{#4} +}% % \end{macrocode} % %\end{macro} @@ -950,7 +1177,9 @@ and the derived files counterz.ins, % \meta{counter2} and otherwise executes \meta{bar}. % % \begin{macrocode} -\newcommand{\ifctrmore}[4]{\ifnumless{\xvalue{#2}}{\xvalue{#1}}{#3}{#4}} +\newcommand{\ifctrmore}[4]{% + \ifnumless{\xvalue{#2}}{\xvalue{#1}}{#3}{#4} +}% % \end{macrocode} % %\end{macro} @@ -961,7 +1190,9 @@ and the derived files counterz.ins, % the value of \meta{counter} is zero and otherwise executes \meta{bar}. % % \begin{macrocode} -\newcommand{\ifctrzero}[3]{\ifnumequal{\xvalue{#1}}{0}{#2}{#3}} +\newcommand{\ifctrzero}[3]{% + \ifnumequal{\xvalue{#1}}{0}{#2}{#3} +}% % \end{macrocode} % %\end{macro} @@ -972,7 +1203,9 @@ and the derived files counterz.ins, % value of \meta{counter} is negative and otherwise executes \meta{bar}. % % \begin{macrocode} -\newcommand{\ifctrneg}[3]{\ifnumless{\xvalue{#1}}{0}{#2}{#3}} +\newcommand{\ifctrneg}[3]{% + \ifnumless{\xvalue{#1}}{0}{#2}{#3} +}% % \end{macrocode} % %\end{macro} @@ -983,7 +1216,9 @@ and the derived files counterz.ins, % value of \meta{counter} is positive and otherwise executes \meta{bar}. % % \begin{macrocode} -\newcommand{\ifctrpos}[3]{\ifnumless{\xvalue{#1}}{1}{#3}{#2}} +\newcommand{\ifctrpos}[3]{% + \ifnumless{\xvalue{#1}}{1}{#3}{#2} +}% % \end{macrocode} % %\end{macro} @@ -1042,7 +1277,9 @@ and the derived files counterz.ins, % includes the value zero. % % \begin{macrocode} -\newcommand{\xsignof}[1]{\ifctrneg{#1}{-}{+}} +\newcommand{\xsignof}[1]{% + \ifctrneg{#1}{-}{+} +}% % \end{macrocode} % %\end{macro} @@ -1054,7 +1291,9 @@ and the derived files counterz.ins, % includes the value zero. % % \begin{macrocode} -\newcommand{\xnegsignof}[1]{\ifctrneg{#1}{+}{-}} +\newcommand{\xnegsignof}[1]{% + \ifctrneg{#1}{+}{-} +}% % \end{macrocode} % %\end{macro} @@ -1066,7 +1305,9 @@ and the derived files counterz.ins, % \cs{xsignof}. % % \begin{macrocode} -\newcommand{\xsigned}[1]{\xsignof{#1} \xabsof{#1}} +\newcommand{\xsigned}[1]{% + \xsignof{#1} \xabsof{#1} +}% % \end{macrocode} % %\end{macro} @@ -1077,7 +1318,9 @@ and the derived files counterz.ins, % zero. % % \begin{macrocode} -\newcommand{\xsignednz}[1]{\ifctrzero{#1}{}{\xsigned{#1}}} +\newcommand{\xsignednz}[1]{% + \ifctrzero{#1}{}{\xsigned{#1}} +}% % \end{macrocode} % %\end{macro} @@ -1088,7 +1331,9 @@ and the derived files counterz.ins, % zero. % % \begin{macrocode} -\newcommand{\xarabicnz}[1]{\ifctrzero{#1}{}{\xarabic{#1}}} +\newcommand{\xarabicnz}[1]{% + \ifctrzero{#1}{}{\xarabic{#1}} +}% % \end{macrocode} % %\end{macro} @@ -1100,7 +1345,9 @@ and the derived files counterz.ins, % \cs{xnegsignof}. % % \begin{macrocode} -\newcommand{\xnegsigned}[1]{\xnegsignof{#1} \xabsof{#1}} +\newcommand{\xnegsigned}[1]{% + \xnegsignof{#1} \xabsof{#1} +}% % \end{macrocode} % %\end{macro} @@ -1111,7 +1358,9 @@ and the derived files counterz.ins, % number zero. % % \begin{macrocode} -\newcommand{\xnegsignednz}[1]{\ifctrzero{#1}{}{\xnegsigned{#1}}} +\newcommand{\xnegsignednz}[1]{% + \ifctrzero{#1}{}{\xnegsigned{#1}} +}% % \end{macrocode} % %\end{macro} @@ -1121,7 +1370,9 @@ and the derived files counterz.ins, % \cs{xnegof}\marg{counter} prints the negative of the value of \meta{counter}. % % \begin{macrocode} -\newcommand{\xnegof}[1]{\ifctrpos{#1}{-}{}\xabsof{#1}} +\newcommand{\xnegof}[1]{% + \ifctrpos{#1}{-}{}\xabsof{#1} +}% % \end{macrocode} % %\end{macro} @@ -1132,7 +1383,9 @@ and the derived files counterz.ins, % zero. % % \begin{macrocode} -\newcommand{\xnegofnz}[1]{\ifctrzero{#1}{}{\xnegof{#1}}} +\newcommand{\xnegofnz}[1]{% + \ifctrzero{#1}{}{\xnegof{#1}} +}% % \end{macrocode} % %\end{macro} @@ -1144,13 +1397,15 @@ and the derived files counterz.ins, % % \begin{macrocode} \newcommand{\xcoef}[1]{% - \ifboolexpr{test {\ifnumless{\xvalue{#1}}{-1}}% - or test {\ifnumgreater{\xvalue{#1}}{1}}}{% - \xarabic{#1}% + \ifboolexpr{% + test {\ifnumless{\xvalue{#1}}{-1}} + or test {\ifnumgreater{\xvalue{#1}}{1}} + }{% + \xarabic{#1} }{% }% - \ifnumequal{\xvalue{#1}}{-1}{-}{}% -} + \ifnumequal{\xvalue{#1}}{-1}{-}{} +}% % \end{macrocode} % %\end{macro} @@ -1162,13 +1417,15 @@ and the derived files counterz.ins, % % \begin{macrocode} \newcommand{\xnegcoef}[1]{% - \ifboolexpr{test {\ifnumless{\xvalue{#1}}{-1}}% - or test {\ifnumgreater{\xvalue{#1}}{1}}}{% - \xnegof{#1}% + \ifboolexpr{% + test {\ifnumless{\xvalue{#1}}{-1}} + or test {\ifnumgreater{\xvalue{#1}}{1}} + }{% + \xnegof{#1} }{% }% - \ifnumequal{\xvalue{#1}}{1}{-}{}% -} + \ifnumequal{\xvalue{#1}}{1}{-}{} +}% % \end{macrocode} % %\end{macro} @@ -1180,12 +1437,14 @@ and the derived files counterz.ins, % % \begin{macrocode} \newcommand{\xabsofcoef}[1]{% - \ifboolexpr{test {\ifnumless{\xvalue{#1}}{-1}}% - or test {\ifnumgreater{\xvalue{#1}}{1}}}{% - \xabsof{#1}% + \ifboolexpr{% + test {\ifnumless{\xvalue{#1}}{-1}} + or test {\ifnumgreater{\xvalue{#1}}{1}} }{% - } -} + \xabsof{#1} + }{% + }% +}% % \end{macrocode} % %\end{macro} @@ -1196,7 +1455,9 @@ and the derived files counterz.ins, % \meta{counter} is nonzero. % % \begin{macrocode} -\newcommand{\xsignofcoef}[1]{\ifctrzero{#1}{}{\xsignof{#1}}} +\newcommand{\xsignofcoef}[1]{% + \ifctrzero{#1}{}{\xsignof{#1}} +}% % \end{macrocode} % %\end{macro} @@ -1207,7 +1468,9 @@ and the derived files counterz.ins, % if \meta{counter} is nonzero. % % \begin{macrocode} -\newcommand{\xnegsignofcoef}[1]{\ifctrzero{#1}{}{\xnegsignof{#1}}} +\newcommand{\xnegsignofcoef}[1]{% + \ifctrzero{#1}{}{\xnegsignof{#1}} +}% % \end{macrocode} % %\end{macro} @@ -1218,7 +1481,9 @@ and the derived files counterz.ins, % are preceded by a plus sign ``+''. % % \begin{macrocode} -\newcommand{\xsignedcoef}[1]{\xsignofcoef{#1} \xabsofcoef{#1}} +\newcommand{\xsignedcoef}[1]{% + \xsignofcoef{#1} \xabsofcoef{#1} +}% % \end{macrocode} % %\end{macro} @@ -1229,68 +1494,190 @@ and the derived files counterz.ins, % opposite sign. % % \begin{macrocode} -\newcommand{\xnegsignedcoef}[1]{\xnegsignofcoef{#1} \xabsofcoef{#1}} +\newcommand{\xnegsignedcoef}[1]{% + \xnegsignofcoef{#1} \xabsofcoef{#1} +}% % \end{macrocode} % %\end{macro} % %\subsection{Random Counters} % -% The commands \cs{xrandsetcounter} and \cs{xrandaddtocounter} use the pdf\TeX\ +% The commands \cs{randsetcounter} and \cs{randaddtocounter} use the pdf\TeX\ % primitive \cs{pdfuniformdeviate} to provide random versions of -% \cs{xsetcounter} and \cs{xaddtocounter}. +% \cs{setcounter} and \cs{addtocounter}. The commands \cs{xrandsetcounter} and +% \cs{xrandaddtocounter} are prefix versions of \cs{randsetcounter} and +% \cs{randaddtocounter}, respectively. % +%\begin{macro}{\randsetcounter} +%\changes{v1.1.0}{2023/05/30}{new} %\begin{macro}{\xrandsetcounter} +%\changes{v1.1.0}{2023/05/30}{now based on a new \cs{randsetcounter}} % -% \cs{xrandsetcounter}\marg{counter}\marg{min}\marg{max} assigns to (the -% prefixed) \meta{counter} a random integer value between \meta{min} and -% \meta{max}. +% \cs{randsetcounter}\marg{counter}\marg{min}\marg{max} assigns to +% \meta{counter} a random integer value between \meta{min} and \meta{max}. % % \begin{macrocode} -\newcommand{\xrandsetcounter}[3]{% - \xsetcounter{#1}{% +\newcommand{\randsetcounter}[3]{% + \setcounter{#1}{% \the \numexpr #2+\pdfuniformdeviate \numexpr #3-#2+1 \relax - } -} + }% +}% +\newcommand{\xrandsetcounter}[3]{% + \randsetcounter{\@counterz@counterprefix#1}{#2}{#3} +}% % \end{macrocode} % %\end{macro} +%\end{macro} % +%\begin{macro}{\randaddtocounter} +%\changes{v1.1.0}{2023/05/30}{new} %\begin{macro}{\xrandaddtocounter} +%\changes{v1.1.0}{2023/05/30}{now based on a new \cs{randaddtocounter}} % -% \cs{xrandaddtocounter}\marg{counter}\marg{min}\marg{max} adds to (the -% prefixed) \meta{counter} a random integer value between \meta{min} and -% \meta{max}. +% \cs{randaddtocounter}\marg{counter}\marg{min}\marg{max} adds to \meta{counter} +% a random integer value between \meta{min} and \meta{max}. % % \begin{macrocode} -\newcommand{\xrandaddtocounter}[3]{% - \xaddtocounter{#1}{% +\newcommand{\randaddtocounter}[3]{% + \addtocounter{#1}{% \the \numexpr #2+\pdfuniformdeviate \numexpr #3-#2+1 \relax - } -} + }% +}% +\newcommand{\xrandaddtocounter}[3]{% + \randaddtocounter{\@counterz@counterprefix#1}{#2}{#3} +}% % \end{macrocode} % %\end{macro} +%\end{macro} % The following commands are designed to provide a means by which authors can % generate random values for counters but also preserve those values for future % typesettings. This is accomplished by storing counters and their values in an % external file and then inputting the file before a subsequent typesetting. % +%\begin{macro}{\randomizectr} +%\begin{macro}{\norandomizectr} +% +% In order to assign a random value to a counter during one typesetting and +% avoid overwriting this value with a random assignment during another +% typesetting, the boolean \textit{@counterz@random} is used to distinguish +% between the two typesetting options. The value of \textit{@counterz@random} may +% be changed by the commands \cs{randomizectr} and \cs{norandomizectr}. +% +% \begin{macrocode} +\newbool{@counterz@random} +\booltrue{@counterz@random} +\newcommand{\randomizectr}{\booltrue{@counterz@random}} +\newcommand{\norandomizectr}{\boolfalse{@counterz@random}} +% \end{macrocode} +% +%\end{macro} +%\end{macro} +% +%\begin{macro}{\ifrandomizectr} +% +% \cs{ifrandomizectr}\marg{foo}\marg{bar} executes \meta{foo} if the boolean +% \textit{@counterz@random} is true and otherwise executes \meta{bar}. +% +% \begin{macrocode} +\newcommand{\ifrandomizectr}[2]{% + \ifbool{@counterz@random}{#1}{#2} +}% +% \end{macrocode} +% +%\end{macro} +% +%\begin{macro}{\promptrandomizectr} +%\changes{v1.1.0}{2023/05/30}{new} +% +% \cs{promptrandomizectr}\oarg{command}\marg{message}\marg{string} writes +% \meta{message} to the terminal and awaits a response from the user at the +% prompt. The user's response is stored in \meta{command} and compared to the +% text of \meta{string}. If they are equal, then \cs{randomizectr} is executed. +% If they are not equal, then \cs{norandomizectr} is executed. +% +% \begin{macrocode} +\newcommand{\promptrandomizectr}[3][\@typein]{% + \typein[#1]{#2} + \ifdefstring{#1}{#3}{% + \randomizectr + }{% + \norandomizectr + }% +}% +% \end{macrocode} +% +%\end{macro} +% %\begin{macro}{\opencountersfile} +%\changes{v1.1.0}{2023/05/30}{new error reports} % % The command \cs{opencountersfile} creates and opens the write stream to the % file \textsf{\meta{jobname}.counters.tex}, referenced by the macro -% \cs{countersfile}. +% \cs{countersfile}. If the file already exists, it is overwritten. For this +% reason, % % \begin{macrocode} +\newbool{@counterz@fileISopen} +\boolfalse{@counterz@fileISopen} \newcommand{\opencountersfile}{% - \newwrite\countersfile - \immediate\openout\countersfile=\jobname.counters.tex + \ifbool{@counterz@fileISopen}{% + \PackageError{counterz}{% + The write stream is already open! + \MessageBreak Process interrupted to prevent overwriting + \MessageBreak \jobname.counters.tex + }{% + Be sure to include only one instance of + \protect\opencountersfile. + }% + }{% + \ifrandomizectr{% + \newwrite\countersfile + \immediate\openout\countersfile=\jobname.counters.tex + \booltrue{@counterz@fileISopen} + }{% + \PackageError{counterz}{% + \protect\opencountersfile\space requires + \protect\randomizectr + \MessageBreak Process interrupted to prevent overwriting + \MessageBreak \jobname.counters.tex + }{% + \protect\opencountersfile\space is designed to open a file + for saving newly randomized counters. See the Random + Counters section of the counterz package documentation for + details. + }% + }% + }% } % \end{macrocode} % %\end{macro} % +%\begin{macro}{\inputcountersfile} +%\changes{v1.1.0}{2023/05/30}{new} +% +% The command \cs{inputcountersfile} inputs \textsf{\meta{jobname}.counters.tex} +% if the file exists and reports a package error if the file does not exist. +% +% \begin{macrocode} +\newcommand{\inputcountersfile}{% + \InputIfFileExists{\jobname.counters}{% + }{% + \PackageError{counterz}{% + The file \jobname.counters.tex does not exist. + }{% + See the Random Counters section of the counterz package + documentation. + }% + }% +}% +% \end{macrocode} +% +%\end{macro} +% %\begin{macro}{\@counterz@openbrace} %\begin{macro}{\@counterz@closebrace} % @@ -1309,59 +1696,79 @@ and the derived files counterz.ins, %\end{macro} %\end{macro} % +%\begin{macro}{\savecounter} +%\changes{v1.1.0}{2023/05/30}{new} %\begin{macro}{\xsavecounter} +%\changes{v1.1.0}{2023/05/30}{now based on a new \cs{savecounter}} % -% \cs{xsavecounter}\marg{counter} writes \cs{providecounter} and \cs{setcounter} +% \cs{savecounter}\marg{counter} writes \cs{providecounter} and \cs{setcounter} % commands to the file \textsf{\meta{jobname}.counters.tex} so that they may be -% inputted as part of a future typesetting. +% inputted as part of a future typesetting. The command reports a package error +% if the write stream to \textsf{\meta{jobname}.counters.tex} is not open. The +% command \cs{xsavecounter} is a prefix version of \cs{savecounter}. +% % % \begin{macrocode} -\newcommand{\xsavecounter}[1]{% - \immediate\write\countersfile{% - \unexpanded{\providecounter}\@counterz@openbrace% - \@counterz@counterprefix #1\@counterz@closebrace% - \unexpanded{ \setcounter}\@counterz@openbrace% - \@counterz@counterprefix #1\@counterz@closebrace% - \@counterz@openbrace% - \arabic{\@counterz@counterprefix #1}\@counterz@closebrace% +\newcommand{\savecounter}[1]{% + \ifbool{@counterz@fileISopen}{% + \immediate\write\countersfile{% + \unexpanded{\providecounter} + \@counterz@openbrace#1\@counterz@closebrace + \unexpanded{\setcounter} + \@counterz@openbrace#1\@counterz@closebrace + \@counterz@openbrace\arabic{#1}\@counterz@closebrace + }% + }{% + \PackageError{counterz}{% + The write stream to the file \jobname.counters.tex must be + opened before \protect\savecounter\space can be executed. + }{% + See \protect\opencountersfile\space and + \protect\savecounter\space in the counterz package + documentation. + }% }% }% -% \end{macrocode} -% -%\end{macro} -% -%\begin{macro}{\randomizectr} -%\begin{macro}{\norandomizectr} -% -% In order to assign a random value to a counter during one typesetting and -% avoid overwriting this value with a random assignment during another -% typesetting, the boolean \textit{@counterz@random} is used to distinguish -% between the two typesettings. The value of \textit{@counterz@random} may -% be changed by the commands \cs{randomizectr} and \cs{norandomizectr}. -% -% \begin{macrocode} -\newbool{@counterz@random} -\newcommand{\randomizectr}{\booltrue{@counterz@random}} -\newcommand{\norandomizectr}{\boolfalse{@counterz@random}} + +\newcommand{\xsavecounter}[1]{% + \savecounter{\@counterz@counterprefix#1}% +}% % \end{macrocode} % %\end{macro} %\end{macro} % -%\begin{macro}{\ifrandomizectr} +%\begin{macro}{\randprovidecounter} +%\changes{v1.1.0}{2023/05/30}{new} % -% \cs{ifrandomizectr}\marg{foo}\marg{bar} executes \meta{foo} if the boolean -% \textit{@counterz@random} is true and otherwise executes \meta{bar}. +% \cs{randprovidecounter}\marg{counter}\marg{min}\marg{max} creates +% \meta{counter} if it does not already exist, and if the boolean +% \textit{@counterz@random} is true then \meta{counter} is assigned a +% random integer value between \meta{min} and \meta{max} and then saved. % % \begin{macrocode} -\newcommand{\ifrandomizectr}[2]{% - \ifbool{@counterz@random}{#1}{#2} +\newcommand{\randprovidecounter}[3]{% + \ifltxcounter{#1}{% + \@ifnextchar]{% + \m@k@gobbleendoptarg + }{% + }% + }{% + \newcounter{#1} + \ifrandomizectr{% + \randsetcounter{#1}{#2}{#3} + \savecounter{#1} + }{% + }% + }% }% % \end{macrocode} % %\end{macro} % %\begin{macro}{\xrandprovidecounter} +%\changes{v1.1.0}{2023/05/30}{no longer randomizes if already defined; +%now based on a new \cs{randprovidecounter}} % % \cs{xrandprovidecounter}\marg{counter}\marg{min}\marg{max} creates % \meta{counter} if it does not already exist, and if the boolean @@ -1370,18 +1777,45 @@ and the derived files counterz.ins, % % \begin{macrocode} \newcommand{\xrandprovidecounter}[3]{% - \xprovidecounter{#1} - \ifrandomizectr{% - \xrandsetcounter{#1}{#2}{#3} - \xsavecounter{#1} - }{% - } -} + \randprovidecounter{\@counterz@counterprefix#1}{#2}{#3} +}% % \end{macrocode} % %\end{macro} % +%\begin{macro}{\randprovidecounternz} +%\changes{v1.1.0}{2023/05/30}{new} +% +% \cs{randprovidecounternz}\marg{counter}\marg{min}\marg{max} does the same +% job as the command \cs{xrandprovidecounter} except that the value of +% \meta{counter} is randomized until it is nonzero. +% +% \begin{macrocode} +\newcommand{\randprovidecounternz}[3]{% + \ifltxcounter{#1}{% + \@ifnextchar]{% + \m@k@gobbleendoptarg + }{% + }% + }{% + \newcounter{#1} + \ifrandomizectr{% + \setcounter{#1}{0} + \whileboolexpr{test {\ifnumequal{\value{#1}}{0}}}{% + \randsetcounter{#1}{#2}{#3} + }% + \savecounter{#1} + }{% + }% + }% +}% +% \end{macrocode} +% +%\end{macro}% +% %\begin{macro}{\xrandprovidecounternz} +%\changes{v1.1.0}{2023/05/30}{no longer randomizes if already defined; +%now based on a new \cs{randprovidecounternz}} % % \cs{xrandprovidecounternz}\marg{counter}\marg{min}\marg{max} does the same % job as the command \cs{xrandprovidecounter} except that the value of @@ -1389,16 +1823,8 @@ and the derived files counterz.ins, % % \begin{macrocode} \newcommand{\xrandprovidecounternz}[3]{% - \xprovidecounter{#1} - \ifrandomizectr{% - \xsetcounter{#1}{0} - \whileboolexpr{ test {\ifnumequal{\xvalue{#1}}{0}}}{% - \xrandsetcounter{#1}{#2}{#3} - } - \xsavecounter{#1} - }{% - } -} + \randprovidecounternz{\@counterz@counterprefix#1}{#2}{#3} +}% % \end{macrocode} % %\end{macro}% diff --git a/macros/latex/contrib/counterz/counterz.ins b/macros/latex/contrib/counterz/counterz.ins index 7de3d88187..73c612db3d 100644 --- a/macros/latex/contrib/counterz/counterz.ins +++ b/macros/latex/contrib/counterz/counterz.ins @@ -6,7 +6,7 @@ %% %% counterz.dtx (with options: `install') %% ---------------------------------------------------------------------- -%% `counterz' --- 2023/05/19 Version 1.0.0 Additional tools for counters +%% `counterz' --- 2023/05/30 Version 1.1.0 Additional tools for counters %% E-mail: christopher.mcclain@mail.wvu.edu %% Released under the LaTeX Project Public License v1.3c or later %% See https://www.latex-project.org/lppl.txt @@ -22,7 +22,7 @@ \preamble ---------------------------------------------------------------------- -`counterz' --- 2023/05/19 Version 1.0.0 Additional tools for counters +`counterz' --- 2023/05/30 Version 1.1.0 Additional tools for counters E-mail: christopher.mcclain@mail.wvu.edu Released under the LaTeX Project Public License v1.3c or later See https://www.latex-project.org/lppl.txt diff --git a/macros/latex/contrib/counterz/counterz.pdf b/macros/latex/contrib/counterz/counterz.pdf Binary files differindex 4b358cbb48..66820f93e8 100644 --- a/macros/latex/contrib/counterz/counterz.pdf +++ b/macros/latex/contrib/counterz/counterz.pdf diff --git a/macros/luatex/latex/luacas/doc/appendix/luacas.dat b/macros/luatex/latex/luacas/doc/appendix/luacas.dat index 7f2efd0a10..c0011e71b2 100644 --- a/macros/luatex/latex/luacas/doc/appendix/luacas.dat +++ b/macros/luatex/latex/luacas/doc/appendix/luacas.dat @@ -1,4 +1,4 @@ -% Copyright (c) ??? +% Copyright (c) % % Permission is granted to copy, distribute and/or modify this % software under the terms of the LaTeX Project Public License @@ -11,7 +11,7 @@ \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luacas} - [2022/11/15 v1.0.1 CAS written in Lua for LaTeX] + [2023/05/26 v1.0.2 CAS written in Lua for LaTeX] \RequirePackage{iftex} \ifluatex diff --git a/macros/luatex/latex/luacas/doc/appendix/versionhistory.tex b/macros/luatex/latex/luacas/doc/appendix/versionhistory.tex index 26754b3c7d..ad7e4f53fe 100644 --- a/macros/luatex/latex/luacas/doc/appendix/versionhistory.tex +++ b/macros/luatex/latex/luacas/doc/appendix/versionhistory.tex @@ -7,6 +7,15 @@ \section{Version History} +\subsection*{v1.0.2} + +\begin{itemize} + \item Fix Polynomial Rings displaying redundant ones in \LaTeX{} + \item Fix rational forced non-simplification not working + \item Added ability to load LuaCAS modules as local variables + \item Fix arithmetic with decimal expressions +\end{itemize} + \subsection*{v1.0.1} \begin{itemize} diff --git a/macros/luatex/latex/luacas/doc/luacas.pdf b/macros/luatex/latex/luacas/doc/luacas.pdf Binary files differindex 667791efb6..13f358c5e8 100644 --- a/macros/luatex/latex/luacas/doc/luacas.pdf +++ b/macros/luatex/latex/luacas/doc/luacas.pdf diff --git a/macros/luatex/latex/luacas/doc/luacas.tex b/macros/luatex/latex/luacas/doc/luacas.tex index 3d0c51a343..7c49b9eef5 100644 --- a/macros/luatex/latex/luacas/doc/luacas.tex +++ b/macros/luatex/latex/luacas/doc/luacas.tex @@ -160,7 +160,7 @@ style=numeric, \begin{document} \title{The {\ttfamily luacas} package} \author{Evan Cochrane\thanks{\href{mailto:cochraef@rose-hulman.edu}{\ttfamily cochraef@rose-hulman.edu}} , Timothy All\thanks{\href{mailto:timothy.all@rose-hulman.edu}{\ttfamily timothy.all@rose-hulman.edu}}} -\date{v1.0.1 \\ \today} +\date{v1.0.2\\ \today} \maketitle diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-absexpression.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-absexpression.lua index 2bf0b6a7ef..264730cbbe 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-absexpression.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-absexpression.lua @@ -2,7 +2,7 @@ --- The absolute value of an expression. --- @field expression Expression AbsExpression = {} -__AbsExpression = {} +local __AbsExpression = {} ---------------------------- -- Instance functionality -- diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-algebra_init.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-algebra_init.lua deleted file mode 100644 index ac37c56a5b..0000000000 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-algebra_init.lua +++ /dev/null @@ -1,24 +0,0 @@ --- Loads algebra files in the correct order. -require("_lib.luacas-table") - -require("core.luacas-core_init") - -require("algebra.luacas-ring") -require("algebra.luacas-euclideandomain") -require("algebra.luacas-field") -require("algebra.luacas-polynomialring") -require("algebra.luacas-integer") -require("algebra.luacas-rational") -require("algebra.luacas-integerquotientring") -require("algebra.luacas-sqrtexpression") - -require("algebra.luacas-absexpression") -require("algebra.luacas-equation") -require("algebra.luacas-factorialexpression") -require("algebra.luacas-logarithm") -require("algebra.luacas-rootexpression") -require("algebra.luacas-trigexpression") - -require("algebra.polynomialring.luacas-berlekampfactoring") -require("algebra.polynomialring.luacas-zassenhausfactoring") -require("algebra.polynomialring.luacas-decomposition")
\ No newline at end of file diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-equation.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-equation.lua index 4e81d5ca95..18d6a9c091 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-equation.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-equation.lua @@ -3,7 +3,7 @@ --- @field lhs Expression --- @field rhs Expression Equation = {} -__Equation = {} +local __Equation = {} -------------------------- -- Static functionality -- diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-euclideandomain.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-euclideandomain.lua index fab2c5c7c2..fec52d5d81 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-euclideandomain.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-euclideandomain.lua @@ -1,7 +1,7 @@ --- @class EuclideanDomain --- Interface for an element of a euclidean domain. EuclideanDomain = {} -__EuclideanDomain = {} +local __EuclideanDomain = {} ---------------------- -- Required methods -- diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-factorialexpression.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-factorialexpression.lua index 1c5b824ff7..5920d22733 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-factorialexpression.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-factorialexpression.lua @@ -2,7 +2,7 @@ --- The factorial of an expression. --- @field expression Expression FactorialExpression = {} -__FactorialExpression = {} +local __FactorialExpression = {} ---------------------------- -- Instance functionality -- diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-field.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-field.lua index c845e94c55..0846a90ec9 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-field.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-field.lua @@ -1,7 +1,7 @@ --- @class Field --- Interface for an element of a field. Field = {} -__Field = {} +local __Field = {} ---------------------- -- Required methods -- diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-integer.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-integer.lua index a3c54f498c..6a81be295b 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-integer.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-integer.lua @@ -3,7 +3,7 @@ --- @field self table<number, number> --- @field sign number Integer = {} -__Integer = {} +local __Integer = {} -------------------------- -- Static functionality -- @@ -90,6 +90,9 @@ end --- @return Integer function Integer.ceillog(a, base) base = base or Integer(10) + if a == Integer.one() then + return Integer.one() + end local k = Integer.zero() while (base ^ k) < a do @@ -138,9 +141,9 @@ end ---------------------------- -- So we don't have to copy the Euclidean operations each time we create an integer. -local __o = Copy(__EuclideanOperations) -__o.__index = Integer -__o.__tostring = function(a) -- Only works if the digit size is a power of 10 +__IntegerOperations = Copy(__EuclideanOperations) +__IntegerOperations.__index = Integer +__IntegerOperations.__tostring = function(a) -- Only works if the digit size is a power of 10 local out = "" for i, digit in ipairs(a) do local pre = tostring(math.floor(digit)) @@ -156,7 +159,7 @@ __o.__tostring = function(a) -- Only works if the digit size is a power of 10 end return out end -__o.__div = function(a, b) -- Constructor for a rational number disguised as division +__IntegerOperations.__div = function(a, b) -- Constructor for a rational number disguised as division if not b.getring then return BinaryOperation.DIVEXP({a, b}) end @@ -165,7 +168,7 @@ __o.__div = function(a, b) -- Constructor for a rational number disguised as d end return __FieldOperations.__div(a, b) end -__o.__concat = function(a, b) -- Like a decimal, but fancier. Used mainly for the parser with decimal numbers. +__IntegerOperations.__concat = function(a, b) -- Like a decimal, but fancier. Used mainly for the parser with decimal numbers. return a + b / (Integer(10) ^ Integer.ceillog(b)) end @@ -174,7 +177,7 @@ end --- @return Integer function Integer:new(n) local o = {} - o = setmetatable(o, __o) + o = setmetatable(o, __IntegerOperations) if not n then o[1] = 0 diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-integerquotientring.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-integerquotientring.lua index 4c2188c9e5..6fe0ce1cdf 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-integerquotientring.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-integerquotientring.lua @@ -4,7 +4,7 @@ --- @field modulus Integer IntegerModN = {} -__IntegerModN = {} +local __IntegerModN = {} -- Metatable for ring objects. local __obj = {__index = IntegerModN, __eq = function(a, b) @@ -37,11 +37,10 @@ end ---------------------------- -- So we don't have to copy the field operations each time -local __o -__o = Copy(__FieldOperations) +__IntegerModNOperations = Copy(__FieldOperations) -__o.__index = IntegerModN -__o.__tostring = function(a) +__IntegerModNOperations.__index = IntegerModN +__IntegerModNOperations.__tostring = function(a) return tostring(a.element) end @@ -56,7 +55,7 @@ function IntegerModN:new(i, n) error("Argument error: modulus must be an integer greater than 0.") end - o = setmetatable(o, __o) + o = setmetatable(o, __IntegerModNOperations) if i < Integer.zero() or i >= n then i = i % n diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua index 07a9db84d1..135a74f326 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-logarithm.lua @@ -4,7 +4,7 @@ --- @field base Expression --- @field expression Expression Logarithm = {} -__Logarithm = {} +local __Logarithm = {} ---------------------------- -- Instance functionality -- diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-polynomialring.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-polynomialring.lua index 568c21c921..2a76bda265 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-polynomialring.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-polynomialring.lua @@ -4,7 +4,7 @@ --- @field symbol SymbolExpression --- @field ring RingIdentifier PolynomialRing = {} -__PolynomialRing = {} +local __PolynomialRing = {} -- Metatable for ring objects. local __obj = {__index = PolynomialRing, __eq = function(a, b) @@ -214,9 +214,9 @@ end ---------------------------- -- So we don't have to copy the Euclidean operations each time -local __o = Copy(__EuclideanOperations) -__o.__index = PolynomialRing -__o.__tostring = function(a) +__PolynomialOperations = Copy(__EuclideanOperations) +__PolynomialOperations.__index = PolynomialRing +__PolynomialOperations.__tostring = function(a) local out = "" local loc = a.degree:asnumber() while loc >= 0 do @@ -229,7 +229,7 @@ __o.__tostring = function(a) end return string.sub(out, 1, string.len(out) - 1) end -__o.__div = function(a, b) +__PolynomialOperations.__div = function(a, b) if not b.getring then return BinaryOperation.DIVEXP({a, b}) end @@ -258,13 +258,20 @@ function PolynomialRing:tolatex() end if self.ring == Rational.getring() or self.ring == Integer.getring() or self.ring == IntegerModN.getring() then if self.coefficients[loc] ~= Integer.one() then - out = out .. self.coefficients[loc]:tolatex() .. self.symbol + if self.coefficients[loc] == Integer(-1) then + out = out .. "-" .. self.symbol + elseif self.coefficients[loc] == Integer.zero() then + goto lead + else + out = out .. self.coefficients[loc]:tolatex() .. self.symbol + end else out = out .. self.symbol end if loc ~=1 then out = out .. "^{" .. loc .. "}" end + ::lead:: loc = loc -1 while loc >=0 do local coeff = self.coefficients[loc] @@ -330,7 +337,7 @@ end -- Creates a new polynomial ring given an array of coefficients and a symbol function PolynomialRing:new(coefficients, symbol, degree) local o = {} - o = setmetatable(o, __o) + o = setmetatable(o, __PolynomialOperations) if type(coefficients) ~= "table" then error("Sent parameter of wrong type: Coefficients must be in an array") diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-rational.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-rational.lua index 811909a948..0802eb5172 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-rational.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-rational.lua @@ -4,7 +4,7 @@ --- @field denominator Ring --- @field ring RingIdentifier Rational = {} -__Rational = {} +local __Rational = {} -------------------------- @@ -54,9 +54,9 @@ end ---------------------------- -- So we don't have to copy the field operations each time. -local __o = Copy(__FieldOperations) -__o.__index = Rational -__o.__tostring = function(a) +__RationalOperations = Copy(__FieldOperations) +__RationalOperations.__index = Rational +__RationalOperations.__tostring = function(a) if a.ring.symbol then return "(" .. tostring(a.numerator)..")/("..tostring(a.denominator) .. ")" end @@ -70,7 +70,7 @@ end --- @param keep boolean function Rational:new(n, d, keep) local o = {} - o = setmetatable(o, __o) + o = setmetatable(o, __RationalOperations) if n:getring() == PolynomialRing.getring() then o.symbol = n.symbol @@ -88,7 +88,15 @@ function Rational:new(n, d, keep) d = d or Integer.one() o.numerator = n o.denominator = d - o:reduce() + if not keep then + o:reduce() + end + + if o.numerator:getring() == Integer.getring() then + o.ring = Integer.getring() + elseif o.numerator:getring() == PolynomialRing.getring() then + o.ring = Ring.resultantring(o.numerator:getring(), o.denominator:getring()) + end if (not keep) and o.denominator == Integer.one() or (not keep) and o.numerator == Integer.zero() then return o.numerator @@ -107,7 +115,6 @@ function Rational:reduce() local gcd = Integer.gcd(self.numerator, self.denominator) self.numerator = self.numerator//gcd self.denominator = self.denominator//gcd - self.ring = Integer.getring() elseif self.numerator:getring() == PolynomialRing.getring() then local lc = self.denominator:lc() self.denominator = self.denominator/lc @@ -115,7 +122,6 @@ function Rational:reduce() local gcd = PolynomialRing.gcd(self.numerator, self.denominator) self.numerator = self.numerator//gcd self.denominator = self.denominator//gcd - self.ring = Ring.resultantring(self.numerator:getring(), self.denominator:getring()) end end diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-ring.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-ring.lua index 4903b9c912..81df15d18c 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-ring.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-ring.lua @@ -1,7 +1,7 @@ --- @class Ring --- Interface for an element of a ring with unity. Ring = {} -__Ring = {} +local __Ring = {} -------------------------- -- Static functionality -- diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-rootexpression.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-rootexpression.lua index a66182579a..6f9a492591 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-rootexpression.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-rootexpression.lua @@ -2,7 +2,7 @@ --- An expression that represents the solutions to expression = 0. --- @field expression Expression RootExpression = {} -__RootExpression = {} +local __RootExpression = {} ---------------------------- -- Instance functionality -- diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-sqrtexpression.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-sqrtexpression.lua index bc96f4e1d8..641198fd65 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-sqrtexpression.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-sqrtexpression.lua @@ -2,7 +2,7 @@ --- An expression that represents the positive real solution to x^n = a where n is a positive integer and a is constant. --- @field expression Expression SqrtExpression = {} -__SqrtExpression = {} +local __SqrtExpression = {} ---------------------------- -- Instance functionality -- diff --git a/macros/luatex/latex/luacas/tex/algebra/luacas-trigexpression.lua b/macros/luatex/latex/luacas/tex/algebra/luacas-trigexpression.lua index 307bfe1737..6f09e39f57 100644 --- a/macros/luatex/latex/luacas/tex/algebra/luacas-trigexpression.lua +++ b/macros/luatex/latex/luacas/tex/algebra/luacas-trigexpression.lua @@ -3,7 +3,7 @@ --- @field name string --- @field expression Expression TrigExpression = {} -__TrigExpression = {} +local __TrigExpression = {} ---------------------------- -- Instance functionality -- diff --git a/macros/luatex/latex/luacas/tex/calculus/luacas-calculus_init.lua b/macros/luatex/latex/luacas/tex/calculus/luacas-calculus_init.lua deleted file mode 100644 index 89b38b650e..0000000000 --- a/macros/luatex/latex/luacas/tex/calculus/luacas-calculus_init.lua +++ /dev/null @@ -1,6 +0,0 @@ --- Loads calculus files in the correct order. -require("algebra.luacas-algebra_init") - -require("calculus.luacas-derivativeexpression") -require("calculus.luacas-integralexpression") -require("calculus.luacas-diffexpression") diff --git a/macros/luatex/latex/luacas/tex/calculus/luacas-derivativeexpression.lua b/macros/luatex/latex/luacas/tex/calculus/luacas-derivativeexpression.lua index b757759c4c..2ef4c43125 100644 --- a/macros/luatex/latex/luacas/tex/calculus/luacas-derivativeexpression.lua +++ b/macros/luatex/latex/luacas/tex/calculus/luacas-derivativeexpression.lua @@ -3,7 +3,7 @@ --- @field symbol SymbolExpression --- @field expression Expression DerivativeExpression = {} -__DerivativeExpression = {} +local __DerivativeExpression = {} ---------------------------- -- Instance functionality -- diff --git a/macros/luatex/latex/luacas/tex/calculus/luacas-diffexpression.lua b/macros/luatex/latex/luacas/tex/calculus/luacas-diffexpression.lua index df1dc90f33..bf15c56b6f 100644 --- a/macros/luatex/latex/luacas/tex/calculus/luacas-diffexpression.lua +++ b/macros/luatex/latex/luacas/tex/calculus/luacas-diffexpression.lua @@ -4,7 +4,7 @@ --- @field expression Expression DiffExpression = {} -__DiffExpression = {} +local __DiffExpression = {} ---------------------------- -- Instance functionality -- diff --git a/macros/luatex/latex/luacas/tex/calculus/luacas-integralexpression.lua b/macros/luatex/latex/luacas/tex/calculus/luacas-integralexpression.lua index 5267127264..bddc179fa4 100644 --- a/macros/luatex/latex/luacas/tex/calculus/luacas-integralexpression.lua +++ b/macros/luatex/latex/luacas/tex/calculus/luacas-integralexpression.lua @@ -10,7 +10,7 @@ --- @field recursive boolean IntegralExpression = {} -__IntegralExpression = {} +local __IntegralExpression = {} -------------------------- diff --git a/macros/luatex/latex/luacas/tex/core/luacas-atomicexpression.lua b/macros/luatex/latex/luacas/tex/core/luacas-atomicexpression.lua index 6cc6e4f58e..4eed1592f4 100644 --- a/macros/luatex/latex/luacas/tex/core/luacas-atomicexpression.lua +++ b/macros/luatex/latex/luacas/tex/core/luacas-atomicexpression.lua @@ -1,7 +1,7 @@ --- @class AtomicExpression --- Interface for an atomic mathematical expression that has no sub-expressions. AtomicExpression = {} -__AtomicExpression = {} +local __AtomicExpression = {} ---------------------- diff --git a/macros/luatex/latex/luacas/tex/core/luacas-binaryoperation.lua b/macros/luatex/latex/luacas/tex/core/luacas-binaryoperation.lua index 708a6c1540..f2998436eb 100644 --- a/macros/luatex/latex/luacas/tex/core/luacas-binaryoperation.lua +++ b/macros/luatex/latex/luacas/tex/core/luacas-binaryoperation.lua @@ -5,7 +5,7 @@ --- @field operation function --- @field expressions table<number, Expression> BinaryOperation = {} -__BinaryOperation = {} +local __BinaryOperation = {} ---------------------------- -- Instance functionality -- @@ -621,6 +621,8 @@ function BinaryOperation:tolatex() for index, expression in ipairs(self.expressions) do if index == 1 then out = out .. expression:tolatex() + elseif expression:isconstant() and expression < Integer.zero() then + out = out .. "\\cdot (" .. expression:tolatex() .. ")" else out = out .. "\\cdot " .. expression:tolatex() end diff --git a/macros/luatex/latex/luacas/tex/core/luacas-compoundexpression.lua b/macros/luatex/latex/luacas/tex/core/luacas-compoundexpression.lua index 792c36cfc2..cae82719ac 100644 --- a/macros/luatex/latex/luacas/tex/core/luacas-compoundexpression.lua +++ b/macros/luatex/latex/luacas/tex/core/luacas-compoundexpression.lua @@ -1,7 +1,7 @@ --- @class CompoundExpression --- Interface for an expression consisting of one or more subexpressions. CompoundExpression = {} -__CompoundExpression = {} +local __CompoundExpression = {} ---------------------- -- Instance methods -- diff --git a/macros/luatex/latex/luacas/tex/core/luacas-constantexpression.lua b/macros/luatex/latex/luacas/tex/core/luacas-constantexpression.lua index 0f91a037a9..6d9f8e96d2 100644 --- a/macros/luatex/latex/luacas/tex/core/luacas-constantexpression.lua +++ b/macros/luatex/latex/luacas/tex/core/luacas-constantexpression.lua @@ -3,7 +3,7 @@ --- Interface for a mathematical expression without any symbols. --- ConstantExpressions are AtomicExpressions by default, but individual classes may overwrite that inheritance. ConstantExpression = {} -__ConstantExpression = {} +local __ConstantExpression = {} ---------------------- -- Instance methods -- diff --git a/macros/luatex/latex/luacas/tex/core/luacas-core_init.lua b/macros/luatex/latex/luacas/tex/core/luacas-core_init.lua deleted file mode 100644 index c9c3b89126..0000000000 --- a/macros/luatex/latex/luacas/tex/core/luacas-core_init.lua +++ /dev/null @@ -1,16 +0,0 @@ --- Loads core files in the correct order. - -require("core.luacas-expression") -require("core.luacas-atomicexpression") -require("core.luacas-compoundexpression") -require("core.luacas-constantexpression") -require("core.luacas-symbolexpression") -require("core.luacas-binaryoperation") -require("core.luacas-functionexpression") - - -require("core.binaryoperation.luacas-power") -require("core.binaryoperation.luacas-product") -require("core.binaryoperation.luacas-sum") -require("core.binaryoperation.luacas-quotient") -require("core.binaryoperation.luacas-difference")
\ No newline at end of file diff --git a/macros/luatex/latex/luacas/tex/core/luacas-functionexpression.lua b/macros/luatex/latex/luacas/tex/core/luacas-functionexpression.lua index dc94790b81..f6f3a99720 100644 --- a/macros/luatex/latex/luacas/tex/core/luacas-functionexpression.lua +++ b/macros/luatex/latex/luacas/tex/core/luacas-functionexpression.lua @@ -6,7 +6,7 @@ --- @field variables table<number,SymbolExpression> --- @alias Function FunctionExpression FunctionExpression = {} -__FunctionExpression = {} +local __FunctionExpression = {} ---------------------------- -- Instance functionality -- diff --git a/macros/luatex/latex/luacas/tex/core/luacas-symbolexpression.lua b/macros/luatex/latex/luacas/tex/core/luacas-symbolexpression.lua index 0d9192c084..068ab71dcc 100644 --- a/macros/luatex/latex/luacas/tex/core/luacas-symbolexpression.lua +++ b/macros/luatex/latex/luacas/tex/core/luacas-symbolexpression.lua @@ -3,7 +3,7 @@ --- @field symbol string --- @alias Symbol SymbolExpression SymbolExpression = {} -__SymbolExpression = {} +local __SymbolExpression = {} ---------------------- -- Instance methods -- diff --git a/macros/luatex/latex/luacas/tex/luacas.sty b/macros/luatex/latex/luacas/tex/luacas.sty index d46ee847fc..939bd9d392 100644 --- a/macros/luatex/latex/luacas/tex/luacas.sty +++ b/macros/luatex/latex/luacas/tex/luacas.sty @@ -9,7 +9,7 @@ \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luacas} - [2022/11/15 v1.0.1 CAS written in Lua for LaTeX] + [2023/05/26 v1.0.2 CAS written in Lua for LaTeX] \RequirePackage{iftex} \ifluatex diff --git a/macros/luatex/latex/luacas/tex/luacas_init.lua b/macros/luatex/latex/luacas/tex/luacas_init.lua new file mode 100644 index 0000000000..8d58e259e7 --- /dev/null +++ b/macros/luatex/latex/luacas/tex/luacas_init.lua @@ -0,0 +1,304 @@ +--- @class LuaCAS +--- @class Module +--- A table that initalizes and stores luacas modules. +--- @field moduleinfo table<string, table> +--- @field core Module +--- @field algebra Module +--- @field calculus Module +local luacas = {} + + +-- a table containing a list of module names, descriptions, class names, variables, required files, and dependent modules +luacas.moduleinfo = { + ["core"]= + { + "Classes needed for creating basic expressions such as symbols, binary operations, and functions.", + { + "Expression", + "AtomicExpression", + "CompoundExpression", + "ConstantExpression", + "SymbolExpression", + "BinaryOperation", + "FunctionExpression", + + "__ExpressionOperations" + }, + {"E", "I", "PI"}, + { + "_lib.luacas-table", + "core.luacas-expression", + "core.luacas-atomicexpression", + "core.luacas-compoundexpression", + "core.luacas-constantexpression", + "core.luacas-symbolexpression", + "core.luacas-binaryoperation", + "core.luacas-functionexpression", + "core.binaryoperation.luacas-power", + "core.binaryoperation.luacas-product", + "core.binaryoperation.luacas-sum", + "core.binaryoperation.luacas-quotient", + "core.binaryoperation.luacas-difference", + }, + {} + }, + + ["algebra"]= + { + "Classes for elements of rings (integers, rationals, polynomials, etc., and operations on those elements (polynomial factoring). Also includes some elementary functions like logs and trig functions.", + { + "Ring", + "EuclideanDomain", + "Field", + "PolynomialRing", + "Integer", + "Rational", + "IntegerModN", + "SqrtExpression", + "AbsExpression", + "Equation", + "FactorialExpression", + "Logarithm", + "RootExpression", + "TrigExpression", + + "__RingOperations", -- Not classes, but we need to set the environment for this + "__EuclideanOperations", + "__FieldOperations", + "__PolynomialOperations", + "__IntegerOperations", + "__IntegerModNOperations", + "__RationalOperations" + }, + {"ABS", "ARCCOS", "ARCCOT", "ARCCSC", "ARCSEC", "ARCSIN", "ARCTAN", "COS", "COT", "CSC", "FACT", "LN", "LOG", "SEC", "SIN", "TAN"}, + { + "algebra.luacas-ring", + "algebra.luacas-euclideandomain", + "algebra.luacas-field", + "algebra.luacas-polynomialring", + "algebra.luacas-integer", + "algebra.luacas-rational", + "algebra.luacas-integerquotientring", + "algebra.luacas-sqrtexpression", + "algebra.luacas-absexpression", + "algebra.luacas-equation", + "algebra.luacas-factorialexpression", + "algebra.luacas-logarithm", + "algebra.luacas-rootexpression", + "algebra.luacas-trigexpression", + "algebra.polynomialring.luacas-berlekampfactoring", + "algebra.polynomialring.luacas-zassenhausfactoring", + "algebra.polynomialring.luacas-decomposition" + }, + {"core"} + }, + + ["calculus"]= + { + "Classes for symbolic differentation and integration.", + { + "DerivativeExpression", + "IntegralExpression", + "DiffExpression" + }, + {"DD", "INT"}, + { + "calculus.luacas-derivativeexpression", + "calculus.luacas-integralexpression", + "calculus.luacas-diffexpression" + }, + {"algebra"} + } +} + +--- Retrieves a short description for a module. +--- @param mod string +--- @return string +function luacas:moduledesc(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][1] + end + return "Module not found." +end + +--- Retrieves a list of class names included in a module. +--- @param mod string +--- @return table<number,string> +function luacas:moduleclasses(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][2] + end + return {} +end + +--- Retrieves a list of fields included in a module. +--- @param mod string +--- @return table<number,string> +function luacas:modulefields(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][3] + end + return {} +end + +--- Retrieves a list of files included in a module. +--- @param mod string +--- @return table<number,string> +function luacas:modulefiles(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][4] + end + return {} +end + +--- Retrieves a list of a module's immediate dependencies. +--- @param mod string +--- @return table<number,string> +function luacas:moduledependencies(mod) + if self.moduleinfo[mod] then + return self.moduleinfo[mod][5] + end + return {} +end + +--- Initalizes a LuaCAS module and returns it. +--- Since this creates temporary global variables, it should be treated as atomic to avoid collisions. +--- @param mod string +--- @return Module|nil +function luacas:initmodule(mod) + if not self.moduleinfo[mod] then + return nil + end + + -- TODO: This is a special case, since the algebra module is dependent on the core module and vice versa, + -- but our initialization scheme does not allow for circular dependencies. + if mod == "core" then + self:initmodule("algebra") + end + + -- Since other modules require globals from their dependencies, we only remove global variables after all modules have been initalized + local G = self:saveglobalstate() + self:_initmodulerec(mod) + self:restoreglobalstate(G) + + return self[mod] +end + +--- Recursive part of initalizing modules. +--- @param mod string +function luacas:_initmodulerec(mod) + + for _, dep in ipairs(self:moduledependencies(mod)) do + self:_initmodulerec(dep) + end + + if self[mod] then + -- Since globals from a dependency might be needed to initialize a module, we create globals even if a module has already been initalized. + for class, _ in self[mod] do + _G[class] = self[mod][class] + end + else + -- Creates globals needed for modules dependent on this one. + self:initglobalmodule(mod) + + -- Adds classes to module table. + self[mod] = {} + for _, class in ipairs(self:moduleclasses(mod)) do + self[mod][class] = _G[class] + end + + -- Sets the environment for each method of each class to include all dependent classes of this class. + self:setmoduleenvironment(mod) + -- TODO: This is a special case, since the algebra module is dependent on the core module and vice versa, + -- but our initialization scheme does not allow for circular dependencies. + if mod == "algebra" then + self:setmoduleenvironment("core") + end + + -- Adds fields to module tables + for _, class in ipairs(self:modulefields(mod)) do + self[mod][class] = _G[class] + end + end +end + +--- Sets the environment for each method of each class in a module to the current global environment, so it can keep using global variables when reset. +--- @param mod string +function luacas:setmoduleenvironment(mod) + local realenv = self:saveglobalstate() + for _, class in pairs(self[mod]) do + -- print(_) + for _, func in pairs(class) do + -- print(" ", _) + if type(func) == "function" then + --- Functions only have an _ENV upvalue if they have a global variable, so this should work + local _envloc = debug.findupvalue(func, "_ENV") + if _envloc then + debug.upvaluejoin(func, debug.findupvalue(func, "_ENV"), function () return realenv end, 1) + end + end + end + + -- local meta = getmetatable(class) + -- if meta then + -- for _, func in pairs(meta) do + -- print(" ", _) + -- if type(func) == "function" then + -- --- Functions only have an _ENV upvalue if they have a global variable, so this should work + -- local _envloc = debug.findupvalue(func, "_ENV") + -- if _envloc then + -- debug.upvaluejoin(func, debug.findupvalue(func, "_ENV"), function () return realenv end, 1) + -- end + -- end + -- end + -- end + end +end + +--- Saves the current global state as a table and returns it. +--- @return table +function luacas:saveglobalstate() + local G = {} + for key, value in pairs(_G) do + G[key] = value + end + return G +end + +--- Saves the current global state as a table and returns it. +--- @param G table +function luacas:restoreglobalstate(G) + for key, _ in pairs(_G) do + if key ~= "_G" and key ~= "pairs" then + _G[key] = nil + end + end + for key, value in pairs(G) do + _G[key] = value + end +end + +--- Initalizes a LuaCAS module and creates global variables for all classes in that module. +--- DOES NOT HANDLE DEPENDENCIES, so dependent modules must be globally initalized first. +--- @param mod string +--- @return Module|nil +function luacas:initglobalmodule(mod) + for _, filename in ipairs(self:modulefiles(mod)) do + require(filename) + end +end + +--- Why is Lua like this? +function debug.findupvalue(fn, search_name) + local i = 1 + while true do + local name, val = debug.getupvalue(fn, i) + if not name then break end + if name == search_name then + return i, val + end + i = i + 1 + end +end + +return luacas
\ No newline at end of file diff --git a/macros/luatex/latex/luacas/tex/test/luacas-main.lua b/macros/luatex/latex/luacas/tex/test/luacas-main.lua index 95f77885e4..78b25aa587 100644 --- a/macros/luatex/latex/luacas/tex/test/luacas-main.lua +++ b/macros/luatex/latex/luacas/tex/test/luacas-main.lua @@ -1,8 +1,23 @@ ---@diagnostic disable: lowercase-global -- Runs test code from test files. -require("calculus.luacas-calculus_init") require("_lib.luacas-pepperfish") +require("_lib.luacas-table") + +local luacas = require("luacas_init") +luacas:initmodule("calculus") + +for name, class in pairs(luacas.core) do + _G[name] = class +end + +for name, class in pairs(luacas.algebra) do + _G[name] = class +end + +for name, class in pairs(luacas.calculus) do + _G[name] = class +end -- Stuff required for the basic parser. local constants = {e="E", pi = "PI", ln = "LN", log = "LOG", Integer = "Integer", DD = "DD", int = "INT", abs = "ABS", fact="FACT"} diff --git a/macros/luatex/latex/luacas/tex/test/luacas-parser.lua b/macros/luatex/latex/luacas/tex/test/luacas-parser.lua index 3380549620..47f0640c3f 100644 --- a/macros/luatex/latex/luacas/tex/test/luacas-parser.lua +++ b/macros/luatex/latex/luacas/tex/test/luacas-parser.lua @@ -1,8 +1,10 @@ -- Rudimentary parser for making the CAS easier to use. Essentially just wraps SymbolExpression() around symbols and Integer() around integers. - -require("calculus.luacas-calculus_init") +local luacas = require("luacas_init") +luacas:initglobalmodule("core") +luacas:initglobalmodule("algebra") +luacas:initglobalmodule("calculus") -- Splits a string on a seperator. function split(str, sep) @@ -284,8 +286,16 @@ end --- @param input string function CASparse(input) - -- First, we replace any occurance of a number with an integer or rational version of itself. - local str = string.gsub(input, ".?[0-9]+", function (s) + -- First, we replace any occurrence of a decimal with the appropriate fraction. + + local str = string.gsub(input,"%d+%.%d+", function(s) + local ints = split(s,"%.") + return "("..ints[1]..ints[2].." / 10^" .. tostring(string.len(ints[2])) .. ")" + end) + + -- Next, we replace any occurance of a number with an integer or rational version of itself. + + str = string.gsub(str, ".?[0-9]+", function (s) -- Here, we are part of an identifier, so we don't replace anything if string.match(string.sub(s, 1, 1), "[A-Z]") or string.match(string.sub(s, 1, 1), "[a-z]") or string.match(string.sub(s, 1, 1), "_") then return @@ -301,11 +311,11 @@ function CASparse(input) -------------------------- -- HERE COMES THE JANK. -- -------------------------- - + -- The JANK may not actually be doing anything now. But for the sake of posterity... -- Replaces each instance of a decimal with .., so we can use integer metatables to convert it into a rational properly. str = string.gsub(str, "Integer%('[0-9]+'%)%.Integer%('[0-9]+'%)", function (s) local ints = split(s, "%.") - return ints[1] .. ".." .. ints[2] + return "("..ints[1] .. ".." .. ints[2] .. ")" end) str = string.gsub(str, ".?%.Integer%('[0-9]+'%)", function (s) if string.sub(s, 1, 2) == ".." then @@ -320,4 +330,4 @@ function CASparse(input) else print(err) end -end
\ No newline at end of file +end diff --git a/macros/luatex/latex/luacas/tex/test/rings/luacas-conversion.lua b/macros/luatex/latex/luacas/tex/test/rings/luacas-conversion.lua index b9eb523c7a..6a53dacd17 100644 --- a/macros/luatex/latex/luacas/tex/test/rings/luacas-conversion.lua +++ b/macros/luatex/latex/luacas/tex/test/rings/luacas-conversion.lua @@ -120,9 +120,9 @@ testringconvert(a, fring, "12/1x^0", "QQ[x]") testringconvert(a, gring, "((12x^0)y^0)z^0", "ZZ[x][y][z]") testringconvert(a, hring, "((12/1z^0)y^0)x^0", "QQ[z][y][x]") testringconvert(a, iring, "(12x^0)/(1x^0)", "ZZ(x)") -testringconvert(a, jring, "(12x^0)/(1x^0)", "ZZ(x)") +testringconvert(a, jring, "(12/1x^0)/(1/1x^0)", "QQ(x)") testringconvert(a, kring, "2x^0", "Z/Z5[x]") -testringconvert(a, lring, "((12x^0)/(1x^0))y^0", "ZZ(x)[y]") +testringconvert(a, lring, "((12/1x^0)/(1/1x^0))y^0", "QQ(x)[y]") -- testringconvert(b, aring, "3/2", "ZZ") testringconvert(b, bring, "3/2", "QQ") @@ -133,9 +133,9 @@ testringconvert(b, fring, "3/2x^0", "QQ[x]") -- testringconvert(b, gring, "3/2", "ZZ[x][y][z]") testringconvert(b, hring, "((3/2z^0)y^0)x^0", "QQ[z][y][x]") -- testringconvert(b, iring, "3/2", "ZZ(x)") -testringconvert(b, jring, "(3/2x^0)/(1x^0)", "QQ(x)") +testringconvert(b, jring, "(3/2x^0)/(1/1x^0)", "QQ(x)") -- testringconvert(b, kring, "3/2", "Z/Z5[x]") -testringconvert(b, lring, "((3/2x^0)/(1x^0))y^0", "QQ(x)[y]") +testringconvert(b, lring, "((3/2x^0)/(1/1x^0))y^0", "QQ(x)[y]") testringconvert(c, aring, "4", "ZZ") -- testringconvert(c, bring, "4", "QQ") @@ -172,9 +172,9 @@ testringconvert(e, fring, "3/1x^2+0/1x^1+6/1x^0", "QQ[x]") testringconvert(e, gring, "((3x^2+0x^1+6x^0)y^0)z^0", "ZZ[x][y][z]") testringconvert(e, hring, "((3/1z^0)y^0)x^2+((0/1z^0)y^0)x^1+((6/1z^0)y^0)x^0", "QQ[z][y][x]") testringconvert(e, iring, "(3x^2+0x^1+6x^0)/(1x^0)", "ZZ(x)") -testringconvert(e, jring, "(3x^2+0x^1+6x^0)/(1x^0)", "ZZ(x)") +testringconvert(e, jring, "(3/1x^2+0/1x^1+6/1x^0)/(1/1x^0)", "QQ(x)") testringconvert(e, kring, "3x^2+0x^1+1x^0", "Z/Z5[x]") -testringconvert(e, lring, "((3x^2+0x^1+6x^0)/(1x^0))y^0", "ZZ(x)[y]") +testringconvert(e, lring, "((3/1x^2+0/1x^1+6/1x^0)/(1/1x^0))y^0", "QQ(x)[y]") -- testringconvert(f, aring, "12x^1+4/5x^0", "ZZ") -- testringconvert(f, bring, "12x^1+4/5x^0", "QQ") @@ -185,9 +185,9 @@ testringconvert(f, fring, "12x^1+4/5x^0", "QQ[x]") -- testringconvert(f, gring, "12x^1+4/5x^0", "ZZ[x][y][z]") testringconvert(f, hring, "((12/1z^0)y^0)x^1+((4/5z^0)y^0)x^0", "QQ[z][y][x]") -- testringconvert(f, iring, "12x^1+4/5x^0", "ZZ(x)") -testringconvert(f, jring, "(12x^1+4/5x^0)/(1x^0)", "QQ(x)") +testringconvert(f, jring, "(12x^1+4/5x^0)/(1/1x^0)", "QQ(x)") -- testringconvert(f, kring, "12x^1+4/5x^0", "Z/Z5[x]") -testringconvert(f, lring, "((12x^1+4/5x^0)/(1x^0))y^0", "QQ(x)[y]") +testringconvert(f, lring, "((12x^1+4/5x^0)/(1/1x^0))y^0", "QQ(x)[y]") -- testringconvert(g, aring, "", "ZZ") -- testringconvert(g, bring, "", "QQ") @@ -224,9 +224,9 @@ testringconvert(h, hring, "((1z^0)y^0)x^3+((1z^0)y^3+(1/9z^1+-16z^0)y^2+(1z^2+8z -- testringconvert(i, gring, "", "ZZ[x][y][z]") -- testringconvert(i, hring, "", "QQ[z][y][x]") testringconvert(i, iring, "(1x^1+-2x^0)/(1x^2+3x^1+3x^0)", "ZZ(x)") -testringconvert(i, jring, "(1x^1+-2x^0)/(1x^2+3x^1+3x^0)", "ZZ(x)") +testringconvert(i, jring, "(1/1x^1+-2/1x^0)/(1/1x^2+3/1x^1+3/1x^0)", "QQ(x)") -- testringconvert(i, kring, "", "Z/Z5[x]") -testringconvert(i, lring, "((1x^1+-2x^0)/(1x^2+3x^1+3x^0))y^0", "ZZ(x)[y]") +testringconvert(i, lring, "((1/1x^1+-2/1x^0)/(1/1x^2+3/1x^1+3/1x^0))y^0", "QQ(x)[y]") -- testringconvert(j, aring, "", "ZZ") -- testringconvert(j, bring, "", "QQ") |