summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-11-06 03:01:22 +0000
committerNorbert Preining <norbert@preining.info>2022-11-06 03:01:22 +0000
commit0a7c9b85de9aeaffafa0cf8944fa81ffe9652d09 (patch)
treea8a932b3f786cf3601808c4e352b3b6b07731742
parent5a47812f51f3d10a580db0c74aa20d73f5ed2ae4 (diff)
CTAN sync 202211060301
-rw-r--r--FILES.byname599
-rw-r--r--FILES.byname.gzbin2472696 -> 2473819 bytes
-rw-r--r--FILES.last07days614
-rw-r--r--biblio/ctan-bibdata/ctan.bib23
-rw-r--r--biblio/ctan-bibdata/ctan.pdfbin3622884 -> 3621869 bytes
-rw-r--r--macros/latex/contrib/eolang/eolang.dtx244
-rw-r--r--macros/latex/contrib/eolang/eolang.pdfbin1036825 -> 1106103 bytes
-rw-r--r--macros/luatex/latex/luacas/README.md23
-rw-r--r--macros/luatex/latex/luacas/doc/appendix/latexcode.tex140
-rw-r--r--macros/luatex/latex/luacas/doc/appendix/luacas.dat230
-rw-r--r--macros/luatex/latex/luacas/doc/intro/intro.tex143
-rw-r--r--macros/luatex/latex/luacas/doc/intro/intropart.tex202
-rw-r--r--macros/luatex/latex/luacas/doc/luacas.pdfbin0 -> 758211 bytes
-rw-r--r--macros/luatex/latex/luacas/doc/luacas.tex214
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref.tex236
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra.tex336
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_classes/ref_algebra_classes.tex977
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_methods/ref_algebra_methods.tex1305
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus.tex225
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_classes/ref_calculus_classes.tex277
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_methods/ref_calculus_methods.tex473
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref_core/ref_core.tex249
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_classes/ref_core_classes.tex455
-rw-r--r--macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_methods/ref_core_methods.tex1155
-rw-r--r--macros/luatex/latex/luacas/doc/tutorial/tut.tex217
-rw-r--r--macros/luatex/latex/luacas/doc/tutorial/tut1/tut1.tex238
-rw-r--r--macros/luatex/latex/luacas/doc/tutorial/tut2/tut2.tex476
-rw-r--r--macros/luatex/latex/luacas/doc/tutorial/tut3/demotut3.dat46
-rw-r--r--macros/luatex/latex/luacas/doc/tutorial/tut3/tut3.tex575
-rw-r--r--macros/luatex/latex/luacas/tex/_lib/inspect.lua335
-rw-r--r--macros/luatex/latex/luacas/tex/_lib/pepperfish.lua629
-rw-r--r--macros/luatex/latex/luacas/tex/_lib/table.lua185
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/_init.lua24
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/absexpression.lua80
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/equation.lua183
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/euclideandomain.lua63
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/factorialexpression.lua102
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/field.lua69
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/integer.lua952
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/integerquotientring.lua197
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/logarithm.lua186
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/polynomialring.lua860
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/polynomialring/berlekampfactoring.lua187
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/polynomialring/decomposition.lua93
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/polynomialring/zassenhausfactoring.lua220
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/rational.lua241
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/ring.lua326
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/rootexpression.lua135
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/sqrtexpression.lua196
-rw-r--r--macros/luatex/latex/luacas/tex/algebra/trigexpression.lua355
-rw-r--r--macros/luatex/latex/luacas/tex/calculus/_init.lua6
-rw-r--r--macros/luatex/latex/luacas/tex/calculus/derivativeexpression.lua265
-rw-r--r--macros/luatex/latex/luacas/tex/calculus/diffexpression.lua190
-rw-r--r--macros/luatex/latex/luacas/tex/calculus/integralexpression.lua934
-rw-r--r--macros/luatex/latex/luacas/tex/core/_init.lua16
-rw-r--r--macros/luatex/latex/luacas/tex/core/atomicexpression.lua70
-rw-r--r--macros/luatex/latex/luacas/tex/core/binaryoperation.lua800
-rw-r--r--macros/luatex/latex/luacas/tex/core/binaryoperation/difference.lua14
-rw-r--r--macros/luatex/latex/luacas/tex/core/binaryoperation/power.lua169
-rw-r--r--macros/luatex/latex/luacas/tex/core/binaryoperation/product.lua231
-rw-r--r--macros/luatex/latex/luacas/tex/core/binaryoperation/quotient.lua14
-rw-r--r--macros/luatex/latex/luacas/tex/core/binaryoperation/sum.lua226
-rw-r--r--macros/luatex/latex/luacas/tex/core/compoundexpression.lua52
-rw-r--r--macros/luatex/latex/luacas/tex/core/constantexpression.lua62
-rw-r--r--macros/luatex/latex/luacas/tex/core/expression.lua280
-rw-r--r--macros/luatex/latex/luacas/tex/core/functionexpression.lua295
-rw-r--r--macros/luatex/latex/luacas/tex/core/symbolexpression.lua132
-rw-r--r--macros/luatex/latex/luacas/tex/luacas.sty312
-rw-r--r--macros/luatex/latex/luacas/tex/test/calculus/derivatives.lua30
-rw-r--r--macros/luatex/latex/luacas/tex/test/calculus/integrals.lua54
-rw-r--r--macros/luatex/latex/luacas/tex/test/expressions/autosimplify.lua199
-rw-r--r--macros/luatex/latex/luacas/tex/test/expressions/collect.lua26
-rw-r--r--macros/luatex/latex/luacas/tex/test/expressions/equations.lua17
-rw-r--r--macros/luatex/latex/luacas/tex/test/expressions/functions.lua17
-rw-r--r--macros/luatex/latex/luacas/tex/test/expressions/logarithms.lua26
-rw-r--r--macros/luatex/latex/luacas/tex/test/expressions/rationalexponent.lua15
-rw-r--r--macros/luatex/latex/luacas/tex/test/expressions/simplify.lua22
-rw-r--r--macros/luatex/latex/luacas/tex/test/expressions/substitute.lua10
-rw-r--r--macros/luatex/latex/luacas/tex/test/helper.lua322
-rw-r--r--macros/luatex/latex/luacas/tex/test/main.lua154
-rw-r--r--macros/luatex/latex/luacas/tex/test/parser.lua323
-rw-r--r--macros/luatex/latex/luacas/tex/test/polynomials/partialfractions.lua12
-rw-r--r--macros/luatex/latex/luacas/tex/test/polynomials/polynomial.lua153
-rw-r--r--macros/luatex/latex/luacas/tex/test/polynomials/polynomialmod.lua76
-rw-r--r--macros/luatex/latex/luacas/tex/test/polynomials/roots.lua43
-rw-r--r--macros/luatex/latex/luacas/tex/test/rings/conversion.lua273
-rw-r--r--macros/luatex/latex/luacas/tex/test/rings/modulararithmetic.lua20
-rw-r--r--macros/luatex/latex/luacas/tex/test/rings/number.lua118
-rw-r--r--support/TeX4ht/source/ChangeLog5
-rw-r--r--support/TeX4ht/source/mktex4ht-cnf.tex3
-rw-r--r--support/TeX4ht/source/tex4ht-4ht.tex70
-rw-r--r--support/TeX4ht/source/tex4ht-html4.tex23
-rw-r--r--support/bib2gls/CHANGES19
-rw-r--r--support/bib2gls/bib2gls-begin.pdfbin726769 -> 718818 bytes
-rw-r--r--support/bib2gls/bib2gls-en.xml2
-rw-r--r--support/bib2gls/bib2gls.pdfbin5116129 -> 5135194 bytes
-rw-r--r--support/bib2gls/examples/sample-authors.pdfbin51567 -> 51554 bytes
-rw-r--r--support/bib2gls/examples/sample-bacteria.pdfbin54233 -> 54221 bytes
-rw-r--r--support/bib2gls/examples/sample-chemical.pdfbin54014 -> 54006 bytes
-rw-r--r--support/bib2gls/examples/sample-citations.pdfbin49757 -> 49745 bytes
-rw-r--r--support/bib2gls/examples/sample-constants.pdfbin97800 -> 97789 bytes
-rw-r--r--support/bib2gls/examples/sample-hierarchical.pdfbin52517 -> 52505 bytes
-rw-r--r--support/bib2gls/examples/sample-markuplanguages.pdfbin61083 -> 61070 bytes
-rw-r--r--support/bib2gls/examples/sample-markuplanguages.tex2
-rw-r--r--support/bib2gls/examples/sample-maths.pdfbin121233 -> 121219 bytes
-rw-r--r--support/bib2gls/examples/sample-media.pdfbin166497 -> 166484 bytes
-rw-r--r--support/bib2gls/examples/sample-msymbols.pdfbin109224 -> 109211 bytes
-rw-r--r--support/bib2gls/examples/sample-multi1.pdfbin255321 -> 255310 bytes
-rw-r--r--support/bib2gls/examples/sample-multi2.pdfbin330718 -> 330706 bytes
-rw-r--r--support/bib2gls/examples/sample-nested.pdfbin52529 -> 52518 bytes
-rw-r--r--support/bib2gls/examples/sample-people.pdfbin50332 -> 50320 bytes
-rw-r--r--support/bib2gls/examples/sample-textsymbols.pdfbin73055 -> 73042 bytes
-rw-r--r--support/bib2gls/examples/sample-textsymbols2.pdfbin70266 -> 70256 bytes
-rw-r--r--support/bib2gls/examples/sample-units1.pdfbin57356 -> 57344 bytes
-rw-r--r--support/bib2gls/examples/sample-units2.pdfbin59088 -> 59076 bytes
-rw-r--r--support/bib2gls/examples/sample-units3.pdfbin44656 -> 44644 bytes
-rw-r--r--support/bib2gls/examples/sample-usergroups.pdfbin23840 -> 23827 bytes
-rw-r--r--support/bib2gls/src/bib2gls-begin.tex189
-rw-r--r--support/bib2gls/src/bib2gls-terms.bib2
-rw-r--r--support/bib2gls/src/bib2gls.bib204
-rw-r--r--support/bib2gls/src/bib2gls.tex254
-rw-r--r--systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl5
-rw-r--r--systems/texlive/tlnet/tlpkg/texlive.tlpdb228
-rw-r--r--systems/texlive/tlnet/tlpkg/texlive.tlpdb.md52
-rw-r--r--systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha5122
-rw-r--r--systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc14
-rw-r--r--systems/texlive/tlnet/tlpkg/texlive.tlpdb.xzbin2424844 -> 2424508 bytes
-rw-r--r--systems/win32/miktex/tm/packages/files.csv.lzmabin672234 -> 672226 bytes
-rw-r--r--systems/win32/miktex/tm/packages/next/pr.ini8
-rw-r--r--systems/win32/miktex/tm/packages/pr.ini8
-rw-r--r--timestamp2
131 files changed, 21306 insertions, 1004 deletions
diff --git a/FILES.byname b/FILES.byname
index 88ba2242b3..58b0ad33e3 100644
--- a/FILES.byname
+++ b/FILES.byname
@@ -2035,12 +2035,12 @@
2022/09/23 | 1673 | biblio/citation-style-language/README.md
2022/09/23 | 11958 | biblio/citation-style-language/vancouver.csl
2022/09/23 | 591448 | biblio/citation-style-language.zip
-2022/11/04 | 1678 | biblio/ctan-bibdata/biblatex-ctan/ctan.bbx
+2022/11/05 | 1678 | biblio/ctan-bibdata/biblatex-ctan/ctan.bbx
2022/10/08 | 127 | biblio/ctan-bibdata/biblatex-ctan/ctan.cbx
-2022/11/04 | 2744449 | biblio/ctan-bibdata/ctan.bib
-2022/11/04 | 3622971 | biblio/ctan-bibdata/ctan.pdf
+2022/11/05 | 2744451 | biblio/ctan-bibdata/ctan.bib
+2022/11/05 | 3622884 | biblio/ctan-bibdata/ctan.pdf
2022/10/08 | 1415 | biblio/ctan-bibdata/README
-2022/11/04 | 4015632 | biblio/ctan-bibdata.zip
+2022/11/05 | 4015231 | biblio/ctan-bibdata.zip
2022/02/06 | 3110 | biblio/pbibtex/pbibtex-base/cpp.awk
2022/02/06 | 1009 | biblio/pbibtex/pbibtex-base/generate.sh
2022/02/06 | 22422 | biblio/pbibtex/pbibtex-base/jabbrv.bst
@@ -5414,7 +5414,7 @@
1990/10/02 | 5440 | dviware/vutex/vaxvms.hlp
1990/10/02 | 812 | dviware/vutex/vaxvms.ins
1990/10/02 | 127576 | dviware/vutex/vutex.web
-2022/11/04 | 188686 | FILES.last07days
+2022/11/05 | 195579 | FILES.last07days
2021/07/24 | 1139410 | fonts/aboensis/Aboensis.glyphs
2021/07/24 | 18907382 | fonts/aboensis/aboensis.pdf
2021/07/24 | 137860 | fonts/aboensis/Aboensis-Regular.otf
@@ -190038,6 +190038,7 @@
2021/07/17 | 1183440 | install/macros/luatex/latex/emoji.tds.zip
2022/10/03 | 1430525 | install/macros/luatex/latex/letgut.tds.zip
2013/09/26 | 155171 | install/macros/luatex/latex/luabibentry.tds.zip
+2022/11/04 | 799414 | install/macros/luatex/latex/luacas.tds.zip
2012/01/23 | 423265 | install/macros/luatex/latex/luacode.tds.zip
2010/11/19 | 426632 | install/macros/luatex/latex/luainputenc.tds.zip
2022/01/01 | 214658 | install/macros/luatex/latex/lualatex-math.tds.zip
@@ -215615,15 +215616,15 @@
2008/06/01 | 200380 | macros/latex/contrib/envlab/envlab.pdf
1997/07/16 | 3824 | macros/latex/contrib/envlab/readme.v12
2008/06/02 | 372141 | macros/latex/contrib/envlab.zip
-2022/10/30 | 47687 | macros/latex/contrib/eolang/cactus.pdf
-2022/10/30 | 159 | macros/latex/contrib/eolang/DEPENDS.txt
-2022/10/30 | 641 | macros/latex/contrib/eolang/eolang.bib
-2022/10/30 | 27563 | macros/latex/contrib/eolang/eolang.dtx
-2022/10/30 | 2628 | macros/latex/contrib/eolang/eolang.ins
-2022/10/30 | 1036825 | macros/latex/contrib/eolang/eolang.pdf
-2022/10/30 | 1083 | macros/latex/contrib/eolang/LICENSE.txt
-2022/10/30 | 1352 | macros/latex/contrib/eolang/README.md
-2022/10/30 | 1063401 | macros/latex/contrib/eolang.zip
+2022/11/05 | 47687 | macros/latex/contrib/eolang/cactus.pdf
+2022/11/05 | 159 | macros/latex/contrib/eolang/DEPENDS.txt
+2022/11/05 | 641 | macros/latex/contrib/eolang/eolang.bib
+2022/11/05 | 30856 | macros/latex/contrib/eolang/eolang.dtx
+2022/11/05 | 2628 | macros/latex/contrib/eolang/eolang.ins
+2022/11/05 | 1106103 | macros/latex/contrib/eolang/eolang.pdf
+2022/11/05 | 1083 | macros/latex/contrib/eolang/LICENSE.txt
+2022/11/05 | 1352 | macros/latex/contrib/eolang/README.md
+2022/11/05 | 1131113 | macros/latex/contrib/eolang.zip
1991/01/31 | 1291 | macros/latex/contrib/epic/epic.readme
2009/07/09 | 25846 | macros/latex/contrib/epic/epic.sty
1991/01/31 | 2444 | macros/latex/contrib/epic/misc.sty
@@ -239273,6 +239274,88 @@
2019/10/27 | 2147 | macros/luatex/latex/luabidi/tex/luabidi-footnotes.def
2019/10/27 | 2307 | macros/luatex/latex/luabidi/tex/luabidi.sty
2019/10/28 | 97434 | macros/luatex/latex/luabidi.zip
+2022/11/04 | 3149 | macros/luatex/latex/luacas/doc/appendix/latexcode.tex
+2022/11/04 | 5281 | macros/luatex/latex/luacas/doc/appendix/luacas.dat
+2022/11/04 | 3753 | macros/luatex/latex/luacas/doc/intro/intropart.tex
+2022/11/04 | 6610 | macros/luatex/latex/luacas/doc/intro/intro.tex
+2022/11/03 | 758211 | macros/luatex/latex/luacas/doc/luacas.pdf
+2022/11/04 | 4610 | macros/luatex/latex/luacas/doc/luacas.tex
+2022/11/04 | 27612 | macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_classes/ref_algebra_classes.tex
+2022/11/04 | 37803 | macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_methods/ref_algebra_methods.tex
+2022/11/04 | 10876 | macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra.tex
+2022/11/04 | 7351 | macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_classes/ref_calculus_classes.tex
+2022/11/04 | 13931 | macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_methods/ref_calculus_methods.tex
+2022/11/04 | 6105 | macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus.tex
+2022/11/04 | 13963 | macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_classes/ref_core_classes.tex
+2022/11/04 | 32763 | macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_methods/ref_core_methods.tex
+2022/11/04 | 7876 | macros/luatex/latex/luacas/doc/reference/ref_core/ref_core.tex
+2022/11/04 | 8536 | macros/luatex/latex/luacas/doc/reference/ref.tex
+2022/11/04 | 7828 | macros/luatex/latex/luacas/doc/tutorial/tut1/tut1.tex
+2022/11/04 | 18161 | macros/luatex/latex/luacas/doc/tutorial/tut2/tut2.tex
+2022/11/04 | 957 | macros/luatex/latex/luacas/doc/tutorial/tut3/demotut3.dat
+2022/11/04 | 18004 | macros/luatex/latex/luacas/doc/tutorial/tut3/tut3.tex
+2022/11/04 | 6636 | macros/luatex/latex/luacas/doc/tutorial/tut.tex
+2022/11/04 | 1277 | macros/luatex/latex/luacas/README.md
+2022/11/04 | 1951 | macros/luatex/latex/luacas/tex/algebra/absexpression.lua
+2022/11/04 | 5768 | macros/luatex/latex/luacas/tex/algebra/equation.lua
+2022/11/04 | 1770 | macros/luatex/latex/luacas/tex/algebra/euclideandomain.lua
+2022/11/04 | 2732 | macros/luatex/latex/luacas/tex/algebra/factorialexpression.lua
+2022/11/04 | 1567 | macros/luatex/latex/luacas/tex/algebra/field.lua
+2022/11/04 | 690 | macros/luatex/latex/luacas/tex/algebra/_init.lua
+2022/11/04 | 24905 | macros/luatex/latex/luacas/tex/algebra/integer.lua
+2022/11/04 | 4833 | macros/luatex/latex/luacas/tex/algebra/integerquotientring.lua
+2022/11/04 | 5239 | macros/luatex/latex/luacas/tex/algebra/logarithm.lua
+2022/11/04 | 5622 | macros/luatex/latex/luacas/tex/algebra/polynomialring/berlekampfactoring.lua
+2022/11/04 | 3067 | macros/luatex/latex/luacas/tex/algebra/polynomialring/decomposition.lua
+2022/11/04 | 28395 | macros/luatex/latex/luacas/tex/algebra/polynomialring.lua
+2022/11/04 | 7332 | macros/luatex/latex/luacas/tex/algebra/polynomialring/zassenhausfactoring.lua
+2022/11/04 | 7111 | macros/luatex/latex/luacas/tex/algebra/rational.lua
+2022/11/04 | 9634 | macros/luatex/latex/luacas/tex/algebra/ring.lua
+2022/11/04 | 4872 | macros/luatex/latex/luacas/tex/algebra/rootexpression.lua
+2022/11/04 | 7120 | macros/luatex/latex/luacas/tex/algebra/sqrtexpression.lua
+2022/11/04 | 12129 | macros/luatex/latex/luacas/tex/algebra/trigexpression.lua
+2022/11/04 | 9708 | macros/luatex/latex/luacas/tex/calculus/derivativeexpression.lua
+2022/11/04 | 5701 | macros/luatex/latex/luacas/tex/calculus/diffexpression.lua
+2022/11/04 | 187 | macros/luatex/latex/luacas/tex/calculus/_init.lua
+2022/11/04 | 33292 | macros/luatex/latex/luacas/tex/calculus/integralexpression.lua
+2022/11/04 | 1594 | macros/luatex/latex/luacas/tex/core/atomicexpression.lua
+2022/11/04 | 558 | macros/luatex/latex/luacas/tex/core/binaryoperation/difference.lua
+2022/11/04 | 31724 | macros/luatex/latex/luacas/tex/core/binaryoperation.lua
+2022/11/04 | 6409 | macros/luatex/latex/luacas/tex/core/binaryoperation/power.lua
+2022/11/04 | 9926 | macros/luatex/latex/luacas/tex/core/binaryoperation/product.lua
+2022/11/04 | 557 | macros/luatex/latex/luacas/tex/core/binaryoperation/quotient.lua
+2022/11/04 | 8622 | macros/luatex/latex/luacas/tex/core/binaryoperation/sum.lua
+2022/11/04 | 1271 | macros/luatex/latex/luacas/tex/core/compoundexpression.lua
+2022/11/04 | 1555 | macros/luatex/latex/luacas/tex/core/constantexpression.lua
+2022/11/04 | 8940 | macros/luatex/latex/luacas/tex/core/expression.lua
+2022/11/04 | 9890 | macros/luatex/latex/luacas/tex/core/functionexpression.lua
+2022/11/04 | 472 | macros/luatex/latex/luacas/tex/core/_init.lua
+2022/11/04 | 3463 | macros/luatex/latex/luacas/tex/core/symbolexpression.lua
+2022/11/04 | 9605 | macros/luatex/latex/luacas/tex/_lib/inspect.lua
+2022/11/04 | 19972 | macros/luatex/latex/luacas/tex/_lib/pepperfish.lua
+2022/11/04 | 4582 | macros/luatex/latex/luacas/tex/_lib/table.lua
+2022/11/04 | 7722 | macros/luatex/latex/luacas/tex/luacas.sty
+2022/11/04 | 1641 | macros/luatex/latex/luacas/tex/test/calculus/derivatives.lua
+2022/11/04 | 6936 | macros/luatex/latex/luacas/tex/test/calculus/integrals.lua
+2022/11/04 | 6298 | macros/luatex/latex/luacas/tex/test/expressions/autosimplify.lua
+2022/11/04 | 837 | macros/luatex/latex/luacas/tex/test/expressions/collect.lua
+2022/11/04 | 902 | macros/luatex/latex/luacas/tex/test/expressions/equations.lua
+2022/11/04 | 550 | macros/luatex/latex/luacas/tex/test/expressions/functions.lua
+2022/11/04 | 1007 | macros/luatex/latex/luacas/tex/test/expressions/logarithms.lua
+2022/11/04 | 835 | macros/luatex/latex/luacas/tex/test/expressions/rationalexponent.lua
+2022/11/04 | 930 | macros/luatex/latex/luacas/tex/test/expressions/simplify.lua
+2022/11/04 | 396 | macros/luatex/latex/luacas/tex/test/expressions/substitute.lua
+2022/11/04 | 11472 | macros/luatex/latex/luacas/tex/test/helper.lua
+2022/11/04 | 4692 | macros/luatex/latex/luacas/tex/test/main.lua
+2022/11/04 | 8300 | macros/luatex/latex/luacas/tex/test/parser.lua
+2022/11/04 | 601 | macros/luatex/latex/luacas/tex/test/polynomials/partialfractions.lua
+2022/11/04 | 7334 | macros/luatex/latex/luacas/tex/test/polynomials/polynomial.lua
+2022/11/04 | 6268 | macros/luatex/latex/luacas/tex/test/polynomials/polynomialmod.lua
+2022/11/04 | 4054 | macros/luatex/latex/luacas/tex/test/polynomials/roots.lua
+2022/11/04 | 12966 | macros/luatex/latex/luacas/tex/test/rings/conversion.lua
+2022/11/04 | 537 | macros/luatex/latex/luacas/tex/test/rings/modulararithmetic.lua
+2022/11/04 | 3393 | macros/luatex/latex/luacas/tex/test/rings/number.lua
+2022/11/05 | 791617 | macros/luatex/latex/luacas.zip
2022/02/23 | 158771 | macros/luatex/latex/luacensor/luacensor.pdf
2022/02/23 | 14334 | macros/luatex/latex/luacensor/luacensor.sty
2022/02/23 | 27205 | macros/luatex/latex/luacensor/luacensor.tex
@@ -273087,96 +273170,96 @@
1994/05/11 | 11989 | support/bbfig/bb.ps
1994/05/11 | 462 | support/bbfig/installation
2006/03/12 | 6604 | support/bbfig.zip
-2022/10/15 | 19584 | support/bib2gls/bib2gls.1
-2022/10/15 | 726769 | support/bib2gls/bib2gls-begin.pdf
-2022/10/15 | 45204 | support/bib2gls/bib2gls-en.xml
-2022/10/15 | 351599 | support/bib2gls/bib2gls.jar
-2022/10/15 | 5116129 | support/bib2gls/bib2gls.pdf
-2022/10/15 | 630 | support/bib2gls/bib2gls.sh
-2022/10/15 | 17018 | support/bib2gls/CHANGES
-2022/10/15 | 9614 | support/bib2gls/convertgls2bib.1
-2022/10/15 | 40686 | support/bib2gls/convertgls2bib.jar
-2022/10/15 | 404 | support/bib2gls/convertgls2bib.sh
-2022/10/15 | 22 | support/bib2gls/DEPENDS.txt
-2022/10/15 | 1603 | support/bib2gls/examples/animals.bib
-2022/10/15 | 1219 | support/bib2gls/examples/bacteria.bib
-2022/10/15 | 1031 | support/bib2gls/examples/baseunits.bib
-2022/10/15 | 2994 | support/bib2gls/examples/bigmathsymbols.bib
-2022/10/15 | 576 | support/bib2gls/examples/binaryoperators.bib
-2022/10/15 | 3496 | support/bib2gls/examples/books.bib
-2022/10/15 | 2585 | support/bib2gls/examples/chemicalformula.bib
-2022/10/15 | 1371 | support/bib2gls/examples/citations.bib
-2022/10/15 | 2392 | support/bib2gls/examples/constants.bib
-2022/10/15 | 1580 | support/bib2gls/examples/derivedunits.bib
-2022/10/15 | 5455 | support/bib2gls/examples/films.bib
-2022/10/15 | 338 | support/bib2gls/examples/interpret-preamble2.bib
-2022/10/15 | 328 | support/bib2gls/examples/interpret-preamble.bib
-2022/10/15 | 1772 | support/bib2gls/examples/markuplanguages.bib
-2022/10/15 | 3211 | support/bib2gls/examples/mathgreek.bib
-2022/10/15 | 3672 | support/bib2gls/examples/mathsobjects.bib
-2022/10/15 | 1265 | support/bib2gls/examples/mathsrelations.bib
-2022/10/15 | 2419 | support/bib2gls/examples/minerals.bib
-2022/10/15 | 2301 | support/bib2gls/examples/miscsymbols.bib
-2022/10/15 | 328 | support/bib2gls/examples/no-interpret-preamble.bib
-2022/10/15 | 4446 | support/bib2gls/examples/people.bib
-2022/10/15 | 51567 | support/bib2gls/examples/sample-authors.pdf
-2022/10/15 | 1506 | support/bib2gls/examples/sample-authors.tex
-2022/10/15 | 54233 | support/bib2gls/examples/sample-bacteria.pdf
-2022/10/15 | 1382 | support/bib2gls/examples/sample-bacteria.tex
-2022/10/15 | 54014 | support/bib2gls/examples/sample-chemical.pdf
-2022/10/15 | 1291 | support/bib2gls/examples/sample-chemical.tex
-2022/10/15 | 49757 | support/bib2gls/examples/sample-citations.pdf
-2022/10/15 | 1941 | support/bib2gls/examples/sample-citations.tex
-2022/10/15 | 97800 | support/bib2gls/examples/sample-constants.pdf
-2022/10/15 | 1378 | support/bib2gls/examples/sample-constants.tex
-2022/10/15 | 52517 | support/bib2gls/examples/sample-hierarchical.pdf
-2022/10/15 | 986 | support/bib2gls/examples/sample-hierarchical.tex
-2022/10/15 | 61083 | support/bib2gls/examples/sample-markuplanguages.pdf
-2022/10/15 | 1396 | support/bib2gls/examples/sample-markuplanguages.tex
-2022/10/15 | 121233 | support/bib2gls/examples/sample-maths.pdf
-2022/10/15 | 3129 | support/bib2gls/examples/sample-maths.tex
-2022/10/15 | 166497 | support/bib2gls/examples/sample-media.pdf
-2022/10/15 | 1734 | support/bib2gls/examples/sample-media.tex
-2022/10/15 | 109224 | support/bib2gls/examples/sample-msymbols.pdf
-2022/10/15 | 780 | support/bib2gls/examples/sample-msymbols.tex
-2022/10/15 | 255321 | support/bib2gls/examples/sample-multi1.pdf
-2022/10/15 | 5520 | support/bib2gls/examples/sample-multi1.tex
-2022/10/15 | 330718 | support/bib2gls/examples/sample-multi2.pdf
-2022/10/15 | 12775 | support/bib2gls/examples/sample-multi2.tex
-2022/10/15 | 52529 | support/bib2gls/examples/sample-nested.pdf
-2022/10/15 | 1344 | support/bib2gls/examples/sample-nested.tex
-2022/10/15 | 50332 | support/bib2gls/examples/sample-people.pdf
-2022/10/15 | 2367 | support/bib2gls/examples/sample-people.tex
-2022/10/15 | 70266 | support/bib2gls/examples/sample-textsymbols2.pdf
-2022/10/15 | 913 | support/bib2gls/examples/sample-textsymbols2.tex
-2022/10/15 | 73055 | support/bib2gls/examples/sample-textsymbols.pdf
-2022/10/15 | 1207 | support/bib2gls/examples/sample-textsymbols.tex
-2022/10/15 | 57356 | support/bib2gls/examples/sample-units1.pdf
-2022/10/15 | 1070 | support/bib2gls/examples/sample-units1.tex
-2022/10/15 | 59088 | support/bib2gls/examples/sample-units2.pdf
-2022/10/15 | 1234 | support/bib2gls/examples/sample-units2.tex
-2022/10/15 | 44656 | support/bib2gls/examples/sample-units3.pdf
-2022/10/15 | 1740 | support/bib2gls/examples/sample-units3.tex
-2022/10/15 | 23840 | support/bib2gls/examples/sample-usergroups.pdf
-2022/10/15 | 3254 | support/bib2gls/examples/sample-usergroups.tex
-2022/10/15 | 640 | support/bib2gls/examples/terms.bib
-2022/10/15 | 266 | support/bib2gls/examples/topics.bib
-2022/10/15 | 557 | support/bib2gls/examples/unaryoperators.bib
-2022/10/15 | 4053 | support/bib2gls/examples/usergroups.bib
-2022/10/15 | 1091 | support/bib2gls/examples/vegetables.bib
-2022/10/15 | 6793 | support/bib2gls/README.md
-2022/10/15 | 241533 | support/bib2gls/src/bib2gls-begin.tex
-2022/10/15 | 548895 | support/bib2gls/src/bib2gls.bib
-2022/10/15 | 4078 | support/bib2gls/src/bib2gls-cite.bib
-2022/10/15 | 11628 | support/bib2gls/src/bib2gls.pod
-2022/10/15 | 264591 | support/bib2gls/src/bib2gls-src.zip
-2022/10/15 | 16525 | support/bib2gls/src/bib2gls-terms.bib
-2022/10/15 | 1060384 | support/bib2gls/src/bib2gls.tex
-2022/10/15 | 4045 | support/bib2gls/src/convertgls2bib.pod
-2022/10/15 | 33144 | support/bib2gls/src/gls2bib-src.zip
-2022/10/15 | 1322528 | support/bib2gls/src/texparser-src.zip
-2022/10/15 | 1534189 | support/bib2gls/texparserlib.jar
-2022/10/15 | 11250143 | support/bib2gls.zip
+2022/11/05 | 19584 | support/bib2gls/bib2gls.1
+2022/11/05 | 718818 | support/bib2gls/bib2gls-begin.pdf
+2022/11/05 | 45419 | support/bib2gls/bib2gls-en.xml
+2022/11/05 | 353466 | support/bib2gls/bib2gls.jar
+2022/11/05 | 5135194 | support/bib2gls/bib2gls.pdf
+2022/11/05 | 630 | support/bib2gls/bib2gls.sh
+2022/11/05 | 17650 | support/bib2gls/CHANGES
+2022/11/05 | 9614 | support/bib2gls/convertgls2bib.1
+2022/11/05 | 40754 | support/bib2gls/convertgls2bib.jar
+2022/11/05 | 404 | support/bib2gls/convertgls2bib.sh
+2022/11/05 | 22 | support/bib2gls/DEPENDS.txt
+2022/11/05 | 1603 | support/bib2gls/examples/animals.bib
+2022/11/05 | 1219 | support/bib2gls/examples/bacteria.bib
+2022/11/05 | 1031 | support/bib2gls/examples/baseunits.bib
+2022/11/05 | 2994 | support/bib2gls/examples/bigmathsymbols.bib
+2022/11/05 | 576 | support/bib2gls/examples/binaryoperators.bib
+2022/11/05 | 3496 | support/bib2gls/examples/books.bib
+2022/11/05 | 2585 | support/bib2gls/examples/chemicalformula.bib
+2022/11/05 | 1371 | support/bib2gls/examples/citations.bib
+2022/11/05 | 2392 | support/bib2gls/examples/constants.bib
+2022/11/05 | 1580 | support/bib2gls/examples/derivedunits.bib
+2022/11/05 | 5455 | support/bib2gls/examples/films.bib
+2022/11/05 | 338 | support/bib2gls/examples/interpret-preamble2.bib
+2022/11/05 | 328 | support/bib2gls/examples/interpret-preamble.bib
+2022/11/05 | 1772 | support/bib2gls/examples/markuplanguages.bib
+2022/11/05 | 3211 | support/bib2gls/examples/mathgreek.bib
+2022/11/05 | 3672 | support/bib2gls/examples/mathsobjects.bib
+2022/11/05 | 1265 | support/bib2gls/examples/mathsrelations.bib
+2022/11/05 | 2419 | support/bib2gls/examples/minerals.bib
+2022/11/05 | 2301 | support/bib2gls/examples/miscsymbols.bib
+2022/11/05 | 328 | support/bib2gls/examples/no-interpret-preamble.bib
+2022/11/05 | 4446 | support/bib2gls/examples/people.bib
+2022/11/05 | 51554 | support/bib2gls/examples/sample-authors.pdf
+2022/11/05 | 1506 | support/bib2gls/examples/sample-authors.tex
+2022/11/05 | 54221 | support/bib2gls/examples/sample-bacteria.pdf
+2022/11/05 | 1382 | support/bib2gls/examples/sample-bacteria.tex
+2022/11/05 | 54006 | support/bib2gls/examples/sample-chemical.pdf
+2022/11/05 | 1291 | support/bib2gls/examples/sample-chemical.tex
+2022/11/05 | 49745 | support/bib2gls/examples/sample-citations.pdf
+2022/11/05 | 1941 | support/bib2gls/examples/sample-citations.tex
+2022/11/05 | 97789 | support/bib2gls/examples/sample-constants.pdf
+2022/11/05 | 1378 | support/bib2gls/examples/sample-constants.tex
+2022/11/05 | 52505 | support/bib2gls/examples/sample-hierarchical.pdf
+2022/11/05 | 986 | support/bib2gls/examples/sample-hierarchical.tex
+2022/11/05 | 61070 | support/bib2gls/examples/sample-markuplanguages.pdf
+2022/11/05 | 1391 | support/bib2gls/examples/sample-markuplanguages.tex
+2022/11/05 | 121219 | support/bib2gls/examples/sample-maths.pdf
+2022/11/05 | 3129 | support/bib2gls/examples/sample-maths.tex
+2022/11/05 | 166484 | support/bib2gls/examples/sample-media.pdf
+2022/11/05 | 1734 | support/bib2gls/examples/sample-media.tex
+2022/11/05 | 109211 | support/bib2gls/examples/sample-msymbols.pdf
+2022/11/05 | 780 | support/bib2gls/examples/sample-msymbols.tex
+2022/11/05 | 255310 | support/bib2gls/examples/sample-multi1.pdf
+2022/11/05 | 5520 | support/bib2gls/examples/sample-multi1.tex
+2022/11/05 | 330706 | support/bib2gls/examples/sample-multi2.pdf
+2022/11/05 | 12775 | support/bib2gls/examples/sample-multi2.tex
+2022/11/05 | 52518 | support/bib2gls/examples/sample-nested.pdf
+2022/11/05 | 1344 | support/bib2gls/examples/sample-nested.tex
+2022/11/05 | 50320 | support/bib2gls/examples/sample-people.pdf
+2022/11/05 | 2367 | support/bib2gls/examples/sample-people.tex
+2022/11/05 | 70256 | support/bib2gls/examples/sample-textsymbols2.pdf
+2022/11/05 | 913 | support/bib2gls/examples/sample-textsymbols2.tex
+2022/11/05 | 73042 | support/bib2gls/examples/sample-textsymbols.pdf
+2022/11/05 | 1207 | support/bib2gls/examples/sample-textsymbols.tex
+2022/11/05 | 57344 | support/bib2gls/examples/sample-units1.pdf
+2022/11/05 | 1070 | support/bib2gls/examples/sample-units1.tex
+2022/11/05 | 59076 | support/bib2gls/examples/sample-units2.pdf
+2022/11/05 | 1234 | support/bib2gls/examples/sample-units2.tex
+2022/11/05 | 44644 | support/bib2gls/examples/sample-units3.pdf
+2022/11/05 | 1740 | support/bib2gls/examples/sample-units3.tex
+2022/11/05 | 23827 | support/bib2gls/examples/sample-usergroups.pdf
+2022/11/05 | 3254 | support/bib2gls/examples/sample-usergroups.tex
+2022/11/05 | 640 | support/bib2gls/examples/terms.bib
+2022/11/05 | 266 | support/bib2gls/examples/topics.bib
+2022/11/05 | 557 | support/bib2gls/examples/unaryoperators.bib
+2022/11/05 | 4053 | support/bib2gls/examples/usergroups.bib
+2022/11/05 | 1091 | support/bib2gls/examples/vegetables.bib
+2022/11/05 | 6793 | support/bib2gls/README.md
+2022/11/05 | 236672 | support/bib2gls/src/bib2gls-begin.tex
+2022/11/05 | 554266 | support/bib2gls/src/bib2gls.bib
+2022/11/05 | 4078 | support/bib2gls/src/bib2gls-cite.bib
+2022/11/05 | 11628 | support/bib2gls/src/bib2gls.pod
+2022/11/05 | 266494 | support/bib2gls/src/bib2gls-src.zip
+2022/11/05 | 16525 | support/bib2gls/src/bib2gls-terms.bib
+2022/11/05 | 1064409 | support/bib2gls/src/bib2gls.tex
+2022/11/05 | 4045 | support/bib2gls/src/convertgls2bib.pod
+2022/11/05 | 33176 | support/bib2gls/src/gls2bib-src.zip
+2022/11/05 | 1391320 | support/bib2gls/src/texparser-src.zip
+2022/11/05 | 1624931 | support/bib2gls/texparserlib.jar
+2022/11/05 | 11414020 | support/bib2gls.zip
1999/04/14 | 372041 | support/bibdb/bibdb20.zip
1999/04/14 | 1228948 | support/bibdb/bibdbsrc.zip
1999/04/14 | 686556 | support/bibdb/wbibdb22.zip
@@ -278684,7 +278767,7 @@
2021/05/03 | 629 | support/TeX4ht/doc/README
2020/08/24 | 358454 | support/TeX4ht/doc/TeX4ht-doc.tex
2021/05/11 | 262 | support/TeX4ht/README
-2022/11/02 | 207112 | support/TeX4ht/source/ChangeLog
+2022/11/04 | 207266 | support/TeX4ht/source/ChangeLog
2021/05/03 | 1375 | support/TeX4ht/source/checklog.lua
2021/05/03 | 1189 | support/TeX4ht/source/common-code.tex
2021/05/03 | 2727 | support/TeX4ht/source/common-info.tex
@@ -278692,11 +278775,11 @@
2021/05/06 | 15 | support/TeX4ht/source/extra-bin/vcss
2021/05/06 | 15 | support/TeX4ht/source/extra-bin/xmllint
2022/10/11 | 27330 | support/TeX4ht/source/Makefile
-2022/10/20 | 8205 | support/TeX4ht/source/mktex4ht-cnf.tex
+2022/11/04 | 8224 | support/TeX4ht/source/mktex4ht-cnf.tex
2021/05/03 | 1121 | support/TeX4ht/source/README
2021/05/03 | 1744 | support/TeX4ht/source/t1fontmake.lua
2021/05/03 | 297 | support/TeX4ht/source/t1fontsample.tex
-2022/10/31 | 1134369 | support/TeX4ht/source/tex4ht-4ht.tex
+2022/11/04 | 1136033 | support/TeX4ht/source/tex4ht-4ht.tex
2021/05/03 | 7127 | support/TeX4ht/source/tex4ht-auto-script.tex
2021/05/16 | 1539 | support/TeX4ht/source/tex4ht-bibtex2.tex
2022/06/05 | 21307 | support/TeX4ht/source/tex4ht-cond4ht.tex
@@ -278722,7 +278805,7 @@
2021/05/03 | 14218 | support/TeX4ht/source/tex4ht-htcmd.tex
2021/05/03 | 70683 | support/TeX4ht/source/tex4ht-html0.tex
2021/05/03 | 164748 | support/TeX4ht/source/tex4ht-html32.tex
-2022/10/27 | 619023 | support/TeX4ht/source/tex4ht-html4.tex
+2022/11/04 | 619595 | support/TeX4ht/source/tex4ht-html4.tex
2022/08/11 | 11077 | support/TeX4ht/source/tex4ht-html5.tex
2021/06/17 | 648233 | support/TeX4ht/source/tex4ht-html-speech.tex
2021/05/03 | 59483 | support/TeX4ht/source/tex4ht-html-speech-xtpipes.tex
@@ -284948,8 +285031,8 @@
2019/02/28 | 266164 | systems/texlive/tlnet/archive/concmath.doc.r17219.tar.xz
2019/02/28 | 2640 | systems/texlive/tlnet/archive/concmath-fonts.doc.r17218.tar.xz
2019/02/28 | 15176 | systems/texlive/tlnet/archive/concmath-fonts.r17218.tar.xz
-2022/10/07 | 1856412 | systems/texlive/tlnet/archive/concmath-otf.doc.r64635.tar.xz
-2022/10/07 | 133876 | systems/texlive/tlnet/archive/concmath-otf.r64635.tar.xz
+2022/11/05 | 1856924 | systems/texlive/tlnet/archive/concmath-otf.doc.r64924.tar.xz
+2022/11/05 | 133888 | systems/texlive/tlnet/archive/concmath-otf.r64924.tar.xz
2019/02/28 | 2176 | systems/texlive/tlnet/archive/concmath.r17219.tar.xz
2019/02/28 | 9604 | systems/texlive/tlnet/archive/concmath.source.r17219.tar.xz
2019/02/28 | 1064 | systems/texlive/tlnet/archive/concprog.doc.r18791.tar.xz
@@ -286223,9 +286306,9 @@
2021/07/19 | 313228 | systems/texlive/tlnet/archive/easing.doc.r59975.tar.xz
2021/07/19 | 2220 | systems/texlive/tlnet/archive/easing.r59975.tar.xz
2021/07/19 | 7876 | systems/texlive/tlnet/archive/easing.source.r59975.tar.xz
-2022/10/28 | 610744 | systems/texlive/tlnet/archive/easybook.doc.r64821.tar.xz
-2022/10/28 | 16568 | systems/texlive/tlnet/archive/easybook.r64821.tar.xz
-2022/10/28 | 16468 | systems/texlive/tlnet/archive/easybook.source.r64821.tar.xz
+2022/11/05 | 611556 | systems/texlive/tlnet/archive/easybook.doc.r64929.tar.xz
+2022/11/05 | 16564 | systems/texlive/tlnet/archive/easybook.r64929.tar.xz
+2022/11/05 | 16480 | systems/texlive/tlnet/archive/easybook.source.r64929.tar.xz
2019/02/28 | 4540 | systems/texlive/tlnet/archive/easy.doc.r19440.tar.xz
2020/09/08 | 175976 | systems/texlive/tlnet/archive/easyfig.doc.r56291.tar.xz
2020/09/08 | 1936 | systems/texlive/tlnet/archive/easyfig.r56291.tar.xz
@@ -286594,8 +286677,8 @@
2019/02/28 | 6056 | systems/texlive/tlnet/archive/erdc.r15878.tar.xz
2019/02/28 | 16276 | systems/texlive/tlnet/archive/erdc.source.r15878.tar.xz
2022/05/17 | 378364 | systems/texlive/tlnet/archive/erewhon.doc.r63312.tar.xz
-2022/10/20 | 1987076 | systems/texlive/tlnet/archive/erewhon-math.doc.r64751.tar.xz
-2022/10/20 | 267704 | systems/texlive/tlnet/archive/erewhon-math.r64751.tar.xz
+2022/11/05 | 1987428 | systems/texlive/tlnet/archive/erewhon-math.doc.r64925.tar.xz
+2022/11/05 | 267620 | systems/texlive/tlnet/archive/erewhon-math.r64925.tar.xz
2022/05/17 | 2596492 | systems/texlive/tlnet/archive/erewhon.r63312.tar.xz
2019/02/28 | 147644 | systems/texlive/tlnet/archive/errata.doc.r42428.tar.xz
2019/02/28 | 1652 | systems/texlive/tlnet/archive/errata.r42428.tar.xz
@@ -288700,8 +288783,8 @@
2022/10/13 | 7399992 | systems/texlive/tlnet/archive/koma-script.r64685.tar.xz
2019/02/28 | 37524 | systems/texlive/tlnet/archive/koma-script-sfs.doc.r26137.tar.xz
2019/02/28 | 3400 | systems/texlive/tlnet/archive/koma-script-sfs.r26137.tar.xz
-2022/04/12 | 539232 | systems/texlive/tlnet/archive/kotex-oblivoir.doc.r63005.tar.xz
-2022/04/12 | 43476 | systems/texlive/tlnet/archive/kotex-oblivoir.r63005.tar.xz
+2022/11/05 | 539216 | systems/texlive/tlnet/archive/kotex-oblivoir.doc.r64928.tar.xz
+2022/11/05 | 43484 | systems/texlive/tlnet/archive/kotex-oblivoir.r64928.tar.xz
2022/06/23 | 1852 | systems/texlive/tlnet/archive/kotex-plain.doc.r63689.tar.xz
2022/06/23 | 9116 | systems/texlive/tlnet/archive/kotex-plain.r63689.tar.xz
2022/06/23 | 9496848 | systems/texlive/tlnet/archive/kotex-utf.doc.r63690.tar.xz
@@ -289672,13 +289755,13 @@
2021/12/03 | 344 | systems/texlive/tlnet/archive/luafindfont.amd64-freebsd.r61207.tar.xz
2021/12/03 | 340 | systems/texlive/tlnet/archive/luafindfont.amd64-netbsd.r61207.tar.xz
2021/12/03 | 340 | systems/texlive/tlnet/archive/luafindfont.armhf-linux.r61207.tar.xz
-2022/09/03 | 116040 | systems/texlive/tlnet/archive/luafindfont.doc.r64270.tar.xz
+2022/11/05 | 124356 | systems/texlive/tlnet/archive/luafindfont.doc.r64936.tar.xz
2021/12/03 | 340 | systems/texlive/tlnet/archive/luafindfont.i386-cygwin.r61207.tar.xz
2021/12/03 | 340 | systems/texlive/tlnet/archive/luafindfont.i386-freebsd.r61207.tar.xz
2021/12/03 | 340 | systems/texlive/tlnet/archive/luafindfont.i386-linux.r61207.tar.xz
2021/12/03 | 340 | systems/texlive/tlnet/archive/luafindfont.i386-netbsd.r61207.tar.xz
2021/12/03 | 340 | systems/texlive/tlnet/archive/luafindfont.i386-solaris.r61207.tar.xz
-2022/09/03 | 5372 | systems/texlive/tlnet/archive/luafindfont.r64270.tar.xz
+2022/11/05 | 5536 | systems/texlive/tlnet/archive/luafindfont.r64936.tar.xz
2021/12/03 | 344 | systems/texlive/tlnet/archive/luafindfont.universal-darwin.r61207.tar.xz
2021/12/03 | 684 | systems/texlive/tlnet/archive/luafindfont.win32.r61207.tar.xz
2021/12/03 | 344 | systems/texlive/tlnet/archive/luafindfont.x86_64-cygwin.r61207.tar.xz
@@ -289785,8 +289868,8 @@
2020/08/15 | 7092 | systems/texlive/tlnet/archive/luaprogtable.r56113.tar.xz
2022/09/13 | 1524 | systems/texlive/tlnet/archive/luapstricks.doc.r64345.tar.xz
2022/09/13 | 26996 | systems/texlive/tlnet/archive/luapstricks.r64345.tar.xz
-2022/10/26 | 113916 | systems/texlive/tlnet/archive/luaquotes.doc.r64812.tar.xz
-2022/10/26 | 2628 | systems/texlive/tlnet/archive/luaquotes.r64812.tar.xz
+2022/11/05 | 114004 | systems/texlive/tlnet/archive/luaquotes.doc.r64934.tar.xz
+2022/11/05 | 2628 | systems/texlive/tlnet/archive/luaquotes.r64934.tar.xz
2019/02/28 | 50272 | systems/texlive/tlnet/archive/luarandom.doc.r49419.tar.xz
2019/02/28 | 1392 | systems/texlive/tlnet/archive/luarandom.r49419.tar.xz
2019/02/28 | 224504 | systems/texlive/tlnet/archive/luasseq.doc.r37877.tar.xz
@@ -290061,9 +290144,9 @@
2019/02/28 | 1858004 | systems/texlive/tlnet/archive/mathabx-type1.r21129.tar.xz
2021/11/19 | 1888792 | systems/texlive/tlnet/archive/mathalpha.doc.r61089.tar.xz
2021/11/19 | 7156 | systems/texlive/tlnet/archive/mathalpha.r61089.tar.xz
-2019/11/18 | 530004 | systems/texlive/tlnet/archive/mathastext.doc.r52840.tar.xz
-2019/11/18 | 15984 | systems/texlive/tlnet/archive/mathastext.r52840.tar.xz
-2019/11/18 | 94384 | systems/texlive/tlnet/archive/mathastext.source.r52840.tar.xz
+2022/11/05 | 584908 | systems/texlive/tlnet/archive/mathastext.doc.r64930.tar.xz
+2022/11/05 | 17188 | systems/texlive/tlnet/archive/mathastext.r64930.tar.xz
+2022/11/05 | 105248 | systems/texlive/tlnet/archive/mathastext.source.r64930.tar.xz
2021/06/08 | 452340 | systems/texlive/tlnet/archive/mathcommand.doc.r59512.tar.xz
2021/06/08 | 4748 | systems/texlive/tlnet/archive/mathcommand.r59512.tar.xz
2021/06/08 | 12088 | systems/texlive/tlnet/archive/mathcommand.source.r59512.tar.xz
@@ -291539,9 +291622,9 @@
2022/10/30 | 314616 | systems/texlive/tlnet/archive/pdflscape.doc.r64851.tar.xz
2022/10/30 | 2612 | systems/texlive/tlnet/archive/pdflscape.r64851.tar.xz
2022/10/30 | 6508 | systems/texlive/tlnet/archive/pdflscape.source.r64851.tar.xz
-2022/09/27 | 8723252 | systems/texlive/tlnet/archive/pdfmanagement-testphase.doc.r64521.tar.xz
-2022/09/27 | 76696 | systems/texlive/tlnet/archive/pdfmanagement-testphase.r64521.tar.xz
-2022/09/27 | 137740 | systems/texlive/tlnet/archive/pdfmanagement-testphase.source.r64521.tar.xz
+2022/11/05 | 8727504 | systems/texlive/tlnet/archive/pdfmanagement-testphase.doc.r64931.tar.xz
+2022/11/05 | 75348 | systems/texlive/tlnet/archive/pdfmanagement-testphase.r64931.tar.xz
+2022/11/05 | 136668 | systems/texlive/tlnet/archive/pdfmanagement-testphase.source.r64931.tar.xz
2019/02/28 | 381552 | systems/texlive/tlnet/archive/pdfmarginpar.doc.r23492.tar.xz
2019/02/28 | 2424 | systems/texlive/tlnet/archive/pdfmarginpar.r23492.tar.xz
2022/08/28 | 565420 | systems/texlive/tlnet/archive/pdfoverlay.doc.r64210.tar.xz
@@ -292148,8 +292231,8 @@
2022/11/04 | 1249676 | systems/texlive/tlnet/archive/profcollege.r64918.tar.xz
2022/04/27 | 134096 | systems/texlive/tlnet/archive/proflabo.doc.r63147.tar.xz
2022/04/27 | 2748 | systems/texlive/tlnet/archive/proflabo.r63147.tar.xz
-2022/10/21 | 1069480 | systems/texlive/tlnet/archive/proflycee.doc.r64765.tar.xz
-2022/10/21 | 20544 | systems/texlive/tlnet/archive/proflycee.r64765.tar.xz
+2022/11/05 | 835036 | systems/texlive/tlnet/archive/proflycee.doc.r64932.tar.xz
+2022/11/05 | 20704 | systems/texlive/tlnet/archive/proflycee.r64932.tar.xz
2019/02/28 | 49880 | systems/texlive/tlnet/archive/program.doc.r44214.tar.xz
2019/02/28 | 11780 | systems/texlive/tlnet/archive/program.r44214.tar.xz
2019/02/28 | 86236 | systems/texlive/tlnet/archive/progressbar.doc.r33822.tar.xz
@@ -293339,8 +293422,8 @@
2019/02/28 | 292656 | systems/texlive/tlnet/archive/simplecv.doc.r35537.tar.xz
2019/02/28 | 2112 | systems/texlive/tlnet/archive/simplecv.r35537.tar.xz
2019/02/28 | 5844 | systems/texlive/tlnet/archive/simplecv.source.r35537.tar.xz
-2022/10/30 | 1982104 | systems/texlive/tlnet/archive/simpleicons.doc.r64855.tar.xz
-2022/10/30 | 2700300 | systems/texlive/tlnet/archive/simpleicons.r64855.tar.xz
+2022/11/05 | 1983352 | systems/texlive/tlnet/archive/simpleicons.doc.r64926.tar.xz
+2022/11/05 | 2701752 | systems/texlive/tlnet/archive/simpleicons.r64926.tar.xz
2019/02/28 | 224844 | systems/texlive/tlnet/archive/simpleinvoice.doc.r45673.tar.xz
2019/02/28 | 2420 | systems/texlive/tlnet/archive/simpleinvoice.r45673.tar.xz
2022/10/03 | 130940 | systems/texlive/tlnet/archive/simplekv.doc.r64578.tar.xz
@@ -294023,14 +294106,14 @@
2022/02/27 | 108804 | systems/texlive/tlnet/archive/tex4ht.amd64-freebsd.r62206.tar.xz
2022/02/27 | 87364 | systems/texlive/tlnet/archive/tex4ht.amd64-netbsd.r62220.tar.xz
2022/04/21 | 69948 | systems/texlive/tlnet/archive/tex4ht.armhf-linux.r63092.tar.xz
-2022/11/03 | 136384 | systems/texlive/tlnet/archive/tex4ht.doc.r64911.tar.xz
+2022/11/05 | 136384 | systems/texlive/tlnet/archive/tex4ht.doc.r64937.tar.xz
2022/03/01 | 58328 | systems/texlive/tlnet/archive/tex4ht.i386-cygwin.r62282.tar.xz
2022/02/27 | 93760 | systems/texlive/tlnet/archive/tex4ht.i386-freebsd.r62206.tar.xz
2022/02/27 | 91280 | systems/texlive/tlnet/archive/tex4ht.i386-linux.r62210.tar.xz
2022/02/27 | 76200 | systems/texlive/tlnet/archive/tex4ht.i386-netbsd.r62220.tar.xz
2022/02/27 | 83964 | systems/texlive/tlnet/archive/tex4ht.i386-solaris.r62206.tar.xz
-2022/11/03 | 945148 | systems/texlive/tlnet/archive/tex4ht.r64911.tar.xz
-2022/11/03 | 1278688 | systems/texlive/tlnet/archive/tex4ht.source.r64911.tar.xz
+2022/11/05 | 945544 | systems/texlive/tlnet/archive/tex4ht.r64937.tar.xz
+2022/11/05 | 1273088 | systems/texlive/tlnet/archive/tex4ht.source.r64937.tar.xz
2022/02/28 | 184748 | systems/texlive/tlnet/archive/tex4ht.universal-darwin.r62258.tar.xz
2022/02/27 | 57768 | systems/texlive/tlnet/archive/tex4ht.win32.r62199.tar.xz
2022/03/01 | 58016 | systems/texlive/tlnet/archive/tex4ht.x86_64-cygwin.r62279.tar.xz
@@ -294284,7 +294367,7 @@
2022/09/13 | 600 | systems/texlive/tlnet/archive/texlive-scripts.amd64-freebsd.r64356.tar.xz
2022/09/13 | 596 | systems/texlive/tlnet/archive/texlive-scripts.amd64-netbsd.r64356.tar.xz
2022/09/13 | 596 | systems/texlive/tlnet/archive/texlive-scripts.armhf-linux.r64356.tar.xz
-2022/11/04 | 423096 | systems/texlive/tlnet/archive/texlive-scripts.doc.r64913.tar.xz
+2022/11/05 | 423096 | systems/texlive/tlnet/archive/texlive-scripts.doc.r64922.tar.xz
2020/01/27 | 916 | systems/texlive/tlnet/archive/texlive-scripts-extra.aarch64-linux.r53577.tar.xz
2020/01/27 | 924 | systems/texlive/tlnet/archive/texlive-scripts-extra.amd64-freebsd.r53577.tar.xz
2020/01/27 | 916 | systems/texlive/tlnet/archive/texlive-scripts-extra.amd64-netbsd.r53577.tar.xz
@@ -294308,7 +294391,7 @@
2022/09/13 | 592 | systems/texlive/tlnet/archive/texlive-scripts.i386-linux.r64356.tar.xz
2022/09/13 | 596 | systems/texlive/tlnet/archive/texlive-scripts.i386-netbsd.r64356.tar.xz
2022/09/13 | 596 | systems/texlive/tlnet/archive/texlive-scripts.i386-solaris.r64356.tar.xz
-2022/11/04 | 113708 | systems/texlive/tlnet/archive/texlive-scripts.r64913.tar.xz
+2022/11/05 | 113712 | systems/texlive/tlnet/archive/texlive-scripts.r64922.tar.xz
2022/09/13 | 580 | systems/texlive/tlnet/archive/texlive-scripts.universal-darwin.r64356.tar.xz
2022/04/19 | 36296 | systems/texlive/tlnet/archive/texlive-scripts.win32.r63068.tar.xz
2022/09/13 | 596 | systems/texlive/tlnet/archive/texlive-scripts.x86_64-cygwin.r64356.tar.xz
@@ -294467,8 +294550,8 @@
2022/07/11 | 215640 | systems/texlive/tlnet/archive/textcase.doc.r63868.tar.xz
2022/07/11 | 1624 | systems/texlive/tlnet/archive/textcase.r63868.tar.xz
2022/07/11 | 5124 | systems/texlive/tlnet/archive/textcase.source.r63868.tar.xz
-2022/09/13 | 51980 | systems/texlive/tlnet/archive/textcsc.doc.r64365.tar.xz
-2022/09/13 | 1120 | systems/texlive/tlnet/archive/textcsc.r64365.tar.xz
+2022/11/05 | 51964 | systems/texlive/tlnet/archive/textcsc.doc.r64935.tar.xz
+2022/11/05 | 1120 | systems/texlive/tlnet/archive/textcsc.r64935.tar.xz
2019/02/28 | 162372 | systems/texlive/tlnet/archive/textfit.doc.r20591.tar.xz
2019/02/28 | 2204 | systems/texlive/tlnet/archive/textfit.r20591.tar.xz
2019/02/28 | 3592 | systems/texlive/tlnet/archive/textfit.source.r20591.tar.xz
@@ -295025,8 +295108,8 @@
2019/02/28 | 254104 | systems/texlive/tlnet/archive/tucv.doc.r20680.tar.xz
2019/02/28 | 1860 | systems/texlive/tlnet/archive/tucv.r20680.tar.xz
2019/02/28 | 4996 | systems/texlive/tlnet/archive/tucv.source.r20680.tar.xz
-2022/08/04 | 1132348 | systems/texlive/tlnet/archive/tuda-ci.doc.r64039.tar.xz
-2022/08/04 | 43676 | systems/texlive/tlnet/archive/tuda-ci.r64039.tar.xz
+2022/11/05 | 1131412 | systems/texlive/tlnet/archive/tuda-ci.doc.r64933.tar.xz
+2022/11/05 | 43792 | systems/texlive/tlnet/archive/tuda-ci.r64933.tar.xz
2022/08/10 | 3971388 | systems/texlive/tlnet/archive/tudscr.doc.r64085.tar.xz
2022/08/10 | 164240 | systems/texlive/tlnet/archive/tudscr.r64085.tar.xz
2022/08/10 | 325572 | systems/texlive/tlnet/archive/tudscr.source.r64085.tar.xz
@@ -295699,8 +295782,8 @@
2020/01/19 | 4124 | systems/texlive/tlnet/archive/xbmks.r53448.tar.xz
2020/01/19 | 8024 | systems/texlive/tlnet/archive/xbmks.source.r53448.tar.xz
2022/04/18 | 224040 | systems/texlive/tlnet/archive/xcharter.doc.r63057.tar.xz
-2022/10/09 | 1947500 | systems/texlive/tlnet/archive/xcharter-math.doc.r64653.tar.xz
-2022/10/09 | 201872 | systems/texlive/tlnet/archive/xcharter-math.r64653.tar.xz
+2022/11/05 | 1948324 | systems/texlive/tlnet/archive/xcharter-math.doc.r64927.tar.xz
+2022/11/05 | 217836 | systems/texlive/tlnet/archive/xcharter-math.r64927.tar.xz
2022/04/18 | 2200760 | systems/texlive/tlnet/archive/xcharter.r63057.tar.xz
2020/01/22 | 159256 | systems/texlive/tlnet/archive/xcite.doc.r53486.tar.xz
2020/01/22 | 1048 | systems/texlive/tlnet/archive/xcite.r53486.tar.xz
@@ -296155,16 +296238,16 @@
2020/07/18 | 47816 | systems/texlive/tlnet/archive/zztex.doc.r55862.tar.xz
2020/07/18 | 101724 | systems/texlive/tlnet/archive/zztex.r55862.tar.xz
2022/07/17 | 124404 | systems/texlive/tlnet/install-tl
-2022/11/04 | 5834873 | systems/texlive/tlnet/install-tl-unx.tar.gz
-2022/11/04 | 152 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512
-2022/11/04 | 455 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512.asc
+2022/11/05 | 5834605 | systems/texlive/tlnet/install-tl-unx.tar.gz
+2022/11/05 | 152 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512
+2022/11/05 | 455 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512.asc
2021/04/11 | 4945 | systems/texlive/tlnet/install-tl-windows.bat
-2022/11/04 | 20433467 | systems/texlive/tlnet/install-tl-windows.exe
-2022/11/04 | 153 | systems/texlive/tlnet/install-tl-windows.exe.sha512
-2022/11/04 | 455 | systems/texlive/tlnet/install-tl-windows.exe.sha512.asc
-2022/11/04 | 25518558 | systems/texlive/tlnet/install-tl.zip
-2022/11/04 | 145 | systems/texlive/tlnet/install-tl.zip.sha512
-2022/11/04 | 455 | systems/texlive/tlnet/install-tl.zip.sha512.asc
+2022/11/05 | 20435953 | systems/texlive/tlnet/install-tl-windows.exe
+2022/11/05 | 153 | systems/texlive/tlnet/install-tl-windows.exe.sha512
+2022/11/05 | 455 | systems/texlive/tlnet/install-tl-windows.exe.sha512.asc
+2022/11/05 | 25518540 | systems/texlive/tlnet/install-tl.zip
+2022/11/05 | 145 | systems/texlive/tlnet/install-tl.zip.sha512
+2022/11/05 | 455 | systems/texlive/tlnet/install-tl.zip.sha512.asc
2022/04/03 | 1229 | systems/texlive/tlnet/README.md
2022/04/03 | 0 | systems/texlive/tlnet/TEXLIVE_2022
2022/02/28 | 7791 | systems/texlive/tlnet/tlpkg/gpg/pubring.gpg
@@ -296174,7 +296257,7 @@
2022/02/28 | 1280 | systems/texlive/tlnet/tlpkg/gpg/trustdb.gpg
2022/10/09 | 49938 | systems/texlive/tlnet/tlpkg/installer/config.guess
2010/11/11 | 11651 | systems/texlive/tlnet/tlpkg/installer/COPYING.MinGW-runtime.txt
-2022/11/03 | 7482 | systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl
+2022/11/04 | 7408 | systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl
2022/10/13 | 225403 | systems/texlive/tlnet/tlpkg/installer/curl/curl-ca-bundle.crt
2020/04/15 | 2606592 | systems/texlive/tlnet/tlpkg/installer/curl/curl.exe
2017/04/16 | 2629 | systems/texlive/tlnet/tlpkg/installer/installer-options.txt
@@ -296217,12 +296300,12 @@
2021/05/16 | 20371 | systems/texlive/tlnet/tlpkg/TeXLive/TLCrypto.pm
2021/12/21 | 5429 | systems/texlive/tlnet/tlpkg/TeXLive/TLDownload.pm
2021/12/21 | 27582 | systems/texlive/tlnet/tlpkg/TeXLive/TLPaper.pm
-2022/11/04 | 17802927 | systems/texlive/tlnet/tlpkg/texlive.tlpdb
-2022/11/04 | 48 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5
+2022/11/05 | 17802958 | systems/texlive/tlnet/tlpkg/texlive.tlpdb
+2022/11/05 | 48 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5
2022/04/18 | 86658 | systems/texlive/tlnet/tlpkg/TeXLive/TLPDB.pm
-2022/11/04 | 144 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512
-2022/11/04 | 455 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc
-2022/11/04 | 2424844 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz
+2022/11/05 | 144 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512
+2022/11/05 | 455 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc
+2022/11/05 | 2424508 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz
2022/09/14 | 58922 | systems/texlive/tlnet/tlpkg/TeXLive/TLPOBJ.pm
2022/10/04 | 42404 | systems/texlive/tlnet/tlpkg/TeXLive/TLPSRC.pm
2021/12/21 | 16130 | systems/texlive/tlnet/tlpkg/TeXLive/TLTREE.pm
@@ -301638,7 +301721,7 @@
2022/11/01 | 5115 | systems/win32/miktex/tm/packages/filehook.tar.lzma
2012/12/04 | 924573 | systems/win32/miktex/tm/packages/fileinfo.tar.lzma
2011/09/21 | 281409 | systems/win32/miktex/tm/packages/filemod.tar.lzma
-2022/11/04 | 672234 | systems/win32/miktex/tm/packages/files.csv.lzma
+2022/11/05 | 672226 | systems/win32/miktex/tm/packages/files.csv.lzma
2001/05/14 | 5617 | systems/win32/miktex/tm/packages/finbib.tar.lzma
2015/10/20 | 44379 | systems/win32/miktex/tm/packages/findhyph.tar.lzma
2011/10/21 | 89323 | systems/win32/miktex/tm/packages/fink.tar.lzma
@@ -303393,9 +303476,9 @@
2018/01/12 | 157226 | systems/win32/miktex/tm/packages/miktex-zip-bin-x64.tar.lzma
2022/07/26 | 39695 | systems/win32/miktex/tm/packages/miktex-zlib-bin-2.9.tar.lzma
2022/10/15 | 43540 | systems/win32/miktex/tm/packages/miktex-zlib-bin-x64-2.9.tar.lzma
-2022/11/04 | 302896 | systems/win32/miktex/tm/packages/miktex-zzdb1-2.9.tar.lzma
-2022/11/04 | 1515668 | systems/win32/miktex/tm/packages/miktex-zzdb2-2.9.tar.lzma
-2022/11/04 | 1391026 | systems/win32/miktex/tm/packages/miktex-zzdb3-2.9.tar.lzma
+2022/11/05 | 302877 | systems/win32/miktex/tm/packages/miktex-zzdb1-2.9.tar.lzma
+2022/11/05 | 1514196 | systems/win32/miktex/tm/packages/miktex-zzdb2-2.9.tar.lzma
+2022/11/05 | 1390983 | systems/win32/miktex/tm/packages/miktex-zzdb3-2.9.tar.lzma
2022/07/26 | 13519 | systems/win32/miktex/tm/packages/miktex-zzip-bin-2.9.tar.lzma
2022/10/15 | 14426 | systems/win32/miktex/tm/packages/miktex-zzip-bin-x64-2.9.tar.lzma
2016/07/15 | 35914 | systems/win32/miktex/tm/packages/mil3.tar.lzma
@@ -303648,102 +303731,102 @@
2022/11/01 | 230073 | systems/win32/miktex/tm/packages/newverbs__doc.tar.lzma
2022/11/01 | 7969 | systems/win32/miktex/tm/packages/newverbs__source.tar.lzma
2022/11/01 | 3030 | systems/win32/miktex/tm/packages/newverbs.tar.lzma
-2022/11/04 | 672226 | systems/win32/miktex/tm/packages/next/files.csv.lzma
-2022/11/04 | 57053 | systems/win32/miktex/tm/packages/next/miktex-arctrl-bin-x64-2.9.tar.lzma
-2022/11/04 | 1161038 | systems/win32/miktex/tm/packages/next/miktex-asymptote-bin-x64-2.9.tar.lzma
-2022/11/04 | 31588 | systems/win32/miktex/tm/packages/next/miktex-autosp-bin-x64-2.9.tar.lzma
-2022/11/04 | 35215 | systems/win32/miktex/tm/packages/next/miktex-axohelp-bin-x64-2.9.tar.lzma
-2022/11/04 | 65776 | systems/win32/miktex/tm/packages/next/miktex-bibarts-bin-x64-2.9.tar.lzma
-2022/11/04 | 101625 | systems/win32/miktex/tm/packages/next/miktex-bibtex8bit-bin-x64-2.9.tar.lzma
-2022/11/04 | 77418 | systems/win32/miktex/tm/packages/next/miktex-bibtex-bin-x64-2.9.tar.lzma
-2022/11/04 | 31487 | systems/win32/miktex/tm/packages/next/miktex-bzip2-bin-x64-2.9.tar.lzma
-2022/11/04 | 394301 | systems/win32/miktex/tm/packages/next/miktex-cairo-bin-x64-2.9.tar.lzma
-2022/11/04 | 36967 | systems/win32/miktex/tm/packages/next/miktex-chktex-bin-x64-2.9.tar.lzma
-2022/11/04 | 57476 | systems/win32/miktex/tm/packages/next/miktex-cjkutils-bin-x64-2.9.tar.lzma
-2022/11/04 | 271029 | systems/win32/miktex/tm/packages/next/miktex-console-bin-x64-2.9.tar.lzma
-2022/11/04 | 196216 | systems/win32/miktex/tm/packages/next/miktex-curl-bin-x64-2.9.tar.lzma
-2022/11/04 | 103346 | systems/win32/miktex/tm/packages/next/miktex-cweb-bin-x64-2.9.tar.lzma
-2022/11/04 | 24142 | systems/win32/miktex/tm/packages/next/miktex-devnag-bin-x64-2.9.tar.lzma
-2022/11/04 | 472775 | systems/win32/miktex/tm/packages/next/miktex-doc.tar.lzma
-2022/11/04 | 52164 | systems/win32/miktex/tm/packages/next/miktex-dvicopy-bin-x64-2.9.tar.lzma
-2022/11/04 | 326702 | systems/win32/miktex/tm/packages/next/miktex-dvipdfmx-bin-x64-2.9.tar.lzma
-2022/11/04 | 45778 | systems/win32/miktex/tm/packages/next/miktex-dvipng-bin-x64-2.9.tar.lzma
-2022/11/04 | 136019 | systems/win32/miktex/tm/packages/next/miktex-dvips-bin-x64-2.9.tar.lzma
-2022/11/04 | 1457861 | systems/win32/miktex/tm/packages/next/miktex-dvisvgm-bin-x64-2.9.tar.lzma
-2022/11/04 | 115459 | systems/win32/miktex/tm/packages/next/miktex-epstopdf-bin-x64-2.9.tar.lzma
-2022/11/04 | 48800 | systems/win32/miktex/tm/packages/next/miktex-expat-bin-x64-2.9.tar.lzma
-2022/11/04 | 28718 | systems/win32/miktex/tm/packages/next/miktex-findtexmf-bin-x64-2.9.tar.lzma
-2022/11/04 | 62675 | systems/win32/miktex/tm/packages/next/miktex-fmt-bin-x64-2.9.tar.lzma
-2022/11/04 | 155341 | systems/win32/miktex/tm/packages/next/miktex-fontconfig-bin-x64-2.9.tar.lzma
-2022/11/04 | 79421 | systems/win32/miktex/tm/packages/next/miktex-fonts-bin-x64-2.9.tar.lzma
-2022/11/04 | 81628 | systems/win32/miktex/tm/packages/next/miktex-freeglut-bin-x64-2.9.tar.lzma
-2022/11/04 | 295867 | systems/win32/miktex/tm/packages/next/miktex-freetype2-bin-x64-2.9.tar.lzma
-2022/11/04 | 25085 | systems/win32/miktex/tm/packages/next/miktex-fribidixetex-bin-x64-2.9.tar.lzma
-2022/11/04 | 112806 | systems/win32/miktex/tm/packages/next/miktex-gd-bin-x64-2.9.tar.lzma
-2022/11/04 | 73235 | systems/win32/miktex/tm/packages/next/miktex-graphite2-bin-x64-2.9.tar.lzma
-2022/11/04 | 115497 | systems/win32/miktex/tm/packages/next/miktex-gregorio-bin-x64-2.9.tar.lzma
-2022/11/04 | 43912 | systems/win32/miktex/tm/packages/next/miktex-gsf2pk-bin-x64-2.9.tar.lzma
-2022/11/04 | 564563 | systems/win32/miktex/tm/packages/next/miktex-harfbuzz-bin-x64-2.9.tar.lzma
-2022/11/04 | 247447 | systems/win32/miktex/tm/packages/next/miktex-hitex-bin-x64.tar.lzma
-2022/11/04 | 170589 | systems/win32/miktex/tm/packages/next/miktex-hunspell-bin-x64-2.9.tar.lzma
-2022/11/04 | 109528 | systems/win32/miktex/tm/packages/next/miktex-jpeg-bin-x64-2.9.tar.lzma
-2022/11/04 | 18367 | systems/win32/miktex/tm/packages/next/miktex-kpathsea-bin-x64-2.9.tar.lzma
-2022/11/04 | 30550 | systems/win32/miktex/tm/packages/next/miktex-lacheck-bin-x64-2.9.tar.lzma
-2022/11/04 | 688938 | systems/win32/miktex/tm/packages/next/miktex-lcdf-typetools-bin-x64-2.9.tar.lzma
-2022/11/04 | 599103 | systems/win32/miktex/tm/packages/next/miktex-libressl-bin-x64-2.9.tar.lzma
-2022/11/04 | 527949 | systems/win32/miktex/tm/packages/next/miktex-log4cxx-bin-x64-2.9.tar.lzma
-2022/11/04 | 103590 | systems/win32/miktex/tm/packages/next/miktex-lua53-bin-x64-2.9.tar.lzma
-2022/11/04 | 1978369 | systems/win32/miktex/tm/packages/next/miktex-luatex-bin-x64-2.9.tar.lzma
-2022/11/04 | 74094 | systems/win32/miktex/tm/packages/next/miktex-lzma-bin-x64-2.9.tar.lzma
-2022/11/04 | 32361 | systems/win32/miktex/tm/packages/next/miktex-makeindex-bin-x64-2.9.tar.lzma
-2022/11/04 | 8783 | systems/win32/miktex/tm/packages/next/miktex-md5-bin-x64-2.9.tar.lzma
-2022/11/04 | 208897 | systems/win32/miktex/tm/packages/next/miktex-metafont-bin-x64-2.9.tar.lzma
-2022/11/04 | 286818 | systems/win32/miktex/tm/packages/next/miktex-metapost-bin-x64-2.9.tar.lzma
-2022/11/04 | 105656 | systems/win32/miktex/tm/packages/next/miktex-mfware-bin-x64-2.9.tar.lzma
-2022/11/04 | 140095 | systems/win32/miktex/tm/packages/next/miktex-mktex-bin-x64-2.9.tar.lzma
-2022/11/04 | 326609 | systems/win32/miktex/tm/packages/next/miktex-mo-bin-x64-2.9.tar.lzma
-2022/11/04 | 181469 | systems/win32/miktex/tm/packages/next/miktex-mpfr-bin-x64-2.9.tar.lzma
-2022/11/04 | 55414 | systems/win32/miktex/tm/packages/next/miktex-mpmcli-bin-x64-2.9.tar.lzma
-2022/11/04 | 35206 | systems/win32/miktex/tm/packages/next/miktex-mspack-bin-x64-2.9.tar.lzma
-2022/11/04 | 43500 | systems/win32/miktex/tm/packages/next/miktex-mthelp-bin-x64-2.9.tar.lzma
-2022/11/04 | 48238 | systems/win32/miktex/tm/packages/next/miktex-mtprint-bin-x64-2.9.tar.lzma
-2022/11/04 | 52342 | systems/win32/miktex/tm/packages/next/miktex-m-tx-bin-x64-2.9.tar.lzma
-2022/11/04 | 213471 | systems/win32/miktex/tm/packages/next/miktex-omegaware-bin-x64-2.9.tar.lzma
-2022/11/04 | 41415 | systems/win32/miktex/tm/packages/next/miktex-patgen-bin-x64.tar.lzma
-2022/11/04 | 825693 | systems/win32/miktex/tm/packages/next/miktex-pdftex-bin-x64-2.9.tar.lzma
-2022/11/04 | 203418 | systems/win32/miktex/tm/packages/next/miktex-pixman-bin-x64-2.9.tar.lzma
-2022/11/04 | 245226 | systems/win32/miktex/tm/packages/next/miktex-pmx-bin-x64-2.9.tar.lzma
-2022/11/04 | 88301 | systems/win32/miktex/tm/packages/next/miktex-png-bin-x64-2.9.tar.lzma
-2022/11/04 | 1329235 | systems/win32/miktex/tm/packages/next/miktex-poppler-bin-x64-2.9.tar.lzma
-2022/11/04 | 23927 | systems/win32/miktex/tm/packages/next/miktex-popt-bin-x64-2.9.tar.lzma
-2022/11/04 | 37466 | systems/win32/miktex/tm/packages/next/miktex-posix-bin-x64-2.9.tar.lzma
-2022/11/04 | 73039 | systems/win32/miktex/tm/packages/next/miktex-ps2pk-bin-x64-2.9.tar.lzma
-2022/11/04 | 58695 | systems/win32/miktex/tm/packages/next/miktex-psutils-bin-x64-2.9.tar.lzma
-2022/11/04 | 770774 | systems/win32/miktex/tm/packages/next/miktex-ptex-bin-x64.tar.lzma
-2022/11/04 | 1505660 | systems/win32/miktex/tm/packages/next/miktex-runtime-bin-x64-2.9.tar.lzma
-2022/11/04 | 52549 | systems/win32/miktex/tm/packages/next/miktex-synctex-bin-x64-2.9.tar.lzma
-2022/11/04 | 63845 | systems/win32/miktex/tm/packages/next/miktex-tdsutil-bin-x64.tar.lzma
-2022/11/04 | 27541 | systems/win32/miktex/tm/packages/next/miktex-teckit-bin-x64-2.9.tar.lzma
-2022/11/04 | 18496 | systems/win32/miktex/tm/packages/next/miktex-tex2xindy-bin-x64-2.9.tar.lzma
-2022/11/04 | 70848 | systems/win32/miktex/tm/packages/next/miktex-tex4ht-bin-x64-2.9.tar.lzma
-2022/11/04 | 173342 | systems/win32/miktex/tm/packages/next/miktex-tex-bin-x64-2.9.tar.lzma
-2022/11/04 | 68523 | systems/win32/miktex/tm/packages/next/miktex-texify-bin-x64-2.9.tar.lzma
-2022/11/04 | 108489 | systems/win32/miktex/tm/packages/next/miktex-texware-bin-x64-2.9.tar.lzma
-2022/11/04 | 2826249 | systems/win32/miktex/tm/packages/next/miktex-texworks-bin-x64-2.9.tar.lzma
-2022/11/04 | 62542 | systems/win32/miktex/tm/packages/next/miktex-ttf2pk2-bin-x64-2.9.tar.lzma
-2022/11/04 | 42214 | systems/win32/miktex/tm/packages/next/miktex-upmendex-bin-x64.tar.lzma
-2022/11/04 | 33816 | systems/win32/miktex/tm/packages/next/miktex-uriparser-bin-x64-2.9.tar.lzma
-2022/11/04 | 73944 | systems/win32/miktex/tm/packages/next/miktex-web-bin-x64-2.9.tar.lzma
-2022/11/04 | 352252 | systems/win32/miktex/tm/packages/next/miktex-xetex-bin-x64-2.9.tar.lzma
-2022/11/04 | 57455 | systems/win32/miktex/tm/packages/next/miktex-xml2pmx-bin-x64.tar.lzma
-2022/11/04 | 630707 | systems/win32/miktex/tm/packages/next/miktex-yap-bin-x64-2.9.tar.lzma
-2022/11/04 | 43633 | systems/win32/miktex/tm/packages/next/miktex-zlib-bin-x64-2.9.tar.lzma
-2022/11/04 | 302883 | systems/win32/miktex/tm/packages/next/miktex-zzdb1-2.9.tar.lzma
-2022/11/04 | 1513811 | systems/win32/miktex/tm/packages/next/miktex-zzdb2-2.9.tar.lzma
-2022/11/04 | 1391566 | systems/win32/miktex/tm/packages/next/miktex-zzdb3-2.9.tar.lzma
-2022/11/04 | 14373 | systems/win32/miktex/tm/packages/next/miktex-zzip-bin-x64-2.9.tar.lzma
+2022/11/05 | 672226 | systems/win32/miktex/tm/packages/next/files.csv.lzma
+2022/11/05 | 57057 | systems/win32/miktex/tm/packages/next/miktex-arctrl-bin-x64-2.9.tar.lzma
+2022/11/05 | 1162400 | systems/win32/miktex/tm/packages/next/miktex-asymptote-bin-x64-2.9.tar.lzma
+2022/11/05 | 31598 | systems/win32/miktex/tm/packages/next/miktex-autosp-bin-x64-2.9.tar.lzma
+2022/11/05 | 35218 | systems/win32/miktex/tm/packages/next/miktex-axohelp-bin-x64-2.9.tar.lzma
+2022/11/05 | 65843 | systems/win32/miktex/tm/packages/next/miktex-bibarts-bin-x64-2.9.tar.lzma
+2022/11/05 | 101613 | systems/win32/miktex/tm/packages/next/miktex-bibtex8bit-bin-x64-2.9.tar.lzma
+2022/11/05 | 77455 | systems/win32/miktex/tm/packages/next/miktex-bibtex-bin-x64-2.9.tar.lzma
+2022/11/05 | 31503 | systems/win32/miktex/tm/packages/next/miktex-bzip2-bin-x64-2.9.tar.lzma
+2022/11/05 | 394108 | systems/win32/miktex/tm/packages/next/miktex-cairo-bin-x64-2.9.tar.lzma
+2022/11/05 | 37012 | systems/win32/miktex/tm/packages/next/miktex-chktex-bin-x64-2.9.tar.lzma
+2022/11/05 | 57434 | systems/win32/miktex/tm/packages/next/miktex-cjkutils-bin-x64-2.9.tar.lzma
+2022/11/05 | 270877 | systems/win32/miktex/tm/packages/next/miktex-console-bin-x64-2.9.tar.lzma
+2022/11/05 | 196208 | systems/win32/miktex/tm/packages/next/miktex-curl-bin-x64-2.9.tar.lzma
+2022/11/05 | 103469 | systems/win32/miktex/tm/packages/next/miktex-cweb-bin-x64-2.9.tar.lzma
+2022/11/05 | 24118 | systems/win32/miktex/tm/packages/next/miktex-devnag-bin-x64-2.9.tar.lzma
+2022/11/05 | 472895 | systems/win32/miktex/tm/packages/next/miktex-doc.tar.lzma
+2022/11/05 | 52193 | systems/win32/miktex/tm/packages/next/miktex-dvicopy-bin-x64-2.9.tar.lzma
+2022/11/05 | 326708 | systems/win32/miktex/tm/packages/next/miktex-dvipdfmx-bin-x64-2.9.tar.lzma
+2022/11/05 | 45759 | systems/win32/miktex/tm/packages/next/miktex-dvipng-bin-x64-2.9.tar.lzma
+2022/11/05 | 135966 | systems/win32/miktex/tm/packages/next/miktex-dvips-bin-x64-2.9.tar.lzma
+2022/11/05 | 1455891 | systems/win32/miktex/tm/packages/next/miktex-dvisvgm-bin-x64-2.9.tar.lzma
+2022/11/05 | 115443 | systems/win32/miktex/tm/packages/next/miktex-epstopdf-bin-x64-2.9.tar.lzma
+2022/11/05 | 48818 | systems/win32/miktex/tm/packages/next/miktex-expat-bin-x64-2.9.tar.lzma
+2022/11/05 | 28684 | systems/win32/miktex/tm/packages/next/miktex-findtexmf-bin-x64-2.9.tar.lzma
+2022/11/05 | 62690 | systems/win32/miktex/tm/packages/next/miktex-fmt-bin-x64-2.9.tar.lzma
+2022/11/05 | 155357 | systems/win32/miktex/tm/packages/next/miktex-fontconfig-bin-x64-2.9.tar.lzma
+2022/11/05 | 79455 | systems/win32/miktex/tm/packages/next/miktex-fonts-bin-x64-2.9.tar.lzma
+2022/11/05 | 81570 | systems/win32/miktex/tm/packages/next/miktex-freeglut-bin-x64-2.9.tar.lzma
+2022/11/05 | 295729 | systems/win32/miktex/tm/packages/next/miktex-freetype2-bin-x64-2.9.tar.lzma
+2022/11/05 | 25090 | systems/win32/miktex/tm/packages/next/miktex-fribidixetex-bin-x64-2.9.tar.lzma
+2022/11/05 | 112816 | systems/win32/miktex/tm/packages/next/miktex-gd-bin-x64-2.9.tar.lzma
+2022/11/05 | 73234 | systems/win32/miktex/tm/packages/next/miktex-graphite2-bin-x64-2.9.tar.lzma
+2022/11/05 | 115736 | systems/win32/miktex/tm/packages/next/miktex-gregorio-bin-x64-2.9.tar.lzma
+2022/11/05 | 43888 | systems/win32/miktex/tm/packages/next/miktex-gsf2pk-bin-x64-2.9.tar.lzma
+2022/11/05 | 565169 | systems/win32/miktex/tm/packages/next/miktex-harfbuzz-bin-x64-2.9.tar.lzma
+2022/11/05 | 247359 | systems/win32/miktex/tm/packages/next/miktex-hitex-bin-x64.tar.lzma
+2022/11/05 | 170874 | systems/win32/miktex/tm/packages/next/miktex-hunspell-bin-x64-2.9.tar.lzma
+2022/11/05 | 109530 | systems/win32/miktex/tm/packages/next/miktex-jpeg-bin-x64-2.9.tar.lzma
+2022/11/05 | 18357 | systems/win32/miktex/tm/packages/next/miktex-kpathsea-bin-x64-2.9.tar.lzma
+2022/11/05 | 30551 | systems/win32/miktex/tm/packages/next/miktex-lacheck-bin-x64-2.9.tar.lzma
+2022/11/05 | 688971 | systems/win32/miktex/tm/packages/next/miktex-lcdf-typetools-bin-x64-2.9.tar.lzma
+2022/11/05 | 598686 | systems/win32/miktex/tm/packages/next/miktex-libressl-bin-x64-2.9.tar.lzma
+2022/11/05 | 528071 | systems/win32/miktex/tm/packages/next/miktex-log4cxx-bin-x64-2.9.tar.lzma
+2022/11/05 | 103733 | systems/win32/miktex/tm/packages/next/miktex-lua53-bin-x64-2.9.tar.lzma
+2022/11/05 | 1979008 | systems/win32/miktex/tm/packages/next/miktex-luatex-bin-x64-2.9.tar.lzma
+2022/11/05 | 74103 | systems/win32/miktex/tm/packages/next/miktex-lzma-bin-x64-2.9.tar.lzma
+2022/11/05 | 32396 | systems/win32/miktex/tm/packages/next/miktex-makeindex-bin-x64-2.9.tar.lzma
+2022/11/05 | 8786 | systems/win32/miktex/tm/packages/next/miktex-md5-bin-x64-2.9.tar.lzma
+2022/11/05 | 208809 | systems/win32/miktex/tm/packages/next/miktex-metafont-bin-x64-2.9.tar.lzma
+2022/11/05 | 286780 | systems/win32/miktex/tm/packages/next/miktex-metapost-bin-x64-2.9.tar.lzma
+2022/11/05 | 105546 | systems/win32/miktex/tm/packages/next/miktex-mfware-bin-x64-2.9.tar.lzma
+2022/11/05 | 139905 | systems/win32/miktex/tm/packages/next/miktex-mktex-bin-x64-2.9.tar.lzma
+2022/11/05 | 326623 | systems/win32/miktex/tm/packages/next/miktex-mo-bin-x64-2.9.tar.lzma
+2022/11/05 | 181517 | systems/win32/miktex/tm/packages/next/miktex-mpfr-bin-x64-2.9.tar.lzma
+2022/11/05 | 55390 | systems/win32/miktex/tm/packages/next/miktex-mpmcli-bin-x64-2.9.tar.lzma
+2022/11/05 | 35191 | systems/win32/miktex/tm/packages/next/miktex-mspack-bin-x64-2.9.tar.lzma
+2022/11/05 | 43510 | systems/win32/miktex/tm/packages/next/miktex-mthelp-bin-x64-2.9.tar.lzma
+2022/11/05 | 48113 | systems/win32/miktex/tm/packages/next/miktex-mtprint-bin-x64-2.9.tar.lzma
+2022/11/05 | 52295 | systems/win32/miktex/tm/packages/next/miktex-m-tx-bin-x64-2.9.tar.lzma
+2022/11/05 | 214121 | systems/win32/miktex/tm/packages/next/miktex-omegaware-bin-x64-2.9.tar.lzma
+2022/11/05 | 41483 | systems/win32/miktex/tm/packages/next/miktex-patgen-bin-x64.tar.lzma
+2022/11/05 | 825235 | systems/win32/miktex/tm/packages/next/miktex-pdftex-bin-x64-2.9.tar.lzma
+2022/11/05 | 203093 | systems/win32/miktex/tm/packages/next/miktex-pixman-bin-x64-2.9.tar.lzma
+2022/11/05 | 245243 | systems/win32/miktex/tm/packages/next/miktex-pmx-bin-x64-2.9.tar.lzma
+2022/11/05 | 88345 | systems/win32/miktex/tm/packages/next/miktex-png-bin-x64-2.9.tar.lzma
+2022/11/05 | 1327635 | systems/win32/miktex/tm/packages/next/miktex-poppler-bin-x64-2.9.tar.lzma
+2022/11/05 | 23945 | systems/win32/miktex/tm/packages/next/miktex-popt-bin-x64-2.9.tar.lzma
+2022/11/05 | 37457 | systems/win32/miktex/tm/packages/next/miktex-posix-bin-x64-2.9.tar.lzma
+2022/11/05 | 73037 | systems/win32/miktex/tm/packages/next/miktex-ps2pk-bin-x64-2.9.tar.lzma
+2022/11/05 | 58789 | systems/win32/miktex/tm/packages/next/miktex-psutils-bin-x64-2.9.tar.lzma
+2022/11/05 | 770565 | systems/win32/miktex/tm/packages/next/miktex-ptex-bin-x64.tar.lzma
+2022/11/05 | 1505852 | systems/win32/miktex/tm/packages/next/miktex-runtime-bin-x64-2.9.tar.lzma
+2022/11/05 | 52574 | systems/win32/miktex/tm/packages/next/miktex-synctex-bin-x64-2.9.tar.lzma
+2022/11/05 | 63877 | systems/win32/miktex/tm/packages/next/miktex-tdsutil-bin-x64.tar.lzma
+2022/11/05 | 27567 | systems/win32/miktex/tm/packages/next/miktex-teckit-bin-x64-2.9.tar.lzma
+2022/11/05 | 18492 | systems/win32/miktex/tm/packages/next/miktex-tex2xindy-bin-x64-2.9.tar.lzma
+2022/11/05 | 70864 | systems/win32/miktex/tm/packages/next/miktex-tex4ht-bin-x64-2.9.tar.lzma
+2022/11/05 | 173505 | systems/win32/miktex/tm/packages/next/miktex-tex-bin-x64-2.9.tar.lzma
+2022/11/05 | 68546 | systems/win32/miktex/tm/packages/next/miktex-texify-bin-x64-2.9.tar.lzma
+2022/11/05 | 108418 | systems/win32/miktex/tm/packages/next/miktex-texware-bin-x64-2.9.tar.lzma
+2022/11/05 | 2827934 | systems/win32/miktex/tm/packages/next/miktex-texworks-bin-x64-2.9.tar.lzma
+2022/11/05 | 62898 | systems/win32/miktex/tm/packages/next/miktex-ttf2pk2-bin-x64-2.9.tar.lzma
+2022/11/05 | 42150 | systems/win32/miktex/tm/packages/next/miktex-upmendex-bin-x64.tar.lzma
+2022/11/05 | 33854 | systems/win32/miktex/tm/packages/next/miktex-uriparser-bin-x64-2.9.tar.lzma
+2022/11/05 | 74017 | systems/win32/miktex/tm/packages/next/miktex-web-bin-x64-2.9.tar.lzma
+2022/11/05 | 352103 | systems/win32/miktex/tm/packages/next/miktex-xetex-bin-x64-2.9.tar.lzma
+2022/11/05 | 57448 | systems/win32/miktex/tm/packages/next/miktex-xml2pmx-bin-x64.tar.lzma
+2022/11/05 | 630775 | systems/win32/miktex/tm/packages/next/miktex-yap-bin-x64-2.9.tar.lzma
+2022/11/05 | 43627 | systems/win32/miktex/tm/packages/next/miktex-zlib-bin-x64-2.9.tar.lzma
+2022/11/05 | 302846 | systems/win32/miktex/tm/packages/next/miktex-zzdb1-2.9.tar.lzma
+2022/11/05 | 1513118 | systems/win32/miktex/tm/packages/next/miktex-zzdb2-2.9.tar.lzma
+2022/11/05 | 1389877 | systems/win32/miktex/tm/packages/next/miktex-zzdb3-2.9.tar.lzma
+2022/11/05 | 14385 | systems/win32/miktex/tm/packages/next/miktex-zzip-bin-x64-2.9.tar.lzma
2019/03/12 | 1412 | systems/win32/miktex/tm/packages/nextpage.tar.lzma
-2022/11/04 | 1172 | systems/win32/miktex/tm/packages/next/pr.ini
+2022/11/05 | 1172 | systems/win32/miktex/tm/packages/next/pr.ini
2018/07/06 | 145915 | systems/win32/miktex/tm/packages/nexus-otf.tar.lzma
2017/03/30 | 191149 | systems/win32/miktex/tm/packages/nfssext-cfr.tar.lzma
2012/12/20 | 612251 | systems/win32/miktex/tm/packages/nicefilelist.tar.lzma
@@ -304266,7 +304349,7 @@
2022/02/22 | 6917 | systems/win32/miktex/tm/packages/preview.tar.lzma
2019/06/20 | 375963 | systems/win32/miktex/tm/packages/prftree.tar.lzma
2021/04/23 | 274028 | systems/win32/miktex/tm/packages/principia.tar.lzma
-2022/11/04 | 929 | systems/win32/miktex/tm/packages/pr.ini
+2022/11/05 | 929 | systems/win32/miktex/tm/packages/pr.ini
2010/09/27 | 248469 | systems/win32/miktex/tm/packages/printlen.tar.lzma
2008/06/17 | 77313 | systems/win32/miktex/tm/packages/proba.tar.lzma
2017/07/12 | 988486 | systems/win32/miktex/tm/packages/probsoln.tar.lzma
@@ -306267,7 +306350,7 @@
2014/07/05 | 611 | systems/win32/yandy/README
2014/07/05 | 1004628 | systems/win32/yandy/yandy-src.tar.xz
2014/07/05 | 1012416 | systems/win32/yandy.zip
-2022/11/05 | 186 | timestamp
+2022/11/06 | 186 | timestamp
2002/08/26 | 61898 | usergrps/dante/antrag.pdf
2002/08/26 | 118217 | usergrps/dante/antrag.ps
2022/02/28 | 917 | usergrps/dante/dtk/doc/beispiel.bib
diff --git a/FILES.byname.gz b/FILES.byname.gz
index 0b8b65059d..bd0958fc2b 100644
--- a/FILES.byname.gz
+++ b/FILES.byname.gz
Binary files differ
diff --git a/FILES.last07days b/FILES.last07days
index 38a636a65f..8b10a05cc5 100644
--- a/FILES.last07days
+++ b/FILES.last07days
@@ -1,155 +1,398 @@
+2022/11/05 | 986 | support/bib2gls/examples/sample-hierarchical.tex
+2022/11/05 | 97789 | support/bib2gls/examples/sample-constants.pdf
+2022/11/05 | 9614 | support/bib2gls/convertgls2bib.1
+2022/11/05 | 945544 | systems/texlive/tlnet/archive/tex4ht.r64937.tar.xz
+2022/11/05 | 929 | systems/win32/miktex/tm/packages/pr.ini
+2022/11/05 | 913 | support/bib2gls/examples/sample-textsymbols2.tex
+2022/11/05 | 88345 | systems/win32/miktex/tm/packages/next/miktex-png-bin-x64-2.9.tar.lzma
+2022/11/05 | 8786 | systems/win32/miktex/tm/packages/next/miktex-md5-bin-x64-2.9.tar.lzma
+2022/11/05 | 8727504 | systems/texlive/tlnet/archive/pdfmanagement-testphase.doc.r64931.tar.xz
+2022/11/05 | 835036 | systems/texlive/tlnet/archive/proflycee.doc.r64932.tar.xz
+2022/11/05 | 825235 | systems/win32/miktex/tm/packages/next/miktex-pdftex-bin-x64-2.9.tar.lzma
+2022/11/05 | 81570 | systems/win32/miktex/tm/packages/next/miktex-freeglut-bin-x64-2.9.tar.lzma
+2022/11/05 | 79455 | systems/win32/miktex/tm/packages/next/miktex-fonts-bin-x64-2.9.tar.lzma
+2022/11/05 | 791617 | macros/luatex/latex/luacas.zip
+2022/11/05 | 780 | support/bib2gls/examples/sample-msymbols.tex
+2022/11/05 | 77455 | systems/win32/miktex/tm/packages/next/miktex-bibtex-bin-x64-2.9.tar.lzma
+2022/11/05 | 770565 | systems/win32/miktex/tm/packages/next/miktex-ptex-bin-x64.tar.lzma
+2022/11/05 | 75348 | systems/texlive/tlnet/archive/pdfmanagement-testphase.r64931.tar.xz
+2022/11/05 | 74103 | systems/win32/miktex/tm/packages/next/miktex-lzma-bin-x64-2.9.tar.lzma
+2022/11/05 | 74017 | systems/win32/miktex/tm/packages/next/miktex-web-bin-x64-2.9.tar.lzma
+2022/11/05 | 73234 | systems/win32/miktex/tm/packages/next/miktex-graphite2-bin-x64-2.9.tar.lzma
+2022/11/05 | 73042 | support/bib2gls/examples/sample-textsymbols.pdf
+2022/11/05 | 73037 | systems/win32/miktex/tm/packages/next/miktex-ps2pk-bin-x64-2.9.tar.lzma
+2022/11/05 | 718818 | support/bib2gls/bib2gls-begin.pdf
+2022/11/05 | 70864 | systems/win32/miktex/tm/packages/next/miktex-tex4ht-bin-x64-2.9.tar.lzma
+2022/11/05 | 70256 | support/bib2gls/examples/sample-textsymbols2.pdf
+2022/11/05 | 688971 | systems/win32/miktex/tm/packages/next/miktex-lcdf-typetools-bin-x64-2.9.tar.lzma
+2022/11/05 | 68546 | systems/win32/miktex/tm/packages/next/miktex-texify-bin-x64-2.9.tar.lzma
+2022/11/05 | 6793 | support/bib2gls/README.md
+2022/11/05 | 672226 | systems/win32/miktex/tm/packages/next/files.csv.lzma
+2022/11/05 | 672226 | systems/win32/miktex/tm/packages/files.csv.lzma
+2022/11/05 | 65843 | systems/win32/miktex/tm/packages/next/miktex-bibarts-bin-x64-2.9.tar.lzma
+2022/11/05 | 641 | macros/latex/contrib/eolang/eolang.bib
+2022/11/05 | 640 | support/bib2gls/examples/terms.bib
+2022/11/05 | 63877 | systems/win32/miktex/tm/packages/next/miktex-tdsutil-bin-x64.tar.lzma
+2022/11/05 | 630 | support/bib2gls/bib2gls.sh
+2022/11/05 | 630775 | systems/win32/miktex/tm/packages/next/miktex-yap-bin-x64-2.9.tar.lzma
+2022/11/05 | 62898 | systems/win32/miktex/tm/packages/next/miktex-ttf2pk2-bin-x64-2.9.tar.lzma
+2022/11/05 | 62690 | systems/win32/miktex/tm/packages/next/miktex-fmt-bin-x64-2.9.tar.lzma
+2022/11/05 | 611556 | systems/texlive/tlnet/archive/easybook.doc.r64929.tar.xz
+2022/11/05 | 61070 | support/bib2gls/examples/sample-markuplanguages.pdf
+2022/11/05 | 598686 | systems/win32/miktex/tm/packages/next/miktex-libressl-bin-x64-2.9.tar.lzma
+2022/11/05 | 59076 | support/bib2gls/examples/sample-units2.pdf
+2022/11/05 | 58789 | systems/win32/miktex/tm/packages/next/miktex-psutils-bin-x64-2.9.tar.lzma
+2022/11/05 | 584908 | systems/texlive/tlnet/archive/mathastext.doc.r64930.tar.xz
+2022/11/05 | 5834605 | systems/texlive/tlnet/install-tl-unx.tar.gz
+2022/11/05 | 576 | support/bib2gls/examples/binaryoperators.bib
+2022/11/05 | 57448 | systems/win32/miktex/tm/packages/next/miktex-xml2pmx-bin-x64.tar.lzma
+2022/11/05 | 57434 | systems/win32/miktex/tm/packages/next/miktex-cjkutils-bin-x64-2.9.tar.lzma
+2022/11/05 | 57344 | support/bib2gls/examples/sample-units1.pdf
+2022/11/05 | 57057 | systems/win32/miktex/tm/packages/next/miktex-arctrl-bin-x64-2.9.tar.lzma
+2022/11/05 | 565169 | systems/win32/miktex/tm/packages/next/miktex-harfbuzz-bin-x64-2.9.tar.lzma
+2022/11/05 | 557 | support/bib2gls/examples/unaryoperators.bib
+2022/11/05 | 554266 | support/bib2gls/src/bib2gls.bib
+2022/11/05 | 55390 | systems/win32/miktex/tm/packages/next/miktex-mpmcli-bin-x64-2.9.tar.lzma
+2022/11/05 | 5536 | systems/texlive/tlnet/archive/luafindfont.r64936.tar.xz
+2022/11/05 | 5520 | support/bib2gls/examples/sample-multi1.tex
+2022/11/05 | 5455 | support/bib2gls/examples/films.bib
+2022/11/05 | 54221 | support/bib2gls/examples/sample-bacteria.pdf
+2022/11/05 | 54006 | support/bib2gls/examples/sample-chemical.pdf
+2022/11/05 | 539216 | systems/texlive/tlnet/archive/kotex-oblivoir.doc.r64928.tar.xz
+2022/11/05 | 528071 | systems/win32/miktex/tm/packages/next/miktex-log4cxx-bin-x64-2.9.tar.lzma
+2022/11/05 | 52574 | systems/win32/miktex/tm/packages/next/miktex-synctex-bin-x64-2.9.tar.lzma
+2022/11/05 | 52518 | support/bib2gls/examples/sample-nested.pdf
+2022/11/05 | 52505 | support/bib2gls/examples/sample-hierarchical.pdf
+2022/11/05 | 52295 | systems/win32/miktex/tm/packages/next/miktex-m-tx-bin-x64-2.9.tar.lzma
+2022/11/05 | 52193 | systems/win32/miktex/tm/packages/next/miktex-dvicopy-bin-x64-2.9.tar.lzma
+2022/11/05 | 51964 | systems/texlive/tlnet/archive/textcsc.doc.r64935.tar.xz
+2022/11/05 | 51554 | support/bib2gls/examples/sample-authors.pdf
+2022/11/05 | 5135194 | support/bib2gls/bib2gls.pdf
+2022/11/05 | 50320 | support/bib2gls/examples/sample-people.pdf
+2022/11/05 | 49745 | support/bib2gls/examples/sample-citations.pdf
+2022/11/05 | 48 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5
+2022/11/05 | 48818 | systems/win32/miktex/tm/packages/next/miktex-expat-bin-x64-2.9.tar.lzma
+2022/11/05 | 48113 | systems/win32/miktex/tm/packages/next/miktex-mtprint-bin-x64-2.9.tar.lzma
+2022/11/05 | 47687 | macros/latex/contrib/eolang/cactus.pdf
+2022/11/05 | 472895 | systems/win32/miktex/tm/packages/next/miktex-doc.tar.lzma
+2022/11/05 | 45759 | systems/win32/miktex/tm/packages/next/miktex-dvipng-bin-x64-2.9.tar.lzma
+2022/11/05 | 455 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc
+2022/11/05 | 455 | systems/texlive/tlnet/install-tl.zip.sha512.asc
+2022/11/05 | 455 | systems/texlive/tlnet/install-tl-windows.exe.sha512.asc
+2022/11/05 | 455 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512.asc
+2022/11/05 | 45419 | support/bib2gls/bib2gls-en.xml
+2022/11/05 | 44644 | support/bib2gls/examples/sample-units3.pdf
+2022/11/05 | 4446 | support/bib2gls/examples/people.bib
+2022/11/05 | 43888 | systems/win32/miktex/tm/packages/next/miktex-gsf2pk-bin-x64-2.9.tar.lzma
+2022/11/05 | 43792 | systems/texlive/tlnet/archive/tuda-ci.r64933.tar.xz
+2022/11/05 | 43627 | systems/win32/miktex/tm/packages/next/miktex-zlib-bin-x64-2.9.tar.lzma
+2022/11/05 | 43510 | systems/win32/miktex/tm/packages/next/miktex-mthelp-bin-x64-2.9.tar.lzma
+2022/11/05 | 43484 | systems/texlive/tlnet/archive/kotex-oblivoir.r64928.tar.xz
+2022/11/05 | 423096 | systems/texlive/tlnet/archive/texlive-scripts.doc.r64922.tar.xz
+2022/11/05 | 42150 | systems/win32/miktex/tm/packages/next/miktex-upmendex-bin-x64.tar.lzma
+2022/11/05 | 41483 | systems/win32/miktex/tm/packages/next/miktex-patgen-bin-x64.tar.lzma
+2022/11/05 | 4078 | support/bib2gls/src/bib2gls-cite.bib
+2022/11/05 | 40754 | support/bib2gls/convertgls2bib.jar
+2022/11/05 | 4053 | support/bib2gls/examples/usergroups.bib
+2022/11/05 | 404 | support/bib2gls/convertgls2bib.sh
+2022/11/05 | 4045 | support/bib2gls/src/convertgls2bib.pod
+2022/11/05 | 4015231 | biblio/ctan-bibdata.zip
+2022/11/05 | 394108 | systems/win32/miktex/tm/packages/next/miktex-cairo-bin-x64-2.9.tar.lzma
+2022/11/05 | 37457 | systems/win32/miktex/tm/packages/next/miktex-posix-bin-x64-2.9.tar.lzma
+2022/11/05 | 37012 | systems/win32/miktex/tm/packages/next/miktex-chktex-bin-x64-2.9.tar.lzma
+2022/11/05 | 3672 | support/bib2gls/examples/mathsobjects.bib
+2022/11/05 | 3622884 | biblio/ctan-bibdata/ctan.pdf
+2022/11/05 | 353466 | support/bib2gls/bib2gls.jar
+2022/11/05 | 35218 | systems/win32/miktex/tm/packages/next/miktex-axohelp-bin-x64-2.9.tar.lzma
+2022/11/05 | 352103 | systems/win32/miktex/tm/packages/next/miktex-xetex-bin-x64-2.9.tar.lzma
+2022/11/05 | 35191 | systems/win32/miktex/tm/packages/next/miktex-mspack-bin-x64-2.9.tar.lzma
+2022/11/05 | 3496 | support/bib2gls/examples/books.bib
+2022/11/05 | 338 | support/bib2gls/examples/interpret-preamble2.bib
+2022/11/05 | 33854 | systems/win32/miktex/tm/packages/next/miktex-uriparser-bin-x64-2.9.tar.lzma
+2022/11/05 | 33176 | support/bib2gls/src/gls2bib-src.zip
+2022/11/05 | 330706 | support/bib2gls/examples/sample-multi2.pdf
+2022/11/05 | 328 | support/bib2gls/examples/no-interpret-preamble.bib
+2022/11/05 | 328 | support/bib2gls/examples/interpret-preamble.bib
+2022/11/05 | 326708 | systems/win32/miktex/tm/packages/next/miktex-dvipdfmx-bin-x64-2.9.tar.lzma
+2022/11/05 | 326623 | systems/win32/miktex/tm/packages/next/miktex-mo-bin-x64-2.9.tar.lzma
+2022/11/05 | 3254 | support/bib2gls/examples/sample-usergroups.tex
+2022/11/05 | 32396 | systems/win32/miktex/tm/packages/next/miktex-makeindex-bin-x64-2.9.tar.lzma
+2022/11/05 | 3211 | support/bib2gls/examples/mathgreek.bib
+2022/11/05 | 31598 | systems/win32/miktex/tm/packages/next/miktex-autosp-bin-x64-2.9.tar.lzma
+2022/11/05 | 31503 | systems/win32/miktex/tm/packages/next/miktex-bzip2-bin-x64-2.9.tar.lzma
+2022/11/05 | 3129 | support/bib2gls/examples/sample-maths.tex
+2022/11/05 | 30856 | macros/latex/contrib/eolang/eolang.dtx
+2022/11/05 | 30551 | systems/win32/miktex/tm/packages/next/miktex-lacheck-bin-x64-2.9.tar.lzma
+2022/11/05 | 302877 | systems/win32/miktex/tm/packages/miktex-zzdb1-2.9.tar.lzma
+2022/11/05 | 302846 | systems/win32/miktex/tm/packages/next/miktex-zzdb1-2.9.tar.lzma
+2022/11/05 | 2994 | support/bib2gls/examples/bigmathsymbols.bib
+2022/11/05 | 295729 | systems/win32/miktex/tm/packages/next/miktex-freetype2-bin-x64-2.9.tar.lzma
+2022/11/05 | 28684 | systems/win32/miktex/tm/packages/next/miktex-findtexmf-bin-x64-2.9.tar.lzma
+2022/11/05 | 286780 | systems/win32/miktex/tm/packages/next/miktex-metapost-bin-x64-2.9.tar.lzma
+2022/11/05 | 2827934 | systems/win32/miktex/tm/packages/next/miktex-texworks-bin-x64-2.9.tar.lzma
+2022/11/05 | 27567 | systems/win32/miktex/tm/packages/next/miktex-teckit-bin-x64-2.9.tar.lzma
+2022/11/05 | 2744451 | biblio/ctan-bibdata/ctan.bib
+2022/11/05 | 270877 | systems/win32/miktex/tm/packages/next/miktex-console-bin-x64-2.9.tar.lzma
+2022/11/05 | 2701752 | systems/texlive/tlnet/archive/simpleicons.r64926.tar.xz
+2022/11/05 | 267620 | systems/texlive/tlnet/archive/erewhon-math.r64925.tar.xz
+2022/11/05 | 266 | support/bib2gls/examples/topics.bib
+2022/11/05 | 266494 | support/bib2gls/src/bib2gls-src.zip
+2022/11/05 | 2628 | systems/texlive/tlnet/archive/luaquotes.r64934.tar.xz
+2022/11/05 | 2628 | macros/latex/contrib/eolang/eolang.ins
+2022/11/05 | 2585 | support/bib2gls/examples/chemicalformula.bib
+2022/11/05 | 255310 | support/bib2gls/examples/sample-multi1.pdf
+2022/11/05 | 25518540 | systems/texlive/tlnet/install-tl.zip
+2022/11/05 | 25090 | systems/win32/miktex/tm/packages/next/miktex-fribidixetex-bin-x64-2.9.tar.lzma
+2022/11/05 | 247359 | systems/win32/miktex/tm/packages/next/miktex-hitex-bin-x64.tar.lzma
+2022/11/05 | 245243 | systems/win32/miktex/tm/packages/next/miktex-pmx-bin-x64-2.9.tar.lzma
+2022/11/05 | 2424508 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz
+2022/11/05 | 2419 | support/bib2gls/examples/minerals.bib
+2022/11/05 | 24118 | systems/win32/miktex/tm/packages/next/miktex-devnag-bin-x64-2.9.tar.lzma
+2022/11/05 | 23945 | systems/win32/miktex/tm/packages/next/miktex-popt-bin-x64-2.9.tar.lzma
+2022/11/05 | 2392 | support/bib2gls/examples/constants.bib
+2022/11/05 | 23827 | support/bib2gls/examples/sample-usergroups.pdf
+2022/11/05 | 2367 | support/bib2gls/examples/sample-people.tex
+2022/11/05 | 236672 | support/bib2gls/src/bib2gls-begin.tex
+2022/11/05 | 2301 | support/bib2gls/examples/miscsymbols.bib
+2022/11/05 | 22 | support/bib2gls/DEPENDS.txt
+2022/11/05 | 217836 | systems/texlive/tlnet/archive/xcharter-math.r64927.tar.xz
+2022/11/05 | 214121 | systems/win32/miktex/tm/packages/next/miktex-omegaware-bin-x64-2.9.tar.lzma
+2022/11/05 | 208809 | systems/win32/miktex/tm/packages/next/miktex-metafont-bin-x64-2.9.tar.lzma
+2022/11/05 | 20704 | systems/texlive/tlnet/archive/proflycee.r64932.tar.xz
+2022/11/05 | 20435953 | systems/texlive/tlnet/install-tl-windows.exe
+2022/11/05 | 203093 | systems/win32/miktex/tm/packages/next/miktex-pixman-bin-x64-2.9.tar.lzma
+2022/11/05 | 1987428 | systems/texlive/tlnet/archive/erewhon-math.doc.r64925.tar.xz
+2022/11/05 | 1983352 | systems/texlive/tlnet/archive/simpleicons.doc.r64926.tar.xz
+2022/11/05 | 1979008 | systems/win32/miktex/tm/packages/next/miktex-luatex-bin-x64-2.9.tar.lzma
+2022/11/05 | 196208 | systems/win32/miktex/tm/packages/next/miktex-curl-bin-x64-2.9.tar.lzma
+2022/11/05 | 19584 | support/bib2gls/bib2gls.1
+2022/11/05 | 1948324 | systems/texlive/tlnet/archive/xcharter-math.doc.r64927.tar.xz
+2022/11/05 | 1941 | support/bib2gls/examples/sample-citations.tex
+2022/11/05 | 1856924 | systems/texlive/tlnet/archive/concmath-otf.doc.r64924.tar.xz
+2022/11/05 | 18492 | systems/win32/miktex/tm/packages/next/miktex-tex2xindy-bin-x64-2.9.tar.lzma
+2022/11/05 | 18357 | systems/win32/miktex/tm/packages/next/miktex-kpathsea-bin-x64-2.9.tar.lzma
+2022/11/05 | 181517 | systems/win32/miktex/tm/packages/next/miktex-mpfr-bin-x64-2.9.tar.lzma
+2022/11/05 | 17802958 | systems/texlive/tlnet/tlpkg/texlive.tlpdb
+2022/11/05 | 1772 | support/bib2gls/examples/markuplanguages.bib
+2022/11/05 | 17650 | support/bib2gls/CHANGES
+2022/11/05 | 1740 | support/bib2gls/examples/sample-units3.tex
+2022/11/05 | 173505 | systems/win32/miktex/tm/packages/next/miktex-tex-bin-x64-2.9.tar.lzma
+2022/11/05 | 1734 | support/bib2gls/examples/sample-media.tex
+2022/11/05 | 17188 | systems/texlive/tlnet/archive/mathastext.r64930.tar.xz
+2022/11/05 | 170874 | systems/win32/miktex/tm/packages/next/miktex-hunspell-bin-x64-2.9.tar.lzma
+2022/11/05 | 1678 | biblio/ctan-bibdata/biblatex-ctan/ctan.bbx
+2022/11/05 | 166484 | support/bib2gls/examples/sample-media.pdf
+2022/11/05 | 16564 | systems/texlive/tlnet/archive/easybook.r64929.tar.xz
+2022/11/05 | 16525 | support/bib2gls/src/bib2gls-terms.bib
+2022/11/05 | 16480 | systems/texlive/tlnet/archive/easybook.source.r64929.tar.xz
+2022/11/05 | 1624931 | support/bib2gls/texparserlib.jar
+2022/11/05 | 1603 | support/bib2gls/examples/animals.bib
+2022/11/05 | 159 | macros/latex/contrib/eolang/DEPENDS.txt
+2022/11/05 | 1580 | support/bib2gls/examples/derivedunits.bib
+2022/11/05 | 155357 | systems/win32/miktex/tm/packages/next/miktex-fontconfig-bin-x64-2.9.tar.lzma
+2022/11/05 | 153 | systems/texlive/tlnet/install-tl-windows.exe.sha512
+2022/11/05 | 152 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512
+2022/11/05 | 1514196 | systems/win32/miktex/tm/packages/miktex-zzdb2-2.9.tar.lzma
+2022/11/05 | 1513118 | systems/win32/miktex/tm/packages/next/miktex-zzdb2-2.9.tar.lzma
+2022/11/05 | 1506 | support/bib2gls/examples/sample-authors.tex
+2022/11/05 | 1505852 | systems/win32/miktex/tm/packages/next/miktex-runtime-bin-x64-2.9.tar.lzma
+2022/11/05 | 145 | systems/texlive/tlnet/install-tl.zip.sha512
+2022/11/05 | 1455891 | systems/win32/miktex/tm/packages/next/miktex-dvisvgm-bin-x64-2.9.tar.lzma
+2022/11/05 | 144 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512
+2022/11/05 | 14385 | systems/win32/miktex/tm/packages/next/miktex-zzip-bin-x64-2.9.tar.lzma
+2022/11/05 | 139905 | systems/win32/miktex/tm/packages/next/miktex-mktex-bin-x64-2.9.tar.lzma
+2022/11/05 | 1391 | support/bib2gls/examples/sample-markuplanguages.tex
+2022/11/05 | 1391320 | support/bib2gls/src/texparser-src.zip
+2022/11/05 | 1390983 | systems/win32/miktex/tm/packages/miktex-zzdb3-2.9.tar.lzma
+2022/11/05 | 1389877 | systems/win32/miktex/tm/packages/next/miktex-zzdb3-2.9.tar.lzma
+2022/11/05 | 1382 | support/bib2gls/examples/sample-bacteria.tex
+2022/11/05 | 1378 | support/bib2gls/examples/sample-constants.tex
+2022/11/05 | 1371 | support/bib2gls/examples/citations.bib
+2022/11/05 | 136668 | systems/texlive/tlnet/archive/pdfmanagement-testphase.source.r64931.tar.xz
+2022/11/05 | 136384 | systems/texlive/tlnet/archive/tex4ht.doc.r64937.tar.xz
+2022/11/05 | 135966 | systems/win32/miktex/tm/packages/next/miktex-dvips-bin-x64-2.9.tar.lzma
+2022/11/05 | 1352 | macros/latex/contrib/eolang/README.md
+2022/11/05 | 1344 | support/bib2gls/examples/sample-nested.tex
+2022/11/05 | 133888 | systems/texlive/tlnet/archive/concmath-otf.r64924.tar.xz
+2022/11/05 | 1327635 | systems/win32/miktex/tm/packages/next/miktex-poppler-bin-x64-2.9.tar.lzma
+2022/11/05 | 1291 | support/bib2gls/examples/sample-chemical.tex
+2022/11/05 | 12775 | support/bib2gls/examples/sample-multi2.tex
+2022/11/05 | 1273088 | systems/texlive/tlnet/archive/tex4ht.source.r64937.tar.xz
+2022/11/05 | 1265 | support/bib2gls/examples/mathsrelations.bib
+2022/11/05 | 124356 | systems/texlive/tlnet/archive/luafindfont.doc.r64936.tar.xz
+2022/11/05 | 1234 | support/bib2gls/examples/sample-units2.tex
+2022/11/05 | 1219 | support/bib2gls/examples/bacteria.bib
+2022/11/05 | 121219 | support/bib2gls/examples/sample-maths.pdf
+2022/11/05 | 1207 | support/bib2gls/examples/sample-textsymbols.tex
+2022/11/05 | 1172 | systems/win32/miktex/tm/packages/next/pr.ini
+2022/11/05 | 11628 | support/bib2gls/src/bib2gls.pod
+2022/11/05 | 1162400 | systems/win32/miktex/tm/packages/next/miktex-asymptote-bin-x64-2.9.tar.lzma
+2022/11/05 | 115736 | systems/win32/miktex/tm/packages/next/miktex-gregorio-bin-x64-2.9.tar.lzma
+2022/11/05 | 115443 | systems/win32/miktex/tm/packages/next/miktex-epstopdf-bin-x64-2.9.tar.lzma
+2022/11/05 | 11414020 | support/bib2gls.zip
+2022/11/05 | 114004 | systems/texlive/tlnet/archive/luaquotes.doc.r64934.tar.xz
+2022/11/05 | 113712 | systems/texlive/tlnet/archive/texlive-scripts.r64922.tar.xz
+2022/11/05 | 1131412 | systems/texlive/tlnet/archive/tuda-ci.doc.r64933.tar.xz
+2022/11/05 | 1131113 | macros/latex/contrib/eolang.zip
+2022/11/05 | 112816 | systems/win32/miktex/tm/packages/next/miktex-gd-bin-x64-2.9.tar.lzma
+2022/11/05 | 1120 | systems/texlive/tlnet/archive/textcsc.r64935.tar.xz
+2022/11/05 | 1106103 | macros/latex/contrib/eolang/eolang.pdf
+2022/11/05 | 109530 | systems/win32/miktex/tm/packages/next/miktex-jpeg-bin-x64-2.9.tar.lzma
+2022/11/05 | 109211 | support/bib2gls/examples/sample-msymbols.pdf
+2022/11/05 | 1091 | support/bib2gls/examples/vegetables.bib
+2022/11/05 | 108418 | systems/win32/miktex/tm/packages/next/miktex-texware-bin-x64-2.9.tar.lzma
+2022/11/05 | 1083 | macros/latex/contrib/eolang/LICENSE.txt
+2022/11/05 | 1070 | support/bib2gls/examples/sample-units1.tex
+2022/11/05 | 1064409 | support/bib2gls/src/bib2gls.tex
+2022/11/05 | 105546 | systems/win32/miktex/tm/packages/next/miktex-mfware-bin-x64-2.9.tar.lzma
+2022/11/05 | 105248 | systems/texlive/tlnet/archive/mathastext.source.r64930.tar.xz
+2022/11/05 | 103733 | systems/win32/miktex/tm/packages/next/miktex-lua53-bin-x64-2.9.tar.lzma
+2022/11/05 | 103469 | systems/win32/miktex/tm/packages/next/miktex-cweb-bin-x64-2.9.tar.lzma
+2022/11/05 | 1031 | support/bib2gls/examples/baseunits.bib
+2022/11/05 | 101613 | systems/win32/miktex/tm/packages/next/miktex-bibtex8bit-bin-x64-2.9.tar.lzma
+2022/11/04 | 9926 | macros/luatex/latex/luacas/tex/core/binaryoperation/product.lua
+2022/11/04 | 9890 | macros/luatex/latex/luacas/tex/core/functionexpression.lua
2022/11/04 | 9751 | macros/latex/contrib/tuda-ci/tex/tudamecheng.cfg
+2022/11/04 | 9708 | macros/luatex/latex/luacas/tex/calculus/derivativeexpression.lua
2022/11/04 | 968 | macros/latex/contrib/proflycee/doc/cap.csv
+2022/11/04 | 9634 | macros/luatex/latex/luacas/tex/algebra/ring.lua
+2022/11/04 | 9605 | macros/luatex/latex/luacas/tex/_lib/inspect.lua
+2022/11/04 | 957 | macros/luatex/latex/luacas/doc/tutorial/tut3/demotut3.dat
2022/11/04 | 9376 | macros/latex/contrib/tuda-ci/tex/beamerouterthemeTUDa.sty
-2022/11/04 | 929 | systems/win32/miktex/tm/packages/pr.ini
+2022/11/04 | 930 | macros/luatex/latex/luacas/tex/test/expressions/simplify.lua
2022/11/04 | 92336 | systems/texlive/tlnet/archive/glossaries.r64919.tar.xz
+2022/11/04 | 902 | macros/luatex/latex/luacas/tex/test/expressions/equations.lua
+2022/11/04 | 8940 | macros/luatex/latex/luacas/tex/core/expression.lua
2022/11/04 | 8887 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaSciPoster.tex
-2022/11/04 | 88301 | systems/win32/miktex/tm/packages/next/miktex-png-bin-x64-2.9.tar.lzma
-2022/11/04 | 8783 | systems/win32/miktex/tm/packages/next/miktex-md5-bin-x64-2.9.tar.lzma
+2022/11/04 | 8622 | macros/luatex/latex/luacas/tex/core/binaryoperation/sum.lua
2022/11/04 | 8579 | macros/latex/contrib/tuda-ci/tex/beamerinnerthemeTUDa.sty
2022/11/04 | 8550 | fonts/erewhon-math/fourier-otf.sty
+2022/11/04 | 8536 | macros/luatex/latex/luacas/doc/reference/ref.tex
2022/11/04 | 84046 | macros/latex/contrib/easybook/easybook.tex
-2022/11/04 | 825693 | systems/win32/miktex/tm/packages/next/miktex-pdftex-bin-x64-2.9.tar.lzma
-2022/11/04 | 81628 | systems/win32/miktex/tm/packages/next/miktex-freeglut-bin-x64-2.9.tar.lzma
+2022/11/04 | 837 | macros/luatex/latex/luacas/tex/test/expressions/collect.lua
+2022/11/04 | 835 | macros/luatex/latex/luacas/tex/test/expressions/rationalexponent.lua
+2022/11/04 | 8300 | macros/luatex/latex/luacas/tex/test/parser.lua
+2022/11/04 | 8224 | support/TeX4ht/source/mktex4ht-cnf.tex
2022/11/04 | 80204 | fonts/xcharter-math/XCharter-Math-Bold.otf
-2022/11/04 | 79421 | systems/win32/miktex/tm/packages/next/miktex-fonts-bin-x64-2.9.tar.lzma
-2022/11/04 | 77418 | systems/win32/miktex/tm/packages/next/miktex-bibtex-bin-x64-2.9.tar.lzma
-2022/11/04 | 770774 | systems/win32/miktex/tm/packages/next/miktex-ptex-bin-x64.tar.lzma
+2022/11/04 | 799414 | install/macros/luatex/latex/luacas.tds.zip
+2022/11/04 | 7876 | macros/luatex/latex/luacas/doc/reference/ref_core/ref_core.tex
+2022/11/04 | 7828 | macros/luatex/latex/luacas/doc/tutorial/tut1/tut1.tex
+2022/11/04 | 7722 | macros/luatex/latex/luacas/tex/luacas.sty
2022/11/04 | 75665 | macros/latex/contrib/easybook/easybook.dtx
-2022/11/04 | 74094 | systems/win32/miktex/tm/packages/next/miktex-lzma-bin-x64-2.9.tar.lzma
-2022/11/04 | 73944 | systems/win32/miktex/tm/packages/next/miktex-web-bin-x64-2.9.tar.lzma
+2022/11/04 | 7408 | systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl
2022/11/04 | 736 | macros/latex/contrib/proflycee/doc/perroquet.csv
+2022/11/04 | 7351 | macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_classes/ref_calculus_classes.tex
2022/11/04 | 7336 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaBeamer.tex
-2022/11/04 | 73235 | systems/win32/miktex/tm/packages/next/miktex-graphite2-bin-x64-2.9.tar.lzma
-2022/11/04 | 73039 | systems/win32/miktex/tm/packages/next/miktex-ps2pk-bin-x64-2.9.tar.lzma
+2022/11/04 | 7334 | macros/luatex/latex/luacas/tex/test/polynomials/polynomial.lua
+2022/11/04 | 7332 | macros/luatex/latex/luacas/tex/algebra/polynomialring/zassenhausfactoring.lua
2022/11/04 | 7218 | systems/win32/miktex/tm/packages/huaz__source.tar.lzma
2022/11/04 | 716709 | macros/latex/contrib/proflycee/doc/ProfLycee-doc.pdf
+2022/11/04 | 7120 | macros/luatex/latex/luacas/tex/algebra/sqrtexpression.lua
+2022/11/04 | 7111 | macros/luatex/latex/luacas/tex/algebra/rational.lua
2022/11/04 | 7101 | language/korean/kotex-oblivoir/doc/ob-mathleading-doc.tex
-2022/11/04 | 70848 | systems/win32/miktex/tm/packages/next/miktex-tex4ht-bin-x64-2.9.tar.lzma
2022/11/04 | 7022 | macros/latex/contrib/tuda-ci/tex/tudabeamer.cls
2022/11/04 | 699140 | systems/texlive/tlnet/archive/hep-paper.doc.r64917.tar.xz
2022/11/04 | 694246 | macros/latex/contrib/mathastext.zip
-2022/11/04 | 688938 | systems/win32/miktex/tm/packages/next/miktex-lcdf-typetools-bin-x64-2.9.tar.lzma
+2022/11/04 | 6936 | macros/luatex/latex/luacas/tex/test/calculus/integrals.lua
+2022/11/04 | 690 | macros/luatex/latex/luacas/tex/algebra/_init.lua
2022/11/04 | 6881 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaLeaflet.tex
-2022/11/04 | 68523 | systems/win32/miktex/tm/packages/next/miktex-texify-bin-x64-2.9.tar.lzma
2022/11/04 | 674096 | language/korean/kotex-oblivoir.zip
-2022/11/04 | 672234 | systems/win32/miktex/tm/packages/files.csv.lzma
-2022/11/04 | 672226 | systems/win32/miktex/tm/packages/next/files.csv.lzma
2022/11/04 | 6676 | fonts/erewhon-math/README.md
2022/11/04 | 665 | fonts/simpleicons/README.md
+2022/11/04 | 6636 | macros/luatex/latex/luacas/doc/tutorial/tut.tex
2022/11/04 | 6627 | fonts/xcharter-math/xcharter-otf.sty
2022/11/04 | 662322 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaPub.pdf
-2022/11/04 | 65776 | systems/win32/miktex/tm/packages/next/miktex-bibarts-bin-x64-2.9.tar.lzma
+2022/11/04 | 6610 | macros/luatex/latex/luacas/doc/intro/intro.tex
2022/11/04 | 6466 | macros/latex/contrib/tuda-ci/tex/tudarules.sty
2022/11/04 | 6443082 | macros/latex/contrib/tuda-ci.zip
-2022/11/04 | 63845 | systems/win32/miktex/tm/packages/next/miktex-tdsutil-bin-x64.tar.lzma
+2022/11/04 | 6409 | macros/luatex/latex/luacas/tex/core/binaryoperation/power.lua
2022/11/04 | 635903 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaPhD.pdf
2022/11/04 | 630895 | macros/latex/contrib/easybook.zip
-2022/11/04 | 630707 | systems/win32/miktex/tm/packages/next/miktex-yap-bin-x64-2.9.tar.lzma
+2022/11/04 | 6298 | macros/luatex/latex/luacas/tex/test/expressions/autosimplify.lua
2022/11/04 | 627530 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaThesis.pdf
-2022/11/04 | 62675 | systems/win32/miktex/tm/packages/next/miktex-fmt-bin-x64-2.9.tar.lzma
-2022/11/04 | 62542 | systems/win32/miktex/tm/packages/next/miktex-ttf2pk2-bin-x64-2.9.tar.lzma
+2022/11/04 | 6268 | macros/luatex/latex/luacas/tex/test/polynomials/polynomialmod.lua
+2022/11/04 | 619595 | support/TeX4ht/source/tex4ht-html4.tex
+2022/11/04 | 6105 | macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus.tex
2022/11/04 | 604 | fonts/simpleicons/tfm/SimpleIcons--simpleiconsOneZero.tfm
2022/11/04 | 6041 | macros/latex/contrib/tuda-ci/tex/tudacolors.sty
-2022/11/04 | 599103 | systems/win32/miktex/tm/packages/next/miktex-libressl-bin-x64-2.9.tar.lzma
+2022/11/04 | 601 | macros/luatex/latex/luacas/tex/test/polynomials/partialfractions.lua
2022/11/04 | 598742 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaBeamer.pdf
2022/11/04 | 589399 | macros/latex/contrib/easybook/easybook.pdf
-2022/11/04 | 58695 | systems/win32/miktex/tm/packages/next/miktex-psutils-bin-x64-2.9.tar.lzma
2022/11/04 | 5859 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaPoster.tex
-2022/11/04 | 5834873 | systems/texlive/tlnet/install-tl-unx.tar.gz
2022/11/04 | 578580 | macros/latex/contrib/mathastext/mathastext.pdf
-2022/11/04 | 57476 | systems/win32/miktex/tm/packages/next/miktex-cjkutils-bin-x64-2.9.tar.lzma
-2022/11/04 | 57455 | systems/win32/miktex/tm/packages/next/miktex-xml2pmx-bin-x64.tar.lzma
-2022/11/04 | 57053 | systems/win32/miktex/tm/packages/next/miktex-arctrl-bin-x64-2.9.tar.lzma
+2022/11/04 | 5768 | macros/luatex/latex/luacas/tex/algebra/equation.lua
2022/11/04 | 570428 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaExercise.pdf
+2022/11/04 | 5701 | macros/luatex/latex/luacas/tex/calculus/diffexpression.lua
2022/11/04 | 567047 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaLetter.pdf
-2022/11/04 | 564563 | systems/win32/miktex/tm/packages/next/miktex-harfbuzz-bin-x64-2.9.tar.lzma
2022/11/04 | 563674 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaSciPoster.pdf
+2022/11/04 | 5622 | macros/luatex/latex/luacas/tex/algebra/polynomialring/berlekampfactoring.lua
2022/11/04 | 559873 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaLeaflet.pdf
-2022/11/04 | 55414 | systems/win32/miktex/tm/packages/next/miktex-mpmcli-bin-x64-2.9.tar.lzma
+2022/11/04 | 558 | macros/luatex/latex/luacas/tex/core/binaryoperation/difference.lua
+2022/11/04 | 557 | macros/luatex/latex/luacas/tex/core/binaryoperation/quotient.lua
2022/11/04 | 551914 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaAnnouncement.pdf
+2022/11/04 | 550 | macros/luatex/latex/luacas/tex/test/expressions/functions.lua
2022/11/04 | 5433 | macros/latex/contrib/tuda-ci/tex/tudasize9pt.clo
2022/11/04 | 5391 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaLetter.tex
2022/11/04 | 5381 | macros/latex/contrib/tuda-ci/tex/tudalettersize10pt.clo
+2022/11/04 | 537 | macros/luatex/latex/luacas/tex/test/rings/modulararithmetic.lua
2022/11/04 | 536024 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaPoster.pdf
2022/11/04 | 5300 | macros/latex/contrib/mathastext/README.md
2022/11/04 | 529 | macros/latex/contrib/proflycee/doc/testscript.py
2022/11/04 | 52964 | macros/unicodetex/latex/textcsc.zip
-2022/11/04 | 527949 | systems/win32/miktex/tm/packages/next/miktex-log4cxx-bin-x64-2.9.tar.lzma
+2022/11/04 | 5281 | macros/luatex/latex/luacas/doc/appendix/luacas.dat
2022/11/04 | 527090 | macros/latex/contrib/tuda-ci/doc/DEMO-TUDaReport.pdf
2022/11/04 | 5264 | fonts/concmath-otf/concmath-otf.sty
-2022/11/04 | 52549 | systems/win32/miktex/tm/packages/next/miktex-synctex-bin-x64-2.9.tar.lzma
-2022/11/04 | 52342 | systems/win32/miktex/tm/packages/next/miktex-m-tx-bin-x64-2.9.tar.lzma
-2022/11/04 | 52164 | systems/win32/miktex/tm/packages/next/miktex-dvicopy-bin-x64-2.9.tar.lzma
+2022/11/04 | 5239 | macros/luatex/latex/luacas/tex/algebra/logarithm.lua
2022/11/04 | 4930 | systems/win32/miktex/tm/packages/huaz.tar.lzma
2022/11/04 | 4927266 | fonts/simpleicons.zip
-2022/11/04 | 48 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5
-2022/11/04 | 48800 | systems/win32/miktex/tm/packages/next/miktex-expat-bin-x64-2.9.tar.lzma
+2022/11/04 | 4872 | macros/luatex/latex/luacas/tex/algebra/rootexpression.lua
2022/11/04 | 48462 | macros/unicodetex/latex/textcsc/textcsc-documentation.pdf
-2022/11/04 | 48238 | systems/win32/miktex/tm/packages/next/miktex-mtprint-bin-x64-2.9.tar.lzma
-2022/11/04 | 472775 | systems/win32/miktex/tm/packages/next/miktex-doc.tar.lzma
+2022/11/04 | 4833 | macros/luatex/latex/luacas/tex/algebra/integerquotientring.lua
+2022/11/04 | 472 | macros/luatex/latex/luacas/tex/core/_init.lua
+2022/11/04 | 4692 | macros/luatex/latex/luacas/tex/test/main.lua
+2022/11/04 | 4610 | macros/luatex/latex/luacas/doc/luacas.tex
+2022/11/04 | 4582 | macros/luatex/latex/luacas/tex/_lib/table.lua
2022/11/04 | 4579 | fonts/neo-euler/neo-euler.sty
-2022/11/04 | 45778 | systems/win32/miktex/tm/packages/next/miktex-dvipng-bin-x64-2.9.tar.lzma
-2022/11/04 | 455 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc
-2022/11/04 | 455 | systems/texlive/tlnet/install-tl.zip.sha512.asc
-2022/11/04 | 455 | systems/texlive/tlnet/install-tl-windows.exe.sha512.asc
-2022/11/04 | 455 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512.asc
2022/11/04 | 4441 | macros/latex/contrib/tuda-ci/tex/tudacolors.def
2022/11/04 | 443120 | systems/texlive/tlnet/archive/tikz-ext.doc.r64920.tar.xz
2022/11/04 | 44146 | language/korean/kotex-oblivoir/tex/memhangul-common/memhangul-common.sty
-2022/11/04 | 43912 | systems/win32/miktex/tm/packages/next/miktex-gsf2pk-bin-x64-2.9.tar.lzma
2022/11/04 | 438348 | fonts/neo-euler/Neo-Euler.otf
-2022/11/04 | 43633 | systems/win32/miktex/tm/packages/next/miktex-zlib-bin-x64-2.9.tar.lzma
-2022/11/04 | 43500 | systems/win32/miktex/tm/packages/next/miktex-mthelp-bin-x64-2.9.tar.lzma
2022/11/04 | 4328 | macros/latex/contrib/tuda-ci/tex/tuda-pgfplots.sty
2022/11/04 | 43218 | macros/latex/contrib/tuda-ci/tex/tudapub.cls
2022/11/04 | 430631 | macros/latex/contrib/mathastext/mathastext.dtx
-2022/11/04 | 423096 | systems/texlive/tlnet/archive/texlive-scripts.doc.r64913.tar.xz
-2022/11/04 | 42214 | systems/win32/miktex/tm/packages/next/miktex-upmendex-bin-x64.tar.lzma
-2022/11/04 | 41415 | systems/win32/miktex/tm/packages/next/miktex-patgen-bin-x64.tar.lzma
-2022/11/04 | 4015632 | biblio/ctan-bibdata.zip
-2022/11/04 | 394301 | systems/win32/miktex/tm/packages/next/miktex-cairo-bin-x64-2.9.tar.lzma
+2022/11/04 | 4054 | macros/luatex/latex/luacas/tex/test/polynomials/roots.lua
+2022/11/04 | 396 | macros/luatex/latex/luacas/tex/test/expressions/substitute.lua
2022/11/04 | 3871 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaFromaddress.lco
2022/11/04 | 384336 | fonts/erewhon-math/Erewhon-Math.otf
2022/11/04 | 3815 | macros/latex/contrib/tuda-ci/tex/beamercolorthemeTUDa.sty
-2022/11/04 | 37466 | systems/win32/miktex/tm/packages/next/miktex-posix-bin-x64-2.9.tar.lzma
-2022/11/04 | 36967 | systems/win32/miktex/tm/packages/next/miktex-chktex-bin-x64-2.9.tar.lzma
-2022/11/04 | 3622971 | biblio/ctan-bibdata/ctan.pdf
-2022/11/04 | 352252 | systems/win32/miktex/tm/packages/next/miktex-xetex-bin-x64-2.9.tar.lzma
-2022/11/04 | 35215 | systems/win32/miktex/tm/packages/next/miktex-axohelp-bin-x64-2.9.tar.lzma
-2022/11/04 | 35206 | systems/win32/miktex/tm/packages/next/miktex-mspack-bin-x64-2.9.tar.lzma
-2022/11/04 | 33816 | systems/win32/miktex/tm/packages/next/miktex-uriparser-bin-x64-2.9.tar.lzma
+2022/11/04 | 37803 | macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_methods/ref_algebra_methods.tex
+2022/11/04 | 3753 | macros/luatex/latex/luacas/doc/intro/intropart.tex
+2022/11/04 | 3463 | macros/luatex/latex/luacas/tex/core/symbolexpression.lua
+2022/11/04 | 3393 | macros/luatex/latex/luacas/tex/test/rings/number.lua
+2022/11/04 | 33292 | macros/luatex/latex/luacas/tex/calculus/integralexpression.lua
2022/11/04 | 32 | macros/latex/contrib/proflycee/doc/basique.csv
2022/11/04 | 32972 | macros/latex/contrib/tuda-ci/tex/tudaexercise.cls
2022/11/04 | 3287 | macros/latex/contrib/tuda-ci/tex/tudafonts.sty
-2022/11/04 | 326702 | systems/win32/miktex/tm/packages/next/miktex-dvipdfmx-bin-x64-2.9.tar.lzma
-2022/11/04 | 326609 | systems/win32/miktex/tm/packages/next/miktex-mo-bin-x64-2.9.tar.lzma
+2022/11/04 | 32763 | macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_methods/ref_core_methods.tex
2022/11/04 | 32496 | macros/latex/contrib/tuda-ci/tex/tudathesis.cfg
2022/11/04 | 324576 | fonts/xcharter-math/XCharter-Math.otf
2022/11/04 | 323842 | systems/win32/miktex/tm/packages/huaz__doc.tar.lzma
-2022/11/04 | 32361 | systems/win32/miktex/tm/packages/next/miktex-makeindex-bin-x64-2.9.tar.lzma
2022/11/04 | 3175 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaAnnouncement.tex
+2022/11/04 | 31724 | macros/luatex/latex/luacas/tex/core/binaryoperation.lua
2022/11/04 | 316502 | language/korean/kotex-oblivoir/doc/oblivoir-simpledoc.pdf
-2022/11/04 | 31588 | systems/win32/miktex/tm/packages/next/miktex-autosp-bin-x64-2.9.tar.lzma
-2022/11/04 | 31487 | systems/win32/miktex/tm/packages/next/miktex-bzip2-bin-x64-2.9.tar.lzma
-2022/11/04 | 30550 | systems/win32/miktex/tm/packages/next/miktex-lacheck-bin-x64-2.9.tar.lzma
+2022/11/04 | 3149 | macros/luatex/latex/luacas/doc/appendix/latexcode.tex
+2022/11/04 | 3067 | macros/luatex/latex/luacas/tex/algebra/polynomialring/decomposition.lua
2022/11/04 | 30477 | fonts/erewhon-math/Erewhon-Math.ltx
-2022/11/04 | 302896 | systems/win32/miktex/tm/packages/miktex-zzdb1-2.9.tar.lzma
-2022/11/04 | 302883 | systems/win32/miktex/tm/packages/next/miktex-zzdb1-2.9.tar.lzma
-2022/11/04 | 295867 | systems/win32/miktex/tm/packages/next/miktex-freetype2-bin-x64-2.9.tar.lzma
2022/11/04 | 29286 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaPub.tex
-2022/11/04 | 28718 | systems/win32/miktex/tm/packages/next/miktex-findtexmf-bin-x64-2.9.tar.lzma
-2022/11/04 | 286818 | systems/win32/miktex/tm/packages/next/miktex-metapost-bin-x64-2.9.tar.lzma
2022/11/04 | 2847 | fonts/simpleicons/enc/simpleiconsOne.enc
-2022/11/04 | 2826249 | systems/win32/miktex/tm/packages/next/miktex-texworks-bin-x64-2.9.tar.lzma
+2022/11/04 | 28395 | macros/luatex/latex/luacas/tex/algebra/polynomialring.lua
2022/11/04 | 28251 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaPhD.tex
2022/11/04 | 28136 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaThesis.tex
2022/11/04 | 2792 | systems/texlive/tlnet/archive/hep-paper.r64917.tar.xz
+2022/11/04 | 27612 | macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_classes/ref_algebra_classes.tex
2022/11/04 | 2758 | fonts/simpleicons/enc/simpleiconsFour.enc
-2022/11/04 | 27541 | systems/win32/miktex/tm/packages/next/miktex-teckit-bin-x64-2.9.tar.lzma
2022/11/04 | 274 | fonts/simpleicons/tex/usimpleiconsOneZero.fd
-2022/11/04 | 2744449 | biblio/ctan-bibdata/ctan.bib
-2022/11/04 | 271029 | systems/win32/miktex/tm/packages/next/miktex-console-bin-x64-2.9.tar.lzma
+2022/11/04 | 2732 | macros/luatex/latex/luacas/tex/algebra/factorialexpression.lua
2022/11/04 | 2690 | fonts/concmath-otf/README.md
2022/11/04 | 2689 | fonts/simpleicons/enc/simpleiconsEight.enc
2022/11/04 | 2673 | fonts/simpleicons/enc/simpleiconsSix.enc
@@ -164,20 +407,14 @@
2022/11/04 | 259 | fonts/simpleicons/tex/usimpleiconsFour.fd
2022/11/04 | 259 | fonts/simpleicons/tex/usimpleiconsFive.fd
2022/11/04 | 2578 | fonts/simpleicons/enc/simpleiconsNine.enc
-2022/11/04 | 25518558 | systems/texlive/tlnet/install-tl.zip
2022/11/04 | 254 | fonts/simpleicons/tex/usimpleiconsTwo.fd
2022/11/04 | 254 | fonts/simpleicons/tex/usimpleiconsSix.fd
2022/11/04 | 254 | fonts/simpleicons/tex/usimpleiconsOne.fd
2022/11/04 | 2523 | fonts/simpleicons/enc/simpleiconsThree.enc
-2022/11/04 | 25085 | systems/win32/miktex/tm/packages/next/miktex-fribidixetex-bin-x64-2.9.tar.lzma
+2022/11/04 | 24905 | macros/luatex/latex/luacas/tex/algebra/integer.lua
2022/11/04 | 24841 | fonts/xcharter-math/XCharter-Math.ltx
-2022/11/04 | 247447 | systems/win32/miktex/tm/packages/next/miktex-hitex-bin-x64.tar.lzma
-2022/11/04 | 245226 | systems/win32/miktex/tm/packages/next/miktex-pmx-bin-x64-2.9.tar.lzma
-2022/11/04 | 2424844 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz
-2022/11/04 | 24142 | systems/win32/miktex/tm/packages/next/miktex-devnag-bin-x64-2.9.tar.lzma
2022/11/04 | 2410 | fonts/simpleicons/enc/simpleiconsFive.enc
2022/11/04 | 24036 | systems/texlive/tlnet/archive/tikz-ext.r64920.tar.xz
-2022/11/04 | 23927 | systems/win32/miktex/tm/packages/next/miktex-popt-bin-x64-2.9.tar.lzma
2022/11/04 | 2351609 | fonts/erewhon-math.zip
2022/11/04 | 2350 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaReport.tex
2022/11/04 | 2348 | fonts/simpleicons/enc/simpleiconsOneZero.enc
@@ -189,38 +426,33 @@
2022/11/04 | 2180 | macros/latex/contrib/tuda-ci/tex/beamerfontthemeTUDa.sty
2022/11/04 | 2172 | macros/latex/contrib/tuda-ci/tex/beamerthemeTUDa.sty
2022/11/04 | 215852 | fonts/simpleicons/tex/simpleiconsglyphs-pdftex.tex
-2022/11/04 | 213471 | systems/win32/miktex/tm/packages/next/miktex-omegaware-bin-x64-2.9.tar.lzma
2022/11/04 | 211848 | fonts/simpleicons/tex/simpleiconsglyphs-xeluatex.tex
-2022/11/04 | 208897 | systems/win32/miktex/tm/packages/next/miktex-metafont-bin-x64-2.9.tar.lzma
-2022/11/04 | 20433467 | systems/texlive/tlnet/install-tl-windows.exe
-2022/11/04 | 203418 | systems/win32/miktex/tm/packages/next/miktex-pixman-bin-x64-2.9.tar.lzma
+2022/11/04 | 207266 | support/TeX4ht/source/ChangeLog
2022/11/04 | 2013335 | fonts/concmath-otf.zip
+2022/11/04 | 19972 | macros/luatex/latex/luacas/tex/_lib/pepperfish.lua
2022/11/04 | 19801 | macros/latex/contrib/tuda-ci/tex/tudaposter.cls
-2022/11/04 | 1978369 | systems/win32/miktex/tm/packages/next/miktex-luatex-bin-x64-2.9.tar.lzma
2022/11/04 | 1973720 | fonts/simpleicons/doc/simpleicons.pdf
-2022/11/04 | 196216 | systems/win32/miktex/tm/packages/next/miktex-curl-bin-x64-2.9.tar.lzma
2022/11/04 | 195585 | macros/latex/contrib/proflycee/doc/ProfLycee-doc.tex
+2022/11/04 | 1951 | macros/luatex/latex/luacas/tex/algebra/absexpression.lua
2022/11/04 | 19503 | macros/latex/contrib/tuda-ci/tex/tudasciposter.cls
2022/11/04 | 1882195 | fonts/neo-euler.zip
+2022/11/04 | 187 | macros/luatex/latex/luacas/tex/calculus/_init.lua
2022/11/04 | 18751 | fonts/concmath-otf/concmath-otf.ltx
2022/11/04 | 1870960 | fonts/simpleicons/opentype/SimpleIcons.otf
2022/11/04 | 1852 | language/korean/kotex-oblivoir/README
-2022/11/04 | 18496 | systems/win32/miktex/tm/packages/next/miktex-tex2xindy-bin-x64-2.9.tar.lzma
2022/11/04 | 1847980 | fonts/xcharter-math/unimath-xcharter.pdf
2022/11/04 | 1847053 | fonts/erewhon-math/unimath-erewhon.pdf
2022/11/04 | 1844 | fonts/simpleicons/doc/simpleicons.tex
-2022/11/04 | 18367 | systems/win32/miktex/tm/packages/next/miktex-kpathsea-bin-x64-2.9.tar.lzma
-2022/11/04 | 181469 | systems/win32/miktex/tm/packages/next/miktex-mpfr-bin-x64-2.9.tar.lzma
+2022/11/04 | 18161 | macros/luatex/latex/luacas/doc/tutorial/tut2/tut2.tex
2022/11/04 | 18049 | fonts/neo-euler/Neo-Euler.ltx
+2022/11/04 | 18004 | macros/luatex/latex/luacas/doc/tutorial/tut3/tut3.tex
2022/11/04 | 178959 | fonts/xcharter-math/XCharter-Math.pdf
-2022/11/04 | 17802927 | systems/texlive/tlnet/tlpkg/texlive.tlpdb
2022/11/04 | 1779774 | fonts/concmath-otf/unimath-concrete.pdf
+2022/11/04 | 1770 | macros/luatex/latex/luacas/tex/algebra/euclideandomain.lua
2022/11/04 | 17443 | macros/latex/contrib/tuda-ci/README.md
2022/11/04 | 1743245 | fonts/simpleicons/type1/SimpleIcons.pfb
-2022/11/04 | 173342 | systems/win32/miktex/tm/packages/next/miktex-tex-bin-x64-2.9.tar.lzma
2022/11/04 | 1719 | macros/unicodetex/latex/textcsc/textcsc.sty
2022/11/04 | 17127 | fonts/xcharter-math/unimath-xcharter.ltx
-2022/11/04 | 170589 | systems/win32/miktex/tm/packages/next/miktex-hunspell-bin-x64-2.9.tar.lzma
2022/11/04 | 16947 | fonts/erewhon-math/unimath-erewhon.ltx
2022/11/04 | 16909 | macros/latex/contrib/tuda-ci/tex/tudaletter.cls
2022/11/04 | 16886 | fonts/concmath-otf/unimath-concrete.ltx
@@ -232,31 +464,24 @@
2022/11/04 | 1682 | macros/latex/contrib/tuda-ci/tex/tuda-a3paper.clo
2022/11/04 | 1682 | language/korean/kotex-oblivoir/tex/oblivoir.cls
2022/11/04 | 1680 | macros/latex/contrib/tuda-ci/tex/tuda-a2paper.clo
-2022/11/04 | 1678 | biblio/ctan-bibdata/biblatex-ctan/ctan.bbx
2022/11/04 | 16613 | macros/latex/contrib/tuda-ci/example/DEMO-TUDaExercise.tex
+2022/11/04 | 1641 | macros/luatex/latex/luacas/tex/test/calculus/derivatives.lua
2022/11/04 | 1622924 | macros/latex/contrib/proflycee.zip
+2022/11/04 | 1594 | macros/luatex/latex/luacas/tex/core/atomicexpression.lua
2022/11/04 | 157554 | fonts/neo-euler/Neo-Euler.pdf
+2022/11/04 | 1567 | macros/luatex/latex/luacas/tex/algebra/field.lua
2022/11/04 | 1559 | fonts/simpleicons/tex/simpleicons.sty
-2022/11/04 | 155341 | systems/win32/miktex/tm/packages/next/miktex-fontconfig-bin-x64-2.9.tar.lzma
-2022/11/04 | 153 | systems/texlive/tlnet/install-tl-windows.exe.sha512
-2022/11/04 | 152 | systems/texlive/tlnet/install-tl-unx.tar.gz.sha512
-2022/11/04 | 1515668 | systems/win32/miktex/tm/packages/miktex-zzdb2-2.9.tar.lzma
-2022/11/04 | 1513811 | systems/win32/miktex/tm/packages/next/miktex-zzdb2-2.9.tar.lzma
-2022/11/04 | 1505660 | systems/win32/miktex/tm/packages/next/miktex-runtime-bin-x64-2.9.tar.lzma
+2022/11/04 | 1555 | macros/luatex/latex/luacas/tex/core/constantexpression.lua
2022/11/04 | 148508 | systems/texlive/tlnet/archive/glossaries.source.r64919.tar.xz
2022/11/04 | 147583 | fonts/concmath-otf/concmath-otf.pdf
-2022/11/04 | 145 | systems/texlive/tlnet/install-tl.zip.sha512
-2022/11/04 | 1457861 | systems/win32/miktex/tm/packages/next/miktex-dvisvgm-bin-x64-2.9.tar.lzma
2022/11/04 | 1452175 | fonts/neo-euler/unimath-euler.pdf
-2022/11/04 | 144 | systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512
-2022/11/04 | 14373 | systems/win32/miktex/tm/packages/next/miktex-zzip-bin-x64-2.9.tar.lzma
2022/11/04 | 14150 | macros/latex/contrib/tuda-ci/tex/tudaleaflet.cls
-2022/11/04 | 140095 | systems/win32/miktex/tm/packages/next/miktex-mktex-bin-x64-2.9.tar.lzma
-2022/11/04 | 1391566 | systems/win32/miktex/tm/packages/next/miktex-zzdb3-2.9.tar.lzma
-2022/11/04 | 1391026 | systems/win32/miktex/tm/packages/miktex-zzdb3-2.9.tar.lzma
-2022/11/04 | 136019 | systems/win32/miktex/tm/packages/next/miktex-dvips-bin-x64-2.9.tar.lzma
-2022/11/04 | 1329235 | systems/win32/miktex/tm/packages/next/miktex-poppler-bin-x64-2.9.tar.lzma
+2022/11/04 | 13963 | macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_classes/ref_core_classes.tex
+2022/11/04 | 13931 | macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_methods/ref_calculus_methods.tex
+2022/11/04 | 12966 | macros/luatex/latex/luacas/tex/test/rings/conversion.lua
2022/11/04 | 128157 | fonts/simpleicons/doc/bindings.tex
+2022/11/04 | 1277 | macros/luatex/latex/luacas/README.md
+2022/11/04 | 1271 | macros/luatex/latex/luacas/tex/core/compoundexpression.lua
2022/11/04 | 1249676 | systems/texlive/tlnet/archive/profcollege.r64918.tar.xz
2022/11/04 | 1236 | fonts/simpleicons/tfm/SimpleIcons--simpleiconsSeven.tfm
2022/11/04 | 1232 | fonts/simpleicons/tfm/SimpleIcons--simpleiconsTwo.tfm
@@ -267,35 +492,27 @@
2022/11/04 | 1232 | fonts/simpleicons/tfm/SimpleIcons--simpleiconsFive.tfm
2022/11/04 | 1232 | fonts/simpleicons/tfm/SimpleIcons--simpleiconsEight.tfm
2022/11/04 | 1228 | fonts/simpleicons/tfm/SimpleIcons--simpleiconsOne.tfm
-2022/11/04 | 1172 | systems/win32/miktex/tm/packages/next/pr.ini
+2022/11/04 | 12129 | macros/luatex/latex/luacas/tex/algebra/trigexpression.lua
2022/11/04 | 116240 | macros/luatex/latex/luaquotes.zip
-2022/11/04 | 1161038 | systems/win32/miktex/tm/packages/next/miktex-asymptote-bin-x64-2.9.tar.lzma
-2022/11/04 | 115497 | systems/win32/miktex/tm/packages/next/miktex-gregorio-bin-x64-2.9.tar.lzma
-2022/11/04 | 115459 | systems/win32/miktex/tm/packages/next/miktex-epstopdf-bin-x64-2.9.tar.lzma
-2022/11/04 | 113708 | systems/texlive/tlnet/archive/texlive-scripts.r64913.tar.xz
+2022/11/04 | 11472 | macros/luatex/latex/luacas/tex/test/helper.lua
+2022/11/04 | 1136033 | support/TeX4ht/source/tex4ht-4ht.tex
2022/11/04 | 113103 | macros/latex/contrib/proflycee/tex/ProfLycee.sty
2022/11/04 | 112876 | macros/latex/contrib/proflycee/tex/ProfLycee-old.sty
-2022/11/04 | 112806 | systems/win32/miktex/tm/packages/next/miktex-gd-bin-x64-2.9.tar.lzma
2022/11/04 | 1119 | fonts/simpleicons/map/simpleicons.map
2022/11/04 | 11048 | systems/texlive/tlnet/archive/hep-paper.source.r64917.tar.xz
2022/11/04 | 110472 | fonts/erewhon-math/Erewhon-Math-Bold.otf
2022/11/04 | 110367 | macros/luatex/latex/luaquotes/luaquotes-documentation.pdf
-2022/11/04 | 109528 | systems/win32/miktex/tm/packages/next/miktex-jpeg-bin-x64-2.9.tar.lzma
2022/11/04 | 10891 | macros/luatex/latex/luaquotes/luaquotes.sty
-2022/11/04 | 108489 | systems/win32/miktex/tm/packages/next/miktex-texware-bin-x64-2.9.tar.lzma
+2022/11/04 | 10876 | macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra.tex
2022/11/04 | 10772084 | systems/texlive/tlnet/archive/profcollege.doc.r64918.tar.xz
2022/11/04 | 10763820 | systems/texlive/tlnet/archive/glossaries.doc.r64919.tar.xz
-2022/11/04 | 105656 | systems/win32/miktex/tm/packages/next/miktex-mfware-bin-x64-2.9.tar.lzma
-2022/11/04 | 103590 | systems/win32/miktex/tm/packages/next/miktex-lua53-bin-x64-2.9.tar.lzma
-2022/11/04 | 103346 | systems/win32/miktex/tm/packages/next/miktex-cweb-bin-x64-2.9.tar.lzma
-2022/11/04 | 101625 | systems/win32/miktex/tm/packages/next/miktex-bibtex8bit-bin-x64-2.9.tar.lzma
+2022/11/04 | 1007 | macros/luatex/latex/luacas/tex/test/expressions/logarithms.lua
2022/11/04 | 10078 | macros/latex/contrib/tuda-ci/tex/beamerthemeTUDa-mecheng.sty
2022/11/04 | 100595 | language/korean/kotex-oblivoir/doc/oblivoir-simpledoc.tex
2022/11/03 | 99378 | macros/latex/contrib/glossaries/samples/sampleAcrDesc.pdf
2022/11/03 | 98556 | support/luafindfont/doc/luafindfont-doc.pdf
2022/11/03 | 969 | macros/latex/contrib/glossaries/glossaries-user-examples/glossaries-user-example030.tex
2022/11/03 | 961697 | macros/latex/contrib/pdfmanagement-testphase/hyperref-generic.pdf
-2022/11/03 | 945148 | systems/texlive/tlnet/archive/tex4ht.r64911.tar.xz
2022/11/03 | 9443 | macros/latex/contrib/glossaries/glossaries-user-examples/glossaries-user-example008.png
2022/11/03 | 9066 | macros/latex/contrib/glossaries/glossaries-user-examples/glossaries-user-example030.png
2022/11/03 | 9060 | systems/texlive/tlnet/archive/hep-math-font.source.r64901.tar.xz
@@ -322,8 +539,8 @@
2022/11/03 | 7654 | macros/latex/contrib/pdfmanagement-testphase/color-ltx.sty
2022/11/03 | 7644 | systems/texlive/tlnet/archive/scripture.r64910.tar.xz
2022/11/03 | 7587 | macros/latex/contrib/glossaries/makeglossaries-lite.1
+2022/11/03 | 758211 | macros/luatex/latex/luacas/doc/luacas.pdf
2022/11/03 | 749265 | macros/latex/contrib/pdfmanagement-testphase/l3pdffield.pdf
-2022/11/03 | 7482 | systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl
2022/11/03 | 73159 | macros/latex/contrib/glossaries/samples/samplexdy2.pdf
2022/11/03 | 731560 | systems/texlive/tlnet/archive/bodeplot.doc.r64902.tar.xz
2022/11/03 | 72949 | macros/latex/contrib/pdfmanagement-testphase/l3backend-testphase.dtx
@@ -531,13 +748,11 @@
2022/11/03 | 140882 | macros/latex/contrib/glossaries/glossary2glossaries.pdf
2022/11/03 | 1376 | macros/latex/contrib/glossaries/glossaries-user-examples/glossaries-user-example002.tex
2022/11/03 | 1368384 | macros/latex/contrib/glossaries/glossaries-user.tex
-2022/11/03 | 136384 | systems/texlive/tlnet/archive/tex4ht.doc.r64911.tar.xz
2022/11/03 | 13469 | macros/latex/contrib/glossaries/makeglossaries-lite.lua
2022/11/03 | 13207 | macros/latex/contrib/pdfmanagement-testphase/pdfmanagement-firstaid.dtx
2022/11/03 | 13008138 | install/macros/latex/contrib/glossaries.tds.zip
2022/11/03 | 12834 | macros/latex/contrib/glossaries/glossaries.ins
2022/11/03 | 127969 | macros/latex/contrib/glossaries/samples/sample.pdf
-2022/11/03 | 1278688 | systems/texlive/tlnet/archive/tex4ht.source.r64911.tar.xz
2022/11/03 | 12709543 | macros/latex/contrib/glossaries.zip
2022/11/03 | 12372 | macros/latex/contrib/pdfmanagement-testphase/ltdocinit.dtx
2022/11/03 | 123313 | macros/latex/contrib/glossaries/glossary2glossaries.html
@@ -683,7 +898,6 @@
2022/11/02 | 21920 | systems/texlive/tlnet/archive/latex-lab.source.r64892.tar.xz
2022/11/02 | 21653 | macros/latex/contrib/l3kernel/l3text-purify.dtx
2022/11/02 | 21589 | macros/latex/contrib/l3kernel/l3deprecation.dtx
-2022/11/02 | 207112 | support/TeX4ht/source/ChangeLog
2022/11/02 | 204990 | macros/latex/contrib/l3kernel/l3news12.pdf
2022/11/02 | 2024 | macros/latex/contrib/scripture/README.md
2022/11/02 | 19641 | macros/latex/contrib/l3kernel/l3fp-convert.dtx
@@ -1994,7 +2208,6 @@
2022/10/31 | 1142 | macros/latex/required/latex-lab/README.md
2022/10/31 | 11398 | macros/latex-dev/required/amsmath/amstext.dtx
2022/10/31 | 113561 | macros/latex-dev/required/amsmath/amsxtra.pdf
-2022/10/31 | 1134369 | support/TeX4ht/source/tex4ht-4ht.tex
2022/10/31 | 11284 | macros/latex-dev/required/amsmath/amscd.dtx
2022/10/31 | 111948 | macros/latex-dev/base/ltnews11.pdf
2022/10/31 | 111948 | macros/latex/base/ltnews11.pdf
@@ -2045,7 +2258,6 @@
2022/10/31 | 10251 | macros/latex/base/webcomp.err
2022/10/31 | 10199 | macros/latex-dev/required/amsmath/amsopn.dtx
2022/10/31 | 1006656 | systems/texlive/tlnet/archive/xepersian.doc.r64872.tar.xz
-2022/10/30 | 9860 | systems/texlive/tlnet/archive/huawei.source.r64848.tar.xz
2022/10/30 | 984 | macros/luatex/latex/luaoptions/luaoptionsmanual.cls
2022/10/30 | 9843 | support/latexindent/LatexIndent/Lines.pm
2022/10/30 | 983 | support/lyluatex/lyluatexmanual.cls
@@ -2067,7 +2279,6 @@
2022/10/30 | 8078 | macros/latex/contrib/pkuthss/doc/readme/pkuthss.bib
2022/10/30 | 7844 | macros/latex/contrib/biblatex-contrib/biblatex-caspervector/doc/ChangeLog.txt
2022/10/30 | 777678 | macros/xetex/latex/xepersian/xepersian.pdf
-2022/10/30 | 777364 | systems/texlive/tlnet/archive/huawei.doc.r64848.tar.xz
2022/10/30 | 772 | graphics/pgf/contrib/tikz-ext/doc/tikz-ext-manual.tex
2022/10/30 | 75 | macros/generic/markdown/examples/scientists.csv
2022/10/30 | 7599 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-AT.png
@@ -2079,7 +2290,6 @@
2022/10/30 | 7161 | support/latexindent/documentation/figure-schematic.png
2022/10/30 | 714677 | macros/generic/markdown/markdown.dtx
2022/10/30 | 71387 | macros/xetex/latex/xepersian/xepersian-doc-basics.ltx
-2022/10/30 | 712 | systems/texlive/tlnet/archive/twemoji-colr.doc.r64854.tar.xz
2022/10/30 | 7087 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-CSB.png
2022/10/30 | 7065 | macros/latex/contrib/pkuthss/doc/readme/chap/pkuthss-chap1.tex
2022/10/30 | 6964 | support/latexindent/LatexIndent/TrailingComments.pm
@@ -2095,10 +2305,8 @@
2022/10/30 | 6630 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-9T.png
2022/10/30 | 6524 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-PTR.png
2022/10/30 | 6522 | support/latexindent/LatexIndent/OptionalArgument.pm
-2022/10/30 | 6508 | systems/texlive/tlnet/archive/pdflscape.source.r64851.tar.xz
2022/10/30 | 65059 | support/latexindent/LatexIndent/AlignmentAtAmpersand.pm
2022/10/30 | 6458 | macros/latex/contrib/jeuxcartes/tex/Caj-Uno-PTB.png
-2022/10/30 | 641 | macros/latex/contrib/eolang/eolang.bib
2022/10/30 | 6402 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-CSV.png
2022/10/30 | 6393 | graphics/pgf/contrib/tikz-ext/tex/generic/tikzlibraryext.scalepicture.code.tex
2022/10/30 | 6389393 | support/latexindent/bin/macos/latexindent
@@ -2125,7 +2333,6 @@
2022/10/30 | 58662 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-13AT.png
2022/10/30 | 5819 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-10P.png
2022/10/30 | 5819 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-9K.png
-2022/10/30 | 581228 | systems/texlive/tlnet/archive/twemoji-colr.r64854.tar.xz
2022/10/30 | 5809 | support/latexindent/LatexIndent/UnNamedGroupingBracesBrackets.pm
2022/10/30 | 5804 | macros/latex/contrib/pkuthss/doc/readme/chap/pkuthss-chap3.tex
2022/10/30 | 5786 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-P2V.png
@@ -2161,7 +2368,6 @@
2022/10/30 | 53421 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-15AT.png
2022/10/30 | 5332 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-5B.png
2022/10/30 | 532 | macros/latex/contrib/biblatex-contrib/biblatex-caspervector/README.txt
-2022/10/30 | 5284 | systems/texlive/tlnet/archive/transparent.source.r64852.tar.xz
2022/10/30 | 5283 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-8K.png
2022/10/30 | 52620 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-6AT.png
2022/10/30 | 52617 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-12AT.png
@@ -2214,7 +2420,6 @@
2022/10/30 | 4795 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-4R.png
2022/10/30 | 4793 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-6T.png
2022/10/30 | 47803 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-1AT.png
-2022/10/30 | 47687 | macros/latex/contrib/eolang/cactus.pdf
2022/10/30 | 4765 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-6J.png
2022/10/30 | 47649 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-VT.png
2022/10/30 | 4755 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-4V.png
@@ -2227,9 +2432,7 @@
2022/10/30 | 4626 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-6C.png
2022/10/30 | 4621 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-6B.png
2022/10/30 | 461 | macros/latex/contrib/pkuthss/doc/example/latexmkrc
-2022/10/30 | 461932 | systems/texlive/tlnet/archive/hep-reference.doc.r64853.tar.xz
2022/10/30 | 4609 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-7P.png
-2022/10/30 | 46052 | systems/texlive/tlnet/archive/linguisticspro.doc.r64858.tar.xz
2022/10/30 | 4604 | macros/latex/contrib/jeuxcartes/tex/CaJ-Uno-5J.png
2022/10/30 | 4573 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-10K.png
2022/10/30 | 45689 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-DC.png
@@ -2240,7 +2443,6 @@
2022/10/30 | 4532 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-6P.png
2022/10/30 | 4530 | macros/generic/markdown/markdown.css
2022/10/30 | 45016 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-14AT.png
-2022/10/30 | 4468 | systems/texlive/tlnet/archive/hep-reference.source.r64853.tar.xz
2022/10/30 | 44673 | support/lyluatex/lyluatex.lua
2022/10/30 | 44096 | support/latexindent/documentation/logo.png
2022/10/30 | 4403 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-9K.png
@@ -2257,7 +2459,6 @@
2022/10/30 | 424759 | graphics/pgf/contrib/tikz-ext/doc/tikz-ext-manual.pdf
2022/10/30 | 4221 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-6K.png
2022/10/30 | 4219 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-9C.png
-2022/10/30 | 4188 | systems/texlive/tlnet/archive/association-matrix.r64845.tar.xz
2022/10/30 | 417 | macros/generic/markdown/examples/README.md
2022/10/30 | 4149 | macros/xetex/latex/xepersian/xepersian-doc-preamble.ltx
2022/10/30 | 410 | macros/xetex/latex/xepersian/xepersian-doc-boolean.ltx
@@ -2280,7 +2481,6 @@
2022/10/30 | 3747 | support/latexindent/LatexIndent/Replacement.pm
2022/10/30 | 3720 | macros/unicodetex/latex/bithesis/bithesis.ins
2022/10/30 | 3694 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-4T.png
-2022/10/30 | 368064 | systems/texlive/tlnet/archive/accanthis.r64844.tar.xz
2022/10/30 | 36696 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-VP.png
2022/10/30 | 364 | macros/latex/contrib/pkuthss/doc/example/Make.bat
2022/10/30 | 3642 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-7C.png
@@ -2322,13 +2522,10 @@
2022/10/30 | 31821 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-VT.png
2022/10/30 | 31800 | macros/generic/markdown/examples/latex-tex4ht.html
2022/10/30 | 3178 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-2P.png
-2022/10/30 | 314616 | systems/texlive/tlnet/archive/pdflscape.doc.r64851.tar.xz
2022/10/30 | 3134 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-3C.png
2022/10/30 | 3099 | macros/xetex/latex/xepersian/xepersian-doc-captions.ltx
-2022/10/30 | 3084 | systems/texlive/tlnet/archive/debate.source.r64846.tar.xz
2022/10/30 | 3084 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-4K.png
2022/10/30 | 3035 | macros/latex/contrib/pkuthss/doc/readme/ChangeLog-upto-1.3.txt
-2022/10/30 | 300388 | systems/texlive/tlnet/archive/accanthis.doc.r64844.tar.xz
2022/10/30 | 2976 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-3K.png
2022/10/30 | 29764 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-VC.png
2022/10/30 | 296 | graphics/pgf/contrib/tikz-ext/tex/plain/pgfcalendar-ext.tex
@@ -2341,31 +2538,22 @@
2022/10/30 | 2886 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-3K.png
2022/10/30 | 2873 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-4C.png
2022/10/30 | 28593 | macros/luatex/latex/luaoptions/luaoptions.pdf
-2022/10/30 | 285712 | systems/texlive/tlnet/archive/debate.doc.r64846.tar.xz
2022/10/30 | 28311 | macros/generic/markdown/CHANGES.md
2022/10/30 | 2820 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-2T.png
2022/10/30 | 2817 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-2C.png
-2022/10/30 | 27563 | macros/latex/contrib/eolang/eolang.dtx
-2022/10/30 | 275336 | systems/texlive/tlnet/archive/transparent.doc.r64852.tar.xz
2022/10/30 | 27328 | support/latexindent/LatexIndent/Verbatim.pm
2022/10/30 | 2731 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-2K.png
2022/10/30 | 2713 | macros/latex/contrib/pkuthss/doc/readme/chap/pkuthss-intro.tex
2022/10/30 | 2701 | support/latexindent/LatexIndent/Switches.pm
-2022/10/30 | 2700300 | systems/texlive/tlnet/archive/simpleicons.r64855.tar.xz
2022/10/30 | 2690 | support/latexindent/LatexIndent/Preamble.pm
2022/10/30 | 2686 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-2P.png
-2022/10/30 | 268584 | systems/texlive/tlnet/archive/association-matrix.doc.r64845.tar.xz
2022/10/30 | 2651 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-2C.png
2022/10/30 | 2643 | macros/unicodetex/latex/bithesis/contributing-zh.md
2022/10/30 | 2628 | macros/latex/contrib/ffcode/ffcode.ins
-2022/10/30 | 2628 | macros/latex/contrib/eolang/eolang.ins
-2022/10/30 | 2612 | systems/texlive/tlnet/archive/pdflscape.r64851.tar.xz
2022/10/30 | 2609 | macros/xetex/latex/xepersian/xepersian-doc-intro.ltx
-2022/10/30 | 260132 | systems/texlive/tlnet/archive/huawei.r64848.tar.xz
2022/10/30 | 25876 | macros/xetex/latex/xepersian/xepersian-doc-pkgs.ltx
2022/10/30 | 256 | macros/luatex/latex/luaoptions/Contributors.md
2022/10/30 | 2525 | fonts/gillius/latex/gillius.sty
-2022/10/30 | 251116 | systems/texlive/tlnet/archive/playfair.doc.r64857.tar.xz
2022/10/30 | 2473 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-2K.png
2022/10/30 | 2465 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-AT.png
2022/10/30 | 24633 | macros/latex/contrib/biblatex-contrib/biblatex-caspervector/doc/caspervector.bib
@@ -2387,7 +2575,6 @@
2022/10/30 | 22895 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-DosNavy.png
2022/10/30 | 22818 | support/latexindent/LatexIndent/Document.pm
2022/10/30 | 227 | macros/latex/contrib/biblatex-contrib/biblatex-caspervector/doc/latexmkrc
-2022/10/30 | 2272 | systems/texlive/tlnet/archive/transparent.r64852.tar.xz
2022/10/30 | 2272 | macros/latex/contrib/biblatex-contrib/biblatex-caspervector/doc/caspervector-ay.tex
2022/10/30 | 22371 | macros/generic/markdown/libraries/markdown-tinyyaml.lua
2022/10/30 | 22269 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-DosMarron.png
@@ -2412,11 +2599,9 @@
2022/10/30 | 1994 | macros/latex/contrib/pkuthss/doc/readme/chap/pkuthss-ack.tex
2022/10/30 | 1993 | macros/unicodetex/latex/bithesis/README.md
2022/10/30 | 19920 | support/latexindent/defaultSettings.yaml
-2022/10/30 | 1982104 | systems/texlive/tlnet/archive/simpleicons.doc.r64855.tar.xz
2022/10/30 | 19807 | macros/latex/contrib/jeuxcartes/tex/CaJ-Poker-DosJaune.png
2022/10/30 | 1969 | macros/latex/contrib/jeuxcartes/tex/CaJ-Tarot-AK.png
2022/10/30 | 193944 | macros/unicodetex/latex/bithesis/bithesis.dtx
-2022/10/30 | 1920 | systems/texlive/tlnet/archive/hep-reference.r64853.tar.xz
2022/10/30 | 19105 | macros/generic/markdown/LICENSE
2022/10/30 | 1908612 | macros/generic/tracklang.zip
2022/10/30 | 18321 | support/latexindent/LatexIndent/ModifyLineBreaks.pm
@@ -2426,18 +2611,13 @@
2022/10/30 | 17812 | macros/generic/markdown/README.md
2022/10/30 | 1765 | macros/generic/markdown/examples/latex.tex
2022/10/30 | 1743 | macros/generic/markdown/examples/context-mkiv.tex
-2022/10/30 | 172672 | systems/texlive/tlnet/archive/poiretone.r64856.tar.xz
2022/10/30 | 1721181 | install/fonts/gillius.tds.zip
2022/10/30 | 1692229 | fonts/gillius.zip
-2022/10/30 | 1688 | systems/texlive/tlnet/archive/debate.r64846.tar.xz
2022/10/30 | 1682 | macros/latex/contrib/pkuthss/tex/pkuthss-utf8.def
-2022/10/30 | 1679528 | systems/texlive/tlnet/archive/playfair.r64857.tar.xz
2022/10/30 | 1664 | macros/latex/contrib/biblatex-contrib/biblatex-caspervector/tex/caspervector.cbx
2022/10/30 | 16634 | macros/latex/contrib/pkuthss/tex/pkuthss.cls
2022/10/30 | 16627 | macros/latex/contrib/pkuthss/doc/readme/chap/pkuthss-chap2.tex
2022/10/30 | 1657005 | support/lyluatex.zip
-2022/10/30 | 1636 | systems/texlive/tlnet/archive/collection-mathscience.r64853.tar.xz
-2022/10/30 | 159 | macros/latex/contrib/eolang/DEPENDS.txt
2022/10/30 | 15976 | macros/xetex/latex/xepersian/dkun-0.3.py
2022/10/30 | 1593 | macros/latex/contrib/pkuthss/tex/pkuthss-gbk.def
2022/10/30 | 158 | support/lyluatex/ly/eight-systems.ly
@@ -2463,10 +2643,8 @@
2022/10/30 | 138 | macros/latex/contrib/pkuthss/doc/example/chap/chap1.tex
2022/10/30 | 13890 | support/latexindent/LatexIndent/Special.pm
2022/10/30 | 13804 | macros/xetex/latex/xepersian/undk-0.6.py
-2022/10/30 | 1377940 | systems/texlive/tlnet/archive/linguisticspro.r64858.tar.xz
2022/10/30 | 1376 | macros/latex/contrib/ffcode/README.md
2022/10/30 | 13553 | graphics/pgf/contrib/tikz-ext/tex/generic/tikzlibraryext.paths.arcto.code.tex
-2022/10/30 | 1352 | macros/latex/contrib/eolang/README.md
2022/10/30 | 13420 | support/latexindent/LatexIndent/HiddenChildren.pm
2022/10/30 | 133 | macros/latex/contrib/pkuthss/doc/example/chap/encl1.tex
2022/10/30 | 133 | macros/latex/contrib/pkuthss/doc/example/chap/chap2.tex
@@ -2475,7 +2653,6 @@
2022/10/30 | 1324216 | install/macros/generic/markdown.tds.zip
2022/10/30 | 13088 | macros/xetex/latex/xepersian/img/weather/clouds.jpg
2022/10/30 | 13076 | graphics/pgf/contrib/tikz-ext/doc/tikz-ext-manual-en-main-preamble.tex
-2022/10/30 | 12964 | systems/texlive/tlnet/archive/poiretone.doc.r64856.tar.xz
2022/10/30 | 1290311 | install/macros/xetex/latex/xepersian.tds.zip
2022/10/30 | 12897 | support/latexindent/LatexIndent/LogFile.pm
2022/10/30 | 12772 | macros/generic/markdown/examples/latex-tex4ht.css
@@ -2497,127 +2674,14 @@
2022/10/30 | 1090 | support/lyluatex/LICENSE
2022/10/30 | 10896 | macros/xetex/latex/xepersian/img/weather/sun.jpg
2022/10/30 | 1083 | macros/latex/contrib/ffcode/LICENSE.txt
-2022/10/30 | 1083 | macros/latex/contrib/eolang/LICENSE.txt
2022/10/30 | 1073024 | macros/unicodetex/latex/bithesis.zip
-2022/10/30 | 1063401 | macros/latex/contrib/eolang.zip
2022/10/30 | 10622 | support/latexindent/LatexIndent/Command.pm
2022/10/30 | 1061 | macros/latex/contrib/biblatex-contrib/biblatex-caspervector/tex/caspervector.bbx
2022/10/30 | 1043619 | macros/unicodetex/latex/bithesis/bithesis.pdf
2022/10/30 | 1039 | macros/luatex/latex/luaoptions/luaoptionsbase.cls
2022/10/30 | 10371 | macros/latex/contrib/pkuthss/doc/readme/pkuthss-english.patch
2022/10/30 | 103719 | macros/latex/contrib/pkuthss/doc/example.pdf
-2022/10/30 | 1036825 | macros/latex/contrib/eolang/eolang.pdf
2022/10/30 | 10254 | support/lyluatex/lyluatex.sty
2022/10/30 | 10184 | graphics/pgf/contrib/tikz-ext/tex/generic/tikzlibraryext.positioning-plus.code.tex
2022/10/30 | 10161 | macros/luatex/latex/luaoptions/luaoptions.lua
2022/10/30 | 10122 | graphics/pgf/contrib/tikz-ext/tex/generic/pgflibraryext.shapes.uncenteredrectangle.code.tex
-2022/10/29 | 941 | macros/generic/tracklang/samples/regions/regions.sty
-2022/10/29 | 930 | macros/generic/tracklang/README
-2022/10/29 | 848 | macros/generic/tracklang/samples/animals/animals2.sty
-2022/10/29 | 804104 | macros/latex/contrib/huawei/huawei.pdf
-2022/10/29 | 784 | macros/generic/tracklang/samples/animals/animals.sty
-2022/10/29 | 781 | macros/generic/tracklang/samples/sample-tracklang4.tex
-2022/10/29 | 764697 | macros/generic/tracklang/tracklang-manual.pdf
-2022/10/29 | 689 | macros/generic/tracklang/samples/sample-tracklang5.tex
-2022/10/29 | 6869 | macros/generic/tracklang/samples/sample-tracklang4.pdf
-2022/10/29 | 680 | macros/latex/contrib/association-matrix/README.md
-2022/10/29 | 65 | macros/latex/contrib/association-matrix/DEPENDS.txt
-2022/10/29 | 630 | macros/generic/tracklang/samples/regions/sample-regions2-map.tex
-2022/10/29 | 629107 | macros/generic/tracklang/tracklang-code.pdf
-2022/10/29 | 562 | macros/generic/tracklang/samples/regions/sample-regions2-map2.tex
-2022/10/29 | 556 | macros/latex/contrib/transparent/README.md
-2022/10/29 | 5003 | macros/latex/contrib/debate/debate.dtx
-2022/10/29 | 484 | macros/latex/contrib/huawei/DEPENDS.txt
-2022/10/29 | 4436 | macros/generic/tracklang/CHANGES
-2022/10/29 | 40 | macros/latex/contrib/debate/DEPENDS.txt
-2022/10/29 | 402 | macros/latex/contrib/pdflscape/README.md
-2022/10/29 | 3823 | macros/generic/tracklang/samples/animals/sample-animals-de-poly.pdf
-2022/10/29 | 3823 | macros/generic/tracklang/samples/animals/sample-animals2-de4.pdf
-2022/10/29 | 376 | macros/generic/tracklang/samples/regions/sample-regions2-map3.tex
-2022/10/29 | 359 | macros/generic/tracklang/samples/regions/sample-regions2-nomap.tex
-2022/10/29 | 3560 | macros/generic/tracklang/samples/animals/sample-animals-poly.pdf
-2022/10/29 | 3559 | macros/generic/tracklang/samples/animals/sample-animals2-poly.pdf
-2022/10/29 | 327 | macros/generic/tracklang/samples/animals/sample-animals-babel.tex
-2022/10/29 | 323238 | macros/latex/contrib/pdflscape/pdflscape.pdf
-2022/10/29 | 319157 | macros/latex/contrib/pdflscape.zip
-2022/10/29 | 313 | macros/generic/tracklang/samples/animals/animals2-en-GB.ldf
-2022/10/29 | 310 | macros/generic/tracklang/samples/animals/animals2-en-US.ldf
-2022/10/29 | 310371 | macros/generic/tracklang/tracklang.dtx
-2022/10/29 | 30081 | macros/latex/contrib/huawei/huawei.dtx
-2022/10/29 | 289051 | macros/latex/contrib/debate/debate.pdf
-2022/10/29 | 288816 | macros/latex/contrib/debate.zip
-2022/10/29 | 281867 | macros/latex/contrib/transparent/transparent.pdf
-2022/10/29 | 278320 | macros/latex/contrib/transparent.zip
-2022/10/29 | 27666 | macros/generic/tracklang/samples/regions/sample-regions2-map.pdf
-2022/10/29 | 27515 | macros/generic/tracklang/samples/regions/sample-regions2-map2.pdf
-2022/10/29 | 272228 | macros/latex/contrib/association-matrix.zip
-2022/10/29 | 271 | macros/generic/tracklang/samples/animals/sample-animals2.tex
-2022/10/29 | 27134 | macros/generic/tracklang/samples/regions/sample-regions2-nomap.pdf
-2022/10/29 | 26590 | macros/generic/tracklang/samples/regions/sample-regions2-map3.pdf
-2022/10/29 | 264256 | macros/latex/contrib/association-matrix/association-matrix.pdf
-2022/10/29 | 2628 | macros/latex/contrib/debate/debate.ins
-2022/10/29 | 2626 | macros/latex/contrib/huawei/huawei.ins
-2022/10/29 | 257 | macros/generic/tracklang/samples/regions/sample-regions.tex
-2022/10/29 | 253724 | macros/latex/contrib/huawei/huawei-cover-picture.pdf
-2022/10/29 | 249 | macros/generic/tracklang/samples/animals/animals-english.ldf
-2022/10/29 | 249 | macros/generic/tracklang/samples/animals/animals2-english.ldf
-2022/10/29 | 246 | macros/generic/tracklang/samples/animals/animals-german.ldf
-2022/10/29 | 246 | macros/generic/tracklang/samples/animals/animals2-german.ldf
-2022/10/29 | 23587 | macros/generic/tracklang/samples/regions/sample-regions2-manx.pdf
-2022/10/29 | 23435 | macros/latex/contrib/pdflscape/pdflscape.dtx
-2022/10/29 | 232 | macros/generic/tracklang/samples/animals/sample-animals-de-poly.tex
-2022/10/29 | 226 | macros/generic/tracklang/samples/regions/regions-IM.ldf
-2022/10/29 | 223 | macros/generic/tracklang/samples/animals/sample-animals.tex
-2022/10/29 | 221 | macros/generic/tracklang/samples/regions/regions-US.ldf
-2022/10/29 | 221 | macros/generic/tracklang/samples/regions/regions-GB.ldf
-2022/10/29 | 221 | macros/generic/tracklang/samples/regions/regions-CA.ldf
-2022/10/29 | 221 | macros/generic/tracklang/samples/regions/regions-BE.ldf
-2022/10/29 | 221 | macros/generic/tracklang/samples/animals/animals-en-US.ldf
-2022/10/29 | 221 | macros/generic/tracklang/samples/animals/animals-en-GB.ldf
-2022/10/29 | 215 | macros/generic/tracklang/samples/animals/sample-animals2-poly.tex
-2022/10/29 | 214 | macros/generic/tracklang/samples/animals/sample-animals-poly.tex
-2022/10/29 | 210917 | macros/generic/tracklang/tracklang-manual.tex
-2022/10/29 | 20758 | macros/generic/tracklang/samples/sample-tracklang3.pdf
-2022/10/29 | 20747 | macros/generic/tracklang/samples/sample-tracklang2.pdf
-2022/10/29 | 20388 | macros/generic/tracklang/samples/sample-tracklang.pdf
-2022/10/29 | 19898 | macros/generic/tracklang/samples/sample-tracklang6.pdf
-2022/10/29 | 194 | macros/generic/tracklang/samples/animals/sample-animals2-de4.tex
-2022/10/29 | 1943892 | install/macros/generic/tracklang.tds.zip
-2022/10/29 | 193 | macros/generic/tracklang/samples/regions/regions-manx.ldf
-2022/10/29 | 19039 | macros/latex/contrib/association-matrix/LICENCE
-2022/10/29 | 186 | macros/generic/tracklang/samples/regions/sample-regions2.tex
-2022/10/29 | 178 | macros/generic/tracklang/samples/animals/sample-animals2-de3.tex
-2022/10/29 | 17857 | macros/generic/tracklang/samples/sample-tracklang5.pdf
-2022/10/29 | 17543 | macros/latex/contrib/transparent/transparent.dtx
-2022/10/29 | 173 | macros/generic/tracklang/samples/animals/sample-animals2-de.tex
-2022/10/29 | 172 | macros/generic/tracklang/samples/animals/sample-animals-de.tex
-2022/10/29 | 171 | macros/generic/tracklang/samples/regions/regions-german.ldf
-2022/10/29 | 166 | macros/generic/tracklang/samples/animals/sample-animals2-de2.tex
-2022/10/29 | 165 | macros/generic/tracklang/samples/regions/regions-dutch.ldf
-2022/10/29 | 164 | macros/generic/tracklang/samples/regions/regions-french.ldf
-2022/10/29 | 164 | macros/generic/tracklang/samples/regions/regions-english.ldf
-2022/10/29 | 16338 | macros/latex/contrib/association-matrix/association-matrix.tex
-2022/10/29 | 158 | macros/generic/tracklang/samples/regions/sample-regions2-manx.tex
-2022/10/29 | 15690 | macros/generic/tracklang/samples/animals/sample-animals-babel.pdf
-2022/10/29 | 15551 | macros/generic/tracklang/samples/regions/sample-regions2.pdf
-2022/10/29 | 1554 | macros/generic/tracklang/tracklang.ins
-2022/10/29 | 153 | macros/generic/tracklang/samples/regions/regions-italian.ldf
-2022/10/29 | 1513 | macros/latex/contrib/huawei/README.md
-2022/10/29 | 1459 | macros/generic/tracklang/samples/sample-tracklang3.tex
-2022/10/29 | 1454 | macros/generic/tracklang/samples/sample-tracklang.tex
-2022/10/29 | 1454 | macros/generic/tracklang/samples/sample-tracklang2.tex
-2022/10/29 | 14463 | macros/generic/tracklang/samples/animals/sample-animals-de.pdf
-2022/10/29 | 14463 | macros/generic/tracklang/samples/animals/sample-animals2-de.pdf
-2022/10/29 | 14463 | macros/generic/tracklang/samples/animals/sample-animals2-de2.pdf
-2022/10/29 | 14444 | macros/generic/tracklang/samples/animals/sample-animals2-de3.pdf
-2022/10/29 | 14366 | macros/latex/contrib/association-matrix/association-matrix.sty
-2022/10/29 | 13996 | macros/generic/tracklang/samples/regions/sample-regions.pdf
-2022/10/29 | 13489 | macros/generic/tracklang/samples/animals/sample-animals.pdf
-2022/10/29 | 13489 | macros/generic/tracklang/samples/animals/sample-animals2.pdf
-2022/10/29 | 1272 | macros/latex/contrib/debate/README.md
-2022/10/29 | 1124 | macros/generic/tracklang/samples/regions/regions2.sty
-2022/10/29 | 1083 | macros/latex/contrib/huawei/LICENSE.txt
-2022/10/29 | 1083 | macros/latex/contrib/debate/LICENSE.txt
-2022/10/29 | 1046327 | macros/latex/contrib/huawei.zip
-2022/10/29 | 1041 | macros/generic/tracklang/samples/sample-tracklang6.tex
-2022/10/29 | 1023122 | macros/generic/tracklang/tracklang-manual.html
diff --git a/biblio/ctan-bibdata/ctan.bib b/biblio/ctan-bibdata/ctan.bib
index 3f17343c52..e744e5000d 100644
--- a/biblio/ctan-bibdata/ctan.bib
+++ b/biblio/ctan-bibdata/ctan.bib
@@ -1,7 +1,7 @@
%% bib file of all CTAN packages
%% (C) Herbert Voß
%%
-%% created at 05-11-2022, 02:01:27
+%% created at 06-11-2022, 02:01:52
%%
%% This file is provided under the terms of the LPPL v1.3 or
@@ -6602,8 +6602,8 @@
title = {The \texttt{bib2gls} package},
subtitle = {Command line application to convert .bib files to glossaries-extra.sty resource files},
author = {Nicola Talbot},
- date = {2022-10-15},
- version = {3.0},
+ date = {2022-11-05},
+ version = {3.1},
license = {gpl3+},
mirror = {https://mirror.ctan.org/support/bib2gls},
url = {https://ctan.org/pkg/bib2gls},
@@ -21791,8 +21791,8 @@
title = {The \texttt{eolang} package},
subtitle = {Formulas and graphs for the EO programming language},
author = {Yegor Bugayenko},
- date = {2022-11-02},
- version = {0.3.0},
+ date = {2022-11-05},
+ version = {0.4.0},
license = {mit},
mirror = {https://mirror.ctan.org/macros/latex/contrib/eolang},
url = {https://ctan.org/pkg/eolang},
@@ -31552,7 +31552,7 @@
title = {The \texttt{huaz} package},
subtitle = {Automatic Hungarian definite articles},
author = {Tibor Tómács},
- date = {2022-10-17},
+ date = {2022-11-05},
version = {1.0},
license = {lppl1.3},
mirror = {https://mirror.ctan.org/macros/latex/contrib/huaz},
@@ -40600,6 +40600,17 @@
package, in the context of LuaTeX.},
}
+@manual{ctan-luacas,
+ title = {The \texttt{luacas} package},
+ subtitle = {A computer algebra system for users of LuaLaTeX},
+ author = {Timothy All},
+ date = {2022-11-05},
+ version = {1.0.0},
+ license = {lppl1.3c},
+ mirror = {https://mirror.ctan.org/macros/luatex/latex/luacas},
+ url = {https://ctan.org/pkg/luacas},
+}
+
@manual{ctan-luacensor,
title = {The \texttt{luacensor} package},
subtitle = {Securely redact sensitive information using Lua},
diff --git a/biblio/ctan-bibdata/ctan.pdf b/biblio/ctan-bibdata/ctan.pdf
index eba30e7a1d..1d69bc3b6e 100644
--- a/biblio/ctan-bibdata/ctan.pdf
+++ b/biblio/ctan-bibdata/ctan.pdf
Binary files differ
diff --git a/macros/latex/contrib/eolang/eolang.dtx b/macros/latex/contrib/eolang/eolang.dtx
index c7bad93739..3b2db126a4 100644
--- a/macros/latex/contrib/eolang/eolang.dtx
+++ b/macros/latex/contrib/eolang/eolang.dtx
@@ -50,7 +50,7 @@
%<package>\NeedsTeXFormat{LaTeX2e}
%<package>\ProvidesPackage{eolang}
%<*package>
-[2022-10-30 0.3.0 Formulas and Graphs for EO Programming Language]
+[2022-11-05 0.4.0 Formulas and Graphs for EO Programming Language]
%</package>
%<*driver>
\documentclass{ltxdoc}
@@ -61,7 +61,7 @@
\AddToHook{env/verbatim/begin}{\microtypesetup{protrusion=false}}
\usepackage{graphicx}
\usepackage{xcolor}
-\usepackage[runs=1,dtx]{docshots}
+\usepackage[runs=2,dtx]{docshots}
\usepackage{eolang}
\usepackage{href-ul}
\PageIndex
@@ -171,9 +171,9 @@
% \usepackage{eolang}
% \begin{document}
% \begin{sodg}
-% v0
-% v1 xy:v0,-2,+1 data:42
-% v0->v1 a:$x$ rho
+% v0 \\ v0=> \\ v0!A
+% v1 xy:v0,-.8,2.8 data:42 \\ =>v1
+% v0->v1 a:x rho
% v2 xy:v0,+1,+1 atom:\xi.x+1
% v1->v2 a:|hi| bend:-15
% v2->v0 pi bend:10
@@ -182,25 +182,40 @@
% \end{docshot}
% The content of the environment is parsed line by line. Markers in each line are
% separated by a single space. The first marker is either a unique name of a
-% vertex, like |v1| in the example above, or an edge, like |v0->v1|. All other markers are either
-% unary like |rho| or binary like |atom:$\xi.x+1$|. Binary markers have two
-% parts, separated by colon. The following markers are supported for a vertex:
+% vertex, like ``|v1|'' in the example above, or an edge, like ``|v0->v1|.'' All other markers are either
+% unary like ``|rho|'' or binary like ``|atom:$\xi.x+1$|.'' Binary markers have two
+% parts, separated by colon.
+
+% The following markers are supported for a vertex:
% \begin{itemize}\setlength\itemsep{0em}
-% \item ``|data:[<box>]|'' makes it a data vertex with an optional attached |<box>| (the content of the box may only be numeric data),
-% \item ``|atom:[<box>]|'' makes it an atom with an optional attached |<box>| (the content of the box is a math formula),
-% \item ``|box:<txt>|'' attaches a |<box>| to it,
-% \item ``|xy:<v>,<r>,<d>|'' places this vertex in a position relative to the vertex |<v>|,
-% shifting it right by |<r>| % and down by |<d>| centimetres.
+% \item ``|data:[<box>]|'' makes it a data vertex with an optional attached ``|<box>|''
+% (the content of the box may only be numeric data),
+% \item ``|atom:[<box>]|'' makes it an atom with an optional attached ``|<box>|''
+% (the content of the box is a math formula),
+% \item ``|box:<txt>|'' attaches a ``|<box>|'' to it,
+% \item ``|xy:<v>,<r>,<d>|'' places this vertex in a position relative to the vertex ``|<v>|,''
+% shifting it right by ``|<r>|'' and down by ``|<d>|'' centimetres.
% \end{itemize}
+
% The following markers are supported for an edge:
% \begin{itemize}\setlength\itemsep{0em}
% \item ``|rho|'' places a backward snake arrow to the edge,
% \item ``|rrho|'' places a reverse |rho|,
-% \item ``|bend:<angle>|'' bend it right by the amount of |<angle>|,
-% \item ``|a:<txt>|'' attaches label |<txt>| to it,
+% \item ``|bend:<angle>|'' bend it right by the amount of ``|<angle>|,''
+% \item ``|a:<txt>|'' attaches label ``|<txt>|'' to it,
% \item ``|pi|'' makes it dotted, with $\pi$ label.
% \end{itemize}
+% It is also possible to put transformation arrows to the graph, with the help of ``|v0=>v1|'' syntax. The arrow
+% will be placed exactly between two vertices. You can also put an arrow from a vertex to the right,
+% saying for example ``|v3=>|'', of from the left to the vertes, by saying for example ``|=>v5|.''
+
+% You can also put a marker at the left side of a vertex, using ``|v5!A|'' syntax, where ``|v5|'' is the vertex
+% and ``|A|'' is the text in the marker. They are useful when you put a few graphs on a picture
+% explaining how one graph is transformed to another one and so forth.
+
+% Be aware, unrecognized markers are simply ignored, without any error reporting.
+
% \DescribeMacro{\eolang}
% \DescribeMacro{\phic}
% \DescribeMacro{\xmir}
@@ -321,6 +336,20 @@
% \end{document}
% \end{docshot}
+% Inside |phiquation| environment you can use labels too (just put |\label| after the formula):
+% \begin{docshot}
+% \documentclass{article}
+% \usepackage{eolang}
+% \usepackage[paperwidth=2in]{geometry}
+% \pagestyle{empty}
+% \begin{document}
+% \begin{phiquation}
+% x -> [[\Delta -> 42]]. \label{eq:A}
+% \end{phiquation}
+% Eq.~\ref{eq:A} is easy to read.
+% \end{document}
+% \end{docshot}
+
% \StopEventually{}
% \section{Implementation}
@@ -389,12 +418,12 @@
% \end{macro}
% \begin{macro}{eolang-phi.pl}
-% \changes{0.0.2}{2022/10/21}{The symbols ``['' and ``]'' replaced with ``[['' and ``]]'' for abstract object brackets, because they conflicted with normal square brackets}
+% \changes{0.0.2}{2022/10/21}{The symbols ``\texttt{[}'' and ``\texttt{]}'' replaced with ``\texttt{[[}'' and ``\texttt{]]}'' for abstract object brackets, because they conflicted with normal square brackets}
% \changes{0.0.2}{2022/10/21}{New symbol added for basket slots}
-% \changes{0.0.2}{2022/10/21}{Parsing of symbols ``@,'' ``\^{},'' and ``\&'' enabled (varphi, rho, and sigma)}
-% \changes{0.1.0}{2022/10/26}{A new Perl script "eolang-phi.pl" added for parsing of phi expressions.}
-% \changes{0.2.0}{2022/10/29}{Numbers automatically render as "texttt". No need to use vertical bars around them anymore.}
-% \changes{0.3.0}{2022/10/30}{New arrow added, that looks like "leadsto".}
+% \changes{0.0.2}{2022/10/21}{Parsing of symbols ``\texttt{@},'' ``\texttt{\^{}},'' and ``\texttt{\&}'' enabled (\texttt{\char`\\varphi}, \texttt{\char`\\rho}, and \texttt{\char`\\sigma})}
+% \changes{0.1.0}{2022/10/26}{A new Perl script "\texttt{eolang-phi.pl}" added for parsing of phi expressions.}
+% \changes{0.2.0}{2022/10/29}{Numbers automatically render as \texttt{\char`\\texttt}. No need to use vertical bars around them anymore.}
+% \changes{0.3.0}{2022/10/30}{New arrow added, that looks like \texttt{\char`\\leadsto}.}
% Then, we create a Perl script for |phiquation| processing using |VerbatimOut| from
% \href{https://ctan.org/pkg/fancyvrb}{fancyvrb}:
% \begin{macrocode}
@@ -522,13 +551,26 @@ print '\endinput', "\n";
% \begin{macro}{eolang-sodg.pl}
% \changes{0.0.2}{2022/10/24}{The Perl file now has a fixed name, which doesn't depend on the name of the TeX job. This file may be shared among jobs, no need to make it uniquely named.}
-% \changes{0.1.0}{2022/10/26}{There are two Perl scripts now: one for phiquation, another one for sodg.}
-% \changes{0.2.0}{2022/10/28}{The content of "atom" and "data" boxes is parsed automatically as formulas and numbers, respectively.}
+% \changes{0.1.0}{2022/10/26}{There are two Perl scripts now: one for \texttt{phiquation}, another one for \texttt{sodg}.}
+% \changes{0.2.0}{2022/10/28}{The content of \texttt{atom} and \texttt{data} boxes is parsed automatically as formulas and numbers, respectively.}
+% \changes{0.4.0}{2022/10/31}{Labels on the edges are automatically printed as math formulas. Also, boxes are prefixed with \texttt{\char`\\Delta} and \texttt{\char`\\lambda} commands.}
+% \changes{0.4.0}{2022/10/31}{Relative positioning of vertices fixed.}
+% \changes{0.4.0}{2022/10/31}{Transforming arrows added with \texttt{=>} syntax, also markers with exclamation mark syntax.}
% Then, we create a Perl script for |sodg| graphs processing using |VerbatimOut| from
% \href{https://ctan.org/pkg/fancyvrb}{fancyvrb}:
% \begin{macrocode}
\makeatletter
\begin{VerbatimOut}{\eolang@tmpdir/eolang-sodg.pl}
+sub num {
+ my ($i) = @_;
+ $i =~ s/(\+|-)\./\10./g;
+ return $i;
+}
+sub fmt {
+ my ($tex) = @_;
+ $tex =~ s/\|([^\|]+)\|/\\textnormal{\\texttt{\1}}/g;
+ return $tex;
+}
open(my $fh, '<', $ARGV[0]);
my $tex; { local $/; $tex = <$fh>; }
print '% This file is auto-generated', "\n";
@@ -539,15 +581,15 @@ if (index($tex, "\t") > 0) {
print "TABS are prohibited!";
exit 1;
}
-$tex =~ s/^\s+|\s+$//g;
-$tex =~ s/(\\[a-zA-Z]+)\s+/\1/g;
-$tex =~ s/\n\s+/\n/g;
-$tex =~ s/\|([^\|]+)\|/\\textnormal{\\texttt{\1}}/g;
-my @cmds = split (/\n/g, $tex);
-foreach my $t (@cmds) {
+foreach my $t (split (/\n/g, $tex)) {
print '% ', $t, "\n";
}
print '% ---', "\n";
+$tex =~ s/^\s+|\s+$//g;
+$tex =~ s/\\\\/\n/g;
+$tex =~ s/(\\[a-zA-Z]+) +/\1/g;
+$tex =~ s/\n\s+/\n/g;
+my @cmds = split (/\n/g, $tex);
print '\begin{phicture}', "\n";
foreach my $c (@cmds) {
my ($head, $tail) = split (/ /, $c, 2);
@@ -556,23 +598,82 @@ foreach my $c (@cmds) {
my ($q, $t) = split (/:/, $p);
$opts{$q} = $t;
}
- if (index($head, '->') == -1) {
+ if (index($head, '->') >= 0) {
+ print '\draw[';
+ if (exists $opts{'pi'}) {
+ print ',phi-pi';
+ if (not exists $opts{'a'}) {
+ $opts{'a'} = '\pi';
+ }
+ }
+ print ']';
+ my ($from, $to) = split (/->/, $head);
+ print ' (', $from, ') ';
+ if (exists $opts{'bend'}) {
+ print 'edge [bend right=', num($opts{'bend'}), ']';
+ } else {
+ print '--';
+ }
+ if (exists $opts{'rho'} or exists $opts{'rrho'}) {
+ print ' pic[sloped,phi-rho]{parallel arrow={';
+ print '-' if not exists $opts{'rrho'};
+ print '0.3,-0.15}}';
+ }
+ if (exists $opts{'a'}) {
+ my $a = $opts{'a'};
+ if (index($a, '$') == -1) {
+ $a = '$' . fmt($a) . '$';
+ } else {
+ $a = fmt($a);
+ }
+ print ' node [phi-attr] {', $a, '}';
+ }
+ print ' (', $to, ')';
+ } elsif (index($head, '=>') >= 0) {
+ my ($from, $to) = split (/=>/, $head);
+ if ($from eq '') {
+ print '\node [phi-arrow, left=.6cm of ' .
+ $to . ']';
+ } elsif ($to eq '') {
+ print '\node [phi-arrow, right=.6cm of ' .
+ $from . ']';
+ } else {
+ print '\node [phi-arrow] at ($(' .
+ $from . ')!0.5!(' . $to . ')$)';
+ }
+ print '{}';
+ } elsif (index($head, '!') >= 0) {
+ my ($v, $marker) = split (/!/, $head);
+ print '\node [phi-marker, left=.6cm of ' .
+ $v . ']{' . fmt($marker) . '}';
+ } else {
print '\node[';
if (exists $opts{'xy'}) {
my ($v, $right, $down) = split(/,/, $opts{'xy'});
- print ',below right=';
- print $down;
- print 'cm and ';
- print $right;
- print 'cm of ';
- print $v;
+ my $loc = '';
+ if ($down > 0) {
+ $loc = 'below ';
+ } elsif ($down < 0) {
+ $loc = 'above ';
+ }
+ if ($right > 0) {
+ $loc = $loc . 'right';
+ } elsif ($right < 0) {
+ $loc = $loc . 'left';
+ }
+ print ',' . $loc . '=';
+ print abs(num($down)) . 'cm and ' .
+ abs(num($right)) . 'cm of ' . $v;
}
if (exists $opts{'data'}) {
print ',phi-data';
if (not $opts{'data'} eq '') {
my $d = $opts{'data'};
if (index($d, '|') == -1) {
- $d = '\textnormal{\texttt{' . $d . '}}';
+ $d = '$\Delta\phiDotted\text{' .
+ '\textnormal{\texttt{' . fmt($d) . '}}}$';
+ } else {
+ $d = fmt($d);
}
$opts{'box'} = $d;
}
@@ -581,7 +682,9 @@ foreach my $c (@cmds) {
if (not $opts{'atom'} eq '') {
my $a = $opts{'atom'};
if (index($a, '$') == -1) {
- $a = '$' . $a . '$';
+ $a = '$\lambda\phiDotted{}' . fmt($a) . '$';
+ } else {
+ $a = fmt($a);
}
$opts{'box'} = $a;
}
@@ -591,10 +694,13 @@ foreach my $c (@cmds) {
print ']';
print ' (', $head, ')';
print ' {$';
- if ($head eq 'v0') {
+ if (index($head, 'v0') == 0) {
print '\Phi';
} else {
- print 'v_', substr($head, 1);
+ $name = $head;
+ $name =~ s/^v/v_/g;
+ $name =~ s/[^0-9]$//g;
+ print $name;
}
print '$}';
if (exists $opts{'box'}) {
@@ -602,31 +708,6 @@ foreach my $c (@cmds) {
print $head, '.south east) {';
print $opts{'box'}, '}';
}
- } else {
- print '\draw[';
- if (exists $opts{'pi'}) {
- print ',phi-pi';
- if (not exists $opts{'a'}) {
- $opts{'a'} = '$\pi$';
- }
- }
- print ']';
- my ($from, $to) = split (/->/, $head);
- print ' (', $from, ') ';
- if (exists $opts{'bend'}) {
- print 'edge [bend right=', $opts{'bend'}, ']';
- } else {
- print '--';
- }
- if (exists $opts{'rho'} or exists $opts{'rrho'}) {
- print ' pic[sloped,phi-rho]{parallel arrow={';
- print '-' if not exists $opts{'rrho'};
- print '0.3,-0.15}}';
- }
- if (exists $opts{'a'}) {
- print ' node [phi-attr] {', $opts{'a'}, '}';
- }
- print ' (', $to, ')';
}
print ";\n";
}
@@ -672,9 +753,12 @@ print '\end{phicture}', "\n", '\endinput';
pics/parallel arrow/.style={
code={\draw[-latex,phi-rho] (##1) -- (-##1);}}]}%
{\end{tikzpicture}}
-\tikzstyle{transforms} = [fill=white!80!black, single arrow,
+\tikzstyle{phi-arrow} = [fill=white!80!black, single arrow,
minimum height=0.5cm, minimum width=0.5cm,
single arrow head extend=2mm]
+\tikzstyle{phi-marker} = [inner sep=0pt, minimum height=1.4em,
+ minimum width=1.4em, font={\small\color{white}\ttfamily},
+ fill=gray]
\tikzstyle{phi-thing} = [thick,inner sep=0pt,minimum height=2.4em,
draw,font={\small}]
\tikzstyle{phi-object} = [phi-thing,circle]
@@ -695,7 +779,7 @@ print '\end{phicture}', "\n", '\endinput';
% \end{macro}
% \begin{macro}{sodg}
-% \changes{0.0.2}{2022/10/20}{The environment ``phigure'' renamed to ``sodg'' for the sake of better semantic. The graph in the picture is solely a SODG graph, that's why the name ``sodg'' is better.}
+% \changes{0.0.2}{2022/10/20}{The environment \texttt{phigure} renamed to \texttt{sodg} for the sake of better semantic. The graph in the picture is solely a SODG graph, that's why the name \texttt{sodg} is better.}
% Then, create a new environment |sodg|, as suggested
% \href{https://tex.stackexchange.com/questions/661056}{here}:
% \begin{macrocode}
@@ -719,8 +803,8 @@ print '\end{phicture}', "\n", '\endinput';
% \begin{macro}{\eolang}
% Then, we define a simple supplementary command to help you print \eolang{}, the name of our language.
-% \changes{0.1.0}{2022/10/25}{New command "eolang" added to print the name of the language in both
-% normal and anonymous mode of "acmart"}
+% \changes{0.1.0}{2022/10/25}{New command \texttt{\char`\\eolang} added to print the name of the language in both
+% normal and anonymous mode of \texttt{acmart}.}
% \begin{macrocode}
\newcommand\eolang{%
\ifdefined\anon%
@@ -734,13 +818,14 @@ print '\end{phicture}', "\n", '\endinput';
% \begin{macro}{\phic}
% Then, we define a simple supplementary command to help you print \phic{}, the name of our formal apparatus.
-% \changes{0.1.0}{2022/10/25}{New command "phic" prints the name of $\varphi$-calculus in both
-% normal and anonymous mode of "acmart"}
+% \changes{0.1.0}{2022/10/25}{New command \texttt{\char`\\phic} prints the name of $\varphi$-calculus in both
+% normal and anonymous mode of \texttt{acmart}.}
% \begin{macrocode}
\RequirePackage{hyperref}
\newcommand\phic{%
\ifdefined\anon%
- \anon[\texorpdfstring{$\alpha$}{a}-calculus]{\texorpdfstring{$\varphi$}{phi}-calculus}%
+ \anon[\texorpdfstring{$\alpha$}{a}-calculus]
+ {\texorpdfstring{$\varphi$}{phi}-calculus}%
\else%
\texorpdfstring{$\varphi$}{phi}-calculus%
\fi%
@@ -750,7 +835,7 @@ print '\end{phicture}', "\n", '\endinput';
% \begin{macro}{\xmir}
% Then, we define a simple supplementary command to help you print \xmir{}, the name of our XML-based format of program representation.
-% \changes{0.2.0}{2022/10/28}{New command "xmir" prints XMIR in both normal and anonymous mode of "acmart"}
+% \changes{0.2.0}{2022/10/28}{New command \texttt{\char`\\xmir} prints XMIR in both normal and anonymous mode of \texttt{acmart}.}
% \begin{macrocode}
\newcommand\xmir{%
\ifdefined\anon%
@@ -763,17 +848,18 @@ print '\end{phicture}', "\n", '\endinput';
% \end{macro}
% \begin{macro}{\phiConst}
-% \changes{0.1.0}{2022/10/26}{New command "phiConst" added to denote a link to a constant attribute.}
+% \changes{0.1.0}{2022/10/26}{New command \texttt{\char`\\phiConst} added to denote a link to a constant attribute.}
% Then, we define a command to render an arrow for a constant attribute,
% as suggested \href{https://tex.stackexchange.com/questions/663121}{here}:
% \begin{macrocode}
\newcommand\phiConst{%
- \mathrel{\hspace{.15em}}\mapstochar\mathrel{\hspace{-.15em}}\mapsto}
+ \mathrel{\hspace{.15em}}%
+ \mapstochar\mathrel{\hspace{-.15em}}\mapsto}
% \end{macrocode}
% \end{macro}
% \begin{macro}{\phiWave}
-% \changes{0.3.0}{2022/10/30}{New command "phiWave" added to denote a link to a multi-layer attribute.}
+% \changes{0.3.0}{2022/10/30}{New command \texttt{\char`\\phiWave} added to denote a link to a multi-layer attribute.}
% Then, we define a command to render an arrow for a multi-layer attribute,
% as suggested \href{https://tex.stackexchange.com/questions/198755}{here}:
% \begin{macrocode}
@@ -783,14 +869,15 @@ print '\end{phicture}', "\n", '\endinput';
% \end{macro}
% \begin{macro}{\phiDotted}
-% \changes{0.1.0}{2022/10/27}{New command "phiDotted" added to denote a link to a special attribute.}
+% \changes{0.1.0}{2022/10/27}{New command \texttt{\char`\\phiDotted} added to denote a link to a special attribute.}
% Then, we define a command to render an arrow for a special attribute,
% as suggested \href{https://tex.stackexchange.com/questions/663176}{here}:
% \begin{macrocode}
\RequirePackage{trimclip}
\RequirePackage{amsfonts}
\makeatletter
-\newcommand{\phiDotted}{\mapstochar\mathrel{\mathpalette\phiDotted@\relax}}
+\newcommand{\phiDotted}{%
+ \mapstochar\mathrel{\mathpalette\phiDotted@\relax}}
\newcommand{\phiDotted@}[2]{%
\begingroup
\settowidth{\dimen\z@}{$\m@th#1\rightarrow$}%
@@ -801,7 +888,8 @@ print '\end{phicture}', "\n", '\endinput';
{\resizebox{\dimen\z@}{\height}%
{$\m@th#1\dashrightarrow$}}%
\hss%
- \clipbox{{0.69\width} {-0.1\height} 0 {-\height}}{$\m@th#1\rightarrow$}%
+ \clipbox{{0.69\width} {-0.1\height} 0
+ {-\height}}{$\m@th#1\rightarrow$}%
}%
}%
\ht\z@=\dimen\tw@ \dp\z@=\z@%
diff --git a/macros/latex/contrib/eolang/eolang.pdf b/macros/latex/contrib/eolang/eolang.pdf
index 28befcb7bf..73fcd9885e 100644
--- a/macros/latex/contrib/eolang/eolang.pdf
+++ b/macros/latex/contrib/eolang/eolang.pdf
Binary files differ
diff --git a/macros/luatex/latex/luacas/README.md b/macros/luatex/latex/luacas/README.md
new file mode 100644
index 0000000000..8e757dfea9
--- /dev/null
+++ b/macros/luatex/latex/luacas/README.md
@@ -0,0 +1,23 @@
+# LuaLaTeX-CAS
+
+A computer algebra system written purely in Lua for embedding into LaTeX using the LuaLaTeX compiler. Includes symbolic root-finding, factoring, expansion, differentiation, and integration, as well as some number-theoretic and ring-theoretic functionality. Can even run in Overleaf.
+
+Performance is necessarily not extremely fast, given Lua is an interpreted language. Do not expect to be able to factor large-degree polynomials or large integers in a reasonable time (but this is not the point of this CAS).
+
+The Lua CAS itself can be downloaded or cloned from [https://github.com/cochraef/LuaCAS](https://github.com/cochraef/LuaCAS).
+
+# Installation
+
+The package manager for your local TeX distribution ought to work just fine. Alternatively, download luacas.tds.zip and unzip the file in the root of one of your TDS trees. You may need to update some filename database after this (this may vary depending on your TeX distribution).
+
+# Authors
+
+This package is authored and maintained by Evan Cochrane and Timothy All.
+
+# License
+
+Permission is granted to copy, distribute and/or modify this
+software under the terms of the LaTeX Project Public License
+(LPPL), version 1.3c or any later version.
+
+![poweredbylua](http://www.lua.org/images/powered-by-lua.gif)
diff --git a/macros/luatex/latex/luacas/doc/appendix/latexcode.tex b/macros/luatex/latex/luacas/doc/appendix/latexcode.tex
new file mode 100644
index 0000000000..1f8ccd7471
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/appendix/latexcode.tex
@@ -0,0 +1,140 @@
+\documentclass{article}
+
+\usepackage{standalone}
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse}
+\usepackage{microtype}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\begin{document}
+
+\section{The \LaTeX{} code}
+
+As noted above, this package is really a Lua program; the package {\ttfamily luacas.sty} is merely a shell to make accessing that Lua program easy and manageable from within \LaTeX{}.
+\inputminted[
+ firstline=12,
+ lastline=14,
+ breaklines,
+ linenos,
+ numbersep=5pt]
+ {latex}
+ {luacas.dat}
+
+We check to make sure the user is compiling with Lua\LaTeX{}; if not, an error message is printed and compilation is aborted.
+
+\inputminted[
+ firstline=16,
+ lastline=24,
+ breaklines,
+ linenos,
+ numbersep=5pt]
+ {latex}
+ {luacas.dat}
+
+The following packages are required for various macros:
+
+\inputminted[
+ firstline=27,
+ lastline=32,
+ breaklines,
+ linenos,
+ numbersep=5pt]
+ {latex}
+ {luacas.dat}
+
+The files \verb|helper.lua| and \verb|parser.lua| help bridge the gap between the Lua program and \LaTeX{}.
+
+\inputminted[
+ firstline=35,
+ lastline=37,
+ breaklines,
+ linenos,
+ numbersep=5pt]
+ {latex}
+ {luacas.dat}
+
+We now define the \mintinline{latex}{\begin{CAS}..\end{CAS}} environment:
+
+\inputminted[
+ firstline=39,
+ lastline=42,
+ breaklines,
+ linenos,
+ numbersep=5pt]
+ {latex}
+ {luacas.dat}
+
+{\bf Note:} The contents are wrapped in the function \mintinline{lua}{CASparse()}. We now define the retrieving macros \mintinline{latex}{\get}, \mintinline{latex}{\fetch}, and \mintinline{latex}{\store}:
+
+\inputminted[
+ firstline=44,
+ lastline=67,
+ breaklines,
+ linenos,
+ numbersep=5pt]
+ {latex}
+ {luacas.dat}
+And now we define the printing macros \mintinline{latex}{\print}, \mintinline{latex}{\vprint}, and \mintinline{latex}{\lprint}:
+
+\inputminted[
+ firstline=75,
+ lastline=128,
+ breaklines,
+ linenos,
+ numbersep=5pt]
+ {latex}
+ {luacas.dat}
+
+And finally, we define the macros useful for printing expression trees:
+
+\inputminted[
+ firstline=130,
+ lastline=230,
+ breaklines,
+ linenos,
+ numbersep=5pt]
+ {latex}
+ {luacas.dat}
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/appendix/luacas.dat b/macros/luatex/latex/luacas/doc/appendix/luacas.dat
new file mode 100644
index 0000000000..60866bae05
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/appendix/luacas.dat
@@ -0,0 +1,230 @@
+% Copyright (c) ???
+%
+% Permission is granted to copy, distribute and/or modify this
+% software under the terms of the LaTeX Project Public License
+% (LPPL), version 1.3c or any later version.
+%
+% This software is provided 'as is', without warranty of any kind,
+% either expressed or implied, including, but not limited to, the
+% implied warranties of merchantability and fitness for a
+% particular purpose.
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{luacas}
+ [2022/05/07 v1.0 CAS written in Lua for LaTeX]
+
+\RequirePackage{iftex}
+\ifluatex
+ \RequirePackage{luacode}
+\else
+ {\PackageError{luacas}
+ {Not running under LuaLaTeX}
+ {This package requires LuaLaTeX. Try compiling this document with\MessageBreak 'lualatex' instead of 'latex'. This is a fatal error; I'm aborting now.}%
+ }\stop
+\fi
+
+%Required Packages
+\RequirePackage{xparse}
+\RequirePackage{pgfkeys}
+\RequirePackage{verbatim}
+\RequirePackage{tikz}
+\RequirePackage{xcolor}
+\RequirePackage{mathtools}
+
+%These files contain Lua code for parsing luacas output; they also initialize the CAS itself
+\directlua{require('test.parser')
+ require('test.helper')
+}
+
+\NewDocumentEnvironment{CAS}%
+ {+b}%
+ {\luaexec{CASparse([[#1]])}}%
+ {}
+
+\newcommand{\get}%
+ [2][true]%
+ {\directlua{disp(#2, #1)}}
+
+\newcommand{\fetch}[1]{
+ \directlua{tex.print(tostring(#1))}
+}
+
+\NewDocumentCommand{\store}{m O{#1}}{
+ \expandafter\def\csname #2\endcsname{%
+ \directlua{
+ input = #1
+ if not input[1] then
+ tex.sprint{tostring(input)}
+ else
+ tex.sprint("{")
+ for _,entry in ipairs(input) do
+ tex.sprint(tostring(entry),",")
+ end
+ tex.sprint("}")
+ end
+ }%
+ }%
+}%
+
+%%%%%%%%%%
+%% Core %%
+%%%%%%%%%%
+
+%pretty print
+
+\NewDocumentCommand{\print}{s m}{%
+ \IfBooleanTF{#1}{%
+ \directlua{
+ local sym = #2
+ if sym then
+ tex.print(sym:autosimplify():tolatex())
+ else
+ tex.print('nil')
+ end
+ }%
+ }{%
+ \directlua{
+ local sym = #2
+ if sym then
+ tex.print(sym:tolatex())
+ else
+ tex.print('nil')
+ end
+ }%
+ }%
+}
+
+\NewDocumentCommand{\vprint}{s m}{%
+ \IfBooleanTF{#1}{%
+ \directlua{
+ local sym = #2
+ tex.sprint([[\unexpanded{\begin{verbatim}]] .. tostring(sym) .. [[\end{verbatim}}]])
+ }%
+ }{%
+ \directlua{
+ local sym = #2
+ tex.sprint([[\unexpanded{\begin{verbatim}]] .. tostring(sym:autosimplify()) .. [[\end{verbatim}}]])
+ }%
+ }%
+}
+
+\NewDocumentCommand{\lprint}{m O{nil,nil}}{%
+ \luaexec{
+ local tbl = #1
+ local low,upp = #2
+ local tmp =0
+ if tbl[0] == nil then
+ tmp = 1
+ end
+ upp = upp or \#tbl
+ low = low or tmp
+ for i=low,upp do
+ tex.print(tbl[i]:tolatex())
+ if tbl[i+1] then
+ tex.print(",")
+ end
+ end
+ }
+}
+
+%prints the first level of an expression tree; for use within a tikzpicture environment
+
+\NewDocumentCommand{\printshrub}{s m}{%
+ \IfBooleanTF{#1}{%
+ \directlua{
+ local sym = #2
+ sym = sym:autosimplify()
+ tex.print("\\node [label=90:", whatis(sym), "] {", nameof(sym), "}")
+ tex.print(sym:gettheshrub())
+ tex.print(";")
+ }%
+ }{%
+ \directlua{
+ local sym = #2
+ tex.print("\\node [label=90:", whatis(sym), "] {", nameof(sym), "}")
+ tex.print(sym:gettheshrub())
+ tex.print(";")
+ }%
+ }
+}
+
+%prints the full expression tree; for use within a tikzpicture environment
+
+\NewDocumentCommand{\printtree}{s m}{%
+ \IfBooleanTF{#1}{%
+ \luaexec{
+ local sym = #2
+ sym = sym:autosimplify()
+ tex.print("\\node {",nameof(sym),"}")
+ tex.print(sym:getthetree())
+ tex.print(";")
+ }%
+ }{%
+ \luaexec{
+ local sym = #2
+ tex.print("\\node {",nameof(sym),"}")
+ tex.print(sym:getthetree())
+ tex.print(";")
+ }%
+ }
+}
+
+%parses an expression tree for use within the forest environment; result is stored in \forestresult
+
+\NewDocumentCommand{\parseforest}{s m}{%
+ \IfBooleanTF{#1}{%
+ \luaexec{
+ local sym = #2
+ sym = sym:autosimplify()
+ tex.print("\\def\\forestresult{")
+ tex.print("[")
+ tex.print(nameof(sym))
+ tex.print(sym:gettheforest())
+ tex.print("]")
+ tex.print("}")
+ }%
+ }{%
+ \luaexec {
+ local sym = #2
+ tex.print("\\def\\forestresult{")
+ tex.print("[")
+ tex.print(nameof(sym))
+ tex.print(sym:gettheforest())
+ tex.print("]")
+ tex.print("}")
+ }%
+ }
+}
+
+\NewDocumentCommand{\parseshrub}{s m}{%
+ \IfBooleanTF{#1}{%
+ \luaexec{
+ local sym = #2
+ sym = sym:autosimplify()
+ tex.print("\\def\\shrubresult{")
+ tex.print("[")
+ tex.print(nameof(sym))
+ tex.print(", tikz+={\\node[anchor=south] at (.north) {test};}")
+ tex.print(sym:getthefancyshrub())
+ tex.print("]")
+ tex.print("}")
+ }%
+ }{%
+ \luaexec{
+ local sym = #2
+ tex.print("\\def\\shrubresult{")
+ tex.print("[")
+ tex.print(nameof(sym))
+ tex.print(", tikz+={\\node[anchor=south font=\\ttfamily\\footnotesize,gray] at (.north) {",longwhati (sym),"};}")
+ tex.print(sym:getthefancyshrub())
+ tex.print("]")
+ tex.print("}")
+ }%
+ }
+}
+
+\NewDocumentCommand{\whatis}{m}{%
+ \luaexec{
+ tex.sprint("{\\ttfamily",longwhatis(#1),"}")
+ }%
+} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/intro/intro.tex b/macros/luatex/latex/luacas/doc/intro/intro.tex
new file mode 100644
index 0000000000..bf77ffad04
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/intro/intro.tex
@@ -0,0 +1,143 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse}
+\usepackage{microtype}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\begin{document}
+
+\section{What is \texttt{luacas}?}
+
+The package {\ttfamily luacas} allows for symbolic computation within \LaTeX{}. For example:
+\begin{CAS}
+ vars('x','y')
+ f = 3*x*y - x^2*y
+ fxy = diff(f,x,y)
+\end{CAS}
+\begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','y')
+ f = 3*x*y - x^2*y
+ fxy = diff(f,x,y)
+\end{CAS}
+\end{minted}
+The above code will compute the mixed partial derivative $f_{xy}$ of the function $f$ defined by
+\[ f(x,y)=3xy-x^2y.\]
+There are various methods for fetching and/or printing results from the CAS within your \LaTeX{} document:
+
+\begin{codebox}
+\begin{minted}{latex}
+ \[ \print{fxy} = \print*{fxy} \]
+\end{minted}
+\tcblower
+\[ \print{fxy} = \print*{fxy} \]
+\end{codebox}
+
+\subsection{About}
+
+The core CAS program is written purely in Lua and integrated into \LaTeX{} via Lua\LaTeX{}. Currently, most existing computer algebra systems such as Maple and Mathematica allow for converting their stored expressions to \LaTeX{} code, but this still requires exporting code from \LaTeX{} to another program and importing it back, which can be tedious.
+
+The target audience for this package are mathematics students, instructors, and professionals who would like some ability to perform basic symbolic computations within \LaTeX{} without the need for laborious and technical setup. But truly, this package was born out of a desire from the authors to learn more about symbolic computation. What you're looking at here is the proverbial ``carrot at the end of the stick'' to keep our learning moving forward.
+
+Using a scripting language (like Lua) as opposed to a compiled language for the core CAS reduces performance dramatically, but the following considerations make it a good option for our intentions:
+
+\begin{itemize}
+ \item Compiled languages that can communicate with \LaTeX{} in some way (such as C through Lua) require compiling the code on each machine before running, reducing portability.
+ \item Our target usage would generally not involve computations that take longer than a second, such as factoring large primes or polynomials.
+ \item Lua is a fast scripting language, especially when compared to Python, and is designed to be compact and portable.
+ \item If C code could be used, we could tie into one of many open-source C symbolic calculators, but the point of this project was (and continues to be) to learn the mathematics of symbolic computation. The barebones but friendly nature of Lua made it an ideal language for those intents.
+\end{itemize}
+
+\subsection{Features}
+
+Currently, {\ttfamily luacas} includes the following functionality:
+
+\begin{itemize}
+ \item Arbitrary-precision integer and rational arithmetic
+ \item Number-theoretic algorithms for factoring integers and determining primality
+ \item Constructors for arbitrary polynomial rings and integer mod rings, and arithmetic algorithms for both
+ \item Factoring univariate polynomials over the rationals and over finite fields
+ \item Polynomial decomposition and some multivariate functionality, such as pseudodivision
+ \item Basic symbolic root finding and equation solving
+ \item Symbolic expression manipulations such as expansion, substitution, and simplification
+ \item Symbolic differentiation and integration
+\end{itemize}
+
+The CAS is written using object-oriented Lua, so it is modular and would be easy to extend its functionality.
+
+\subsection{Acknowledgements}
+
+We'd like to thank the faculty of the Department of Mathematics at Rose-Hulman Institute of Technology for offering constructive feedback as we worked on this project. A special thanks goes to Dr. Joseph Eichholz for his invaluable input and helpful suggestions.
+
+\section{Installation}
+
+\subsection{Requirements}
+
+The \texttt{luacas} package (naturally) requires you to compile with Lua\LaTeX{}. Lua 5.3 or higher is also required. Beyond that, the following packages are needed:
+\begin{multicols}{2}
+{\ttfamily
+\begin{itemize}
+ \item xparse
+ \item pgfkeys
+ \item verbatim
+ \item mathtools
+ \item luacode
+ \item iftex
+ \item tikz/forest
+ \item xcolor
+\end{itemize}}
+\end{multicols}
+The packages {\ttfamily tikz}, {\ttfamily forest}, and {\ttfamily xcolor} aren't strictly required, but they are needed for drawing expression trees.
+
+\subsection{Installing {\ttfamily luacas}}
+The package manager for your local TeX distribution ought to install the package fine on its own. But for those who like to take matters into their own hands: unpack \texttt{luacas.zip} in the current working directory (or in a directory visible to TeX, like your local texmf directory), and in the preamble of your document, put:
+\begin{minted}{latex}
+\usepackage{luacas}
+\end{minted}
+That's it, you're ready to go.
+
+\subsection{Todo}
+
+Beyond squashing bugs that inevitably exist in any new piece of software, future enhancements to \texttt{luacas} may include:
+\begin{itemize}
+ \item Improvements to existing functionality, e.g., a more powerful \texttt{simplify()} command and more powerful expression manipulation tools in general, particularly in relation to complex numbers, a designated class for multivariable polynomial rings, irreducible factorization over multivariable polynomial rings, and performance improvements;
+ \item New features in the existing packages, such as sum and product expressions \& symbolic evaluation of both, and symbolic differential equation solving;
+ \item New packages, such as for logic (boolean expressions), set theory (sets), and linear algebra (vectors and matrices), and autosimplification rules and algorithms for all of them;
+ \item Numeric functionality, such as numeric root-finding, linear algebra, integration, and differentiation;
+ \item A parser capable of evaluating arbitrary \LaTeX{} code and turning it into CAS expressions.
+\end{itemize}
+
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/intro/intropart.tex b/macros/luatex/latex/luacas/doc/intro/intropart.tex
new file mode 100644
index 0000000000..bfeb4a79b8
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/intro/intropart.tex
@@ -0,0 +1,202 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+
+\makeindex
+
+\def\currref{Core Methods}
+
+\newcommand{\coderef}[2]{%
+\index{\currref!\texttt{#1}}%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\index{\currref!\texttt{#1}}%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+
+\begin{document}
+\thispagestyle{empty}
+\part{Introduction}
+
+\begin{multicols}{2}
+ \begin{minted}{latex}
+\begin{CAS}
+ vars('x')
+ f = x
+ for i in range(1,9) do
+ f = f*x
+ end
+ f = f-1
+\end{CAS}
+\parseforest{f}
+\bracketset{action character = @}
+\begin{center}
+\begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+\end{forest}
+\end{center}
+
+\begin{CAS}
+ f = factor(f)
+\end{CAS}
+\parseforest{f}
+\begin{center}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+\end{forest}
+\end{center}
+\end{minted}
+
+\columnbreak
+\phantom{}
+\vfill
+\begin{CAS}
+ vars('x')
+ f = x
+ for i in range(1,9) do
+ f = f*x
+ end
+ f = f-1
+\end{CAS}
+\parseforest{f}
+\bracketset{action character = @}
+\begin{center}
+\begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+\end{forest}
+\end{center}
+\vfill
+\phantom{}
+\end{multicols}
+\vfill
+
+\begin{CAS}
+ f = factor(f)
+\end{CAS}
+\parseforest{f}
+\begin{center}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+\end{forest}
+\end{center}
+\vfill
+
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/luacas.pdf b/macros/luatex/latex/luacas/doc/luacas.pdf
new file mode 100644
index 0000000000..d3ea07adf3
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/luacas.pdf
Binary files differ
diff --git a/macros/luatex/latex/luacas/doc/luacas.tex b/macros/luatex/latex/luacas/doc/luacas.tex
new file mode 100644
index 0000000000..b5aad9c9f9
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/luacas.tex
@@ -0,0 +1,214 @@
+\documentclass{article}
+
+\usepackage{standalone}
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage[edges]{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse}
+\usepackage{microtype}
+%\usepackage[toc]{multitoc}
+
+\renewcommand{\comment}[1]{}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle,
+ %frame hidden
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\DeclareTotalTCBox{\lilcoderef}{O{} m m}{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ enhanced,
+ nobeforeafter,
+ tcbox raise base,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=1mm,
+ right=1mm,
+ top=1mm,
+ bottom=1mm,
+ oversize,
+ #1
+}{\mintinline{lua}{#2} \mintinline{lua}{#3}}
+
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+\usepackage{makeidx}
+\usepackage{fontawesome5}
+\usepackage{marginnote}
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+
+\def\error{\color{red}}
+\def\self{\color{gray}}
+\def\call{}
+
+%\includeonly{
+% reference/ref_calculus/ref_calculus_methods/ref_calculus_methods_test.tex
+%}
+
+\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.0 \\ \today}
+
+\maketitle
+
+\begin{abstract}
+ The {\ttfamily luacas} package is a portable Computer Algebra System capable of symbolic computation, written entirely in Lua, designed for use in Lua\LaTeX{}.
+\end{abstract}
+
+\tableofcontents
+
+\include{intro/intropart.tex}
+
+\include{intro/intro.tex}
+
+\include{tutorial/tut.tex}
+
+\include{tutorial/tut1/tut1.tex}
+
+\include{tutorial/tut2/tut2.tex}
+
+\include{tutorial/tut3/tut3.tex}
+
+\include{reference/ref.tex}
+
+\include{reference/ref_core/ref_core.tex}
+
+\include{reference/ref_core/ref_core_classes/ref_core_classes.tex}
+
+\include{reference/ref_core/ref_core_methods/ref_core_methods.tex}
+
+\include{reference/ref_algebra/ref_algebra.tex}
+
+\include{reference/ref_algebra/ref_algebra_classes/ref_algebra_classes.tex}
+
+\include{reference/ref_algebra/ref_algebra_methods/ref_algebra_methods.tex}
+
+\include{reference/ref_calculus/ref_calculus.tex}
+
+\include{reference/ref_calculus/ref_calculus_classes/ref_calculus_classes.tex}
+
+\include{reference/ref_calculus/ref_calculus_methods/ref_calculus_methods.tex}
+
+\appendix
+
+\include{appendix/latexcode.tex}
+
+\newpage
+
+\printindex
+
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/reference/ref.tex b/macros/luatex/latex/luacas/doc/reference/ref.tex
new file mode 100644
index 0000000000..426a1e17e5
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref.tex
@@ -0,0 +1,236 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage[edges]{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+
+\begin{document}
+\thispagestyle{empty}
+\part{Reference}
+This part contains reference material for the classes and methods that incorporate the \texttt{luacas} package. Some classes are \emph{concrete} while others are \emph{abstract}. The concrete classes are essentially the objects that a user might reasonably interact with while using \texttt{luacas}. Thankfully, most of this interaction will be filtered through a rudimentary (but functional!) parser. Abstract classes exist for the purposes of inheritance.
+
+The classes in the diagram below are color-coded according to:
+\begin{itemize}
+ \item (\tikz[baseline=-0.5ex]\node[text=rosenavy,fill=roseblue!40]{\ttfamily\bfseries Class};) {\color{rosenavy}\ttfamily\bfseries Class}: a (concrete) class belonging to the core module;
+ \item (\tikz[baseline=-0.5ex]\node[text=rose,fill=rose!30]{\ttfamily\bfseries Class};) {\color{rose}\ttfamily\bfseries Class}: a (concrete) class belonging to the algebra module;
+ \item (\tikz[baseline=-0.5ex]\node[text=roseorange,fill=roseorange!30]{\ttfamily\bfseries Class};) {\color{roseorange}\ttfamily\bfseries Class}: a (concrete) class belonging to the calculus module.
+\end{itemize}
+Inheritance is indicated with an arrow:
+
+\forestset{multiple directions/.style={for tree={#1}, phantom, for relative level=1{no edge, delay={!c.content/.pgfmath=content("!u")}, before computing xy={l=0,s=0}}},
+ multiple directions/.default={},
+ grow subtree/.style={for tree={grow=#1}},
+ grow' subtree/.style={for tree={grow'=#1}}}
+
+\begin{center}
+ \begin{forest}
+ core/.style = {draw={rosenavy,thick},
+ font={\ttfamily\bfseries\color{rosenavy}},
+ fill = roseblue!#1},
+ algebra/.style = {draw={rose,thick},
+ font={\ttfamily\bfseries\color{rose}},
+ fill = rose!#1},
+ calculus/.style = {draw={roseorange,thick},
+ font={\ttfamily\bfseries\color{roseorange}},
+ fill = roseorange!#1},
+ multiple directions,
+ for tree = {font = \ttfamily\bfseries,
+ draw,
+ rounded corners=1pt,
+ edge = {-stealth},
+ s sep = 0.2cm}
+ [Expression
+ [, grow subtree = 90
+ [AtomicExpression,
+ core = {0},
+ for tree = {grow=east,
+ child anchor=west,
+ node options={anchor=west}},
+ forked edges,
+ calign=last,
+ l sep = 0.5cm,
+ s sep = 0.2cm
+ [SymbolExpression,
+ core={50}]
+ [ConstantExpression,
+ core ={0},
+ for tree = {grow = north,
+ node options = {anchor=north}
+ }
+ [Ring,
+ algebra ={0},
+ for tree = {grow = east}
+ [EuclideanDomain,
+ algebra={0}
+ [Field,
+ algebra = {0}
+ [IntegerModN,
+ algebra={30}]
+ [Rational,
+ algebra={30}]
+ ]
+ [Integer,
+ algebra={30}]
+ ]
+ [PolynomialRing,
+ algebra={30}]
+ ]
+ ]
+ ]]
+ [, grow subtree = -90
+ [CompoundExpression,
+ core = {0},
+ for tree = {grow=east,
+ child anchor=west,
+ node options={anchor=west}},forked edges,
+ calign=last,
+ l sep = 0.5cm,
+ s sep = 0.2cm
+ [FunctionExpression,
+ core = {40}
+ [TrigExpression,algebra={30}]
+ ]
+ [BinaryOperation,
+ core = {40}]
+ [Equation,
+ algebra = {30}]
+ [Logarithm,
+ algebra={30}]
+ [FactorialExpression,
+ algebra={30}]
+ [RootExpression,
+ algebra={30}]
+ [SqrtExpression,
+ algebra={30}]
+ [AbsExpression,
+ algebra={30}]
+ [DerivativeExpression,
+ calculus={20}]
+ [DiffExpression,
+ calculus={20}]
+ [IntegralExpression,
+ calculus={20}]
+ ]]
+ ]
+ \end{forest}
+\end{center}
+Every object in \texttt{luacas} is an expression, meaning it inherits from the {\ttfamily Expression} type (class). Since the {\ttfamily Expression} type itself has no constructor and cannot be instantiated, it it closer to an interface in Java OOP terms.\footnote{In reality, interfaces are unnecessary in Lua due to its weak typing - Lua doesn't check whether an object has a method at compile time. The {\ttfamily Expression} type is really an abstract class in Java terms.} {\ttfamily Expression}s can store any number of other expressions as sub-expressions, depending on type. This means that {\ttfamily Expression} objects are really trees. Types that inherit from {\ttfamily Expression} that can not store other expressions are called \emph{atomic expressions}, and correspond to the leaf nodes of the tree. Other expression types are \emph{compound expressions}. Thus, every {\ttfamily Expression} type inherits from one of {\ttfamily AtomicExpression} or {\ttfamily CompoundExpression}. The {\ttfamily ConstantExpresssion} interface is a subinterface to {\ttfamily AtomicExpression}. Types that inherit from {\ttfamily ConstantExpression} roughly correspond to numbers (interpreted broadly).
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra.tex b/macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra.tex
new file mode 100644
index 0000000000..0e9bd011e8
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra.tex
@@ -0,0 +1,336 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,
+ size=small,
+ bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}
+ },
+ title={#2}
+}
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+
+\usetikzlibrary{shapes.multipart}
+\useforestlibrary{edges}
+
+\def\error{\color{red}}
+\def\self{\color{gray}}
+\def\call{$\star$ }
+
+\begin{document}
+\thispagestyle{empty}
+
+\section{Algebra}
+ This section contains reference materials for the algebra functionality of \texttt{luacas}. The classes in this module are diagramed below according to inheritance along with the methods/functions one can call upon them.
+ \begin{itemize}
+ \item {\error\ttfamily\itshape method}: an abstract method;
+ \item {\self\ttfamily\itshape method}: a method that returns the expression unchanged;
+ \item {\ttfamily\itshape method}: method that is either unique, implements an abstract method, or overrides an abstract
+method;
+ \item {\tikz[baseline=-0.5ex]\node[fill=rose!30] {\ttfamily\bfseries Class};}: a concrete class.
+ \end{itemize}
+Here is an inhertiance diagram of the classes in the algebra module that are derived from the \texttt{AtomicExpression} branch of classes. However, not all of them are proper {\ttfamily ConstantExpression}s, so some of them override the {\ttfamily isconstant()} method. Most methods are stated, but some were omitted (because they inherit in the obvious way, they are auxiliary and not likely to be interesting to the end-user, etc).
+ \vfill
+\forestset{
+rectcore/.style = {rectangle split,
+ rectangle split parts=2,
+ draw = {rosenavy,thick},
+ rounded corners = 1pt,
+ font = \ttfamily\bfseries,
+ fill = roseblue!#1
+ },
+rectalg/.style = {rectangle split,
+ rectangle split parts=2,
+ draw = {rose,thick},
+ rounded corners = 1pt,
+ font = \ttfamily\bfseries,
+ fill = rose!#1
+ }
+}
+\forestset{
+ multiple directions/.style={
+ for tree={#1},
+ phantom,
+ for relative level=1{
+ no edge,
+ delay={
+ !c.content/.pgfmath=content("!u")},
+ before computing xy={l=0,s=0}
+ }
+ },
+ multiple directions/.default={},
+ grow subtree/.style={for tree={grow=#1}},
+ grow' subtree/.style={for tree={grow'=#1}}}
+\tikzset{
+ every two node part/.style={font=\ttfamily\itshape\footnotesize}
+}
+\begin{center}
+ \begin{forest}
+ for tree = {node options={align=left},
+ edge = {-stealth}
+ },
+ forked edges
+ [Expression\nodepart{two}$\cdots$,rectcore={0}
+ [AtomicExpression\nodepart{two}$\cdots$,rectcore={0}
+ [SymbolExpression\nodepart{two}$\cdots$,rectcore={50}]
+ [ConstantExpression\nodepart{two}$\cdots$,rectcore={0}
+ [Ring\nodepart{two}\begin{minipage}{0.45\textwidth}\begin{multicols}{3}
+ {\error :getring}\\
+ {\error :inring(ring)}\\
+ {\error :iscommutative}\\
+ {\error :add(b)}\\
+ :sub(b)\\
+ {\error :neg(b)}\\
+ {\error :mul(b)}\\
+ :pow(n)\\
+ {\error :eq(b)}\\
+ {\error :lt(b)}\\
+ {\error :le(b)}\\
+ {\error :zero()}\\
+ {\error :one()}\\
+ {\error .makering}\\
+ .resultantring
+ \end{multicols}\end{minipage}
+ ,rectalg={0}
+ [PolynomialRing\nodepart{two}
+ .makering\\
+ .R\\
+ .gcd\\
+ .extendedgcd\\
+ .resultant\\
+ .resultantmulti\\
+ .monicgcdremainders\\
+ .partialfractions\\
+ %:isatomic\\
+ :new\\
+ .mul{\textunderscore}rec\\
+ :divremainder\\
+ :psuedodivide\\
+ :isconstant\\
+ :isatomic\\
+ :freeof\\
+ :tocompoundexpression\\
+ :evaluateat\\
+ :derivative\\
+ :squarefreefactorization\\
+ :factor\\
+ :rationalroots\\
+ :roots\\
+ $\cdots$
+ ,rectalg={30}]
+ [EuclideanDomain\nodepart{two}
+ {\error :divremainder}\\
+ :iscommutative
+ ,rectalg={0}
+ [Integer\nodepart{two}
+ .gcd\\
+ .extendedgcd\\
+ .max\\
+ .min\\
+ .ceillog\\
+ .powmod\\
+ :new\\
+ :divremainder\\
+ :asnumber\\
+ :divisors\\
+ :primefactorization\\
+ :findafactor\\
+ :isprime\\
+ :abs\\
+ $\cdots$
+ ,rectalg={30}]
+ [Field\nodepart{two}
+ :div
+ ,rectalg={0}
+ [Rational\nodepart{two}
+ :new\\
+ :reduce\\
+ :isconstant\\
+ :tocompoundexpression\\
+ :asnumber\\
+ :div
+ ,rectalg={30}]
+ [IntegerModN\nodepart{two}
+ :new
+ ,rectalg={30}]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ \end{forest}
+\end{center}
+\vfill
+
+\newpage
+
+Here is an inhertiance diagram of the classes in the algebra module that are derived from the \texttt{CompoundExpression} branch of classes. Again, most methods are stated, but some were omitted (because they inherit in the obvious way, they are auxiliary and not likely to be interesting to the end-user, etc).
+\vfill
+\begin{center}
+ \begin{forest}
+ for tree = {node options={align=left},
+ grow = south,
+ edge = {-stealth},
+ child anchor = west
+ },
+ forked edges
+ [Expression\nodepart{two}$\cdots$,
+ rectcore={0}
+ [CompoundExpression\nodepart{two}
+ $\cdots$,
+ rectcore={0},
+ calign = first,
+ for tree = {node options = {anchor = west},
+ grow' = east}
+ [BinaryOperation\nodepart{two}
+ $\cdots$,
+ rectcore={50}]
+ [FunctionExpression\nodepart{two}
+ $\cdots$,
+ rectcore={50}
+ [TrigExpression\nodepart{two}
+ :new
+ ,rectalg={30}]
+ ]
+ [AbsExpression\nodepart{two}
+ :new
+ ,rectalg={30}]
+ [SqrtExpression\nodepart{two}
+ :new\\
+ :topower
+ ,rectalg={30}]
+ [RootExpression\nodepart{two}
+ :new
+ ,rectalg={30}]
+ [FactorialExpression\nodepart{two}
+ :new
+ ,rectalg={30}]
+ [Logarithm\nodepart{two}
+ :new
+ ,rectalg={30}]
+ [Equation\nodepart{two}
+ :new\\
+ :solvefor()
+ ,rectalg={30}]
+ ]
+ ]
+ \end{forest}
+\end{center}
+\vfill
+\end{document}
+
diff --git a/macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_classes/ref_algebra_classes.tex b/macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_classes/ref_algebra_classes.tex
new file mode 100644
index 0000000000..58d9b69f84
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_classes/ref_algebra_classes.tex
@@ -0,0 +1,977 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+\usepackage{fontawesome}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+
+\begin{document}
+\setdescription{style=multiline,
+ topsep=10pt,
+ leftmargin=5cm,
+ }
+
+\subsection{Algebra Classes}
+
+The algebra package contains functionality for arbitrary-precision arithmetic, polynomial arithmetic and factoring, symbolic root finding, and logarithm and trigonometric expression classes. It requires the core package to be loaded.
+
+The abstract classes in the algebra module all inherit from the \texttt{ConstantExpression} branch in the inheritance tree:
+
+\begin{itemize}
+ \item \texttt{Ring}
+ \item \texttt{EuclideanDomain}
+ \item \texttt{Field}
+\end{itemize}
+
+The {\ttfamily EuclideanDomain} class is a sub-class to the {\ttfamily Ring} class, and the {\ttfamily Field} class is a sub-class to the {\ttfamily EuclideanDomain} class.
+
+The following concrete classes inherit from the {\ttfamily Ring} class (or one of the sub-classes mentioned above). However, not all of them are proper {\ttfamily ConstantExpression}s, so some of them override the {\ttfamily isconstant()} method.
+
+\begin{itemize}
+ \item {\ttfamily Integer}
+ \item {\ttfamily IntegerModN}
+ \item {\ttfamily Rational}
+ \item {\ttfamily PolynomialRing}
+\end{itemize}
+
+The other concrete classes in the Algebra package do not inherit from the {\ttfamily Ring} interface, instead they inherit from the {\ttfamily CompoundExpression} interface:
+
+\begin{multicols}{2}
+\begin{itemize}
+ \item {\ttfamily AbsExpression}
+ \item {\ttfamily Logarithm}
+ \item {\ttfamily FactorialExpression}
+ \item {\ttfamily SqrtExpression}
+ \item {\ttfamily TrigExpression}
+ \item {\ttfamily RootExpression}
+ \item {\ttfamily Equation}
+\end{itemize}
+\end{multicols}
+
+\newcoderef{function Integer:new(n)}{return Integer}{n number|string|Integer}
+\index{Algebra!Classes!\texttt{SymbolExpression}}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer}
+
+Takes a \texttt{string}, \texttt{number}, or {\ttfamily Integer} input and constructs an \texttt{Integer} expression. The \texttt{Integer} class allows us to perform exact arithmetic on integers. Indeed, since Lua can only store integers exactly up to a certain point, it is recommended to use strings to build large integers.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+a = Integer(-12435)
+b = Integer('-12435')
+tex.print('\\[',a:tolatex(),
+ '=',
+ b:tolatex(),
+ '\\]')
+\end{minted}
+\tcblower
+\directlua{
+ a = Integer(-12435)
+ b = Integer('-12435')
+ tex.print('\\[',a:tolatex(),
+ '=',
+ b:tolatex(),
+ '\\]')
+}
+\end{codebox}
+An {\ttfamily Integer} is a table 1-indexed by Lua numbers consisting of Lua numbers. For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+tex.print(tostring(b[1]))
+\end{minted}
+\tcblower
+\directlua{
+ tex.print(tostring(b[1]))
+}
+\end{codebox}
+Whereas:
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{lua}
+c = Integer('7240531360949381947528131508')
+tex.print('The first 14 digits of c:', tostring(c[1]),'. ')
+tex.print('The last 14 digits of c:', tostring([2]),'.')
+\end{minted}
+\tcblower
+\directlua{
+ c = Integer('7240531360949381947528131508')
+ tex.print('The first 14 digits of c:', tostring(c[1]),'. ')
+ tex.print('The last 14 digits of c:', tostring(c[2]),'.')
+}
+\end{codebox}
+
+The global field {\ttfamily DIGITSIZE} is set to \texttt{14} so that exact arithmetic on {\ttfamily Integer}s can be done as efficiently as possible while respecting Lua's limitations.
+
+\subsubsection*{Fields}
+{\ttfamily Integer}s have a {\ttfamily .sign} field which contains the Lua number {\ttfamily 1} or {\ttfamily -1} depending on whether \texttt{Integer} is positive or negative.
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{lua}
+tex.print('The sign of',tostring(b),'is:',tostring(b.sign))
+\end{minted}
+\tcblower
+\directlua{
+ tex.print('The sign of',
+ tostring(b),
+ 'is:',
+ tostring(b.sign))
+}
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The contents of the environment \mintinline{latex}{\begin{CAS}..\end{CAS}} are wrapped in the argument of a function \mintinline{lua}{CASparse()} which, among other things, seeks out digit strings intended to represent integers, and wraps those in \texttt{Integer('...')}.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ c = 7240531360949381947528131508
+\end{CAS}
+\directlua{
+ tex.print(tostring(c[1]))
+}
+\end{minted}
+\tcblower
+\begin{CAS}
+ c = 7240531360949381947528131508
+\end{CAS}
+\directlua{
+ tex.print(tostring(c[1]))
+}
+\end{codebox}
+
+\newcoderef{function IntegerModN:new(i,n)}{return IntegerModN}{i Integer, n Integer}
+\index{Algebra!Classes!\texttt{IntegerModN}}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegerModN}
+
+Takes an {\ttfamily Integer i} and {\ttfamily Integer n} and constructs an element in the ring $\mathbf{Z}/n\mathbf{Z}$, the integers modulo $n$.
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{lua}
+i = Integer(143)
+n = Integer(57)
+a = IntegerModN(i,n)
+tex.print('\\[',i:tolatex(),'\\equiv',a:tolatex(true),'\\]')
+\end{minted}
+\tcblower
+\luaexec{
+ i = Integer(143)
+ n = Integer(57)
+ a = IntegerModN(i,n)
+ tex.print('\\[',i:tolatex(),'\\equiv',a:tolatex(true),'\\]')
+}
+\end{codebox}
+
+\subsubsection*{Fields}
+
+{\ttfamily IntegerModN}s have two fields: {\ttfamily .element} and {\ttfamily .modulus}. The reduced input \texttt{i} is stored in {\ttfamily .element} while the input \texttt{n} is stored in {\ttfamily .modulus}:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+tex.print(a.element:tolatex(),'\\newline')
+tex.print(a.modulus:tolatex())
+\end{minted}
+\tcblower
+\luaexec{
+ tex.print(a.element:tolatex(),'\\newline')
+ tex.print(a.modulus:tolatex())
+}
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \texttt{Mod(,)} is a shortcut for \texttt{IntegerModN(,)}:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ i = 143
+ n = 57
+ a = Mod(i,n)
+\end{CAS}
+\[\print{i}\equiv\print{a}\bmod{\print{n}}\]
+\end{minted}
+\tcblower
+\begin{CAS}
+ i = 143
+ n = 57
+ a = Mod(i,n)
+\end{CAS}
+\[ \print{i} \equiv \print{a} \bmod{\print{n}}\]
+\end{codebox}
+
+\newcoderef{function PolynomialRing:new(coefficients, symbol, degree)}{return PolynomialRing}{coefficients table<number,Ring>, symbol string|SymbolExpression, degree Integer}
+\index{Algebra!Classes!\texttt{PolynomialRing}}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing}
+
+Takes a table of {\ttfamily coefficients}, not all necessarily in the same ring, and a {\ttfamily symbol} to create a polynomial in $\mathtt{R[x]}$ where $\mathtt{x}$ is {\ttfamily symbol} and $\mathtt{R}$ is the smallest {\ttfamily Ring} possible given the coefficients. If {\ttfamily degree} is omitted, it will calculate the degree of the polynomial automatically. The list can either be one-indexed or zero-indexed, but if it is one-indexed, the internal list of coefficients will still be zero-indexed.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = PolynomialRing({0,1/3,-1/2,1/6},'t')
+\end{CAS}
+\[ \print{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = PolynomialRing({0,1/3,-1/2,1/6},'t')
+\end{CAS}
+\[ \print{f} \]
+\end{codebox}
+The \texttt{PolynomialRing} class overwrites the \mintinline{lua}{isatomic()} and \mintinline{lua}{isconstant()} inheritances from the abstract class \texttt{ConstantExpression}.
+\subsubsection*{Fields}
+
+\begin{multicols}{2}
+{\ttfamily PolynomialRing}s have several fields:
+\begin{itemize}
+ \item {\ttfamily f.coefficients} stores the 0-indexed table of coefficients of {\ttfamily f};
+ \item {\ttfamily f.degree} stores the {\ttfamily Integer} that represents the degree of {\ttfamily f};
+ \item {\ttfamily f.symbol} stores the {\ttfamily string} representing the variable or {\ttfamily symbol} of {\ttfamily f}.
+ \item {\ttfamily f.ring} stores the \texttt{RingIdentifier} for the ring of coefficients.
+\end{itemize}
+
+\columnbreak
+
+\parseshrub{f}
+\bracketset{action character = @}
+\begin{center}
+\begin{forest}
+ for tree = {font = \ttfamily,
+ draw,
+ rounded corners = 1pt,
+ fill=gray!20,
+ s sep = 1.5cm,
+ l sep = 2cm}
+ @\shrubresult
+\end{forest}
+\end{center}
+\end{multicols}
+For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+for i=0,f.degree:asnumber() do
+ tex.print('\\[',
+ f.coefficients[i]:tolatex(),
+ f.symbol,
+ '^{',
+ tostring(i),
+ '}\\]')
+end
+if f.ring == Rational.getring() then
+ tex.print('Rational coefficients')
+end
+\end{minted}
+\tcblower
+\luaexec{
+for i=0,f.degree:asnumber() do
+ tex.print(
+ '\\[',
+ f.coefficients[i]:tolatex(),
+ f.symbol,
+ '^{',
+ tostring(i),
+ '}\\]'
+ )
+ end
+ if f.ring == Rational.getring() then
+ tex.print('Rational coefficients')
+ end
+}
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{Poly()} is a shortcut for \mintinline{lua}{PolynomialRing:new()}. If the second argument \texttt{symbol} is omitted, then the default is \texttt{'x'}:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({0,1/3,-1/2,1/6})
+\end{CAS}
+\[ \print{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({0,1/3,-1/2,1/6})
+\end{CAS}
+\[ \print{f} \]
+\end{codebox}
+
+Alternatively, one could typeset the polynomial naturally and use the \texttt{topoly()} function. This is the same as the \texttt{topolynomial()} method except that the \texttt{autosimplify()} method is automatically called first:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = 1/3*x - 1/2*x^2 + 1/6*x^3
+ f = topoly(f)
+\end{CAS}
+\[ \print{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = 1/3*x - 1/2*x^2 + 1/6*x^3
+ f = topoly(f)
+\end{CAS}
+\[ \print{f} \]
+\end{codebox}
+
+\newcoderef{function Rational:new(n,d,keep)}{return Rational}{n Ring, d Ring, keep bool}
+\index{Algebra!Classes!\texttt{Rational}}
+\addcontentsline{toc}{subsubsection}{\ttfamily Rational}
+
+Takes a numerator {\ttfamily n} and denominator {\ttfamily d} in the same {\ttfamily Ring} and constructs a rational expression in the field of fractions over that ring. For the integers, this is the ring of rational numbers. If the {\ttfamily keep} flag is omitted, the constructed object will be simplified to have smallest possible denominator, possibly returning an object in the original {\ttfamily Ring}. Typically, the {\ttfamily Ring} will be either {\ttfamily Integer} or {\ttfamily PolynomialRing}, so {\ttfamily Rational} can be viewed as a constructor for either a rational number or a rational function.
+
+For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+a = Integer(6)
+b = Integer(10)
+c = Rational(a,b)
+tex.print('\\[',c:tolatex(),'\\]')
+\end{minted}
+\tcblower
+\luaexec{
+ a = Integer(6)
+ b = Integer(10)
+ c = Rational(a,b)
+ tex.print('\\[',c:tolatex(),'\\]')
+}
+\end{codebox}
+But also:
+\begin{codebox}
+ \begin{minted}{lua}
+a = Poly({Integer(2),Integer(3)})
+b = Poly({Integer(4),Integer(1)})
+c = Rational(a,b)
+tex.print('\\[',c:tolatex(),'\\]')
+\end{minted}
+\tcblower
+\luaexec{
+a = Poly({Integer(2),Integer(3)})
+b = Poly({Integer(4),Integer(1)})
+c = Rational(a,b)
+tex.print('\\[',c:tolatex(),'\\]')
+}
+\end{codebox}
+
+\subsubsection*{Fields}
+
+\texttt{Rational}s naturally have the two fields: \texttt{numerator}, \texttt{denominator}. These fields store precisely what you think. \texttt{Rational}s also have a \texttt{ring} field which stores the \texttt{RingIdentifier} to which the numerator and denominator belong. (This is $\mathbb{Z}$ for the rational numbers.)
+
+If \texttt{numerator} or \texttt{denominator} are \texttt{PolynomialRing}s, then the constructed \texttt{Rational} will have an additional field: \texttt{symbol}. This stores the symbol the polynomial rings are constructed over.
+
+\begin{codebox}[]
+ \begin{minted}{lua}
+if c.ring == PolynomialRing.getring() then
+ tex.print('$',c:tolatex(),'$ is a Rational Function in the variable',c.symbol)
+end
+\end{minted}
+\tcblower
+\luaexec{
+if c.ring == PolynomialRing.getring() then
+ tex.print('$',c:tolatex(),'$ is a Rational Function in the variable',c.symbol)
+end
+}
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+\texttt{Raional}s are constructed naturally using the \texttt{/} operator:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = Poly({2,3})
+ b = Poly({4,1})
+ c = a/b
+\end{CAS}
+\[ \print{c} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = Poly({2,3})
+ b = Poly({4,1})
+ c = a/b
+\end{CAS}
+\[ \print{c} \]
+\end{codebox}
+
+\coderef{function AbsExpression:new(expression)}{return AbsExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily AbsExpression}
+
+Creates a new absolute value expression with the given expression.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({1,1})
+ g = Poly({-1,1})
+ h = AbsExpression(f/g)
+\end{CAS}
+\[ h = \print{h} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({1,1})
+ g = Poly({-1,1})
+ h = AbsExpression(f/g)
+\end{CAS}
+\[ h = \print{h} \]
+\end{codebox}
+
+\subsubsection*{Fields}
+
+\texttt{AbsExpression}s have only one field: \texttt{.expression}. This field simply holds the \texttt{Expression} inside the absolute value:
+\begin{multicols}{2}
+\begin{codebox}[]
+\begin{minted}[fontsize=\small]{lua}
+tex.print('\\[',
+ h.expression:tolatex(),
+ '\\]')
+\end{minted}
+\tcblower
+\directlua{
+ tex.print('\\[',h.expression:tolatex(),'\\]')
+}
+\end{codebox}
+\parseshrub{h}
+\bracketset{action character = @}
+\begin{center}
+\begin{forest}
+ for tree = {font=\ttfamily,
+ draw,
+ rounded corners=1pt,
+ fill=gray!20,
+ l sep =1.5cm}
+ @\shrubresult
+\end{forest}
+\end{center}
+\end{multicols}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{abs()} is a shortcut to \mintinline{lua}{AbsExpression:new()}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({1,1})
+ g = Poly({-1,1})
+ h = abs(f/g)
+\end{CAS}
+\[ h = \print{h} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({1,1})
+ g = Poly({-1,1})
+ h = abs(f/g)
+\end{CAS}
+\[ h = \print{h} \]
+\end{codebox}
+
+\newcoderef{function Logarithm:new(base,arg)}{return Logarithm}{base Expression, arg Expression}
+\addcontentsline{toc}{subsubsection}{\ttfamily Logarithm}
+
+Creates a new \texttt{Logarithm} expression with the given \texttt{base} and \texttt{arg}ument. Some basic simplification rules are known to \texttt{autosimplify()}:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('b','x','y')
+ f = Logarithm(b,x^y)
+\end{CAS}
+\[ \print{f} = \print*{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('b','x','y')
+ f = Logarithm(b,x^y)
+\end{CAS}
+\[ \print{f} = \print*{f} \]
+\end{codebox}
+
+\subsubsection*{Fields}
+
+\begin{multicols}{2}
+\texttt{Logarithm}s have two fields: \texttt{base} and \texttt{expression}; \texttt{base} naturally stores the base of the logarithm (i.e., the first argument of \texttt{Logarithm}) while \texttt{expression} stores the argument of the logarithm (i.e., the second argument of \texttt{Logarithm}).
+
+\begin{center}
+ \parseshrub{f}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {font = \ttfamily,
+ draw,
+ rounded corners=1pt,
+ fill = gray!20,
+ s sep = 1.5cm}
+ @\shrubresult
+ \end{forest}
+\end{center}
+\end{multicols}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{log()} is a shortcut to \texttt{Logarithm}:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('b')
+ f = log(b,b)
+\end{CAS}
+\[ \print{f} = \print*{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('b')
+ f = log(b,b)
+\end{CAS}
+\[ \print{f} = \print*{f} \]
+\end{codebox}
+
+There is also a \mintinline{lua}{ln()} function to shortcut \texttt{Logarithm} where the base is \texttt{e}, the natural exponent.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = ln(e)
+\end{CAS}
+\[ \print{f} = \print*{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = ln(e)
+\end{CAS}
+\[ \print{f} = \print*{f} \]
+\end{codebox}
+
+\newcoderef{function FactorialExpression:new(expression)}{return FactorialExpression}{expression Expression}
+\addcontentsline{toc}{subsubsection}{\ttfamily FactorialExpression}
+
+Creates a new \texttt{FactorialExpression} with the given \texttt{expression}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = FactorialExpression(5)
+\end{CAS}
+\[ \print{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = FactorialExpression(5)
+\end{CAS}
+\[ \print{a} \]
+\end{codebox}
+The \texttt{evaluate()} method will compute factorials of nonnegative \texttt{Integer}s:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = FactorialExpression(5)
+\end{CAS}
+\[ \print{a} = \print{a:evaluate()} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = FactorialExpression(5)
+\end{CAS}
+\[ \print{a} = \print{a:evaluate()} \]
+\end{codebox}
+
+\subsubsection*{Fields}
+
+\texttt{FactorialExpression}s have only one field: \texttt{expression}. This field stores the argument of \texttt{FactorialExpression()}.
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{factorial()} is a shortcut to \texttt{FactorialExpression()}:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = factorial(5)
+\end{CAS}
+\[ \print{a} = \print{a:evaluate()} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = factorial(5)
+\end{CAS}
+\[ \print{a} = \print{a:evaluate()} \]
+\end{codebox}
+
+\newcoderef{function SqrtExpression:new(expression, root)}{return SqrtExpression}{expression Expression, root Integer}
+\addcontentsline{toc}{subsubsection}{\ttfamily SqrtExpression}
+
+Creates a new \texttt{SqrtExpression} with the given \texttt{expression} and \texttt{root}. Typically, \texttt{expression} is an \texttt{Integer} or \texttt{Rational}, and \texttt{SqrtExpression} is intended to represent a positive real number. If \texttt{root} is omitted, then \texttt{root} defaults to \mintinline{lua}{Integer(2)}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+a = SqrtExpression(Integer(8))
+b = SqrtExpression(Integer(8),Integer(3))
+c = a+b
+tex.print('\\[',c:tolatex(),'\\]')
+\end{minted}
+\tcblower
+\directlua{
+ a = SqrtExpression(Integer(8))
+b = SqrtExpression(Integer(8),Integer(3))
+c = a+b
+tex.print('\\[',c:tolatex(),'\\]')
+}
+\end{codebox}
+When \texttt{expression} and \texttt{root} are of the \texttt{Integer} or \texttt{Rational} types, then \texttt{autosimplify()} does a couple things. For example, with \texttt{a,b} as above, we get:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+c = c:autosimplify()
+tex.print('\\[',c:tolatex(),'\\]')
+\end{minted}
+\tcblower
+\directlua{
+c = c:autosimplify()
+tex.print('\\[',c:tolatex(),'\\]')
+}
+\end{codebox}
+On the other hand, if \texttt{root} or \texttt{expression} are not constants, then typically \mintinline{lua}{autosimplify()} will convert \texttt{SqrtExpression} to the appropriate \texttt{BinaryOperation}. For example:
+
+\directlua{
+ vars('x')
+ a = SqrtExpression(x,Integer(3))
+ b = a:autosimplify()
+}
+
+\begin{multicols}{2}
+ \begin{center}
+ \underline{Tree for \texttt{a}}
+
+\parseshrub{a}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {s sep=2cm,
+ font=\ttfamily,
+ draw,
+ rounded corners = 1pt,
+ fill=gray!20}
+ @\shrubresult
+\end{forest}
+
+ \underline{Tree for \texttt{a:autosimplify()}}
+
+\parseshrub{a:autosimplify()}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {s sep=2cm,
+ font=\ttfamily}
+ @\shrubresult
+\end{forest}
+\end{center}
+\end{multicols}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{sqrt()} shortcuts \texttt{SqrtExpression()}:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = sqrt(1/9)
+ b = sqrt(27/16,3)
+ c = a+b
+\end{CAS}
+\[ \print{c} = \print*{c} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = sqrt(1/9)
+ b = sqrt(27/16,3)
+ c = a+b
+\end{CAS}
+\[ \print{c} = \print*{c} \]
+\end{codebox}
+
+\newcoderef{function TrigExpression:new(name,expression)}{return TrigExpression}{name string|SymbolExpression, expression Expression}
+\addcontentsline{toc}{subsubsection}{\ttfamily TrigExpression}
+
+Creates a new trig expression with the given \texttt{name} and \texttt{expression}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+vars('x')
+f = TrigExpression('sin',x)
+tex.print('\\[',f:tolatex(),'\\]')
+\end{minted}
+\tcblower
+\directlua{
+ vars('x')
+ f = TrigExpression('sin',x)
+ tex.print('\\[',f:tolatex(),'\\]')
+}
+\end{codebox}
+
+\subsubsection*{Fields}
+
+\begin{multicols}{2}
+
+\texttt{TrigExpression}s have many fields:
+\begin{itemize}
+ \item \mintinline{lua}{TrigExpression.name} stores the string \texttt{name}, i.e. the first argument of \mintinline{lua}{TrigExpression()};
+ \item \mintinline{lua}{TrigExpression.expression} stores the \texttt{Expression} \texttt{expression}, i.e. the second argument of \mintinline{lua}{TrigExpression()};
+ \item and all fields inherited from \texttt{FunctionExpression} (e.g. \mintinline{lua}{TrigExpression.derivatives} which defaults to \mintinline{lua}{Integer.zero()}).
+\end{itemize}
+
+\columnbreak
+
+\begin{center}
+\parseshrub{f}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily,
+ draw,
+ rounded corners = 1pt,
+ fill = gray!20,
+ l sep = 2cm}
+ @\shrubresult
+\end{forest}
+\end{center}
+\end{multicols}
+
+\subsubsection*{Parsing}
+
+The usual trigonometric functions have the anticipated shortcut names. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = arctan(x^2)
+\end{CAS}
+\[ \print{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = arctan(x^2)
+\end{CAS}
+\[ \print{f} \]
+\end{codebox}
+
+\newcoderef{function RootExpression:new(expression)}{return RootExpression}{expression Expression}
+\addcontentsline{toc}{subsubsection}{\ttfamily RootExpression}
+
+Creates a new \texttt{RootExpression} with the given \texttt{expression}. The method \mintinline{lua}{RootExpression:autosimplify()} attempts to return a list of zeros of \texttt{expression}. If no such set can be found, then
+
+\mintinline{lua}{RootExpression(expression:autosimplify())}
+
+is returned instead. At the moment, \texttt{expression} must be a univariate polynomial of degree $0,1,2$ or $3$ in order for the \texttt{autosimplify()} method to return anything interesting. Of course, \texttt{luacas} can find roots of higher degree polynomials, but this involves more machinery/methods within the \texttt{PolynomialRing} class.
+
+\subsubsection*{Fields}
+\texttt{RootExpression}s have only one field: \texttt{.expression}. For example:
+\begin{multicols}{2}
+ \begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({3,2,1})
+ r = RootExpression(f)
+\end{CAS}
+\[ \print{r} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({3,2,1})
+ r = RootExpression(f)
+\end{CAS}
+\[ \print{r} \]
+\end{codebox}
+
+\begin{center}
+\parseshrub{r}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily,
+ draw,
+ rounded corners=1pt,
+ fill=gray!20,
+ l sep = 2cm}
+ @\shrubresult
+\end{forest}
+\end{center}
+\end{multicols}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{roots()} essentially shortcuts \texttt{RootExpression()}, but when \texttt{expression} is of the \texttt{PolynomialRing}-type, then \texttt{PolynomialRing:roots()} is returned.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ r = roots(f)
+\end{CAS}
+\[ \print{r[1]} \qquad \print{r[2]} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ r = roots(f)
+\end{CAS}
+\[ \print{r[1]} \qquad \print{r[2]} \]
+\end{codebox}
+
+
+\newcoderef{function Equation:new(lhs, rhs)}{return Equation}{lhs Expression, rhs Expression}
+\addcontentsline{toc}{subsubsection}{\ttfamily Equation}
+
+Creates a new \texttt{Equation} expression with the given \texttt{lhs} (left hand side) and \texttt{rhs} (right hand side). If both sides of the equation are constants, or structurally identical, \texttt{autosimplify()} will return a boolean:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','y')
+ f = Equation(sin(x-y),sin(x-y))
+ g = f:autosimplify()
+\end{CAS}
+\[ \print{f} \to \print{g} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x','y')
+ f = Equation(sin(x-y),sin(x-y))
+ g = f:autosimplify()
+\end{CAS}
+\[ \print{f} \to true \]
+\end{codebox}
+
+\subsubsection*{Fields}
+
+\begin{multicols}{2}
+\texttt{Equation}s have two fields: \texttt{lhs} and \texttt{rhs}; which store the expressions on the left and right sides of the equation.
+
+\begin{center}
+ \parseshrub{f}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {font = \ttfamily,
+ draw,
+ rounded corners=1pt,
+ fill = gray!20,
+ s sep = 1.5cm}
+ @\shrubresult
+ \end{forest}
+\end{center}
+\end{multicols}
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_methods/ref_algebra_methods.tex b/macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_methods/ref_algebra_methods.tex
new file mode 100644
index 0000000000..b576cdc1a3
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref_algebra/ref_algebra_methods/ref_algebra_methods.tex
@@ -0,0 +1,1305 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+\usepackage{fontawesome5}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\DeclareTotalTCBox{\lilcoderef}{O{} m m}{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ enhanced,
+ nobeforeafter,
+ tcbox raise base,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=1mm,
+ right=1mm,
+ top=1mm,
+ bottom=1mm,
+ oversize,
+ #1
+}{\mintinline{lua}{#2} \mintinline{lua}{#3}}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+\usepackage{marginnote}
+
+\begin{document}
+\setdescription{style=multiline,
+ topsep=10pt,
+ leftmargin=6.5cm,
+ }
+
+\subsection{Algebra Methods}
+
+Many classes in the algebra package inherit from the {\ttfamily Ring} interface. The {\ttfamily Ring} interface requires the following arithmetic operations, which have corresponding abstract metamethods listed below. Of course, these abstract methods get passed to the appropriate concrete methods in the concrete classes that inherit from {\ttfamily Ring}.
+
+For {\ttfamily Ring} objects {\ttfamily a} and {\ttfamily b}:
+
+\SetLabelAlign{parright}{\parbox[t]{\labelwidth}{\raggedleft#1}}
+\begin{description}%[labelwidth = 6.5cm,align=parright]
+ \item[\lilcoderef{function a:add(b)}{return a + b}] Adds two ring elements.
+ \item[\lilcoderef{function a:sub(b)}{return a - b}] Subtracts one ring element from another. Subtraction has a default implementation in {\ttfamily Ring.lua} as adding the additive inverse, but this can be overwritten if a faster performance method is available.
+ \item[\lilcoderef{function a:neg()}{return -a}] Returns the additive inverse of a ring element.
+ \item[\lilcoderef{function a:mul(b)}{return a * b}] Multiplies two ring elements.
+ \item[\lilcoderef{function a:pow(n)}{return a ^ n}] Raises one ring element to the power of an integer. Exponentiation has a default implementation as repeated multiplication, but this can (and probably should) be overwritten for faster performance.
+ \item[\lilcoderef{function a:eq(b)}{return a == b}] Tests if two ring elements are the same.
+ \item[\lilcoderef{function a:lt(b)}{return a < b}] Tests if one ring element is less than another under some total order. If the ring does not have a natural total order, this method does not need to be implemented.
+ \item[\lilcoderef{function a:le(b)}{return a <= b}] Tests if one ring element is less than or equal to another under some total order. If the ring does not have a natural total order, this method does not need to be implemented.
+ \item[\lilcoderef{function a:zero()}{return Ring}] Returns the additive identity of the ring to which \texttt{a} belongs.
+ \item[\lilcoderef{function a:one()}{return Ring}] Returns the multiplicative identity of the ring to which \texttt{a} belongs.
+\end{description}
+
+\reversemarginpar
+Arithmetic\marginnote{\color{rose}\large\faHandPointRight} of {\ttfamily Ring} elements will (generally) not form a {\ttfamily BinaryOperation}. Instead, the appropriate \mintinline{lua}{__RingOperation} is called which then passes the arithmetic to a specific ring, if possible. For example:
+\begin{codebox}
+ \begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({2,1})
+ g = Poly({2,5})
+ h = f+g
+\end{CAS}
+\[ (\print{f}) + (\print{g}) = \print{h} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({2,1})
+ g = Poly({2,5})
+ h = f+g
+\end{CAS}
+\[ (\print{f}) + (\print{g}) = \print{h} \]
+\end{codebox}
+So why have the {\ttfamily Ring} class to begin with? Many of the rings in the algebra package are subsets of one another. For instance, integers are subsets of rationals, which are subsets of polynomial rings over the rationals, etc. To smoothly convert objects from one ring to another, it's good to have a class like {\ttfamily Ring} to handle all the ``traffic.''
+
+For example, the {\ttfamily RingIdentifier} object acts as a pseudo-class that stores information about the exact ring of an object, including the symbol the ring has if it's a polynomial ring. To perform operations on two elements of different rings, the CAS does the following:
+
+To get the generic {\ttfamily RingIdentifier} from a class, it uses the static method:
+\coderef{function Ring.makering()}{return RingIdentifier}
+
+To get the {\ttfamily RingIdentifier} from a specific instance (element) of a ring, it uses the method:
+
+\coderef{function Ring:getring()}{return RingIdentifier}
+
+So, for example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+a = Integer(2)/Integer(3)
+ring = a:getring()
+if ring == Integer.makering() then
+ tex.print('same rings')
+else
+ tex.print('different rings')
+end
+\end{minted}
+\tcblower
+\luaexec{
+a = Integer(2)/Integer(3)
+ring = a:getring()
+if ring == Integer.makering() then
+ tex.print('same rings')
+else
+ tex.print('different rings')
+end
+}
+\end{codebox}
+
+From there, the CAS computes the smallest {\ttfamily RingIdentifier} that contains the two {\ttfamily RingIdentifier}s as subsets using the static method:
+
+\newcoderef{function Ring.resultantring(ring1,ring2)}{return RingIdentifier}{ring1 RingIdentifier, ring2 RingIdentifier}
+
+So, for example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+a = Poly({Integer(2),Integer(1)})
+b = Integer(3)
+ring1 = a:getring()
+ring2 = b:getring()
+ring = Ring.resultantring(ring1,ring2)
+if ring == a:getring() then
+ tex.print('polynomial ring')
+end
+\end{minted}
+\tcblower
+\luaexec{
+ a = Poly({Integer(2),Integer(1)})
+ b = Integer(3)
+ ring1 = a:getring()
+ ring2 = b:getring()
+ ring = Ring.resultantring(ring1,ring2)
+ if ring == a:getring() then
+ tex.print('polynomial ring')
+ end
+}
+\end{codebox}
+
+Finally, the CAS converts both objects into the resultant {\ttfamily RingIdentifier}, if possible, using the method:
+
+\coderef{function Ring:inring(ring)}{return Ring}
+
+So, for example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+b = b:inring(ring)
+if b:type() == PolynomialRing then
+ tex.print('b is a polynomial now')
+end
+\end{minted}
+\tcblower
+\luaexec{
+ b = b:inring(ring)
+ if b:type() == PolynomialRing then
+ tex.print('b is a polynomial now')
+ end
+}
+\end{codebox}
+
+Finally, the CAS is able to perform the operation with the correct \mintinline{lua}{__RingOperation}. This all happens within the hierarchy of \texttt{Ring} classes automatically:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = Poly({1/2,3,1})
+ b = 1/2
+ c = a+b
+\end{CAS}
+\[ \print{a} + \print{b} = \print{c} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = Poly({1/2,3,1})
+ b = 2/3
+ c = a+b
+\end{CAS}
+\[ \print{a} + \print{b} = \print{c} \]
+\end{codebox}
+
+
+To add another class that implements {\ttfamily Ring} and has proper conversion abilities, the {\ttfamily resultantring} method needs to be updated to include all possible resultant rings constructed from the new ring and existing rings. The other three methods need to be implemented as well.
+
+\hrulefill
+
+We now discuss the more arithmetic methods included in the algebra package beginning with the \texttt{PolynomialRing} class.
+
+\coderef{function PolynomialRing:decompose()}{return table<number, PolynomialRing}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:decompose}
+
+Returns a list of polynomials that form a complete decomposition of the given polynomial. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({5,-4,5,-2,1})
+ d = f:decompose()
+\end{CAS}
+\[ \left\{ \lprint{d} \right\} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({5,-4,5,-2,1})
+ d = f:decompose()
+\end{CAS}
+\[ \left\{ \lprint{d} \right\} \]
+\end{codebox}
+In particular, the code:
+\begin{minted}{lua}
+g = d[2]:evaluateat(d[1])
+tex.print('\\[', g:tolatex(), '\\]')
+\end{minted}
+recovers $f$:
+\luaexec{
+ g = d[2]:evaluateat(d[1])
+ tex.print('\\[', g:tolatex(), '\\]')
+}
+
+
+
+\coderef{function PolynomialRing:derivative()}{return PolynomialRing}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:derivative}
+
+Returns the formal derivative of the given polynomial. For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({1,1,1/2,1/6})
+ g = f:derivative()
+\end{CAS}
+\[ \print{f} \xrightarrow{d/dx}
+ \print{g} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({1,1,1/2,1/6})
+ g = f:derivative()
+\end{CAS}
+\[ \print{f} \xrightarrow{d/dx}
+ \print{g} \]
+\end{codebox}
+
+\coderef{function PolynomialRing:divisors()}{return table<number, PolynomialRing>}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:divisors}
+
+Returns a list of all monic divisors of positive degree of the polynomial, assuming the polynomial ring is a Euclidean Domain. For example:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = topoly(x^4 - 2*x^3 - x + 2)
+ d = f:divisors()
+\end{CAS}
+\[ \left\{ \lprint{d} \right\} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = topoly(x^4 - 2*x^3 - x + 2)
+ d = f:divisors()
+\end{CAS}
+\[ \left\{ \lprint{d} \right\} \]
+\end{codebox}
+
+\newcoderef{function PolynomialRing:divremainder(poly1)}{return poly2,poly3}{poly1 PolynomialRing,..., poly3 PolynomialRing}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:divremainder}
+
+Uses synthetic division to return the quotient (\texttt{poly2}) and remainder (\texttt{poly3}) of \texttt{self/poly1}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({2,2,1})
+ g = Poly({1,1})
+ q,r = f:divremainder(g)
+\end{CAS}
+\[ \print{f} = (\print{g})(\print{q})
+ + \print{r} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({2,2,1})
+ g = Poly({1,1})
+ q,r = f:divremainder(g)
+\end{CAS}
+\[ \print{f} = (\print{g})(\print{q})
+ + \print{r} \]
+\end{codebox}
+
+\newcoderef{function PolynomialRing.extendedgcd(poly1,poly2)}{return poly3, poly4, poly5}{poly1 PolynomialRing, poly2 PolynomialRing, ..., poly5 PolynomialRing}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:extendedgcd}
+
+Given two \texttt{PolynomialRing} elements \texttt{poly1,poly2} returns:
+\begin{itemize}
+ \item \texttt{poly3}: the gcd of \texttt{poly1,poly2};
+ \item \texttt{poly4,poly5}: the coefficients from Bezout's lemma via the extended gcd.
+\end{itemize}
+For example:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = topoly((x-1)*(x-2)*(x-3))
+ g = topoly((x-1)*(x+2)*(x+3))
+ h,a,b = PolynomialRing.extendedgcd(f,g)
+\end{CAS}
+\[ \print{f*a+g*b} = (\print{f})\left( \print{a} \right) +
+ (\print{g})\left(\print{b} \right)\]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = topoly((x-1)*(x-2)*(x-3))
+ g = topoly((x-1)*(x+2)*(x+3))
+ h,a,b = PolynomialRing.extendedgcd(f,g)
+\end{CAS}
+\[ \print{f*a+g*b} = (\print{f})\left( \print{a} \right) +
+ (\print{g})\left(\print{b} \right)\]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \texttt{gcdext()} is a shortcut to \texttt{Polynomial.extendedgcd()}:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = topoly((x+2)*(x-3))
+ g = topoly((x+4)*(x-3))
+ h,a,b = gcdext(f,g)
+\end{CAS}
+\[ \print{h} = (\print{f}) \left( \print{a} \right) +
+ (\print{g})\left( \print{b} \right). \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = topoly((x+2)*(x-3))
+ g = topoly((x+4)*(x-3))
+ h,a,b = gcdext(f,g)
+\end{CAS}
+\[ \print{h} = (\print{f}) \left( \print{a} \right) + (\print{g})\left( \print{b} \right). \]
+\end{codebox}
+
+\coderef{function PolynomialRing:evaluateat(Expression)}{return Expression}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:evaluateat}
+
+Uses Horner's rule to evaluate a polynomial at \texttt{Expression}. Typically, the input \texttt{Expression} is an \texttt{Integer} or \texttt{Rational}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({2,2,1})
+ p = f:evaluateat(1/2)
+\end{CAS}
+\[ \left. \print{f} \right|_{x=1/2}
+ = \print{p} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({2,2,1})
+ p = f:evaluateat(1/2)
+\end{CAS}
+\[ \left. \print{f} \right|_{x=1/2}
+ = \print{p} \]
+\end{codebox}
+
+\coderef{function PolynomialRing:factor()}{return BinaryOperation}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:factor}
+
+Factors the given polynomial into irreducible terms over the polynomial ring to which the coefficients belong. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({8,24,32,24,10,2})
+ a = f:factor()
+\end{CAS}
+\[ \print{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({8,24,32,24,10,2})
+ a = f:factor()
+\end{CAS}
+\[ \print{a} \]
+\end{codebox}
+
+On the other hand:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({Mod(1,5),Mod(0,5),Mod(1,5)})
+ a = f:factor()
+\end{CAS}
+\[ \print{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({Mod(1,5),Mod(0,5),Mod(1,5)})
+ a = f:factor()
+\end{CAS}
+\[ \print{a} \]
+\end{codebox}
+
+The syntax \mintinline{lua}{f = Poly({Mod(1,5),Mod(0,5),Mod(1,5)})} is awkward. Alternatively, one can use the following instead:
+\begin{codebox}
+ \begin{minted}{latex}
+\begin{CAS}
+ f = Mod(Poly({1,0,1}),5)
+ a = f:factor()
+\end{CAS}
+\[ \print{f} = \print{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Mod(Poly({1,0,1}),5)
+ a = f:factor()
+\end{CAS}
+\[ \print{f} = \print{a} \]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{factor()} shortcuts \mintinline{lua}{PolynomialRing:factor()}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({8,24,32,24,10,2})
+ a = factor(f)
+\end{CAS}
+\[ \print{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({8,24,32,24,10,2})
+ a = factor(f)
+\end{CAS}
+\[ \print{a} \]
+\end{codebox}
+
+\newcoderef{function PolynomialRing:freeof(symbol)}{return bool}{symbol SymbolExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:freeof}
+
+Checks the value of the field \mintinline{lua}{PolynomialRing.symbol} against \texttt{symbol}; returns \mintinline{lua}{true} if these symbols are not equal, and returns \mintinline{lua}{false} otherwise.
+
+Recall: the default symbol for \texttt{Poly} is \texttt{'x'}. So, for example:
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = Poly({2,2,1})
+ vars('t')
+ if f:freeof(t) then
+ tex.print('$',f:tolatex(),'$ is free of $',t:tolatex(),'$')
+ else
+ tex.print('$',f:tolatex(),'$ is bound by $',t:tolatex(),'$')
+ end
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = Poly({2,2,1})
+ vars('t')
+ if f:freeof(t) then
+ tex.print('$',f:tolatex(),'$ is free of $',t:tolatex(),'$')
+ else
+ tex.print('$',f:tolatex(),'$ is bound by $',t:tolatex(),'$')
+ end
+\end{CAS}
+\end{codebox}
+
+\newcoderef{function PolynomialRing.gcd(poly1,poly2)}{return poly3}{poly1 PolynomialRing,..., poly3 PolynomialRing}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing.gcd}
+
+Returns the greatest common divisor of two polynomials in a ring (assuming \texttt{poly1,poly2} belong to a Euclidean domain). For example:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = topoly((x^2+1)*(x-1))
+ g = topoly((x^2+1)*(x+2))
+ h = PolynomialRing.gcd(f,g)
+\end{CAS}
+\[ \gcd(\print{f},\print{g}) = \print{h} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = topoly((x^2+1)*(x-1))
+ g = topoly((x^2+1)*(x+2))
+ h = PolynomialRing.gcd(f,g)
+\end{CAS}
+\[ \gcd(\print{f},\print{g}) = \print{h} \]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{gcd()} shortcuts \mintinline{lua}{PolynomialRing.gcd()}. For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = topoly(x^3 - x^2 + x - 1)
+ g = topoly(x^3 + 2*x^2 + x + 2)
+ h = gcd(f,g)
+\end{CAS}
+\[ \gcd(\print{f},\print{g}) = \print{h}.\]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = topoly(x^3 - x^2 + x - 1)
+ g = topoly(x^3 + 2*x^2 + x + 2)
+ h = gcd(f,g)
+\end{CAS}
+\[ \gcd(\print{f},\print{g}) = \print{h}.\]
+\end{codebox}
+
+\coderef{function PolynomialRing:isatomic()}{return false}
+\coderef{function PolynomialRing:isconstant()}{return false}
+
+The inheritances from \texttt{ConstantExpression} are overridden for the \texttt{PolynomialRing} class.
+
+\newcoderef{function PolynomialRing.monicgcdremainders(poly1,poly2)}{return table<number, Ring>}{poly1 PolynomialRing, poly2 PolynomialRing}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing.monicgcdremainders}
+
+Given two polynomials \texttt{poly1} and \texttt{poly2}, returns a list of the remainders generated by the monic Euclidean algorithm.
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = topoly(x^13-1)
+ g = topoly(x^8-1)
+ r = PolynomialRing.monicgcdremainders(f,g)
+\end{CAS}
+\luaexec{
+ for i=1,\#r do
+ tex.print('\\[', r[i]:tolatex(), '\\]')
+ end
+}
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = topoly(x^13-1)
+ g = topoly(x^8-1)
+ r = PolynomialRing.monicgcdremainders(f,g)
+\end{CAS}
+\luaexec{
+ for i=1,\#r do
+ tex.print('\\[', r[i]:tolatex(), '\\]')
+ end
+}
+\end{codebox}
+
+\coderef{function PolynomialRing.mul_rec(poly1,poly2)}{return PolynomialRing}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing.mul{\textunderscore}rec}
+
+Performs Karatsuba multiplication without constructing new polynomials recursively. But grade-school multiplication of polynomials is actually faster here up to a very large polynomial size due to Lua's overhead.
+
+\newcoderef{function PolynomialRing.partialfractions(g,f,ffactors)}{return BinaryOperation}{g PolynomialRing, f PolynomialRing, ffactors BinaryOperation}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing.partialfractions}
+
+Returns the partial fraction decomposition of the rational function \texttt{g/f} given \texttt{PolynomialRing}s \texttt{g, f}, and some (not necessarily irreducible) factorization \texttt{ffactors} of \texttt{f}. If the factorization is omitted, the irreducible factorization is used. The degree of \texttt{g} must be less than the degree of \texttt{f}.
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ g = topoly(4*x^2+2*x+2)
+ f = topoly((x^2+1)^2*(x+1))
+ a = PolynomialRing.partialfractions(g,f)
+\end{CAS}
+\[ \print{g/f} = \print*{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ g = topoly(4*x^2+2*x+2)
+ f = topoly((x^2+1)^2*(x+1))
+ a = PolynomialRing.partialfractions(g,f)
+\end{CAS}
+\[ \print{g/f} = \print*{a} \]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{parfrac()} shortcuts the more long winded \mintinline{lua}{PolynomialRing.partialfractions()}. Additionally, the \texttt{parfrac} function will automatically try to convert the first two arguments to the \texttt{PolynomialRing} type via \mintinline{lua}{topoly()}.
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ g = 4*x^2+2*x+2
+ f = (x^2+1)^2*(x+1)
+ a = parfrac(g,f)
+\end{CAS}
+\[ \print{g/f} = \print*{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ g = 4*x^2+2*x+2
+ f = (x^2+1)^2*(x+1)
+ a = parfrac(g,f)
+\end{CAS}
+\[ \print{g/f} = \print*{a} \]
+\end{codebox}
+
+\newcoderef{function PolynomialRing:rationalroots()}{return remaining, roots}{remaining PolynomialRing, roots table<number,PolynomialRing>}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:rationalroots}
+
+This method finds the factors of \texttt{PolynomialRing} (up to multiplicity) that correspond to rational roots; these factors are stored in a table \texttt{roots} and returned in the second output of the method. Those factors are then divided out of \texttt{Polynomialring}; the \texttt{PolynomialRing} that remains is returned in the first output of the method. For example:
+
+\begin{codebox}
+ \begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{CAS}
+ f = topoly((x-1)^2*(x+1)*(x^2+1))
+ g,r = f:rationalroots()
+\end{CAS}
+The factors of $f$ corresponding to rational roots are:
+\luaexec{
+ for i =1, \#r do
+ tex.print('\\[', r[i]:tolatex(), '\\]')
+ end
+}
+The part of $f$ that remains after dividing out these linear terms is:
+\[ \print{g} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = topoly((x-1)^2*(x+1)*(x^2+1))
+ g,r = f:rationalroots()
+ \end{CAS}
+ The factors of $f$ corresponding to rational roots are:
+ \luaexec{
+ for i =1, \#r do
+ tex.print('\\[', r[i]:tolatex(), '\\]')
+ end
+ }
+ The part of $f$ that remains after dividing out these linear terms is:
+ \[ \print{g} \]
+\end{codebox}
+
+\coderef{function PolynomialRing:roots()}{return table<number, Expression}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:roots}
+
+Returns a list of roots of \texttt{PolynomialRing}, simplified up to cubics. For example:
+
+\begin{codebox}[]
+ \begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{CAS}
+ f = topoly(x^6 + 3*x^5 + 6*x^4 + 7*x^3 + 6*x^2 + 3*x + 2)
+ r = f:roots()
+\end{CAS}
+$ \left\{ \lprint{r} \right\}$
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = topoly(x^6 + 3*x^5 + 6*x^4 + 7*x^3 + 6*x^2 + 3*x + 2)
+ r = f:roots()
+\end{CAS}
+$ \left\{ \lprint{r} \right\}$
+\end{codebox}
+If the decomposition of \texttt{PolynomialRing} (or a factor thereof) is not a chain of cubics or lesser degree polynomials, then \texttt{RootExpression} is returned instead. For example:
+
+\begin{codebox}[]
+ \begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{CAS}
+ f = topoly(x^6 + x^5 - x^4 + 2*x^3 + 4*x^2 - 2)
+ r = f:roots()
+\end{CAS}
+\[ \left\{ \lprint{r} \right\} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = topoly(x^6 + x^5 - x^4 + 2*x^3 + 4*x^2 - 2)
+ r = f:roots()
+\end{CAS}
+\[ \left\{ \lprint{r} \right\} \]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{roots()} shortcuts \mintinline{lua}{PolynomialRing:roots()}. Also, the function \texttt{roots} attempts to cast the argument as a polynomial automatically using \mintinline{lua}{topoly()}. For example:
+
+\begin{codebox}
+ \begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{CAS}
+ f = x^6+x^5-x^4+2*x^3+4*x^2-2
+ r = roots(f)
+\end{CAS}
+$ \left\{ \lprint{r} \right\}$
+\end{minted}
+\tcblower
+ \begin{CAS}
+ f = x^6 + x^5 - x^4 + 2*x^3 + 4*x^2 - 2
+ r = roots(f)
+ \end{CAS}
+ $ \left\{ \lprint{r} \right\}$
+\end{codebox}
+
+\newcoderef{function PolynomialRing.resultant(a,b)}{return Field}{a PolynomialRing, b PolynomialRing}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing.resultant}
+
+Returns the resultant of two polynomials \texttt{a,b} in the same ring, whose coefficients are all part of a field. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = topoly(x^2-2*x+1)
+ g = topoly(x^2+2*x-3)
+ r = PolynomialRing.resultant(f,g)
+\end{CAS}
+\[ \operatorname{res}(f,g) = \print{r} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = topoly(x^2-2*x+1)
+ g = topoly(x^2+2*x-3)
+ r = PolynomialRing.resultant(f,g)
+\end{CAS}
+\[ \operatorname{res}(f,g) = \print{r} \]
+\end{codebox}
+
+\coderef{function PolynomialRing:squarefreefactorization()}{return BinaryOperation}
+\addcontentsline{toc}{subsubsection}{\ttfamily PolynomialRing:squarefreefactorization}
+
+Returns the square-free factorization of a polynomial defined over the rationals.
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = topoly(x^7 - 13*x^6 + 66*x^5 - 158*x^4 + 149*x^3 + 63*x^2 - 216*x + 108)
+ s = f:squarefreefactorization()
+\end{CAS}
+\[ \print{s} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = topoly(x^7 - 13*x^6 + 66*x^5 - 158*x^4 + 149*x^3 + 63*x^2 - 216*x + 108)
+ s = f:squarefreefactorization()
+\end{CAS}
+\[ \print{s} \]
+\end{codebox}
+
+If the polynomial is defined over $\mathbf{Z}/p\mathbf{Z}$ (where $p$ is prime), then the method \texttt{modularsquarefreefactorization()} should be used.
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{factor()} has an optional boolean argument that if set to \mintinline{lua}{true} returns \texttt{squarefreefactorization()} or \texttt{modularsquarefreefactorization()} (as appropriate). For example:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = topoly(x^6 + 2*x^5 + 4*x^4 + 4*x^3 + 5*x^2 + 2*x + 2)
+ s = factor(f,true)
+\end{CAS}
+\[ \print{s} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = topoly(x^6 + 2*x^5 + 4*x^4 + 4*x^3 + 5*x^2 + 2*x + 2)
+ s = factor(f,true)
+\end{CAS}
+\[ \print{s} \]
+\end{codebox}
+And also:
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = topoly(x^6 + 2*x^5 + 4*x^4 + 4*x^3 + 5*x^2 + 2*x + 2)
+ f = Mod(f,5)
+ s = factor(f,true)
+\end{CAS}
+\[ \print{s} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = topoly(x^6 + 2*x^5 + 4*x^4 + 4*x^3 + 5*x^2 + 2*x + 2)
+ f = Mod(f,5)
+ s = factor(f,true)
+\end{CAS}
+\[ \print{s} \]
+\end{codebox}
+
+\newcoderef{function Integer.gcd(a,b)}{return Integer}{a Integer, b Integer}
+\addcontentsline{toc}{subsubsection}{\ttfamily Ingeger.gcd}
+
+Returns the greatest common divisor of \texttt{a,b}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 408
+ b = 252
+ c = Integer.gcd(a,b)
+\end{CAS}
+\[ \gcd(a,b) = \print{c} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 408
+ b = 252
+ c = Integer.gcd(a,b)
+\end{CAS}
+\[ \gcd(a,b) = \print{c} \]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{gcd()} shortcuts \texttt{Integer.gcd()}. For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 408
+ b = 252
+ c = gcd(a,b)
+\[ \gcd(a,b) = \print{c} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 408
+ b = 252
+ c = gcd(a,b)
+\end{CAS}
+\[ \gcd(a,b) = \print{c} \]
+\end{codebox}
+
+
+\newcoderef{function Integer.extendedgcd(a,b)}{return Integer, Integer, Integer}{a Integer, b Integer}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer.extendedgcd}
+
+Returns the greatest common divisor of \texttt{a,b} as well as Bezout's coefficients via extended gcd. For example:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 408
+ b = 252
+ c,x,y = Integer.extendedgcd(a,b)
+\end{CAS}
+\[ \gcd(a,b) = \print{c} = \print{a}(\print{x}) + \print{b}(\print{y}) \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 408
+ b = 252
+ c,x,y = Integer.extendedgcd(a,b)
+\end{CAS}
+\[ \gcd(a,b) = \print{c} = \print{a}(\print{x}) + \print{b}(\print{y}) \]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{gcdext()} shortcuts \mintinline{lua}{Integer.extendedgcd()}. For example:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 408
+ b = 252
+ c,x,y = gcdext(a,b)
+\end{CAS}
+\[ \gcd(a,b) = \print{c} = \print{a}(\print{x}) + \print{b}(\print{y}) \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 408
+ b = 252
+ c,x,y = gcdext(a,b)
+\end{CAS}
+\[ \gcd(a,b) = \print{c} = \print{a}(\print{x}) + \print{b}(\print{y}) \]
+\end{codebox}
+
+\newcoderef{function Integer.max(a,b)}{return Integer, Integer}{a Integer, b Integer}
+\newcoderef{function Integer.min(a,b)}{return Integer, Integer}{a Integer, b Integer}
+\addcontentsline{toc}{subsubsection}{{\ttfamily Integer.max} and {\ttfamily Integer.min}}
+
+Returns the max/min of \texttt{a,b}; the second output is the min/max (respectively).
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 8
+ b = 7
+ c = Integer.max(a,b)
+\end{CAS}
+\[ \max(\print{a},\print{b}) = \print{c} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 8
+ b = 7
+ c = Integer.max(a,b)
+\end{CAS}
+\[ \max(\print{a},\print{b}) = \print{c} \]
+\end{codebox}
+
+
+\newcoderef{function Integer.absmax(a,b)}{return Integer, Integer, number}{a Integer, b Integer}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer.absmax}
+
+Methods for computing the larger magnitude of two integers. Also returns the other integer for sorting purposes, and the number -1 if the two values were swapped, 1 if not.
+
+
+\newcoderef{function Integer.ceillog(a,base)}{return Integer}{a Integer, base Integer}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer.ceillog}
+
+Returns the ceiling of the log base (defaults to 10) of a. In other words, returns the least n such that $\mathtt{(base)^n > a}$.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 101
+ b = 10
+ c = Integer.ceillog(a,b)
+\end{CAS}
+\[ \print{c} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 101
+ b = 10
+ c = Integer.ceillog(a,b)
+\end{CAS}
+\[ \print{c} \]
+\end{codebox}
+
+\newcoderef{function Integer.powmod(a,b,n)}{return Integer}{a Integer,..., n Integer}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer.powmod}
+
+Returns the \texttt{Integer} $c$ such that $c \equiv a^b \bmod{n}$. This should be used when $a^b$ is potentially large.
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 12341
+ b = 2^16+1
+ p = 62501
+ c = Integer.powmod(a,b,p)
+\end{CAS}
+\[ \print{c} \equiv \print{a}^{\print{b}} \bmod{\print{p}} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 12341
+ b = 2^16+1
+ p = 62501
+ c = Integer.powmod(a,b,p)
+\end{CAS}
+\[ \print{c} \equiv \print{a}^{\print{b}} \bmod{\print{p}} \]
+\end{codebox}
+
+\newcoderef{function Integer:divremainder(b)}{return Integer, Integer}{b Integer}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer:divremainder}
+
+Returns the quotient and remainder over the integers. Uses the standard base 10 long division algorithm.
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 408
+ b = 252
+ q,r = Integer.divremainder(a,b)
+\end{CAS}
+\[ \print{a} = \print{b} \cdot \print{q} + \print{r} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 408
+ b = 252
+ q,r = Integer.divremainder(a,b)
+\end{CAS}
+\[ \print{a} = \print{b} \cdot \print{q} + \print{r} \]
+\end{codebox}
+
+\coderef{function Integer:asnumber()}{return number}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer:asnumber}
+
+Returns the integer as a floating point number. Can only approximate the value of large integers.
+
+\coderef{function Integer:divisors()}{return table<number, Integer>}
+
+Returns all positive divisors of the integer. Not guaranteed to be in any order.
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 408
+ d = a:divisors()
+\end{CAS}
+\[ \left\{ \lprint{d} \right\} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 408
+ d = a:divisors()
+\end{CAS}
+\[ \left\{ \lprint{d} \right\} \]
+\end{codebox}
+
+\coderef{function Integer:primefactorization()}{return BinaryOperation}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer:primefactorization}
+
+Returns the prime factorization of the integer as a \texttt{BinaryOperation}.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 408
+ pf = a:primefactorization()
+\end{CAS}
+\[ \print{pf} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 408
+ pf = a:primefactorization()
+\end{CAS}
+\[ \print{pf} \]
+\end{codebox}
+
+\coderef{function Integer:findafactor()}{return Integer}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer:findafactor}
+
+Return a non-trivial factor of {\ttfamily Integer} via Pollard Rho, or returns {\ttfamily Integer} if {\ttfamily Integer} is prime.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 4199
+ f = a:findafactor()
+\end{CAS}
+\[ \print{f} \mid \print{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 4199
+ f = a:findafactor()
+\end{CAS}
+\[ \print{f} \mid \print{a} \]
+\end{codebox}
+
+
+\coderef{function Integer:isprime()}{return bool}
+\addcontentsline{toc}{subsubsection}{\ttfamily Integer:isprime}
+
+Uses Miller-Rabin to determine whether {\ttfamily Integer} is prime up to a very large number.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ p = 7038304939
+ if p:isprime() then
+ tex.print(p:tolatex(), "is prime!")
+ end
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ p = 7038304939
+ if p:isprime() then
+ tex.print(p:tolatex(), "is prime!")
+ end
+\end{CAS}
+\end{codebox}
+
+\coderef{function Rational:reduce()}{return Rational}
+\addcontentsline{toc}{subsubsection}{\ttfamily Rational:reduce}
+
+Reduces a rational expression of integers to standard form. This method is called automatically when a new \texttt{Rational} expression is constructed:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = Rational(8,6)
+\end{CAS}
+\[ \print{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = Rational(8,6)
+\end{CAS}
+\[ \print{a} \]
+\end{codebox}
+
+
+\coderef{function Rational:tocompoundexpression()}{return BinaryOperation}
+\addcontentsline{toc}{subsubsection}{\ttfamily Rational:tocompoundexpression}
+
+Converts a \texttt{Rational} expression into the corresponding \texttt{BinaryOperation} expression.
+
+\coderef{function Rational:asnumber()}{return number}
+\addcontentsline{toc}{subsubsection}{\ttfamily Rational:asnumber}
+
+Returns the given rational as an approximate floating point number. Going the other way, the parser in \mintinline{latex}{\begin{CAS}..\end{CAS}} will convert decimals (as written) to fractions. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ a = 0.375
+\end{CAS}
+\[ \print{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 0.375
+\end{CAS}
+\[ \print{a} \]
+\end{codebox}
+
+\coderef{function SqrtExpression:topower()}{return BinaryOperation}
+\addcontentsline{toc}{subsubsection}{\ttfamily SqrtExpression:topower}
+
+Converts a \texttt{SqrtExpression} to the appropriate \texttt{BinaryOperation}. For example, consider:
+\begin{minted}{latex}
+\begin{CAS}
+ a = sqrt(3)
+ b = a:topower()
+\end{CAS}
+\end{minted}
+\begin{CAS}
+ a = sqrt(3)
+ b = a:topower()
+\end{CAS}
+Then:
+\begin{multicols}{2}
+ \begin{center}
+ \underline{Expression shrub for \texttt{a}:}
+
+ \parseshrub{a}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {font = \ttfamily,
+ draw,
+ rounded corners = 1pt,
+ fill = gray!20,
+ s sep = 1cm}
+ @\shrubresult
+ \end{forest}
+
+ \columnbreak
+
+ \underline{Expression shrub for \texttt{b}:}
+
+ \parseshrub{b}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {font = \ttfamily,
+ draw,
+ rounded corners = 1pt,
+ fill = gray!20,
+ s sep = 1.75cm}
+ @\shrubresult
+ \end{forest}
+ \end{center}
+\end{multicols}
+
+\newcoderef{function Equation:solvefor(var)}{return Equation}{var SymbolExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily Equation:solvefor}
+
+Attempts to solve the equation for a particular variable.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars("x", "y", "z")
+ lhs = e ^ (x^2 * y)
+ rhs = z + 1
+ eq = Equation(lhs, rhs):autosimplify()
+ eqx = eq:solvefor(x)
+\end{CAS}
+\[ \print{eq} \to \print{eqx} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars("x", "y", "z")
+ lhs = e ^ (x^2 * y)
+ rhs = z + 1
+ eq = Equation(lhs, rhs):autosimplify()
+ eqx = eq:solvefor(x)
+\end{CAS}
+\[ \print{eq} \to \print{eqx} \]
+\end{codebox}
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus.tex b/macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus.tex
new file mode 100644
index 0000000000..31d52ab868
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus.tex
@@ -0,0 +1,225 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,
+ size=small,
+ bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}
+ },
+ title={#2}
+}
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+
+\usetikzlibrary{shapes.multipart}
+\useforestlibrary{edges}
+
+\def\error{\color{red}}
+\def\self{\color{gray}}
+\def\call{$\star$ }
+
+\begin{document}
+\thispagestyle{empty}
+
+\section{Calculus}
+ This section contains reference materials for the calculus functionality of \texttt{luacas}. The classes in this module are diagramed below according to inheritance along with the methods/functions one can call upon them.
+ \begin{itemize}
+ \item {\error\ttfamily\itshape method}: an abstract method;
+ \item {\self\ttfamily\itshape method}: a method that returns the expression unchanged;
+ \item {\ttfamily\itshape method}: method that is either unique, implements an abstract method, or overrides an abstract
+method;
+ \item {\tikz[baseline=-0.5ex]\node[fill=roseorange!30] {\ttfamily\bfseries Class};}: a concrete class.
+ \end{itemize}
+Here is an inheritance diagram of the classes in the calculus module; all these classes inherit from the \texttt{CompoundExpression} branch of the inheritance tree. Most methods are stated, but some were omitted (because they inherit in the obvious way, they are auxiliary and not likely to be interesting to the end-user, etc).
+ \vfill
+\forestset{
+rectcore/.style = {rectangle split,
+ rectangle split parts=2,
+ draw = {rosenavy,thick},
+ rounded corners = 1pt,
+ font = \ttfamily\bfseries,
+ fill = roseblue!#1
+ },
+rectalg/.style = {rectangle split,
+ rectangle split parts=2,
+ draw = {rose,thick},
+ rounded corners = 1pt,
+ font = \ttfamily\bfseries,
+ fill = rose!#1
+ },
+rectcalc/.style = {rectangle split,
+ rectangle split parts=2,
+ draw = {roseorange,thick},
+ rounded corners = 1pt,
+ font = \ttfamily\bfseries,
+ fill = roseorange!#1
+ }
+}
+\forestset{
+ multiple directions/.style={
+ for tree={#1},
+ phantom,
+ for relative level=1{
+ no edge,
+ delay={
+ !c.content/.pgfmath=content("!u")},
+ before computing xy={l=0,s=0}
+ }
+ },
+ multiple directions/.default={},
+ grow subtree/.style={for tree={grow=#1}},
+ grow' subtree/.style={for tree={grow'=#1}}}
+\tikzset{
+ every two node part/.style={font=\ttfamily\itshape\footnotesize}
+}
+\begin{center}
+ \begin{forest}
+ for tree = {node options={align=left},
+ edge = {-stealth}
+ },
+ forked edges
+ [Expression\nodepart{two}$\cdots$,rectcore={0}
+ [CompoundExpressions\nodepart{two}$\cdots$,rectcore={0}
+ [DerivativeExpression\nodepart{two}
+ :new() \\
+ :evaluate()\\
+ :autosimplify()
+ ,rectcalc=30]
+ [DiffExpression\nodepart{two}
+ :new()\\
+ :evaluate()\\
+ :autosimplify()
+ ,rectcalc=30]
+ [IntegralExpression\nodepart{two}
+ :new()\\
+ :isdefinite()\\
+ .table()\\
+ .linearproperties()\\
+ .substitutionmethod()\\
+ .enhancedsubstitutionmethod()\\
+ .rationalfunction()\\
+ .partsmethod()\\
+ .eulersformula()\\
+ .integrate()
+ ,rectcalc=30]
+ ]
+ ]
+ \end{forest}
+\end{center}
+\vfill
+
+\end{document}
+
diff --git a/macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_classes/ref_calculus_classes.tex b/macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_classes/ref_calculus_classes.tex
new file mode 100644
index 0000000000..b08a190625
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_classes/ref_calculus_classes.tex
@@ -0,0 +1,277 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+\usepackage{fontawesome}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+
+\begin{document}
+\setdescription{style=multiline,
+ topsep=10pt,
+ leftmargin=5cm,
+ }
+
+\subsection{Calculus Classes}
+
+There are only a few classes (currently) in the calculus module all of which are concrete:
+\begin{itemize}
+ \item {\ttfamily DerivativeExpression}
+ \item {\ttfamily DiffExpression}
+ \item {\ttfamily IntegralExpression}
+\end{itemize}
+
+\newcoderef{function DerivativeExpression:new(expression, symbol)}{return DerivativeExpression}{expression Expression, symbol SymbolExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily DerivativeExpression}
+
+Creates a new single-variable derivative operation of the given \texttt{expression} with respect to the given \texttt{symbol}. If \texttt{symbol} is omitted, then \texttt{symbol} takes the default value of \mintinline{lua}{SymbolExpression("x")}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+vars('x')
+f = DerivativeExpression(sin(x)/x)
+tex.print('\\[', f:tolatex(), '\\]')
+\end{minted}
+\tcblower
+\luaexec{
+ vars('x')
+ f = DerivativeExpression(sin(x)/x)
+ tex.print('\\[', f:tolatex(), '\\]')
+}
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{DD()} shortcuts \mintinline{lua}{DerivativeExpression()}.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = DD(sin(x)/x)
+\end{CAS}
+\[ \print{f} \]
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = DD(sin(x)/x)
+\end{CAS}
+\[ \print{f} \]
+\end{codebox}
+Alternatively, one could also use \mintinline{lua}{diff()} (see below).
+
+\newcoderef{function DiffExpression:new(expression, symbols)}{return DiffExpression}{expression Expression, symbols table<number, Symbol>}
+\addcontentsline{toc}{subsubsection}{\ttfamily DiffExpression}
+
+Creates a new multi-variable higher-order derivative operation of the given \texttt{expression} with respect to the given \texttt{symbols}. As opposed to \texttt{DerivativeExpression}, the argument \texttt{symbols} cannot be omitted. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+vars('x','y')
+f = DiffExpression(sin(x*y)/y,{x,y})
+tex.print('\\[', f:tolatex(), '\\]')
+\end{minted}
+\tcblower
+\luaexec{
+ vars('x','y')
+ f = DiffExpression(sin(x*y)/y,{x,y})
+ tex.print('\\[', f:tolatex(), '\\]')
+}
+\end{codebox}
+We can also use \texttt{DiffExpression} to create higher-order single variable derivatives:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+vars('x')
+f = DiffExpression(sin(x)/x,{x,x})
+tex.print('\\[', f:tolatex(), '\\]')
+\end{minted}
+\tcblower
+\luaexec{
+ vars('x')
+ f = DiffExpression(sin(x)/x,{x,x})
+ tex.print('\\[', f:tolatex(), '\\]')
+}
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{diff()} shortcuts \mintinline{lua}{DiffExpression()}. The arguments of \mintinline{lua}{diff()} can also be given in a more user-friendly, compact form. For example:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','y')
+ f = diff(sin(x)/x, {x,2})
+ g = diff(sin(x*y)/y,x,{y,2})
+\end{CAS}
+\[ \print{f} = \print*{f} \qquad \print{g} = \print*{g} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x','y')
+ f = diff(sin(x)/x, {x,2})
+ g = diff(sin(x*y)/y,x,{y,2})
+\end{CAS}
+\[ \print{f} = \print*{f} \qquad \print{g} = \print*{g} \]
+\end{codebox}
+
+\newcoderef{function IntegralExpression:new(expression,symbol,lower,upper)}{return IntegralExpression}{expression Expression, symbol SymbolExpression, lower Expression, upper Expression}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression}
+
+Creates a new integral operation of the given \texttt{expression} with respect to the given \texttt{symbol} over the given \texttt{lower} and \texttt{upper} bounds. If \texttt{lower} and \texttt{upper} are omitted, then an \emph{indefinite} \texttt{IntegralExpression} is constructed. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small,breaklines]{lua}
+vars('x')
+f = IntegralExpression(sin(sqrt(x)), x)
+g = IntegralExpression(sin(sqrt(x)), x, Integer.zero(), pi)
+tex.print('\\[', f:tolatex(), '\\]')
+tex.print('\\[', g:tolatex(), '\\]')
+\end{minted}
+\tcblower
+\luaexec{
+ vars('x')
+ f = IntegralExpression(sin(sqrt(x)),x)
+ g = IntegralExpression(sin(sqrt(x)),x,Integer.zero(),pi)
+ tex.print('\\[', f:tolatex(), '\\]')
+ tex.print('\\[', g:tolatex(), '\\]')
+}
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The function \mintinline{lua}{int()} shortcuts \mintinline{lua}{IntegralExpression()}. For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ g = int(sin(sqrt(x)),x,0,pi)
+\end{CAS}
+\[ \print{g} = \print*{g} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ g = int(sin(sqrt(x)),x,0,pi)
+\end{CAS}
+\[ \print{g} = \print*{g} \]
+\end{codebox}
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_methods/ref_calculus_methods.tex b/macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_methods/ref_calculus_methods.tex
new file mode 100644
index 0000000000..c64baa5934
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref_calculus/ref_calculus_methods/ref_calculus_methods.tex
@@ -0,0 +1,473 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+\usepackage{fontawesome5}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\DeclareTotalTCBox{\lilcoderef}{O{} m m}{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ enhanced,
+ nobeforeafter,
+ tcbox raise base,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=1mm,
+ right=1mm,
+ top=1mm,
+ bottom=1mm,
+ oversize,
+ #1
+}{\mintinline{lua}{#2} \mintinline{lua}{#3}}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+\usepackage{marginnote}
+
+\begin{document}
+\setdescription{style=multiline,
+ topsep=10pt,
+ leftmargin=6.5cm,
+ }
+
+\subsection{Calculus Methods}
+
+\newcoderef{function IntegralExpression.table(integral)}{return Expression|nil}{integral IntegralExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.table}
+
+Attempts to integrate \texttt{integral.expression} with respect to \texttt{integral.symbol} by checking a table of basic integrals; returns nil if the integrand isn't in the table. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = int(cos(x),x)
+ f = f:table()
+ g = int(x*cos(x),x)
+ g = g:table()
+\end{CAS}
+\[ f = \print{f} \qquad g = \print{g} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = int(cos(x),x)
+ f = f:table()
+ g = int(x*cos(x),x)
+ g = g:table()
+ \end{CAS}
+\[ f = \print{f} \qquad g = \print{g} \]
+\end{codebox}
+The table of integrals consists of power functions, exponentials, logarithms, trigonometric, and inverse trigonometric functions.
+
+\newcoderef{function IntegralExpression.linearproperties(integral)}{return Expression|nil}{integral IntegralExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.linearproperties}
+
+Attempts to integrate \texttt{integral.expression} with respect to \texttt{integral.symbol} by using linearity properties (e.g. the integral of a sum/difference is the sum/difference of integrals); returns nil if any individual component cannot be integrated using \mintinline{lua}{IntegralExpression:integrate()}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = int(sin(x) + e^x,x)
+ g = f:table()
+ f = f:linearproperties()
+\end{CAS}
+\[ f = \print*{f} \qquad g = \print*{g} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = int(sin(x) + e^x,x)
+ g = f:table()
+ f = f:linearproperties()
+\end{CAS}
+\[ f = \print*{f} \qquad g = \print*{g} \]
+\end{codebox}
+
+\newcoderef{function IntegralExpression.substitutionmethod(integral)}{return Expression|nil}{integral IntegralExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.substitutionmethod}
+
+Attempts to integrate \texttt{integral.expression} with respect to \texttt{integral.symbol} via $u$-substitution; returns nil if no suitable substitution is found to be successful.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = int(x*e^(x^2),x)
+ g = int(x*e^x,x)
+ f = f:substitutionmethod()
+ g = g:substitutionmethod()
+\end{CAS}
+\[ f = \print*{f} \qquad g = \print*{g}.\]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = int(x*e^(x^2),x)
+ g = int(x*e^x,x)
+ f = f:substitutionmethod()
+ g = g:substitutionmethod()
+\end{CAS}
+\[ f = \print*{f} \qquad g = \print*{g}.\]
+\end{codebox}
+
+\newcoderef{function IntegralExpression.enhancedsubstitutionmethod(integral)}{return Expression|nil}{integral IntegralExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.enhancedsubstitutionmethod}
+
+Attempts integrate \texttt{integral.expression} with respect to \texttt{integral.symbol} via $u$-substitutions. This method distinguishes itself from the \mintinline{lua}{.substitutionmethod} by attempted to solve $u= g(x)$ for the original variable and then substituting the result into the expression. This behavior is not included in \mintinline{lua}{.substitutionmethod} due to speed concerns. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = int(x^5*sqrt(x^3+1),x)
+ g = f:substitutionmethod()
+ h = f:enhancedsubstitutionmethod()
+\end{CAS}
+\[ g= \print*{g} \]
+\[ h= \print*{h} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = int(x^5*sqrt(x^3+1),x)
+ g = f:substitutionmethod()
+ h = f:enhancedsubstitutionmethod()
+\end{CAS}
+\[ g= \print*{g} \]
+\[ h= \print*{h} \]
+\end{codebox}
+
+
+\newcoderef{function IntegralExpression.trialsubstitutions(Expression)}{return table<number, Expression}{}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.trialsubstitutions}
+
+Generates a list of possible $u$-substitutions to attempt in \texttt{substitutionmethod()} and \texttt{enhancedsubstitutionmethod()}. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = cos(x)/(1+sin(x))
+ f = f:autosimplify()
+ l = IntegralExpression.trialsubstitutions(f)
+\end{CAS}
+$\left\{ \lprint{l} \right\}$.
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = cos(x)/(1+sin(x))
+ f = f:autosimplify()
+ l = IntegralExpression.trialsubstitutions(f)
+\end{CAS}
+$ \left\{ \lprint{l} \right\}$.
+\end{codebox}
+
+
+\newcoderef{function IntegralExpression.rationalfunction(IntegralExpression)}{return Expression|nil}{}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.rationalfunction}
+
+Integrates \texttt{integrand} with respect to \texttt{symbol} via Lazard, Rioboo, Rothstein, and Trager's method in the case when \texttt{expression} is a rational function in the variable \texttt{symbol}. If \texttt{integrand} is not a rational function, then nil is returned.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = (x^2+2*x+2)/(x^2+3*x+2)
+ f = f:autosimplify()
+ g = int(f,x):rationalfunction()
+\end{CAS}
+\[ \int \print{f}\ dx = \print*{g} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = (x^2+2*x+2)/(x^2+3*x+2)
+ f = f:autosimplify()
+ g = int(f,x):rationalfunction()
+\end{CAS}
+\[ \int \print{f}\ dx = \print*{g} \]
+\end{codebox}
+
+In some cases, the \mintinline{lua}{.rationalfunction} method returns non-standard results. For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ num = x^2
+ den = ((x+1)*(x^2+2*x+2)):expand()
+ f = (num/den):autosimplify()
+ f = int(f,x):rationalfunction()
+\end{CAS}
+\[ \print{simplify(f)} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ num = x^2
+ den = ((x+1)*(x^2+2*x+2)):expand()
+ f = (num/den):autosimplify()
+ f = int(f,x):rationalfunction()
+\end{CAS}
+\[ \print{simplify(f)} \]
+\end{codebox}
+On the other hand:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ pfrac = parfrac(num,den)
+\end{CAS}
+\[ \print*{int(pfrac,x)} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ pfrac = parfrac(num,den)
+ \end{CAS}
+\[ \print*{int(pfrac,x)} \]
+\end{codebox}
+
+\newcoderef{function IntegralExpression.partsmethod(IntegralExpression)}{return Expression|nil}{}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.partsmethod}
+
+Attempts to integrate \texttt{integral.expression} with respect to \texttt{integral.symbol} via \emph{integration by parts}; returns nil if no suitable application of IBP is found. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ a = int(x*e^x,x)
+ b = a:partsmethod()
+ c = int(e^(x^2),x)
+ d = c:partsmethod()
+\end{CAS}
+\[ b=\print*{b} \]
+\[ d=\print*{d} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ a = int(x*e^x,x)
+ b = a:partsmethod()
+ c = int(e^(x^2),x)
+ d = c:partsmethod()
+\end{CAS}
+\[ b= \print*{b} \]
+\[ d= \print*{d} \]
+\end{codebox}
+
+
+\newcoderef{function IntegralExpression.eulersformula(integral)}{return Expression|nil}{integral IntegralExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.eulersformula}
+
+Attempts to integrate \texttt{integral.expression} with respect to \texttt{integral.symbol} by using the Euler formulas:
+\[ \cos x = \frac{e^{ix} + e^{-ix}}{2} \qquad \sin x = \frac{e^{ix} - e^{-ix}}{2i}.\]
+Per usual, this method returns nil if such a method is unsuccessful (or if the integrand is unchanged after applying the above substitutions). This can often be used as an alternative for integration by parts. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ a = int(e^x*sin(x),x)
+ b = int(x^2,x)
+ c = a:eulersformula()
+ d = b:eulersformula()
+\end{CAS}
+\[ c= \print*{c} \]
+\[ d= \print*{d} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ a = int(e^x*sin(x),x)
+ b = int(x^2,x)
+ c = a:eulersformula()
+ d = b:eulersformula()
+\end{CAS}
+\[ c= \print*{c} \]
+\[ d= \print*{d} \]
+\end{codebox}
+
+\newcoderef{function IntegralExpression.integrate(integral)}{return Expression|nil}{integral IntegralExpression}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.integrate}
+
+Recursive part of the indefinite integral operator; returns nil if the expression could not be integrated. The methods above get called (roughly) in the following order:
+\begin{enumerate}[(i)]
+ \item \mintinline{lua}{.table}
+ \item \mintinline{lua}{.linearproperties}
+ \item \mintinline{lua}{.substitutionmethod}
+ \item \mintinline{lua}{.rationalfunction}
+ \item \mintinline{lua}{.partsmethod}
+ \item \mintinline{lua}{.eulersformula}
+ \item \mintinline{lua}{.enhancedsubstitutionmethod}
+\end{enumerate}
+Between (vi) and (vii), the \mintinline{lua}{.integrate} method will attempt to expand the integrand and retry. The method is recursive in the sense that (most) of the methods listed above will call \mintinline{lua}{.integrate} at some point. For example, after a list of trial substitutions is created, the method \mintinline{lua}{.substitutionmethod} will call \mintinline{lua}{.integrate} to determine whether the new integrand can be integrated via the methods in the above list.
+
+\subsubsection*{Parsing}
+
+Recall the function \mintinline{lua}{int()} which acts as a shortcut for \mintinline{lua}{IntegralExpression:new()}. When \mintinline{lua}{:autosimplify()} is called upon an \texttt{IntegralExpression}, then \mintinline{lua}{IntegralExpression.integrate} is applied. If \mintinline{lua}{nil} is returned, then \mintinline{lua}{:autosimplify()} returns \mintinline{lua}{self}; otherwise the result of \mintinline{lua}{.integrate} is returned and evaluated over the bounds, if any are given. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = cos(x)*e^(sin(x))
+ f = int(f,x,0,pi/2)
+\end{CAS}
+\[ \print{f} = \print*{f}\]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = cos(x)*e^(sin(x))
+ f = int(f,x,0,pi/2)
+\end{CAS}
+\[ \print{f} = \print*{f}\]
+\end{codebox}
+
+On the other hand:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = e^(e^x)
+ f = int(f,x,0,1)
+\end{CAS}
+\[ \print{f} = \print*{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = e^(e^x)
+ f = int(f,x,0,1)
+\end{CAS}
+\[ \print{f} = \print*{f} \]
+\end{codebox}
+
+\coderef{function IntegralExpression:isdefinite()}{return bool}
+\addcontentsline{toc}{subsubsection}{\ttfamily IntegralExpression.isdefinite}
+
+Returns \mintinline{lua}{true} of \texttt{IntegralExpression} is definite (i.e. if \texttt{.upper} and \texttt{.lower} are defined fields), otherwise returns \mintinline{lua}{false}.
+
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/reference/ref_core/ref_core.tex b/macros/luatex/latex/luacas/doc/reference/ref_core/ref_core.tex
new file mode 100644
index 0000000000..668eda73a1
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref_core/ref_core.tex
@@ -0,0 +1,249 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\index{\currref!\texttt{#1}}%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\index{\currref!\texttt{#1}}%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+\usetikzlibrary{shapes.multipart}
+\useforestlibrary{edges}
+
+\def\error{\color{red}}
+\def\self{\color{gray}}
+\def\call{}
+
+\begin{document}
+\thispagestyle{empty}
+
+\section{Core}
+ This section contains reference materials for the core functionality of \texttt{luacas}. The classes in this module are diagramed below according to inheritance along with the methods/functions one can call upon them.
+ \begin{itemize}
+ \item {\error\ttfamily\itshape method}: an abstract method (a method that must be implemented by a subclass to be called);
+ \item {\self\ttfamily\itshape method}: a method that returns the expression unchanged;
+ \item {\ttfamily\itshape method}: a method that is either unique, implements an abstract method, or overrides an abstract method;
+ \item {\tikz[baseline=-0.5ex]\node[fill=roseblue!50] {\ttfamily\bfseries Class};}: a concrete class.
+ \end{itemize}
+
+\forestset{
+rect/.style = {rectangle split,
+ rectangle split parts=2,
+ draw = {rosenavy,thick},
+ rounded corners = 1pt,
+ font = \ttfamily\bfseries,
+ }
+}
+\tikzset{
+ every two node part/.style={font=\ttfamily\itshape\footnotesize}
+}
+\begin{center}
+ \begin{forest}
+ for tree = {node options={align=left},
+ rect,
+ grow= south,
+ parent anchor=south,
+ child anchor=north,
+ edge = {-stealth}
+ },
+ forked edges
+ [Expression\nodepart{two}\begin{minipage}{0.5\textwidth}\vskip-0.2cm\begin{multicols}{2}
+ {\error :evaluate()} \\
+ {\error :autosimplify()} \\
+ :simplify() \\
+ :size() \\
+ {\error :subexpressions()} \\
+ {\error :setsubexpressions()} \\
+ {\error :freeof(symbol)} \\
+ {\error :substitute(map)} \\
+ {\self :expand()} \\
+ {\self :factor()} \\
+ {\self :combine()} \\
+ :getsubexpressionsrec() \\
+ {\error :isatomic()} \\
+ {\error :isconstant()} \\
+ :isrealconstant() \\
+ :iscomplexconstant() \\
+ {\error :order(other)} \\
+ {\self :topolynomial()} \\
+ {\error :tolatex()} \\
+ :type()\end{multicols}\end{minipage}
+ [AtomicExpression\nodepart{two}
+ {\self :tocompoundexpression()} \\
+ {\self :evaluate()} \\
+ {\self :autosimplify()} \\
+ :subexpressions() \\
+ {\self :setsubexpressions()} \\
+ :substitute(map) \\
+ :isatomic() \\
+ :tolatex()
+ [SymbolExpression\nodepart{two}
+ {\call :new(symbol)} \\
+ :freeof(symbol) \\
+ :isconstant() \\
+ :order(other) \\
+ :topolynomial()
+ ,fill = roseblue!50]
+ [ConstantExpression\nodepart{two}
+ :freeof(symbol)\\
+ :isconstant() \\
+ :order(other)
+ ]
+ ]
+ [CompoundExpression\nodepart{two}
+ :freeof(symbol) \\
+ :substitute(map) \\
+ :isatomic() \\
+ :isconstant()
+ [BinaryOperation\nodepart{two}
+ {\call :new(operation,expressions)} \\
+ :evaluate() \\
+ :autosimplify() (!)\\
+ :subexpressions() \\
+ :setsubexpressions() \\
+ :expand() \\
+ :factor() \\
+ :combine() \\
+ :order(other) \\
+ {:iscommutatitve()}\\
+ :topolynomial() \\
+ :tolatex()
+ , fill=roseblue!50]
+ [FunctionExpression\nodepart{two}
+ {\call :new(name,expressions)}\\
+ :evaluate()\\
+ :autosimplify()\\
+ :subexpressions()\\
+ :setsubexpressions()\\
+ :order(other)\\
+ :tolatex()
+ , fill=roseblue!50]
+ ]
+ ]
+ \end{forest}
+\end{center}
+The number of core methods should generally be kept small, since every new type of expression must implement all of these methods. The exception to this, of course, is core methods that call other core methods that provide a unified interface to expressions. For instance, {\ttfamily size()} calls {\ttfamily subexpressions()}, so it only needs to be implemented in the expression interface.
+
+All expressions should also implement the {\ttfamily \_\_tostring} and {\ttfamily \_\_eq} metamethods. Metamethods cannot be inherited using Lua, thus every expression object created by a constructor must assign a metatable to that object.
+
+\begin{itemize}
+ \item {\ttfamily \_\_tostring} provides a human-readable version of an expression for printing within Lua and exporting to external programs.
+
+ \item {\ttfamily \_\_eq} determines whether an expression is structurally identical to another expression.
+\end{itemize}
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_classes/ref_core_classes.tex b/macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_classes/ref_core_classes.tex
new file mode 100644
index 0000000000..77e80aa16f
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_classes/ref_core_classes.tex
@@ -0,0 +1,455 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+\usepackage{fontawesome5}
+\usepackage{marginnote}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+
+\begin{document}
+
+\subsection{Core Classes}
+
+There are several classes in the core module; but only some classes are concrete:
+
+\begin{multicols}{2}
+ \begin{center}
+ \underline{Abstract classes:}
+ \begin{itemize}
+ \item \texttt{Expression}
+ \item \texttt{AtomicExpression}
+ \item \texttt{CompoundExpression}
+ \item \texttt{ConstantExpression}
+ \end{itemize}
+
+ \underline{Concrete classes:}
+ \begin{itemize}
+ \item \texttt{SymbolExpression}
+ \item \texttt{BinaryOperation}
+ \item \texttt{FunctionExpression}
+ \end{itemize}
+\end{center}
+\end{multicols}
+
+The abstract classes provide a unified interface for the concrete classes (expressions) using inheritance. \emph{Every} expression in \texttt{luacas} inherits from either {\ttfamily AtomicExpression} or {\ttfamily CompoundExpression} which, in turn, inherit from {\ttfamily Expression}.
+
+\coderef{function SymbolExpression:new(string)}{return SymbolExpression}
+\index{Core!Classes!\texttt{SymbolExpression}}
+\addcontentsline{toc}{subsubsection}{\ttfamily SymbolExpression}
+
+Creates a new \texttt{SymbolExpression}. For example:
+\begin{codebox}[]
+\begin{minted}[breaklines,fontsize=\small]{lua}
+foo = SymbolExpression("bar")
+tex.sprint("The Lua variable ``foo'' is the SymbolExpression: ", foo:tolatex(),".")
+\end{minted}
+\tcblower
+\directlua{
+foo = SymbolExpression("bar")
+tex.sprint("The Lua variable 'foo' is the SymbolExpression: ", foo:tolatex(),".")
+}
+\end{codebox}
+
+\subsubsection*{Fields}
+
+\texttt{SymbolExpression}s have only one field: \texttt{symbol}. In the example above, the string \mintinline{lua}{"bar"} is stored in \mintinline{lua}{foo.symbol}.
+
+\subsubsection*{Parsing}
+
+The command \mintinline{lua}{vars()} in \texttt{test.parser} creates a new \texttt{SymbolExpression} for every string in the argument; each such \texttt{SymbolExpression} is assigned to a variable of the same name. For example:
+
+\begin{minted}{lua}
+vars('x','y')
+\end{minted}
+
+is equivalent to:
+
+\begin{minted}{lua}
+x = SymbolExpression("x")
+y = SymbolExpression("y")
+\end{minted}
+
+\newcoderef{function BinaryOperation:new(operation, expressions)}{return BinaryOperation}{operation function, expressions table<number,Expression>}
+\index{Core!Classes!\texttt{BinaryOperation}}
+\addcontentsline{toc}{subsubsection}{\ttfamily BinaryOperation}
+
+Creates a new \texttt{BinaryOperation} expression. For example:
+
+\begin{codebox}
+\begin{minted}[fontsize=\small]{lua}
+vars('x','y','z')
+w = BinaryOperation(
+ BinaryOperation.ADD,
+ {BinaryOperation(
+ BinaryOperation.MUL,
+ {x,y}
+ ),y,z}
+)
+tex.print("\\[w=",w:tolatex(),"\\]")
+\end{minted}
+\tcblower
+\directlua{
+vars('x','y','z')
+w = BinaryOperation(
+ BinaryOperation.ADD,
+ {BinaryOperation(
+ BinaryOperation.MUL,
+ {x,y}
+ ),y,z}
+)
+tex.print("\\[w=",w:tolatex(),"\\]")
+}
+\end{codebox}
+The variable \texttt{operation} must be a function \mintinline{lua}{function f(a,b)} assigned to one of the following types:
+\bgroup
+\setdescription{style=multiline,
+ topsep=10pt,
+ leftmargin=4.5cm,
+ font=\ttfamily
+ }
+\begin{description}
+ \item[BinaryOperation.ADD:] \mintinline{lua}{return a + b}
+ \item[BinaryOperation.SUB:] \mintinline{lua}{return a - b}
+ \item[BinaryOperation.MUL:] \mintinline{lua}{return a * b}
+ \item[BinaryOperation.DIV:] \mintinline{lua}{return a / b}
+ \item[BinaryOperation.IDIV:] \mintinline{lua}{return a // b}
+ \item[BinaryOperation.MOD:] \mintinline{lua}{return a % b}
+ \item[BinaryOperation.POW:] \mintinline{lua}{return a ^ b}
+\end{description}
+\egroup
+The variable \texttt{expressions} must be a table of \texttt{Expression}s index by Lua numbers.
+
+\subsubsection*{Fields}
+
+\texttt{BinaryOperation}s have the following fields: \texttt{name}, \texttt{operation}, and \texttt{expressions}. In the example above, we have:
+\begin{itemize}
+ \item the variable \texttt{expressions} is stored in \mintinline{lua}{w.expressions};
+ \item \mintinline{lua}{w.name} stores the string \mintinline{lua}{"+"}; and
+ \item \mintinline{lua}{w.operation} stores the function:
+ \begin{minted}{lua}
+BinaryOperation.ADD = function(a, b)
+ return a + b
+end
+ \end{minted}
+\end{itemize}
+
+\begin{multicols}{2}
+The entries of \texttt{w.expressions} can be used/fetched in a reasonable way:
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+$\print{w.expressions[1]} \quad
+ \print{w.expressions[2]} \quad
+ \print{w.expressions[3]}$
+ \end{minted}
+ \tcblower
+$\print{w.expressions[1]} \quad
+ \print{w.expressions[2]} \quad
+ \print{w.expressions[3]}$
+\end{codebox}
+
+\begin{center}
+ \bracketset{action character = @}
+ \parseshrub{w}
+ \begin{forest}
+ for tree = {font = \ttfamily,
+ draw,
+ rounded corners = 1pt,
+ fill = gray!20,
+ l sep = 1.5cm,
+ s sep = 2cm}
+ @\shrubresult
+ \end{forest}
+\end{center}
+\end{multicols}
+
+\subsubsection*{Parsing}
+
+Thank goodness for this. Creating new \texttt{BinaryOperation}s isn't nearly as cumbersome as the above would indicate. Using Lua's powerful metamethods, we can parse expressions easily. For example, the construction of \texttt{w} given above can be done much more naturally using:
+\begin{codebox}
+\begin{minted}[fontsize=\small]{lua}
+vars('x','y','z')
+w = x*y+y+z
+tex.print("\\[w=", w:tolatex(), "\\]")
+\end{minted}
+\tcblower
+\directlua{
+ vars('x','y','z')
+ w = x*y+y+z
+ tex.print("\\[w=", w:tolatex(), "\\]")
+}
+\end{codebox}
+\reversemarginpar
+{\bf Warning:}\marginnote{\color{rose}\faExclamationTriangle} There are escape issues to be aware of with the operator \mintinline{latex}{%}. If you're writing custom \texttt{luacas} functions in a separate \texttt{.lua} file, then there are no issues; use \mintinline{latex}{%} with reckless abandon. But when using the operator \mintinline{latex}{%} within, say \mintinline{latex}{\begin{CAS}..\end{CAS}}, then one should write \mintinline{latex}{\%} in place of \mintinline{latex}{%}:
+
+\begin{codebox}
+\begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{CAS}
+ a = 17
+ b = 5
+ c = a \% b
+\end{CAS}
+\[ \print{c} \equiv \print{a} \bmod{\print{b}} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 17
+ b = 5
+ c = a \% b
+\end{CAS}
+\[ \print{c} \equiv
+ \print{a} \bmod{\print{b}} \]
+\end{codebox}
+The above escape will {\bf not} work with \mintinline{latex}{\directlua}, but it will work for \mintinline{latex}{\luaexec} from the \texttt{luacode} package. Indeed, the \texttt{luacode} package was designed (in part) to make escapes like this more manageable. Here is the equivalent code using \mintinline{latex}{\luaexec}:
+\begin{codebox}[]
+\begin{minted}[fontsize=\small]{lua}
+a = Integer(17)
+b = Integer(5)
+c = a \% b
+tex.print("\\[",c:tolatex(),"\\equiv",a:tolatex(), "\\bmod{",b:tolatex(),"} \\]")
+\end{minted}
+\tcblower
+\luaexec{
+a = Integer(17)
+b = Integer(5)
+c = a \% b
+tex.print("\\[", c:tolatex(), "\\equiv", a:tolatex(), "\\bmod{", b:tolatex(), "} \\]")
+}
+\end{codebox}
+
+\newcoderef{function FunctionExpression:new(name,expressions)}{return FunctionExpression}{name string|SymbolExpression, expressions table<number,Expression>}
+\index{Core!Classes!\texttt{FunctionExpression}}
+\addcontentsline{toc}{subsubsection}{\ttfamily FunctionExpression}
+
+Creates a generic function. For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+vars('x','y')
+f = FunctionExpression('f',{x,y})
+tex.print("\\[",f:tolatex(),"\\]")
+ \end{minted}
+ \tcblower
+ \luaexec{
+ vars('x','y')
+ f = FunctionExpression('f',{x,y})
+ tex.print("\\[",f:tolatex(),"\\]")
+ }
+\end{codebox}
+The variable \texttt{name} can be a string (like above), or another \texttt{SymbolExpression}. But in this case, the variable \texttt{name} just takes the value of the string \mintinline{lua}{SymbolExpression.symbol}. The variable \texttt{expressions} must be a table of \texttt{Expression}s indexed by Lua numbers.
+
+\subsubsection*{Fields}
+\texttt{FunctionExpression}s have the following fields: \texttt{name}, \texttt{expressions}, \texttt{variables}, \texttt{derivatives}. In the example above, we have:
+\begin{itemize}
+ \item the variable \texttt{name}, i.e. the string \mintinline{lua}{'f'}, is stored in \mintinline{lua}{f.name}; and
+ \item the variable \texttt{expressions}, i.e. the table \mintinline{lua}{{x,y}} is stored in \mintinline{lua}{f.expressions}.
+\end{itemize}
+
+Wait a minute, what about \texttt{variables} and \texttt{derivatives}!? The field \texttt{variables} essentially stores a copy of the variable \texttt{expressions} \textit{as long as} the entries in that table are atomic. If they aren't, then \texttt{variables} will default to $x,y,z$, or $x_1,x_2,\ldots$ if the number of variables exceeds $3$. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+vars('s','t')
+f = FunctionExpression('f',{s*s,s+t+t})
+tex.print("The variables of f are:")
+for _,symbol in ipairs(f.variables) do
+ tex.print(symbol:tolatex())
+end
+ \end{minted}
+ \tcblower
+\luaexec{
+ vars('s','t')
+ f = FunctionExpression('f',{s*s,s+t+t})
+ tex.print("The variables of f are:")
+ for _,symbol in ipairs(f.variables) do
+ tex.print(symbol:tolatex())
+ end
+}
+\end{codebox}
+The field \texttt{derivatives} is a table of \texttt{Integer}s indexed by Lua numbers whose length equals \mintinline{lua}{#o.variables}. The default value for this table is a table of (\texttt{Integer}) zeros. So for the example above, we have:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+for _,integer in ipairs(f.derivatives) do
+ if integer == Integer.zero() then
+ tex.print("I'm a zero.\\newline")
+ end
+end
+\end{minted}
+\tcblower
+\luaexec{
+ for _,integer in ipairs(f.derivatives) do
+ if integer == Integer.zero() then
+ tex.print("I'm a zero.\\newline")
+ end
+ end
+}
+\end{codebox}
+We can change the values of \texttt{variables} and \texttt{derivatives} manually (or more naturally by other gizmos found in \texttt{luacas}). For example, keeping the variables from above, we have:
+\begin{multicols}{2}
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{lua}
+f.derivatives = {Integer.one(),
+ Integer.one()}
+tex.print("\\[",
+ f:simplify():tolatex(),
+ "\\]")
+\end{minted}
+\tcblower
+\luaexec{
+ f.derivatives = {Integer.one(),Integer.one()}
+ tex.print("\\[", f:simplify():tolatex(), "\\]")
+}
+\end{codebox}
+
+\begin{center}
+\parseshrub{f}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily,
+ draw,
+ rounded corners = 1pt,
+ fill = gray!20,
+ l sep = 1.5cm,
+ s sep = 0.75cm}
+ @\shrubresult
+\end{forest}
+\end{center}
+\end{multicols}
+
+\subsubsection*{Parsing}
+
+Thank goodness for this too. The parser nested within the \LaTeX{} environment \mintinline{latex}{\begin{CAS}..\end{CAS}} allows for fairly natural function assignment; the name of the function must be declared in \mintinline{lua}{vars(...)} (or rather, as a \texttt{SymbolExpression}) beforehand:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('s','t','f')
+ f = f(s^2,s+2*t)
+ f.derivatives = {1,1}
+\end{CAS}
+\[ \print{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('s','t','f')
+ f = f(s^2,s+2*t)
+ f.derivatives = {1,1}
+\end{CAS}
+\[ \print{f} \]
+\end{codebox}
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_methods/ref_core_methods.tex b/macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_methods/ref_core_methods.tex
new file mode 100644
index 0000000000..a88b5a15d8
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/reference/ref_core/ref_core_methods/ref_core_methods.tex
@@ -0,0 +1,1155 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse,documentation}
+\usepackage{microtype}
+\usepackage{makeidx}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\definecolor{codegreen}{HTML}{49BE25}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\newtcolorbox{codehead}[1][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1
+}
+
+\usepackage{varwidth}
+
+\newtcolorbox{newcodehead}[2][]{
+ enhanced,
+ frame hidden,
+ colback=rosegray!15,
+ boxrule=0mm,
+ leftrule=5mm,
+ rightrule=5mm,
+ boxsep=0mm,
+ arc=0mm,
+ outer arc=0mm,
+ left=3mm,
+ right=3mm,
+ top=1mm,
+ bottom=1mm,
+ toptitle=1mm,
+ bottomtitle=1mm,
+ oversize,
+ #1,
+ fonttitle=\bfseries\ttfamily\footnotesize,
+ coltitle=rosegray,
+ attach boxed title to top text right,
+ boxed title style={frame hidden,size=small,bottom=-1mm,
+ interior style={fill=none,
+ top color=white,
+ bottom color=white}},
+ title={#2}
+}
+
+
+\makeindex
+
+\newcommand{\coderef}[2]{%
+\begin{codehead}[sidebyside,segmentation hidden]%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{codehead}%
+}
+
+\newcommand{\newcoderef}[3]{%
+\begin{newcodehead}[sidebyside,segmentation hidden]{#3}%
+ \mintinline{lua}{#1}%
+ \tcblower%
+ \begin{flushright}%
+ \mintinline{lua}{#2}%
+ \end{flushright}%
+\end{newcodehead}%
+}
+
+\begin{document}
+
+\subsection{Core Methods}
+
+Any of the methods below can be used within \mintinline{latex}{\begin{CAS}..\end{CAS}}. There are times when the parser or \LaTeX{} front-end allows for simpler syntax or usability.
+
+\coderef{function Expression:autosimplify()}{return Expression|table<number, Expression>}
+\index{Core!Methods!\texttt{autosimplify}}
+\addcontentsline{toc}{subsubsection}{\ttfamily autosimplify}
+
+Performs fast simplification techniques on an expression. The return depends on the type of input \texttt{Expression}. Generally, one should call {\ttfamily autosimplify()} on expressions before applying other core methods to them.
+
+Consider the code:
+
+\begin{minted}{latex}
+\begin{CAS}
+ vars('x','y','z')
+ w = x/y + y/z + z/x
+\end{CAS}
+\[ \print{w} = \print{w:autosimplify()} \]
+\end{minted}
+\begin{CAS}
+ vars('x','y','z')
+ w = x/y + y/z + z/x
+\end{CAS}
+The output is as follows:
+\[ \print{w} = \print{w:autosimplify()} \]
+It seems that \texttt{autosimplify()} did nothing; but there are significant structural differences between \texttt{w} and \texttt{w:autosimplify()}:
+
+\begin{multicols}{2}
+ \begin{center}
+ \underline{Expression tree for \texttt{w}}
+
+ \parseforest{w}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+ \end{forest}
+
+ \underline{Expression tree for \texttt{w:autosimplify()}}
+
+ \parseforest{w:autosimplify()}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+ \end{forest}
+\end{center}
+\end{multicols}
+
+Ironically, the \emph{autosimplified} expression tree on the right looks more complicated than the one on the left! But one of the primary functions of \texttt{autosimplify()} is to take an expression (that truly could be input in a myriad of ways) and convert that expression into something \emph{anticipatable}.
+
+For example, suppose the user inputs:
+\begin{minted}{latex}
+\begin{CAS}
+ w = x/y + (z/x+y/z)
+\end{CAS}
+\end{minted}
+\begin{CAS}
+ w = x/y + (z/x+y/z)
+\end{CAS}
+In this case, the expression trees for \texttt{w} and \texttt{w:autosimplify()}, respectively, look as follows:
+
+\begin{multicols}{2}
+\begin{center}
+\parseforest{w}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+\end{forest}
+
+\parseforest{w:autosimplify()}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+\end{forest}
+\end{center}
+\end{multicols}
+{\bf Note:} \texttt{w:autosimplify()} is exactly the same as it was before despite the different starting point. This is an essential function of \texttt{autosimplify()}.
+
+\subsubsection*{Parsing}
+
+The starred variant of the \LaTeX{} command \mintinline{latex}{\print} will automatically apply the method \texttt{autosimplify()} to its argument:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ a = x+x/2
+\end{CAS}
+\[ \print{a} = \print*{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ a = x+x/2
+\end{CAS}
+\[ \print{a} = \print*{a} \]
+\end{codebox}
+Alternatively, you can call \texttt{autosimplify()} directly within \mintinline{latex}{\begin{CAS}..\end{CAS}}:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ a = (x+x/2):autosimplify()
+\end{CAS}
+\[ \print{a} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ a = (x+x/2):autosimplify()
+\end{CAS}
+\[ \print{a} \]
+\end{codebox}
+
+\coderef{function Expression:evaluate()}{return Expression}
+\index{Core!Methods!\texttt{evaluate}}
+\addcontentsline{toc}{subsubsection}{\ttfamily evaluate}
+
+Attempts to apply operations found in the expression tree of \texttt{Expression}. For instance, evaluating a {\ttfamily DerivativeExpression} applies the derivative operator with respect to the {\ttfamily symbol} field to its {\ttfamily expression} field. Evaluating a {\ttfamily BinaryOperation} with its {\ttfamily operation} field set to {\ttfamily ADD} returns the sum of the numbers in the {\ttfamily expressions} field, if possible. If the expression does not represent an operation or is unable to be evaluated, calling {\ttfamily evaluate()} on an expression returns itself.
+
+For example, the code:
+\begin{minted}{latex}
+\directlua{
+ x = Integer(1)/Integer(2)
+ y = Integer(2)/Integer(3)
+ z = BinaryOperation(BinaryOperation.ADD,{x,y})
+}
+\[ \print{z} = \print{z:evaluate()}.\]
+\end{minted}
+produces:
+\directlua{
+ x = Integer(1)/Integer(2)
+ y = Integer(2)/Integer(3)
+ z = BinaryOperation(BinaryOperation.ADD,{x,y})
+}
+\[ \print{z} = \print{z:evaluate()}.\]
+
+\subsubsection*{Parsing}
+
+Arithmetic like above is actually done automatically (via the \texttt{Ring} interface):
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ x = 1/2
+ y = 2/3
+ z = x+y
+\end{CAS}
+\[ z = \print{z} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ x = 1/2
+ y = 2/3
+ z = x+y
+\end{CAS}
+\[ z = \print{z} \]
+\end{codebox}
+
+Otherwise, the \texttt{evaluate()} method will attempt to evaluate all subexpressions, and then stop there:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ y = diff(x^2+x,x)+diff(2*x,x)
+ y = y:evaluate()
+\end{CAS}
+\[ \print{y} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ y = diff(x^2+x,x)+diff(2*x,x)
+ y = y:evaluate()
+\end{CAS}
+\[ \print{y} \]
+\end{codebox}
+Whereas \texttt{autosimplify()} will return $3+2x$; indeed, the \texttt{autosimplify()} method (usually) begins by applying \texttt{evaluate()} first.
+
+\coderef{function Expression:expand()}{return Expression}
+\index{Core!Methods!\texttt{expand}}
+\addcontentsline{toc}{subsubsection}{\ttfamily expand}
+
+Expands an expression, turning products of sums into sums of products.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','y','z','w')
+ a = x+y
+ b = z+w
+ c = a*b
+\end{CAS}
+\[ \print{c} = \print{c:expand()} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x','y','z','w')
+ a = x+y
+ b = z+w
+ c = a*b
+\end{CAS}
+\[ \print{c} = \print{c:expand()} \]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+There is an \texttt{expand()} function in the parser; though it calls the \texttt{autosimplify()} method first. So, for example, \mintinline{lua}{expand(c)} is equivalent to \mintinline{lua}{c:autosimplify():expand()}.
+
+\coderef{function Expression:factor()}{return Expression}
+\index{Core!Methods!\texttt{factor}}
+\addcontentsline{toc}{subsubsection}{\ttfamily factor}
+
+Factors an expression, turning sums of products into products of sums. For general \texttt{Expressions} this functionality is somewhat limited. For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ a = x-1
+ b = a*x+a
+\end{CAS}
+\[ \print{b} = \print{b:factor()} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x','y')
+ a = x-y
+ b = a*x+a*y
+\end{CAS}
+\[ \print{b} = \print{b:factor()} \]
+\end{codebox}
+On the other hand:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','y')
+ a = x^2-y^2
+\end{CAS}
+\[ \print{a} = \print{a:factor()} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x','y')
+ a = x^2-y^2
+\end{CAS}
+\[ \print{a} = \print{a:factor()} \]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+There is a \texttt{factor()} function in the parser that is more class-aware than the basic \texttt{:factor()} method mentioned here. For example:
+\begin{codebox}
+ \begin{minted}[fontsize = \small]{latex}
+\begin{CAS}
+ x = 12512
+\end{CAS}
+\[ \print{x:factor()} = \print{factor(x)} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ x = 12512
+\end{CAS}
+\[ \print{x:factor()} = \print{factor(x)} \]
+\end{codebox}
+
+\coderef{function Expression:freeof(symbol)}{return bool}
+\index{Core!Methods!\texttt{freeof}}
+\addcontentsline{toc}{subsubsection}{\ttfamily freeof}
+
+Determines whether or not {\ttfamily Expression} contains a particular {\ttfamily symbol} somewhere in its expression tree.
+
+The method \texttt{freeof()} is quite literal. For example:
+\begin{codebox}
+\begin{minted}[fontsize=\small]{lua}
+vars('foo','bar')
+baz = foo+bar
+if baz:freeof(foo) then
+ tex.sprint(baz:tolatex(), " is free of ",
+ foo:tolatex(),"!")
+else
+ tex.sprint(baz:tolatex(), " is bound by ",
+ foo:tolatex(),".")
+end
+\end{minted}
+\tcblower
+\directlua{
+vars('foo','bar')
+baz = foo+bar
+if baz:freeof(foo) then
+ tex.sprint(baz:tolatex(), " is free of ",
+ foo:tolatex(),"!")
+else
+ tex.sprint(baz:tolatex(), " is bound by ",
+ foo:tolatex(),".")
+end
+}
+\end{codebox}
+On the other hand, the expression tree for \mintinline{lua}{SymbolExpression("foo")} contains a single node with no edges. With nary a \mintinline{lua}{SymbolExpression("fo")} to find in such an expression tree, we have:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+vars('foo','fo')
+if foo:freeof(fo) then
+ tex.sprint(foo:tolatex()," is free of ",
+ fo:tolatex(),"!")
+else
+ tex.sprint(foo:tolatex()," is bound by ",
+ fo:tolatex(),'.')
+end
+ \end{minted}
+ \tcblower
+ \directlua{
+vars('foo','fo')
+if foo:freeof(fo) then
+ tex.sprint(foo:tolatex()," is free of ",
+ fo:tolatex(),"!")
+else
+ tex.sprint(foo:tolatex()," is bound by ",
+ fo:tolatex(),'.')
+end
+}
+ \end{codebox}
+
+\coderef{function Expression:isatomic()}{return bool}
+\index{Core!Methods!\texttt{isatomic}}
+\addcontentsline{toc}{subsubsection}{\ttfamily isatomic}
+
+Determines whether an expression is \emph{atomic}. Typically, atomicity is measured by whether the \texttt{Expression} has any subexpression fields. So, for example, \texttt{Integer(5)} and \texttt{Integer(15)} are atomic, and so is \texttt{Integer(20)}. But:
+\begin{minted}{lua}
+BinaryOperation(BinaryOperation.ADD,
+ {Integer(5),Integer(15)})
+\end{minted}
+is non-atomic.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+x = SymbolExpression("x")
+y = x*x+x
+if x:isatomic() then
+ tex.print(tostring(x),"is atomic;")
+end
+if not y:isatomic() then
+ tex.print(tostring(y),"is compound.")
+end
+\end{minted}
+\tcblower
+\directlua{
+x = SymbolExpression("x")
+y = x*x+x
+if x:isatomic() then
+ tex.print(tostring(x),"is atomic;")
+end
+if not y:isatomic() then
+ tex.print(tostring(y),"is compound.")
+end
+}
+\end{codebox}
+Since \texttt{SymbolExpression} inherits from \texttt{AtomicExpression}, we have that \texttt{isatomic()} is taken literally as well. For example:
+\begin{codebox}
+ \begin{minted}[fontsize=\small,breaklines]{lua}
+y = SymbolExpression("x*x+x")
+if not y:isatomic() then
+ tex.print(tostring(y),"is compound.")
+else
+ tex.print("But",tostring(y),"is atomic,
+ from a certain point of view.")
+end
+\end{minted}
+\tcblower
+\directlua{
+ y = SymbolExpression("x*x+x")
+if not y:isatomic() then
+ tex.print(tostring(y),"is compound.")
+else
+ tex.print("But",tostring(y),"is atomic,
+ from a certain point of view.")
+end
+}
+\end{codebox}
+\vskip 0.2cm
+
+\coderef{function Expression:iscomplexconstant()}{return bool}
+\index{Core!Methods!\texttt{iscomplexconstant}}
+\addcontentsline{toc}{subsubsection}{\ttfamily iscomplexconstant}
+
+Determines whether an expression is a complex number in the mathematical sense, such as $3 + \sqrt{2}i$. It's helpful to keep in mind that, oftentimes, content needs to be simplified/evaluated in order to obtain the intended results:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{lua}
+a = (Integer.one() + I) ^ Integer(2)
+if a:iscomplexconstant() then
+ tex.print("$",a:tolatex(),"$ is a complex constant.")
+else
+ tex.print("$",a:tolatex(),"$ is not a complex constant.")
+end
+\end{minted}
+\tcblower
+\begin{center}
+\luaexec{
+ a = (Integer.one() + I) ^ Integer(2)
+ if a:iscomplexconstant() then
+ tex.print("$",a:tolatex(),"$ is a complex constant.")
+ else
+ tex.print("$",a:tolatex(),"$ is not a complex constant.")
+ end
+}
+\end{center}
+\end{codebox}
+While:
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{lua}
+a = (Integer.one()+I) ^ Integer(2)
+a = a:expand():simplify()
+if a:iscomplexconstant() then
+ tex.print("$",a:tolatex(),"$ is a complex constant.")
+else
+ tex.print("$",a:tolatex(),"$ is not a complex constant.")
+end
+\end{minted}
+\tcblower
+\begin{center}
+\luaexec{
+ a = (Integer.one()+I) ^ Integer(2)
+ a = a:expand():simplify()
+ if a:iscomplexconstant() then
+ tex.print("$",a:tolatex(),"$ is a complex constant.")
+ else
+ tex.print("$",a:tolatex(),"$ is not a complex constant.")
+ end
+}
+\end{center}
+\end{codebox}
+
+\coderef{function Expression:isconstant()}{return bool}
+\index{Core!Methods!\texttt{isconstant}}
+\addcontentsline{toc}{subsubsection}{\ttfamily isconstant}
+
+Determines whether an expression is atomic and contains no variables. This method is counterintuitive in some cases. For instance:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+if not pi:isconstant() then
+ tex.print("$\\pi$ is not constant.")
+end
+\end{minted}
+\tcblower
+\luaexec{
+if not pi:isconstant() then
+ tex.print("$\\pi$ is not constant.")
+end
+}
+\end{codebox}
+This is because {\ttfamily isconstant()} is meant to check for certain autosimplification transformations that can be performed on arbitrary {\ttfamily Ring} elements but not on those constants. Use {\ttfamily isrealconstant()} for what mathematicians think of as constants.
+
+\coderef{function Expression:isrealconstant()}{return bool}
+\index{Core!Methods!\texttt{isrealconstant}}
+\addcontentsline{toc}{subsubsection}{\ttfamily isrealconstant}
+
+Determines whether an expression is a real number in the mathematical sense, such as $2$, $\sqrt{5}$, or $\sin(3)$. For example:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{lua}
+if pi:isrealconstant() then
+ tex.print("$\\pi$ is a real constant.")
+end
+\end{minted}
+\tcblower
+\luaexec{
+ if pi:isrealconstant() then
+ tex.print("$\\pi$ is a real constant.")
+ end
+}
+\end{codebox}
+
+\coderef{function Expression:order(Expression)}{return boolean}
+\index{Core!Methods!\texttt{order}}
+\addcontentsline{toc}{subsubsection}{\ttfamily order}
+
+For the goals of autosimplification, \texttt{Expression}s must be ordered. \texttt{Expression:order(other)} method returns \mintinline{lua}{true} if \texttt{Expression} is ``less than'' \texttt{other} according to this ordering.
+
+\begin{multicols}{2}
+On certain classes, the ordering is intuitive:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{lua}
+a = 4
+b = 3
+if a:order(2) then
+ tex.print(a:tolatex(),
+ "is less than",
+ b:tolatex())
+else
+ tex.print(b:tolatex(),
+ "is less than",
+ a:tolatex())
+end
+\end{minted}
+\tcblower
+\begin{CAS}
+ a = 4
+ b = 3
+ if a:order(2) then
+ tex.print(a:tolatex(),
+ "is less than",
+ b:tolatex())
+ else
+ tex.print(b:tolatex(),
+ "is less than",
+ a:tolatex())
+ end
+\end{CAS}
+\end{codebox}
+
+\columnbreak
+
+On \texttt{SymbolExpression}s, the ordering is lexigraphic:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{lua}
+vars('a')
+vars('b')
+if b:order(a) then
+ tex.print(b:tolatex(),
+ "is less than",
+ a:tolatex())
+else
+ tex.print(a:tolatex(),
+ "is less than",
+ b:tolatex())
+end
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('a','b')
+ if b:order(a) then
+ tex.print(b:tolatex(),
+ "is less than",
+ a:tolatex())
+ else
+ tex.print(a:tolatex(),
+ "is less than",
+ b:tolatex())
+ end
+\end{CAS}
+\end{codebox}
+\end{multicols}
+
+Of course, inter-class comparisons can be made as well -- but these are predominantly dictated by typesetting conventions.
+
+\coderef{function Expression:setsubexpressions(subexpressions)}{return Expression}
+\index{Core!Methods!\texttt{setsubexpressions}}
+\addcontentsline{toc}{subsubsection}{\ttfamily setsubexpressions}
+
+Creates a copy of an expression with the list of subexpressions as its new subexpressions. This can reduce code duplication in other methods.
+
+
+\coderef{function Expression:simplify()}{return Expression}
+\index{Core!Methods!\texttt{simplify}}
+\addcontentsline{toc}{subsubsection}{\ttfamily simplify}
+
+Performs more extensive simplification of an expression. This may be slow, so this function is separate from autosimplification and is not called unless the user specifically directs the CAS to do so. The method aims to find an expression tree equivalent to the one given that is ``smaller'' in size as measured by the number of nodes in the expression tree.
+
+The \texttt{simplify()} method does call the \texttt{autosimplify()} method first. Here's an example of where the results of \texttt{autosimplify()} and \texttt{simplify()} differ:
+
+\begin{minted}[breaklines]{latex}
+\begin{CAS}
+ vars('x')
+ a = 1-x+0*x
+ b = 1+1*x
+ c = a*b
+\end{CAS}
+\[ \print{c} = \print{c:autosimplify()} = \print{c:simplify()}. \]
+\end{minted}
+The code above produces:
+\begin{CAS}
+ vars('x')
+ a = 1-x+0*x
+ b = 1+1*x
+ c = a*b
+\end{CAS}
+\[ \print{c} = \print{c:autosimplify()} = \print{c:simplify()}. \]
+
+\subsubsection*{Parsing}
+
+There is a \texttt{simplify()} function for those unfamiliar with Lua methods. So, for example, \mintinline{lua}{c:simplify()} is equivalent to \mintinline{lua}{simplify(c)}.
+
+\coderef{function Expression:size()}{return Integer}
+\index{Core!Methods!\texttt{size}}
+\addcontentsline{toc}{subsubsection}{\ttfamily size}
+
+Returns the number of nodes of the tree that constitutes an expression, or roughly the total number of expression objects that make up the expression.
+
+For example, consider:
+\begin{minted}{latex}
+\begin{CAS}
+ vars('x')
+ a = (1-x+0*x)
+ b = (1+1*x)
+ c = a*b
+\end{CAS}
+\end{minted}
+\begin{CAS}
+ vars('x')
+ a = (1-x+0*x)
+ b = (1+1*x)
+ c = a*b
+\end{CAS}
+Then the expression trees for \texttt{c}, \texttt{c:autosimplify()}, and \texttt{c:simplify()} are as follows:
+
+\begin{multicols}{3}
+ \begin{center}
+ \underline{Tree for \texttt{c}:}
+\parseforest{c}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+\end{forest}
+
+\columnbreak
+
+\underline{Tree for \texttt{c:autosimplify()}:}
+\parseforest{c:autosimplify()}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+\end{forest}
+
+\columnbreak
+
+\underline{Tree for \texttt{c:simplify()}:}
+\parseforest{c:simplify()}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {font = \ttfamily}
+ @\forestresult
+\end{forest}
+\end{center}
+\end{multicols}
+Accordingly, we have:
+\begin{codebox}
+ \begin{minted}[breaklines,fontsize=\small]{lua}
+tex.print("The size of \\texttt{c} is",
+ tostring(c:size()),"\\newline")
+tex.print("The size of
+ \\texttt{c:autosimplify()} is",
+ tostring(c:autosimplify():size()), "\\newline")
+tex.print("The size of
+ \\texttt{c:simplify()} is",
+ tostring(c:simplify():size()))
+ \end{minted}
+ \tcblower
+ \luaexec{
+ tex.print("The size of \\texttt{c} is", tostring(c:size()),"\\newline")
+ tex.print("The size of \\texttt{c:autosimplify()} is", tostring(c:autosimplify():size()), "\\newline")
+ tex.print("The size of \\texttt{c:simplfy()} is", tostring(c:simplify():size()))
+}
+\end{codebox}
+\vskip 0.2cm
+
+\coderef{function Expression:subexpressions()}{return table<number, Expression>}
+\index{Core!Methods!\texttt{subexpressions}}
+\addcontentsline{toc}{subsubsection}{\ttfamily subexpressions}
+
+Returns a list of all subexpressions of an expression. This gives a unified interface to the instance variables for subexpressions, which have different names across classes. For example, consider:
+\begin{codebox}
+\begin{minted}[breaklines,fontsize=\footnotesize]{latex}
+\begin{CAS}
+ vars('x','y','z')
+ a = x*y+y*z
+ b = int(sin(x),x,0,pi/2)
+\end{CAS}
+\[ a = \print{a} \quad \text{and} \quad b=\print{b}.\]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x','y','z')
+ a = x*y+y*z
+ b = int(sin(x),x,0,pi/2)
+\end{CAS}
+\[ a = \print{a} \quad \text{and} \quad b=\print{b}.\]
+\end{codebox}
+Here are the expression shrubs for \texttt{a} and \texttt{b}:
+
+\begin{multicols}{2}
+\begin{center}
+ \underline{Expression shrub for \texttt{a}}
+
+ \parseshrub{a}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {draw,
+ rectangle,
+ rounded corners=1pt,
+ fill=gray!10,
+ s sep = 2cm,
+ font=\ttfamily}
+ @\shrubresult
+ \end{forest}
+
+ \underline{Expression shrub for \texttt{b}}
+
+\parseshrub{b}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {draw,
+ rectangle,
+ rounded corners=1pt,
+ fill=gray!10,
+ s sep = 1cm,
+ font=\ttfamily}
+ @\shrubresult
+\end{forest}
+\end{center}
+\end{multicols}
+On the other hand:
+\begin{codebox}
+\begin{minted}[breaklines,fontsize=\small]{lua}
+for _,expr in ipairs(a:subexpressions()) do
+ tex.print("$", expr:tolatex(), "$\\quad")
+end
+\end{minted}
+\tcblower
+\luaexec{
+ for _,expr in ipairs(a:subexpressions()) do
+ tex.print("$", expr:tolatex(), "$\\quad")
+ end
+}
+\end{codebox}
+while:
+\begin{codebox}
+\begin{minted}[breaklines,fontsize=\small]{lua}
+for _,expr in ipairs(b:subexpressions()) do
+ tex.print("$", expr:tolatex(), "$\\quad")
+end
+\end{minted}
+\tcblower
+\luaexec{
+ for _,expr in ipairs(b:subexpressions()) do
+ tex.print("$",expr:tolatex(),"$\\quad")
+ end
+}
+\end{codebox}
+\vskip 0.2cm
+
+\coderef{function Expression:substitute(map)}{return Expression}
+\index{Core!Methods!\texttt{substitute}}
+\addcontentsline{toc}{subsubsection}{\ttfamily substitute}
+
+The input \texttt{map} is a table that maps expressions to expressions; the method then recursively maps each instance of an expression with its corresponding table expression. One should take care when replacing multiple compound expressions in a single command, since there is no guarantee as to the order in which subexpressions in the table are replaced.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('foo','bar','baz')
+ qux = (foo/bar)
+ qux = qux:substitute({[foo]=bar,[bar]=baz})
+\end{CAS}
+\[ \print{qux} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('foo','bar','baz')
+ qux = (foo/bar)
+ qux = qux:substitute({[foo]=bar,[bar]=baz})
+ \end{CAS}
+ \[ \print{qux} \]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+There is a \texttt{substitute()} function with a slightly more user-friendly syntax. In particular,
+
+\mintinline{lua}{(foo/bar):substitute({[foo]=bar,[bar]=baz})}
+
+is equivalent to
+
+\mintinline{lua}{substitute({[foo]=bar,[bar]=baz}, foo/bar)}
+
+\coderef{function Expression:tolatex()}{return string}
+\index{Core!Methods!\texttt{tolatex}}
+\addcontentsline{toc}{subsubsection}{\ttfamily tolatex}
+
+Converts an expression to \LaTeX{} code. Some folks have strong feelings about how certain things are typeset. Case and point, which of these is your favorite:
+\[ \int \sin(\frac{y}{2})dy \qquad \int \sin\left( \frac{y}{2} \right)dy \qquad \int \sin\left( \frac{y}{2} \right)\, dy \qquad \int \sin\!\left( \frac{y}{2} \right)\, dy \qquad \int \sin\!\left( \frac{y}{2} \right) \mathop{\mathrm{d}y} \qquad \int \sin\mathopen{}\left( \frac{y}{2} \right) \mathop{dy} \quad ?\]
+We've tried to remain neutral:
+
+\begin{codebox}
+\begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('y')
+ f = diff(int(sin(y/2),y),y)
+\end{CAS}
+\[ \print{f} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('y')
+ f = diff(int(sin(y/2),y),y)
+\end{CAS}
+\[ \print{f} \]
+\end{codebox}
+With any luck, we've pleased at least as many people as we've offended. In desperate times, one could rewrite the \texttt{tolatex()} method for any given class. Here, for example, is the \texttt{tolatex()} method as written for the \texttt{DerivativeExpression} class:
+\begin{minted}[breaklines]{lua}
+function DerivativeExpression:tolatex()
+ return '\\frac{d}{d' .. self.symbol:tolatex() .. '}\\left(' .. self.expression:tolatex() .. '\\right)'
+end
+\end{minted}
+But there are heathens that live among us who might prefer:
+\begin{minted}[breaklines]{lua}
+function DerivativeExpression:tolatex()
+ return '\\frac{\\mathrm{d}}{\\mathrm{d}' .. self.symbol:tolatex() .. '}\\left(' .. self.expression:tolatex() .. '\\right)'
+end
+\end{minted}
+If we include the above function in a separate file, say \texttt{mytex.lua}, and use:
+
+\mintinline{latex}{\directlua{dofile('mytex.lua')}}
+
+or include the above function directly into the document via \mintinline{latex}{\directlua} or \mintinline{latex}{\luaexec}, then we would get:
+\begin{codebox}
+\begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = DerivativeExpression(y+sin(y),y)
+\end{CAS}
+\[ \print{f} \]
+\end{minted}
+\tcblower
+\[ \frac{\mathrm{d}}{\mathrm{d}y} \left( y + \sin\mathopen{}\left(y\right) \right).\]
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The \LaTeX{} command \mintinline{latex}{\print} calls the method \mintinline{lua}{tolatex()} unto its argument and uses \mintinline{lua}{tex.print()} to display the results. The starred variant \mintinline{latex}{\print*} applies the \texttt{autosimplify()} method before applying \mintinline{lua}{tolatex()}.
+
+Additionally, one can use the \texttt{disp()} function within \mintinline{latex}{\begin{CAS}..\end{CAS}}.
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ f = DerivativeExpression(y+sin(y),y)
+ disp(f)
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ f = DerivativeExpression(y+sin(y),y)
+ disp(f)
+\end{CAS}
+\end{codebox}
+
+The function \texttt{disp} takes two optional boolean arguments both are set to \texttt{false} by default. The first optional boolean controls \emph{inline} vs \emph{display} mode; the second optional boolean controls whether the method \texttt{autosimplify()} is called before printing:
+
+\begin{multicols}{3}
+ \begin{codebox}[]
+\begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ disp(f,true)
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ disp(f,true)
+\end{CAS}
+\end{codebox}
+ \begin{codebox}[]
+\begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ disp(f,true,true)
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ disp(f,true,true)
+\end{CAS}
+\end{codebox}
+ \begin{codebox}[]
+\begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ disp(f,false,true)
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ disp(f,false,true)
+\end{CAS}
+\end{codebox}
+\end{multicols}
+
+
+\coderef{function Expression:topolynomial()}{return Expression | bool}
+\index{Core!Methods!\texttt{topolynomial}}
+\addcontentsline{toc}{subsubsection}{\ttfamily topolynomial}
+
+Attempts to cast \texttt{Expression} into a polynomial type ({\ttfamily PolynomialRing}); there are multiple outputs. The first output is \texttt{self} or \texttt{PolynomialRing}; the second output is \mintinline{lua}{false} or \mintinline{lua}{true}, respectively. \texttt{PolynomialRing} is the name of the class that codifies univariate polynomials proper.
+
+Polynomial computations tend to be significantly faster when those polynomials are stored as arrays of coefficients (as opposed to, say, when they are stored as generic \texttt{BinaryOperation}s). Hence the need for a method like \texttt{topolynomial()}.
+
+{\bf Warning:} the \texttt{topolynomial()} method expects the input to be autosimplified. For example:
+
+\begin{multicols}{2}
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = 3+2*x+x^2
+ f,b = f:topolynomial()
+ if b then
+ tex.print("\\[",f:tolatex(),"\\]")
+ else
+ tex.print("womp womp")
+ end
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = 3+2*x+x^2
+ f,b = f:topolynomial()
+ if b then
+ tex.print("\\[",f:tolatex(),"\\]")
+ else
+ tex.print("\\[ \\text{womp womp} \\]")
+ end
+\end{CAS}
+\end{codebox}
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = 3+2*x+x^2
+ f,b = f:autosimplify():topolynomial()
+ if b then
+ tex.print("\\[",f:tolatex(),"\\]")
+ else
+ tex.print("womp womp")
+ end
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = 3+2*x+x^2
+ f,b = f:autosimplify():topolynomial()
+ if b then
+ tex.print("\\[",f:tolatex(),"\\]")
+ else
+ tex.print("\\[ \\text{womp womp} \\]")
+ end
+\end{CAS}
+\end{codebox}
+\end{multicols}
+
+\subsubsection*{Parsing}
+
+There is a \mintinline{lua}{topoly()} function that applies \mintinline{lua}{:autosimplify()} automatically to the input. For example:
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ f = 3+2*x+x^2
+ f = topoly(f)
+\end{CAS}
+The Lua variable \texttt{f} is the \whatis{f}: $\print{f}$.
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ f = 3+2*x+x^2
+ f = topoly(f)
+\end{CAS}
+The Lua variable \texttt{f} is the \whatis{f}: $\print{f}$.
+\end{codebox}
+
+
+\coderef{function Expression:type()}{return Expression | bool}
+\index{Core!Methods!\texttt{type}}
+\addcontentsline{toc}{subsubsection}{\ttfamily type}
+
+Returns the \mintinline{lua}{__index} field in the metatable for \texttt{Expression}. In other words, this function returns the type of \texttt{Expression}. Here's typical usage:
+
+\begin{codebox}
+ \begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{CAS}
+ vars('x')
+ if x:type() == SymbolExpression then
+ tex.print(x:tolatex(), "is a SymbolExpression.")
+ end
+\end{CAS}
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x')
+ if x:type() == SymbolExpression then
+ tex.print(x:tolatex(), "is a SymbolExpression.")
+ end
+\end{CAS}
+\end{codebox}
+
+\subsubsection*{Parsing}
+
+The \LaTeX{} command \mintinline{latex}{\whatis} can be used to print the type of \texttt{Expression}:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+x is a \whatis{x}
+ \end{minted}
+ \tcblower
+ x is a \whatis{x}
+\end{codebox}
+
+Alternatively, there's a \mintinline{lua}{whatis()} function and a \mintinline{lua}{longwhatis()} function that can be called within a Lua environment (like \mintinline{latex}{\directlua} or \mintinline{latex}{\luaexec}):
+
+\begin{codebox}
+\begin{minted}[fontsize=\small]{lua}
+tex.print(whatis(x), '\\newline')
+tex.print(longwhatis(x))
+\end{minted}
+\tcblower
+\directlua{
+ tex.print(whatis(x), '\\newline')
+ tex.print(longwhatis(x))
+}
+\end{codebox}
+
+
+
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/tutorial/tut.tex b/macros/luatex/latex/luacas/doc/tutorial/tut.tex
new file mode 100644
index 0000000000..3e12ec919d
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/tutorial/tut.tex
@@ -0,0 +1,217 @@
+\documentclass{article}
+
+\usepackage{standalone}
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse}
+\usepackage{microtype}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\begin{document}
+
+\section{Tutorials}
+
+Taking a cue from the phenomenal TikZ documentation, we introduce basic usage of the \texttt{luacas} package through a few informal tutorials. In the subsections that follow, we'll walk through how each of the outputs below are made using \texttt{luacas}. {\bf Crucially}, none of the computations below are ``hardcoded''; all computations are performed and printed using \texttt{luacas} to maximize portability and code reuse.
+
+\begin{tcolorbox}[colback=rose!10,
+ colframe=rose,
+ arc=1pt,
+ frame hidden]
+{\bf Tutorial 1:} {\itshape A limit definition of the derivative for Alice.}\vskip0.2cm
+\small
+\begin{CAS}
+ vars('x','h')
+ f = 2*x^3-x
+\end{CAS}
+Let $f(x) = \print{f}$. We wish to compute the derivative of $f(x)$ at $x$ using the limit definition of the derivative. Toward that end, we start with the appropriate difference quotient:
+\begin{CAS}
+ subs = {[x] = x+h}
+ q = (f:substitute(subs) - f)/h
+\end{CAS}
+\[ \begin{aligned}
+ \print{q} &=
+ \begin{CAS}
+ q = expand(q)
+ \end{CAS}
+ \print{q}& &\text{expand/simplify} \\
+ \begin{CAS}
+ subs = {[h]=0}
+ q = q:substitute(subs)
+ \end{CAS}
+ &\xrightarrow{h\to 0} \print{q}& &\text{take limit} \\
+ &=
+ \begin{CAS}
+ q = simplify(q)
+ \end{CAS}
+ \print{q}& &\text{simplify.}
+\end{aligned} \]
+%So $\print{diff(f,x)} = \print*{diff(f,x)}$.
+\end{tcolorbox}
+\vfill
+
+\begin{tcolorbox}[colback=rosenavy!10,
+ colframe=rosenavy,
+ arc=1pt,
+ frame hidden]
+ \small
+ {\bf Tutorial 2:} {\itshape A local max/min diagram for Bob}.
+ \vskip 0.2cm
+ Consider the function $f(x)$ defined by:
+ \begin{CAS}
+ vars('x')
+ f = x^2+2*x-2
+ g = x^2-1
+ subs = {[x] = f}
+ dh = expand(substitute(subs,g))
+ h = simplify(int(dh,x)+10)
+ \end{CAS}
+ $\displaystyle f(x) = \print{h}$.
+\begin{multicols}{2}
+ Note that:
+ \[ f'(x) = \print{dh}.\]
+ The roots to $f'(x)=0$ equation are:
+ \begin{CAS}
+ r = roots(dh)
+ \end{CAS}
+ \[ \print{r[1]}, \quad \print{r[2]}, \quad \print{r[3]}, \quad \print{r[4]}.\]
+ Recall: $f'(x_0)$ measures the slope of the tangent line to $y=f(x)$ at $x=x_0$. The values $r$ where $f'(r)=0$ correspond to places where the slope of the tangent line to $y=f(x)$ is horizontal (see the illustration). This gives us a method for identifying locations where the graph $y=f(x)$ attains a peak (local maximum) or a valley (local minimum).
+ \directlua{
+ v = {}
+ for i=1,4 do
+ table.insert(v,simplify(substitute({[x]=r[i]},h)))
+ end
+ }
+ \columnbreak
+ \store{h}\store{dh}
+ \begin{tikzpicture}[scale=0.95]
+ \begin{axis}[legend pos = north west]
+ \addplot
+ [domain=-3.5:1.5,samples=100] {\h};
+ \addlegendentry{$f$};
+ \addplot[densely dashed]
+ [domain=-3.25:1.25,samples=100] {\dh};
+ \addlegendentry{$df/dx$};
+ \addplot[gray,dashed,thick]
+ [domain=-3.5:1.5] {0};
+ \luaexec{for i=1,4 do
+ tex.print("\\draw[fill=purple,purple]",
+ "(axis cs:{", tostring(r[i]) ,"},0) circle (1.5pt)",
+ "(axis cs:{", tostring(r[i]) ,"},{", tostring(v[i]), "}) circle (1.5pt)",
+ "(axis cs:{", tostring(r[i]) ,"},{", tostring(v[i]), "}) edge[dashed] (axis cs:{", tostring(r[i]) ,"},0);")
+ end}
+ \end{axis}
+ \end{tikzpicture}
+\end{multicols}
+\end{tcolorbox}
+
+\vfill
+
+\begin{tcolorbox}[colback=roseorange!10,
+ colframe=roseorange,
+ arc=1pt,
+ frame hidden]
+ \small
+{\bf Tutorial 3:} {\itshape A limit definition of the derivative for Charlie.}\vskip 0.2cm
+
+\begin{CAS}
+ vars('x','h')
+ f = x/(x^2+1)
+\end{CAS}
+Let $f(x) = \print{f}$. We wish to compute the derivative of $f(x)$ at $x$ using the limit definition of the derivative. Toward that end, we start with the appropriate difference quotient:
+\begin{CAS}
+ subs = {[x] = x+h}
+ q = (f:substitute(subs) - f)/h
+\end{CAS}
+\directlua{
+ function Expression:mycombine()
+ local a = self.expressions[1].expressions[1].expressions[1]
+ local b = self.expressions[1].expressions[1].expressions[2]
+ local c = self.expressions[1].expressions[2].expressions[1]
+ local d = self.expressions[1].expressions[2].expressions[2]
+ local numerator = a*d-b*c
+ local denominator = self.expressions[2]*b*d
+ return numerator/denominator
+ end
+ function Expression:mysimplify()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = simplify(a)
+ return a/b
+ end
+ function Expression:myfactor()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = factor(a)
+ return a/b
+ end
+}
+\[ \begin{aligned}
+ \print{q} &=
+ \begin{CAS}
+ q = q:mycombine()
+ \end{CAS}
+ \print{q}& &\text{get a common denominator} \\
+ &=
+ \begin{CAS}
+ q = q:mysimplify()
+ \end{CAS}
+ \print{q}& &\text{simplify the numerator} \\
+ &=
+ \begin{CAS}
+ q = q:myfactor()
+ \end{CAS}
+ \print{q} & &\text{factor numerator} \\
+ &=
+ \begin{CAS}
+ q = simplify(q)
+ \end{CAS}
+ \print{q}& &\text{cancel the $h$s} \\
+ &\xrightarrow{h\to 0}
+ \begin{CAS}
+ subs = {[h] = 0}
+ q = substitute(subs,q):autosimplify()
+ \end{CAS}
+ \print{q}& &\text{take limit.}
+\end{aligned} \]
+\end{tcolorbox}
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/tutorial/tut1/tut1.tex b/macros/luatex/latex/luacas/doc/tutorial/tut1/tut1.tex
new file mode 100644
index 0000000000..1151d3e80b
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/tutorial/tut1/tut1.tex
@@ -0,0 +1,238 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse}
+\usepackage{microtype}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\begin{document}
+
+\subsection{Tutorial 1: Limit Definition of the Derivative}
+
+Alice is teaching calculus, and she wants to give her students many examples of the dreaded \emph{limit definition of the derivative}. On the other hand, she'd like to avoid working out many examples by-hand. She decides to give \texttt{luacas} a try.
+
+Alice can access the \texttt{luacas} program using a custom environment: \mintinline{latex}{\begin{CAS}..\end{CAS}}. The first thing Alice must do is declare variables that will be used going forward:
+\begin{minted}{latex}
+\begin{CAS}
+ vars('x','h')
+\end{CAS}
+\end{minted}
+Alice decides that $f$, the function to be differentiated, should be $x^2$. So Alice makes this assignment with:
+\begin{minted}{latex}
+\begin{CAS}
+ vars('x','h')
+ f = x^2
+\end{CAS}
+\end{minted}
+Now, Alice wants to use the variable $q$ to store the appropriate \emph{difference quotient} of $f$. Alice could hardcode this into $q$, but that seems to defeat the oft sought after goal of reusable code. So Alice decides to use the \texttt{substitute} command of \texttt{luacas}:
+\begin{minted}{latex}
+\begin{CAS}
+ vars('x','h')
+ f = x^2
+ subs = {[x]=x+h}
+ q = (substitute(subs,f) - f)/h
+\end{CAS}
+\end{minted}
+Alice is curious to know if $q$ is what she thinks it is. So Alice decides to have \LaTeX{} print out the contents of $q$ within her document. For this, she uses the \mintinline{latex}{\print} command.
+\begin{CAS}
+ vars('x','h')
+ f = x^2
+ subs = {[x]=x+h}
+ q = (substitute(subs,f)- f)/h
+\end{CAS}
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\[ \print{q} \]
+ \end{minted}
+ \tcblower
+ \[ \print{q} \]
+\end{codebox}
+So far so good! Alice wants to expand the numerator of $q$; she finds the aptly named \texttt{expand} method helpful in this regard. Alice redefines \mintinline{lua}{q} to be \mintinline{lua}{q=expand(q)}, and prints the result to see if things worked as expected:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','h')
+ f = x^2
+ subs = {[x]=x+h}
+ q = (substitute(subs,f)-f)/h
+ q = expand(q)
+\end{CAS}
+\[ \print{q} \]
+ \end{minted}
+ \tcblower
+ \begin{CAS}
+ q = expand(q)
+ \end{CAS}
+ \[ \print*{q} \]
+\end{codebox}
+Alice is pleasantly surprised that the result of the expansion has been \emph{simplified}, i.e., the factors of $x^2$ and $-x^2$ cancelled each other out, and the resulting extra factor of $h$ has been cancelled out of the numerator and denominator.
+
+Finally, Alice wants to take the limit as $h\to 0$. Now that our difference quotient has been expanded and simplified, this amounts to another substitution:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','h')
+ f = x^2
+ subs = {[x]=x+h}
+ q = (substitute(subs,f)-f)/h
+ q = expand(q)
+ subs = {[h] = 0}
+ q = substitute(subs,q)
+\end{CAS}
+\[ \print{q} \]
+ \end{minted}
+ \tcblower
+ \begin{CAS}
+ subs = {[h]=0}
+ q = q:substitute(subs)
+ \end{CAS}
+ \[ \print{q} \]
+\end{codebox}
+Alice is slightly disappointed that $0+2x$ is returned and not $2x$. Alice takes a guess that there's a \mintinline{lua}{simplify} command. This does the trick: adding the line \mintinline{lua}{q = simplify(q)} before leaving the \texttt{CAS} environment returns the expected $2x$:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','h')
+ f = x^2
+ subs = {[x]=x+h}
+ q = (substitute(subs,f)-f)/h
+ q = expand(q)
+ subs = {[h] = 0}
+ q = substitute(subs,q)
+ q = simplify(q)
+\end{CAS}
+\[ \print{q} \]
+ \end{minted}
+ \tcblower
+ \begin{CAS}
+ q=simplify(q)
+ \end{CAS}
+ \[ \print{q} \]
+\end{codebox}
+
+Alternatively, Alice could have used the \mintinline{latex}{\print*} command instead of \mintinline{latex}{\print} -- the essential difference is that \mintinline{latex}{\print*}, unlike \mintinline{latex}{\print}, automatically simplifies the content of the argument.
+
+Alice is pretty happy with how everything is working, but she wants to be able to typeset the individual steps of this process. Alice is therefore thrilled to learn that the \mintinline{latex}{\begin{CAS}..\end{CAS}} environment is very robust -- it can:
+\begin{itemize}
+ \item Be entered into and exited out of essentially anywhere within her \LaTeX{} document, for example, within \mintinline{latex}{\begin{aligned}..\end{aligned}}; and
+ \item CAS variables persist -- if Alice assigns \mintinline{lua}{f = x^2} within \mintinline{latex}{\begin{CAS}..\end{CAS}}, then the CAS remembers that \mintinline{lua}{f = x^2} the next time Alice enters the CAS environment.
+\end{itemize}
+Here's Alice's completed code:
+\begin{codebox}[frame hidden,breakable]
+\begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','h')
+ f = x^2
+\end{CAS}
+Let $f(x) = \print{f}$. We wish to compute the derivative of $f(x)$ at $x$ using thelimit definition of the derivative. Toward that end, we start with the appopriatedifference quotient:
+\begin{CAS}
+ subs = {[x]=x+h}
+ q = (substitute(subs,f) - f)/h
+\end{CAS}
+\[ \begin{aligned}
+ \print{q} &=
+ \begin{CAS}
+ q = expand(q)
+ \end{CAS}
+ \print{q}& &\text{expand/simplify} \\
+ \begin{CAS}
+ subs = {[h]=0}
+ q = substitute(subs,q)
+ \end{CAS}
+ &\xrightarrow{h\to 0} \print{q}& &\text{take limit}\\
+ &=
+ \begin{CAS}
+ q = simplify(q)
+ \end{CAS}
+ \print{q} & &\text{simplify.}
+\end{aligned} \]
+So $\print{diff(f,x)} = \print*{diff(f,x)}$.
+\end{minted}
+\end{codebox}
+
+Alice can produce another example merely by changing the definition of $f$ on the third line to another polynomial:
+
+\begin{minted}{latex}
+\begin{CAS}
+ vars('x','h')
+ f = 2*x^3-x
+\end{CAS}
+\end{minted}
+And here is Alice's completed project:
+\begin{tcolorbox}[colback=rose!10,
+ colframe=rose,
+ arc=1pt,
+ frame hidden]
+{\bf Tutorial 1:} {\itshape A limit definition of the derivative for Alice.}\vskip0.2cm
+
+\begin{CAS}
+vars('x','h')
+f = 2*x^3-x
+\end{CAS}
+Let $f(x) = \print{f}$. We wish to compute the derivative of $f(x)$ at $x$ using the limit definition of the derivative. Toward that end, we start with the appropriate difference quotient:
+\begin{CAS}
+subs = {[x] = x+h}
+q = (f:substitute(subs) - f)/h
+\end{CAS}
+\[ \begin{aligned}
+\print{q} &=
+\begin{CAS}
+ q = expand(q)
+\end{CAS}
+\print{q}& &\text{expand/simplify} \\
+\begin{CAS}
+ subs = {[h]=0}
+ q = q:substitute(subs)
+\end{CAS}
+&\xrightarrow{h\to 0} \print{q}& &\text{take limit} \\
+&=
+\begin{CAS}
+ q = simplify(q)
+\end{CAS}
+\print{q}& &\text{simplify.}
+\end{aligned} \]
+%So $\print{diff(f,x)} = \print*{diff(f,x)}$.
+\end{tcolorbox}
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/tutorial/tut2/tut2.tex b/macros/luatex/latex/luacas/doc/tutorial/tut2/tut2.tex
new file mode 100644
index 0000000000..a80e285782
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/tutorial/tut2/tut2.tex
@@ -0,0 +1,476 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse}
+\usepackage{microtype}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\begin{document}
+
+\subsection{Tutorial 2: Finding Maxima/Minima}
+
+Bob is teaching calculus too, and he wants to give his students many examples of the process of \emph{finding the local max/min of a given function}. But, like Alice, Bob doesn't want to work out a bunch of examples by-hand. Bob decides to try his hand with \texttt{luacas} after having been taught the basics by Alice.
+
+Bob decides to stick with polynomials for these examples; if anything because those functions are in the wheel-house of \texttt{luacas}. In particular, Bob decides that the \emph{derivative} of the function he wants to use should be a composition of quadratics. This ought to ensure that the roots of that derivative are expressible in a nice way.
+
+Accordingly, Bob declares variables and chooses two quadratic polynomials to compose, say $f$ and $g$, and sets $dh = g \circ f$:
+
+\begin{minted}{latex}
+\begin{CAS}
+ vars('x')
+ f = x^2+2*x-2
+ g = x^2-1
+ subs = {[x] = f}
+ dh = substitute(subs,g)
+\end{CAS}
+\end{minted}
+\begin{CAS}
+ vars('x')
+ f = x^2+2*x-2
+ g = x^2-1
+ subs = {[x] = f}
+ dh = substitute(subs,g)
+\end{CAS}
+
+Bob wants to compute $h$, the integral of $dh$. Bob could certainly compute this quantity by-hand, but why hardcode that information into the document when \texttt{luacas} can do this for you? So Bob uses the \texttt{int} command and shifts the result (with some malice aforethought):
+
+\begin{minted}{latex}
+\begin{CAS}
+ h = int(dh,x) + 10
+\end{CAS}
+\end{minted}
+\begin{CAS}
+ h = int(dh,x) + 10
+\end{CAS}
+
+Bob is curious to know the value of $h$. So he uses \mintinline{latex}{\print{h}} to produce:
+\begin{codebox}
+\begin{minted}[fontsize=\small]{latex}
+\[ \print{h} \]
+\end{minted}
+ \tcblower
+\[\print{h} \]
+\end{codebox}
+This isn't exactly what Bob had in mind. It occurs to Bob that he may need to simplify the expression $h$, so he tries:
+
+\begin{codebox}
+\begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ h = simplify(int(dh,x)+10)
+\end{CAS}
+\[ \print{h} \]
+\end{minted}
+ \tcblower
+\begin{CAS}
+ h = simplify(h)
+\end{CAS}
+\[\print{h} \]
+\end{codebox}
+
+That's more like it! Now, Bob wants to find the roots to $dh$. Bob uses the \texttt{roots} command to do this:
+
+\begin{minted}{latex}
+\begin{CAS}
+ r = roots(dh)
+\end{CAS}
+\end{minted}
+\begin{CAS}
+ r = roots(dh)
+\end{CAS}
+
+But then Bob wonders to himself, ``How do I actually retrieve the roots of $dh$ from \texttt{luacas}?'' The assignment \mintinline{lua}{r = roots(dh)} stores the roots of the polynomial $dh$ in a table named \texttt{r}:
+
+\begin{codebox}[]
+\begin{minted}[fontsize=\small]{latex}
+\[ \print{r[1]}, \quad \print{r[2]}, \quad \print{r[3]}, \quad\print{r[4]} \]
+\end{minted}
+ \tcblower
+ \begin{CAS}
+ r = roots(dh)
+ \end{CAS}
+ \[ \print{r[1]}, \quad \print{r[2]}, \quad \print{r[3]}, \quad \print{r[4]} \]
+\end{codebox}
+If Bob truly wants to print the entire list \texttt{r}, Bob can use the \mintinline{latex}{\lprint} (\textbf{l}ist \textbf{print}) command:
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\[ \left\{ \lprint{r} \right\} \]
+ \end{minted}
+ \tcblower
+ \[ \left\{ \lprint{r} \right\} \]
+\end{codebox}
+Splendid! Bob would now like to evaluate the function $h$ at these roots (for these are the local max/min values of $h$). Here's Bob's first thought:
+
+\begin{codebox}
+\begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ v = simplify(substitute({[x]=r[1]},h))
+\end{CAS}
+\[ \print{v} \]
+\end{minted}
+ \tcblower
+ \begin{CAS}
+ v = simplify(substitute({[x]=r[1]},h))
+ \end{CAS}
+ \[ \print{v} \]
+\end{codebox}
+
+What the heck?! Bob is (understandably) confused. But here's where Bob learns a valuable lesson\dots
+
+\subsubsection{A brief interlude: Lua numbers vs \texttt{luacas Integers}}
+
+The \LaTeX{} environment \mintinline{latex}{\begin{CAS}..\end{CAS}} is really a glorified Lua environment. The ``glory'' comes in how the contents of the environment are parsed in a special manner to make interacting with the CAS (mostly) easy. Bob has encountered a situation where that interaction is not as easy as we'd like.
+
+For comparison, consider the following:
+\begin{multicols}{2}
+Here's some code using the \mintinline{latex}{\begin{CAS}..\end{CAS}}:
+\begin{codebox}\small
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('y')
+ a = 1
+ b = y+a
+\end{CAS}
+\[ \print{b} \]
+\end{minted}
+ \tcblower
+ \begin{CAS}
+ vars('y')
+ a = 1
+ b = y+a
+ \end{CAS}
+ \[ \print{b} \]
+\end{codebox}
+Here's that same code but using \mintinline{latex}{\directlua} instead:
+\begin{codebox}\small
+\begin{minted}[fontsize=\small]{latex}
+\directlua{
+ vars('y')
+ a = Integer(1)
+ b = y+a
+}
+\[ \print{b} \]
+\end{minted}
+ \tcblower
+ \directlua{
+ vars('y')
+ a = Integer(1)
+ b = y+a
+}
+\[ \print{b} \]
+\end{codebox}
+\end{multicols}
+The essential difference being:
+\begin{itemize}
+ \item Using \mintinline{latex}{\begin{CAS}..\end{CAS}}, a parser automatically interprets any digit strings as an \texttt{Integer}; this is a special class defined within the bowels of \texttt{luacas}. Ultimately, it allows for us to define things like the addition of an \texttt{Integer} and an \texttt{Expression} (in this case, the result is a new \texttt{Expression}) as well as arbitrary precision arithmetic.
+ \item Using \mintinline{latex}{\directlua}, there is no parsing, so the user (aka Bob) is responsible for telling \texttt{luacas} what to interpret as an \texttt{Integer} versus what to interpret as a normal Lua \texttt{number}.
+\end{itemize}
+Generally speaking, we like what the parser in \mintinline{latex}{\begin{CAS}..\end{CAS}} does: it keeps us from having to wrap all integers in \texttt{Integer(..)} (among other things). But the price we pay is that the parser indiscriminately wraps \emph{all} (or rather, most) digit strings in \texttt{Integer(..)}. This causes a problem in the following line in Bob's code:
+\begin{minted}{lua}
+v = simplify(substitute({[x]=r[1]},h))
+\end{minted}
+The parser sees \mintinline{lua}{r[1]} and interprets \texttt{1} as \texttt{Integer(1)} -- but \mintinline{lua}{r[Integer(1)]} is \texttt{nil}, so no substitution is performed.
+
+The good news is that, excluding the annoyance between \texttt{Integer} and Lua number, interacting with the CAS via \mintinline{latex}{\directlua} is not much different than interacting with it via \mintinline{latex}{\begin{CAS}..\end{CAS}}.
+
+\subsubsection*{Back to the tutorial...}
+
+After that enlightening interlude, Bob realizes that some care needs to be taken when constructing tables. Here's a solution from within \mintinline{latex}{\begin{CAS}..\end{CAS}}:
+
+\begin{codebox}[]
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ r = ZTable(r)
+ v = ZTable()
+ for i in range(1, 4) do
+ v[i] = simplify(substitute({[x]=r[i]},h))
+ end
+\end{CAS}
+\[ \left\{ \lprint{v} \right\} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ r = ZTable(r)
+ v = ZTable()
+ for i in range(1, 4) do
+ v[i] = simplify(substitute({[x]=r[i]},h))
+ end
+ \end{CAS}
+ \[ \left\{ \lprint{v} \right\} \]
+\end{codebox}
+
+The function \mintinline{lua}{ZTable()} sets indices appropriately for use within \mintinline{latex}{\begin{CAS}..\end{CAS}} while the function \mintinline{lua}{range()} protects the bounds of the for-loop. Alternatively, Bob can make tables directly within \mintinline{latex}{\directlua} (or \mintinline{latex}{\luaexec} from the \texttt{luacode} package) using whatever Lua syntax pleases him:
+
+\begin{codebox}[]
+\begin{minted}[fontsize=\small]{latex}
+\directlua{
+ v = {}
+ for i=1,4 do
+ table.insert(v,simplify(substitute({[x]=r[i]},h)))
+ end}
+\[ \left\{ \lprint{v} \right\} \]
+\end{minted}
+ \tcblower
+ \directlua{
+ v = {}
+ for i=1,4 do
+ table.insert(v,simplify(substitute({[x]=r[i]},h)))
+ end
+}
+\[ \left\{ \lprint{v} \right\} \]
+\end{codebox}
+Great! But still; Bob doesn't want to just pretty-print the roots of $dh$ (or the values that $h$ takes at those roots). Bob is determined to plot the results -- he wants to hammer home the point that the roots of $dh$ point to the local extrema of $h$.
+
+Luckily, Bob is familiar with some of the fantastic graphics tools in the \LaTeX{} ecosystem, like \texttt{pgfplots} and \texttt{asymptote}. But then Bob begins to wonder, ``How can I yoink results out of \texttt{luacas} so that I may yeet them into something like \texttt{pgfplots}?''
+Bob is delighted to find the following commands: \mintinline{latex}{\fetch} and \mintinline{latex}{\store}.
+
+Whereas the \mintinline{latex}{\print} command relies on the \texttt{luacas} method \mintinline{lua}{tolatex()}, the commands \mintinline{latex}{\fetch} and \mintinline{latex}{\store} rely on the \texttt{luacas} function \mintinline{lua}{tostring()}. Bob can view the output of \mintinline{lua}{tostring()} using the \mintinline{latex}{\vprint} command ({\bf v}erbatim {\bf print}). For example, \mintinline{latex}{\vprint{h}} produces:
+\vprint{h}
+This is more-or-less what Bob wants -- but he doesn't want the verbatim output printed to his document, Bob just wants the contents of \mintinline{lua}{tostring(h)}. Here's where \mintinline{latex}{\fetch} comes in. The command \mintinline{latex}{\fetch{h}} is equivalent to:
+\begin{minted}{latex}
+ \directlua{
+ tex.print(tostring(h))
+ }
+\end{minted}
+For comparison, the command \mintinline{latex}{\print{h}} is equivalent to:
+\begin{minted}{latex}
+ \directlua{
+ tex.print(h:tolatex())
+ }
+\end{minted}
+For Bob's purposes, \mintinline{latex}{\fetch{h}} is exactly what he needs:
+
+\begin{codebox}\small
+\begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{tikzpicture}[scale=0.9]
+ \begin{axis}[legend pos = north west]
+ \addplot [domain=-3.5:1.5,samples=100]
+ {\fetch{h}};
+ \addlegendentry{$f$};
+ \addplot[densely dashed]
+ [domain=-3.25:1.25,samples=100]
+ {\fetch{dh}};
+ \addlegendentry{$df/dx$};
+ \addplot[gray,dashed,thick]
+ [domain=-3.5:1.5] {0};
+ \end{axis}
+\end{tikzpicture}
+\end{minted}
+\tcblower
+\begin{tikzpicture}[scale=0.9]
+ \begin{axis}[legend pos = north west]
+ \addplot [domain=-3.5:1.5,samples=100] {\fetch{h}};
+ \addlegendentry{$f$};
+ \addplot[densely dashed]
+ [domain=-3.25:1.25,samples=100] {\fetch{dh}};
+ \addlegendentry{$df/dx$};
+ \addplot[gray,dashed,thick]
+ [domain=-3.5:1.5] {0};
+ \end{axis}
+\end{tikzpicture}
+\end{codebox}
+Alternatively, Bob could use \mintinline{latex}{\store}. The \mintinline{latex}{\store} command will \emph{fetch} the contents of its mandatory argument and store it in a macro of the same name.
+\begin{minted}{latex}
+\store{h}
+\store{dh}
+\end{minted}
+Now the macros \mintinline{latex}{\h} and \mintinline{latex}{\dh} can be used in place of \mintinline{latex}{\fetch{h}} and \mintinline{latex}{\fetch{dh}}, respectively. An optional argument can be used to store contents in a macro under a different name. This is useful for situations like the following:
+\begin{minted}{latex}
+\store{r[1]}[rootone]
+\end{minted}
+Now \mintinline{latex}{\rootone} can be used in place of \mintinline{latex}{\fetch{r[1]}}. But Bob wants to fetch all the values stored in \texttt{r} (and \texttt{v}, for that matter). In this case, Bob can use:
+\begin{minted}{latex}
+\store{r}
+\store{v}
+\end{minted}
+The command \mintinline{latex}{\store{r}} is equivalent to:
+\begin{minted}{latex}
+\def\r{{ \fetch{r[1]}, \fetch{r[2]}, \fetch{r[3]}, \fetch{r[4]} }}
+\end{minted}
+The contents of the \LaTeX{} macro \mintinline{latex}{\r} can be accessed with \mintinline{latex}{\pgfmathsetmacro}. For example:
+
+\begin{codebox}
+\begin{minted}[fontsize=\small,numbersep=6pt,linenos]{latex}
+\begin{tikzpicture}[scale=0.6]
+ \draw [dashed,latex-latex]
+ (-7,0) -- (4,0);
+ \foreach \k in {0,1,2,3}{
+ \pgfmathsetmacro\a{\r[\k]}
+ \draw (\a,0) circle (\a);
+ }
+ \foreach \x in {-6,...,3}{
+ \draw[fill,orange]
+ (\x,0) circle (2pt)
+ node[below] {\footnotesize$\x$};
+ }
+\end{tikzpicture}
+\end{minted}
+ \tcblower
+ \store{r}
+ \begin{center}
+ \begin{tikzpicture}[scale=0.65]
+ \draw [dashed,latex-latex] (-7,0) -- (4,0);
+ \foreach \k in {0,1,2,3}{
+ \pgfmathsetmacro\a{\r[\k]}
+ \draw (\a,0) circle (\a);
+ }
+ \foreach \x in {-6,...,3}{
+ \draw[fill,orange] (\x,0) circle (2pt)
+ node[below] {\footnotesize$\x$};
+ }
+ \end{tikzpicture}
+\end{center}
+\end{codebox}
+
+Alternatively, Bob could avoid the call to \mintinline{latex}{\pgfmathsetmacro} by replacing lines 5-6 in the above code with the slightly more verbose:
+
+\begin{minted}{latex}
+ \draw ({\fetch{r[\k]}},0) circle (\fetch{r[\k]});
+\end{minted}
+
+Alternatively still, Bob could appeal directly to the \mintinline{lua}{tostring()} function in \texttt{luacas} and iterate over tables like \texttt{r} using Lua itself. This can often be a simpler solution (particularly when working within \mintinline{latex}{\begin{axis}..\end{axis}}), and it is exactly what Bob does in his complete project shared below:
+\begin{codebox}[frame hidden,breakable]
+\begin{minted}[breaklines,fontsize=\small]{latex}
+Consider the function $f(x)$ defined by:
+\begin{CAS}
+ vars('x')
+ f = x^2+2*x-2
+ g = x^2-1
+ subs = {[x] = f}
+ dh = expand(substitute(subs,g))
+ h = simplify(int(dh,x)+10)
+\end{CAS}
+$\displaystyle f(x) = \print{h}$.
+\begin{multicols}{2}
+ Note that:
+ \[ f'(x) = \print{dh}.\]
+ The roots to $f'(x)=0$ equation are:
+ \begin{CAS}
+ r = roots(dh)
+ \end{CAS}
+ \[ \left\{ \lprint{r} \right\} \]
+ Recall: $f'(x_0)$ measures the slope of the tangent line to $y= (x)$ at $x=x_0$. The values $r$ where $f'(r)=0$ correspond to places where the slope of the tangent line to $y=f(x)$ is horizontal (see the illustration). This gives us a method for identifying locations where the graph $y=f(x)$ attains a peak (local maximum) or a valley (local minimum).
+ \begin{CAS}
+ r = ZTable(r)
+ v = ZTable()
+ for i in range(1, 4) do
+ v[i] = simplify(substitute({[x]=r[i]},h))
+ end
+ \end{CAS}
+ \columnbreak
+ \store{h}\store{dh}
+ \begin{tikzpicture}[scale=0.95]
+ \begin{axis}[legend pos = north west]
+ \addplot [domain=-3.5:1.5,samples=100] {\h};
+ \addlegendentry{$f$};
+ \addplot[densely dashed] [domain=-3.25:1.25,samples=100] {\dh};
+ \addlegendentry{$df/dx$};
+ \addplot[gray,dashed,thick] [domain=-3.5:1.5] {0};
+ \luaexec{for i=1,4 do
+ tex.print("\\draw[fill=purple,purple]",
+ "(axis cs:{",tostring(r[i]),"},0) circle (1.5pt)",
+ "(axis cs:{",tostring(r[i]),"},{",tostring(v[i]),"}) circle (1.5pt)",
+ "(axis cs:{",tostring(r[i]),"},{",tostring(v[i]),"}) edge[dashed] (axis cs:{",tostring(r[i]),"},0);")
+ end}
+ \end{axis}
+ \end{tikzpicture}
+\end{multicols}
+\end{minted}
+\end{codebox}
+And here is Bob's completed project:
+\begin{tcolorbox}[colback=rosenavy!10,
+ colframe=rosenavy,
+ arc=1pt,
+ frame hidden]
+ {\bf Tutorial 2:} {\itshape A local max/min diagram for Bob}.
+ \vskip 0.2cm
+ Consider the function $f(x)$ defined by:
+ \begin{CAS}
+ vars('x')
+ f = x^2+2*x-2
+ g = x^2-1
+ subs = {[x] = f}
+ dh = expand(substitute(subs,g))
+ h = simplify(int(dh,x)+10)
+ \end{CAS}
+ $\displaystyle f(x) = \print{h}$.
+\begin{multicols}{2}
+ Note that:
+ \[ f'(x) = \print{dh}.\]
+ The roots to $f'(x)=0$ equation are:
+ \begin{CAS}
+ r = roots(dh)
+ \end{CAS}
+ \[ \left\{ \lprint{r} \right\} \]
+ Recall: $f'(x_0)$ measures the slope of the tangent line to $y=f(x)$ at $x=x_0$. The values $r$ where $f'(r)=0$ correspond to places where the slope of the tangent line to $y=f(x)$ is horizontal (see the illustration). This gives us a method for identifying locations where the graph $y=f(x)$ attains a peak (local maximum) or a valley (local minimum).
+ \begin{CAS}
+ r = ZTable(r)
+ v = ZTable()
+ for i in range(1, 4) do
+ v[i] = simplify(substitute({[x]=r[i]},h))
+ end
+ \end{CAS}
+ \columnbreak
+ \store{h}\store{dh}
+ \begin{tikzpicture}[scale=0.95]
+ \begin{axis}[legend pos = north west]
+ \addplot
+ [domain=-3.5:1.5,samples=100] {\h};
+ \addlegendentry{$f$};
+ \addplot[densely dashed]
+ [domain=-3.25:1.25,samples=100] {\dh};
+ \addlegendentry{$df/dx$};
+ \addplot[gray,dashed,thick]
+ [domain=-3.5:1.5] {0};
+ \luaexec{for i=1,4 do
+ tex.print("\\draw[fill=purple,purple]",
+ "(axis cs:{", tostring(r[i]) ,"},0) circle (1.5pt)",
+ "(axis cs:{", tostring(r[i]) ,"},{", tostring(v[i]), "}) circle (1.5pt)",
+ "(axis cs:{", tostring(r[i]) ,"},{", tostring(v[i]), "}) edge[dashed] (axis cs:{", tostring(r[i]) ,"},0);")
+ end}
+ \end{axis}
+ \end{tikzpicture}
+\end{multicols}
+\end{tcolorbox}
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/tutorial/tut3/demotut3.dat b/macros/luatex/latex/luacas/doc/tutorial/tut3/demotut3.dat
new file mode 100644
index 0000000000..95139afaf2
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/tutorial/tut3/demotut3.dat
@@ -0,0 +1,46 @@
+\parseforest{q}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {
+ font=\ttfamily,
+ rectangle,
+ rounded corners=1pt
+ },
+ where level=0{%
+ fill=orange!25
+ }{},
+ @\forestresult
+\end{forest}
+
+\begin{CAS}
+ r = diff(q,x,h)
+\end{CAS}
+\whatis{q} vs \whatis{r}
+
+\luaexec{if q.operation == BinaryOperation.ADD then
+ tex.print("I'm an \\texttt{ADD}")
+end}
+
+\luaexec{tex.print("I'm an order", r.degree, "derivative.")}
+
+\parseshrub{q}
+\begin{forest}
+ for tree = {draw,rectangle,rounded corners=1pt,fill=lightgray!20 font=\ttfamily}
+ @\shrubresult
+\end{forest}
+
+\parseshrub{q.expressions[1]}
+\begin{forest}
+ for tree = {draw,rectangle,
+ rounded corners=1pt,fill=lightgray!20,
+ font=\ttfamily, s sep=2cm}
+ @\shrubresult
+\end{forest}
+
+\parseshrub{r}
+\begin{forest}
+ for tree = {draw,rectangle,
+ rounded corners=1pt,fill=lightgray!20,
+ font=\ttfamily, s sep=1cm}
+ @\shrubresult
+\end{forest} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/doc/tutorial/tut3/tut3.tex b/macros/luatex/latex/luacas/doc/tutorial/tut3/tut3.tex
new file mode 100644
index 0000000000..1fda71a5a0
--- /dev/null
+++ b/macros/luatex/latex/luacas/doc/tutorial/tut3/tut3.tex
@@ -0,0 +1,575 @@
+\documentclass{article}
+
+\usepackage{luacas}
+\usepackage{amsmath}
+\usepackage{amssymb}
+
+\usepackage[margin=1in]{geometry}
+\usepackage[shortlabels]{enumitem}
+
+\usepackage{pgfplots}
+\pgfplotsset{compat=1.18}
+\usetikzlibrary{positioning,calc}
+\usepackage{forest}
+\usepackage{minted}
+\usemintedstyle{pastie}
+\usepackage[hidelinks]{hyperref}
+\usepackage{parskip}
+\usepackage{multicol}
+\usepackage[most]{tcolorbox}
+ \tcbuselibrary{xparse}
+\usepackage{microtype}
+
+\definecolor{rose}{RGB}{128,0,0}
+\definecolor{roseyellow}{RGB}{222,205,99}
+\definecolor{roseblue}{RGB}{167,188,214}
+\definecolor{rosenavy}{RGB}{79,117,139}
+\definecolor{roseorange}{RGB}{232,119,34}
+\definecolor{rosegreen}{RGB}{61,68,30}
+\definecolor{rosewhite}{RGB}{223,209,167}
+\definecolor{rosebrown}{RGB}{108,87,27}
+\definecolor{rosegray}{RGB}{84,88,90}
+
+\usepackage[
+backend=biber,
+style=numeric,
+]{biblatex}
+\addbibresource{sources.bib}
+
+\newtcolorbox{codebox}[1][sidebyside]{
+ enhanced,skin=bicolor,
+ #1,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle
+}
+
+\begin{document}
+
+\subsection{Tutorial 3: Adding Functionality}
+
+Charlie, like Alice and Bob, is also teaching calculus. Charlie likes Alice's examples and wants to try something similar. But Charlie would like to do more involved examples using rational functions. Accordingly, Charlie copy-and-pastes Alice's code:
+\begin{CAS}
+ vars('x','h')
+ f = 1/(x^2+1)
+ subs = {[x]=x+h}
+ q = (substitute(subs,f)-f)/h
+ q = expand(q)
+\end{CAS}
+\begin{minted}{latex}
+\begin{CAS}
+ vars('x','h')
+ f = 1/(x^2+1)
+ subs = {[x]=x+h}
+ q = (substitute(subs,f)-f)/h
+ q = expand(q)
+\end{CAS}
+\end{minted}
+Unfortunately, \mintinline{latex}{\[ q=\print{q} \]} produces:
+\[ q = \print{q} \]
+The \mintinline{lua}{simplify()} command doesn't seem to help either! What Charlie truly needs is to combine terms, i.e., Charlie needs to find a \emph{common denominator}. They're horrified to learn that no such functionality exists in this burgeoning package.
+
+So what's Charlie to do? They could put a feature request in, but they're concerned that the schlubs in charge of managing the package won't get around to it until who-knows-when. So Charlie decides to take matters into their own hands. Besides, looking for that silver lining, they'll learn a little bit about how \texttt{luacas} is structured.
+
+At the heart of any CAS is the idea of an \texttt{Expression}. Mathematically speaking, an \texttt{Expression} is a rooted tree. Luckily, this tree can be drawn using the (wonderful) \texttt{forest} package. In particular, the command \mintinline{latex}{\parseforest{q}} will scan the contents of the expression \texttt{q} and parse the results into a form compatible with the \texttt{forest} package; those results are saved in a macro named \mintinline{latex}{\forestresult}.
+
+\tcbsidebyside[
+ sidebyside adapt=right,
+ enhanced,skin=bicolor,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle,
+ frame hidden
+ ]{
+ \inputminted[
+ firstline = 1,
+ lastline = 13,
+ breaklines,
+ fontsize=\small
+ ]
+ {latex}
+ {demotut3.dat}}
+ {\parseforest{q}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {
+ font=\ttfamily,
+ rectangle,
+ rounded corners=1pt
+ },
+ where level=0{%
+ fill=orange!25
+ }{},
+ @\forestresult
+ \end{forest}}
+
+The root of the tree above is \texttt{ADD} since $q$ is, at its heart, the addition of two other expressions. Charlie wonders how they might check to see if a mystery \texttt{Expression} is an \texttt{ADD}? But this is putting the cart before the horse; Charlie should truly wonder how to check for the \emph{type} of \texttt{Expression} -- then they can worry about other attributes.
+
+Charlie can print the \texttt{Expression} type directly into their document using the \mintinline{latex}{\whatis} command:
+
+\begin{codebox}
+ \inputminted[
+ firstline = 15,
+ lastline = 18,
+ breaklines,
+ fontsize=\small
+ ]
+ {latex}
+ {demotut3.dat}
+ \tcblower
+ \begin{CAS}
+ r = diff(q,x,h)
+ \end{CAS}
+ \whatis{q} vs \whatis{r}
+\end{codebox}
+
+So \texttt{q} is a \texttt{BinaryOperation}? This strikes Charlie as a little strange. On the other hand, \texttt{q} is the result of a binary operation applied to two other expressions; so perhaps this makes a modicum of sense.
+
+At any rate, Charlie now knows, according to \texttt{luacas}, that \texttt{q} is of the \texttt{Expression}-type \whatis{q}. The actual operator that's used to form \texttt{q} is stored in the attribute \mintinline{lua}{q.operation}:
+
+\tcbsidebyside[
+ sidebyside adapt=right,
+ enhanced,skin=bicolor,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle,
+ frame hidden
+ ]{
+ \inputminted[
+ firstline = 20,
+ lastline = 22,
+ breaklines,
+ fontsize=\small
+ ]
+ {latex}
+ {demotut3.dat}}
+ {
+ \luaexec{
+ if q.operation == BinaryOperation.ADD then
+ tex.sprint("I'm an \\texttt{ADD}")
+ end
+ }}
+
+Of course, different \texttt{Expression} types have different attributes. For example, being a \texttt{DiffExpression}, \texttt{r} has the attribute \texttt{r.degree}:
+
+\tcbsidebyside[
+ sidebyside adapt=right,
+ enhanced,skin=bicolor,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle,
+ frame hidden
+ ]{
+ \inputminted[
+ firstline = 24,
+ lastline = 24,
+ breaklines,
+ fontsize=\small
+ ]
+ {latex}
+ {demotut3.dat}}
+ {
+ \luaexec{
+ tex.print("I'm an order", r.degree, "derivative.")
+ }}
+
+\texttt{BinaryOperation}s have several attributes, but the most important attribute for Charlie's purposes is \texttt{q.expressions}. In this case, \texttt{q.expressions} is a table with two entries; those two entries are precisely the \texttt{Expressions} whose sum forms \texttt{q}. In particular,
+
+\mintinline{latex}{\[ \print{q.expressions[1]} \qquad \text{and} \qquad \print{q.expressions[2]} \]}
+
+produces:
+\[ \print{q.expressions[1]} \qquad\text{and} \qquad \print{q.expressions[2]} \]
+
+The expression \texttt{q.expressions[1]} is another \texttt{BinaryOperation}. Instead of printing the entire expression tree (as we've done above), Charlie might be interested in the commands \mintinline{latex}{\parseshrub} and \mintinline{latex}{\shrubresult}:
+
+\tcbsidebyside[
+ sidebyside adapt=right,
+ enhanced,skin=bicolor,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle,
+ frame hidden
+ ]{\small
+ \inputminted[
+ firstline = 26,
+ lastline = 30,
+ breaklines
+ ]
+ {latex}
+ {demotut3.dat}}
+ {\parseshrub{q.expressions[1]}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {draw,rectangle,rounded corners=1pt,fill=lightgray!20,font=\ttfamily, s sep=1.5cm}
+ @\shrubresult
+ \end{forest}
+}
+
+The ``shrub'' is essentially the first level of the ``forest'', but with some extra information concerning attributes. For contrast, here's the result of \mintinline{latex}{\parseshrub} and \mintinline{latex}{\shrubresult} applied to \texttt{r}, the \texttt{DiffExpression} defined above.
+
+\tcbsidebyside[
+ sidebyside adapt=right,
+ enhanced,skin=bicolor,
+ arc=1pt,
+ colframe=brown,
+ colback=brown!15,colbacklower=white,
+ boxrule=1pt,
+ notitle,
+ frame hidden
+ ]{\small
+ \inputminted[
+ firstline = 40,
+ lastline = 46,
+ breaklines
+ ]
+ {latex}
+ {demotut3.dat}}
+ {\parseshrub{r}
+ \bracketset{action character = @}
+ \begin{forest}
+ for tree = {draw,rectangle,rounded corners=1pt,
+ fill=lightgray!20,font=\ttfamily, s sep=1.5cm}
+ @\shrubresult
+ \end{forest}
+}
+The attribute \mintinline{lua}{r.degree} returns the size of the table stored in \mintinline{lua}{r.symbols} which, in turn, records the variables (and order from left-to-right) with which to differentiate the expression stored in \mintinline{lua}{r.expression}.
+
+Now that Charlie knows the basics of how \texttt{luacas} is structured, they're ready to try their hand at adding some functionality.
+
+
+\begin{multicols}{2}
+First, Charlie decides to up the complexity of their expression {\ttfamily f} so that they have something more general to work with:
+\begin{CAS}
+ vars('x','h')
+ f = x/(x^2+1)
+ subs = {[x]=x+h}
+ q = (substitute(subs,f)-f)/h
+\end{CAS}
+\begin{minted}{latex}
+\begin{CAS}
+ vars('x','h')
+ f = x/(x^2+1)
+ subs = {[x]=x+h}
+ q = (substitute(subs,f)-f)/h
+\end{CAS}
+\end{minted}
+Next, Charlie decides to print the un\texttt{expand}ed expression tree for $q$ to help give them a clear view (see right).
+
+\begin{center}
+\parseforest{q}
+\bracketset{action character = @}
+\begin{forest}
+ for tree = {
+ font=\ttfamily}
+ @\forestresult
+\end{forest}
+\end{center}
+\end{multicols}
+
+Charlie now wants to write their own function for combining expressions like this into a single denominator. It's probably best that Charlie writes this function in a separate file, say \texttt{myfile.lua}. Like most functions in \texttt{luacas}, Charlie defines this function as a \emph{method} applied to an \texttt{Expression}:
+
+\begin{minted}[linenos]{lua}
+function Expression:mycombine()
+\end{minted}
+
+Next, Charlie declares some local variables to identify appropriate numerators and denominators:
+\begin{minted}[linenos,firstnumber=2]{lua}
+ local a = self.expressions[1].expressions[1].expressions[1]
+ local b = self.expressions[1].expressions[1].expressions[2]
+ local c = self.expressions[1].expressions[2].expressions[1]
+ local d = self.expressions[1].expressions[2].expressions[2]
+\end{minted}
+
+So, for example, $a = x+h$, $b = (x+h)^2+1$, and so on. Charlie now forms the numerator and denominator, and returns the function:
+
+\begin{minted}[linenos,firstnumber=6]{lua}
+ local numerator = a*d-b*c
+ local denominator = self.expressions[2]*b*d
+ return numerator/denominator
+end
+\end{minted}
+
+\luaexec{
+ function Expression:mycombine()
+ local a = self.expressions[1].expressions[1].expressions[1]
+ local b = self.expressions[1].expressions[1].expressions[2]
+ local c = self.expressions[1].expressions[2].expressions[1]
+ local d = self.expressions[1].expressions[2].expressions[2]
+ local numerator = a*d-b*c
+ local denominator = self.expressions[2]*b*d
+ return numerator/denominator
+ end
+}
+Now Charlie only needs to ensure that \texttt{myfile.lua} is in a location visible to their TeX installation (e.g. in the current working folder). Charlie can then produce the following:
+
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\directlua{dofile('myfile.lua')}
+\begin{CAS}
+ q = q:mycombine()
+\end{CAS}
+\[ \print{q} \]
+ \end{minted}
+ \tcblower
+ \begin{CAS}
+ q = q:mycombine()
+ \end{CAS}
+ \[ \print{q}\]
+\end{codebox}
+
+Charlie wants to simplify the numerator (but not the denominator). So they decide to write another function in \texttt{myfile.lua} that does precisely this:
+
+\begin{minted}[linenos,firstnumber = 11]{lua}
+function Expression:mysimplify()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = simplify(a)
+ return a/b
+end
+\end{minted}
+\luaexec{
+function Expression:simplifynum()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = simplify(a)
+ return a/b
+end
+}
+Now Charlie has:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ q = q:mysimplify()
+\end{CAS}
+\[ \print{q} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ q = q:simplifynum()
+\end{CAS}
+\[ \print{q} \]
+\end{codebox}
+Finally, Charlie wants to factor the numerator. So Charlie writes the following final function to \texttt{myfile.lua}:
+\begin{minted}[linenos,firstnumber = 18]{lua}
+function Expression:myfactor()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = factor(a)
+ return a/b
+end
+\end{minted}
+
+\luaexec{
+function Expression:factornum()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = factor(a)
+ return a/b
+end
+}
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+After factoring the numerator:
+\begin{CAS}
+ q = q:myfactor()
+\end{CAS}
+\[ \print{q} \]
+And then simplifying:
+\begin{CAS}
+ q = simplify(q)
+\end{CAS}
+\[ \print{q} \]
+\end{minted}
+\tcblower
+After factoring the numerator:
+\begin{CAS}
+ q = q:factornum()
+\end{CAS}
+\[ \print{q} \]
+And then simplifying:
+\begin{CAS}
+ q = simplify(q)
+\end{CAS}
+\[ \print{q} \]
+\end{codebox}
+Armed with their custom functions \texttt{mycombine}, \texttt{mysimplify}, and \texttt{myfactor}, Charlie can write examples just like Alice's examples, but using rational functions instead.
+
+Of course, the schlubs that manage this package feel for Charlie, and recognize that there are other situations in which folks may want to combine a sum of rational expressions into a single rational expression. Accordingly, there is indeed a \texttt{combine} command included in \texttt{luacas} that performs this task:
+\begin{codebox}
+ \begin{minted}[fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','y','z')
+ a = y/z
+ b = z/x
+ c = x/y
+ d = combine(a+b+c)
+\end{CAS}
+\[ \print{a+b+c} = \print{d} \]
+\end{minted}
+\tcblower
+\begin{CAS}
+ vars('x','y','z')
+ a = y/z
+ b = z/x
+ c = x/y
+ d = combine(a+b+c)
+\end{CAS}
+\[ \print{a+b+c} = \print{d} \]
+\end{codebox}
+
+Here's Charlie's complete code (but using \mintinline{latex}{\directlua}) instead:
+
+\begin{codebox}[frame hidden,breakable]
+\begin{minted}[breaklines,fontsize=\small]{latex}
+\begin{CAS}
+ vars('x','h')
+ f = x/(x^2+1)
+\end{CAS}
+Let $f(x) = \print{f}$. We wish to compute the derivative of $f(x)$ at $x$ using the limit definition of the derivative. Toward that end, we start with the appropriate difference quotient:
+\begin{CAS}
+ subs = {[x] = x+h}
+ q = (f:substitute(subs) - f)/h
+\end{CAS}
+\directlua{
+\end{minted}
+\end{codebox}
+And now the Lua code:
+\begin{codebox}[frame hidden, breakable]
+\begin{minted}[breaklines,fontsize=\small]{lua}
+ function Expression:mycombine()
+ local a = self.expressions[1].expressions[1].expressions[1]
+ local b = self.expressions[1].expressions[1].expressions[2]
+ local c = self.expressions[1].expressions[2].expressions[1]
+ local d = self.expressions[1].expressions[2].expressions[2]
+ local numerator = a*d-b*c
+ local denominator = self.expressions[2]*b*d
+ return numerator/denominator
+ end
+ function Expression:mysimplify()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = simplify(a)
+ return a/b
+ end
+ function Expression:myfactor()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = factor(a)
+ return a/b
+ end
+\end{minted}
+\end{codebox}
+And now back to the \LaTeX{} code:
+\begin{codebox}[frame hidden,breakable]
+\begin{minted}[breaklines,fontsize=\small]{latex}
+}
+\[ \begin{aligned}
+ \print{q} &=
+ \begin{CAS}
+ q = q:mycombine()
+ \end{CAS}
+ \print{q}& &\text{get a common denominator} \\
+ &=
+ \begin{CAS}
+ q = q:mysimplify()
+ \end{CAS}
+ \print{q}& &\text{simplify the numerator} \\
+ &=
+ \begin{CAS}
+ q = q:myfactor()
+ \end{CAS}
+ \print{q} & &\text{factor numerator} \\
+ &=
+ \begin{CAS}
+ q = simplify(q)
+ \end{CAS}
+ \print{q}& &\text{cancel the $h$s} \\
+ &\xrightarrow{h\to 0}
+ \begin{CAS}
+ subs = {[h] = 0}
+ q = substitute(subs,q):autosimplify()
+ \end{CAS}
+ \print{q}& &\text{take limit.}
+\end{aligned} \]
+\end{minted}
+\end{codebox}
+And here is Charlie's completed project:
+\begin{tcolorbox}[colback=roseorange!10,
+ colframe=roseorange,
+ arc=1pt,
+ frame hidden]
+{\bf Tutorial 3:} {\itshape A limit definition of the derivative for Charlie.}\vskip 0.2cm
+
+\begin{CAS}
+ vars('x','h')
+ f = x/(x^2+1)
+\end{CAS}
+Let $f(x) = \print{f}$. We wish to compute the derivative of $f(x)$ at $x$ using the limit definition of the derivative. Toward that end, we start with the appropriate difference quotient:
+\begin{CAS}
+ subs = {[x] = x+h}
+ q = (f:substitute(subs) - f)/h
+\end{CAS}
+\directlua{
+ function Expression:mycombine()
+ local a = self.expressions[1].expressions[1].expressions[1]
+ local b = self.expressions[1].expressions[1].expressions[2]
+ local c = self.expressions[1].expressions[2].expressions[1]
+ local d = self.expressions[1].expressions[2].expressions[2]
+ local numerator = a*d-b*c
+ local denominator = self.expressions[2]*b*d
+ return numerator/denominator
+ end
+ function Expression:mysimplify()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = simplify(a)
+ return a/b
+ end
+ function Expression:myfactor()
+ local a = self.expressions[1]
+ local b = self.expressions[2]
+ a = factor(a)
+ return a/b
+ end
+}
+\[ \begin{aligned}
+ \print{q} &=
+ \begin{CAS}
+ q = q:mycombine()
+ \end{CAS}
+ \print{q}& &\text{get a common denominator} \\
+ &=
+ \begin{CAS}
+ q = q:mysimplify()
+ \end{CAS}
+ \print{q}& &\text{simplify the numerator} \\
+ &=
+ \begin{CAS}
+ q = q:myfactor()
+ \end{CAS}
+ \print{q} & &\text{factor numerator} \\
+ &=
+ \begin{CAS}
+ q = simplify(q)
+ \end{CAS}
+ \print{q}& &\text{cancel the $h$s} \\
+ &\xrightarrow{h\to 0}
+ \begin{CAS}
+ subs = {[h] = 0}
+ q = substitute(subs,q):autosimplify()
+ \end{CAS}
+ \print{q}& &\text{take limit.}
+\end{aligned} \]
+\end{tcolorbox}
+
+\end{document} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/_lib/inspect.lua b/macros/luatex/latex/luacas/tex/_lib/inspect.lua
new file mode 100644
index 0000000000..dedd91f248
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/_lib/inspect.lua
@@ -0,0 +1,335 @@
+local inspect ={
+ _VERSION = 'inspect.lua 3.1.0',
+ _URL = 'http://github.com/kikito/inspect.lua',
+ _DESCRIPTION = 'human-readable representations of tables',
+ _LICENSE = [[
+ MIT LICENSE
+
+ Copyright (c) 2013 Enrique García Cota
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ]]
+}
+
+local tostring = tostring
+
+inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
+inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
+
+local function rawpairs(t)
+ return next, t, nil
+end
+
+-- Apostrophizes the string if it has quotes, but not aphostrophes
+-- Otherwise, it returns a regular quoted string
+local function smartQuote(str)
+ if str:match('"') and not str:match("'") then
+ return "'" .. str .. "'"
+ end
+ return '"' .. str:gsub('"', '\\"') .. '"'
+end
+
+-- \a => '\\a', \0 => nil
+local shortControlCharEscapes = {
+ ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
+ ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\127"] = "\\127",
+}
+local longControlCharEscapes = {["\127"]="\127"} -- \a => nil, \0 => \000, 31 => \031
+for i=0, 31 do
+ local ch = string.char(i)
+ if not shortControlCharEscapes[ch] then
+ shortControlCharEscapes[ch] = "\\"..i
+ longControlCharEscapes[ch] = string.format("\\%03d", i)
+ end
+end
+--longControlCharEscapes["\127"]="\\127"
+
+local function escape(str)
+ return (str:gsub("\\", "\\\\")
+ :gsub("(%c)%f[0-9]", longControlCharEscapes)
+ :gsub("%c", shortControlCharEscapes))
+end
+
+local function isIdentifier(str)
+ return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
+end
+
+local function isSequenceKey(k, sequenceLength)
+ return type(k) == 'number'
+ and 1 <= k
+ and k <= sequenceLength
+ and math.floor(k) == k
+end
+
+local defaultTypeOrders = {
+ ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
+ ['function'] = 5, ['userdata'] = 6, ['thread'] = 7
+}
+
+local function sortKeys(a, b)
+ local ta, tb = type(a), type(b)
+
+ -- strings and numbers are sorted numerically/alphabetically
+ if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
+
+ local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
+ -- Two default types are compared according to the defaultTypeOrders table
+ if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
+ elseif dta then return true -- default types before custom ones
+ elseif dtb then return false -- custom types after default ones
+ end
+
+ -- custom types are sorted out alphabetically
+ return ta < tb
+end
+
+-- For implementation reasons, the behavior of rawlen & # is "undefined" when
+-- tables aren't pure sequences. So we implement our own # operator.
+local function getSequenceLength(t)
+ local len = 1
+ local v = rawget(t,len)
+ while v ~= nil do
+ len = len + 1
+ v = rawget(t,len)
+ end
+ return len - 1
+end
+
+local function getNonSequentialKeys(t)
+ local keys, keysLength = {}, 0
+ local sequenceLength = getSequenceLength(t)
+ for k,_ in rawpairs(t) do
+ if not isSequenceKey(k, sequenceLength) then
+ keysLength = keysLength + 1
+ keys[keysLength] = k
+ end
+ end
+ table.sort(keys, sortKeys)
+ return keys, keysLength, sequenceLength
+end
+
+local function countTableAppearances(t, tableAppearances)
+ tableAppearances = tableAppearances or {}
+
+ if type(t) == 'table' then
+ if not tableAppearances[t] then
+ tableAppearances[t] = 1
+ for k,v in rawpairs(t) do
+ countTableAppearances(k, tableAppearances)
+ countTableAppearances(v, tableAppearances)
+ end
+ countTableAppearances(getmetatable(t), tableAppearances)
+ else
+ tableAppearances[t] = tableAppearances[t] + 1
+ end
+ end
+
+ return tableAppearances
+end
+
+local copySequence = function(s)
+ local copy, len = {}, #s
+ for i=1, len do copy[i] = s[i] end
+ return copy, len
+end
+
+local function makePath(path, ...)
+ local keys = {...}
+ local newPath, len = copySequence(path)
+ for i=1, #keys do
+ newPath[len + i] = keys[i]
+ end
+ return newPath
+end
+
+local function processRecursive(process, item, path, visited)
+ if item == nil then return nil end
+ if visited[item] then return visited[item] end
+
+ local processed = process(item, path)
+ if type(processed) == 'table' then
+ local processedCopy = {}
+ visited[item] = processedCopy
+ local processedKey
+
+ for k,v in rawpairs(processed) do
+ processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
+ if processedKey ~= nil then
+ processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
+ end
+ end
+
+ local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
+ if type(mt) ~= 'table' then mt = nil end -- ignore not nil/table __metatable field
+ setmetatable(processedCopy, mt)
+ processed = processedCopy
+ end
+ return processed
+end
+
+
+
+-------------------------------------------------------------------
+
+local Inspector = {}
+local Inspector_mt = {__index = Inspector}
+
+function Inspector:puts(...)
+ local args = {...}
+ local buffer = self.buffer
+ local len = #buffer
+ for i=1, #args do
+ len = len + 1
+ buffer[len] = args[i]
+ end
+end
+
+function Inspector:down(f)
+ self.level = self.level + 1
+ f()
+ self.level = self.level - 1
+end
+
+function Inspector:tabify()
+ self:puts(self.newline, string.rep(self.indent, self.level))
+end
+
+function Inspector:alreadyVisited(v)
+ return self.ids[v] ~= nil
+end
+
+function Inspector:getId(v)
+ local id = self.ids[v]
+ if not id then
+ local tv = type(v)
+ id = (self.maxIds[tv] or 0) + 1
+ self.maxIds[tv] = id
+ self.ids[v] = id
+ end
+ return tostring(id)
+end
+
+function Inspector:putKey(k)
+ if isIdentifier(k) then return self:puts(k) end
+ self:puts("[")
+ self:putValue(k)
+ self:puts("]")
+end
+
+function Inspector:putTable(t)
+ if t == inspect.KEY or t == inspect.METATABLE then
+ self:puts(tostring(t))
+ elseif self:alreadyVisited(t) then
+ self:puts('<table ', self:getId(t), '>')
+ elseif self.level >= self.depth then
+ self:puts('{...}')
+ else
+ if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
+
+ local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t)
+ local mt = getmetatable(t)
+
+ self:puts('{')
+ self:down(function()
+ local count = 0
+ for i=1, sequenceLength do
+ if count > 0 then self:puts(',') end
+ self:puts(' ')
+ self:putValue(t[i])
+ count = count + 1
+ end
+
+ for i=1, nonSequentialKeysLength do
+ local k = nonSequentialKeys[i]
+ if count > 0 then self:puts(',') end
+ self:tabify()
+ self:putKey(k)
+ self:puts(' = ')
+ self:putValue(t[k])
+ count = count + 1
+ end
+
+ if type(mt) == 'table' then
+ if count > 0 then self:puts(',') end
+ self:tabify()
+ self:puts('<metatable> = ')
+ self:putValue(mt)
+ end
+ end)
+
+ if nonSequentialKeysLength > 0 or type(mt) == 'table' then -- result is multi-lined. Justify closing }
+ self:tabify()
+ elseif sequenceLength > 0 then -- array tables have one extra space before closing }
+ self:puts(' ')
+ end
+
+ self:puts('}')
+ end
+end
+
+function Inspector:putValue(v)
+ local tv = type(v)
+
+ if tv == 'string' then
+ self:puts(smartQuote(escape(v)))
+ elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
+ tv == 'cdata' or tv == 'ctype' then
+ self:puts(tostring(v))
+ elseif tv == 'table' then
+ self:putTable(v)
+ else
+ self:puts('<', tv, ' ', self:getId(v), '>')
+ end
+end
+
+-------------------------------------------------------------------
+
+function inspect.inspect(root, options)
+ options = options or {}
+
+ local depth = options.depth or math.huge
+ local newline = options.newline or '\n'
+ local indent = options.indent or ' '
+ local process = options.process
+
+ if process then
+ root = processRecursive(process, root, {}, {})
+ end
+
+ local inspector = setmetatable({
+ depth = depth,
+ level = 0,
+ buffer = {},
+ ids = {},
+ maxIds = {},
+ newline = newline,
+ indent = indent,
+ tableAppearances = countTableAppearances(root)
+ }, Inspector_mt)
+
+ inspector:putValue(root)
+
+ return table.concat(inspector.buffer)
+end
+
+setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
+
+return inspect
+
diff --git a/macros/luatex/latex/luacas/tex/_lib/pepperfish.lua b/macros/luatex/latex/luacas/tex/_lib/pepperfish.lua
new file mode 100644
index 0000000000..4717543c4b
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/_lib/pepperfish.lua
@@ -0,0 +1,629 @@
+---@diagnostic disable: param-type-mismatch, lowercase-global, need-check-nil, assign-type-mismatch
+--[[
+
+== Introduction ==
+
+ Note that this requires os.clock(), debug.sethook(),
+ and debug.getinfo() or your equivalent replacements to
+ be available if this is an embedded application.
+
+ Example usage:
+
+ profiler = newProfiler()
+ profiler:start()
+
+ < call some functions that take time >
+
+ profiler:stop()
+
+ local outfile = io.open( "profile.txt", "w+" )
+ profiler:report( outfile )
+ outfile:close()
+
+== Optionally choosing profiling method ==
+
+The rest of this comment can be ignored if you merely want a good profiler.
+
+ newProfiler(method, sampledelay):
+
+If method is omitted or "time", will profile based on real performance.
+optionally, frequency can be provided to control the number of opcodes
+per profiling tick. By default this is 100000, which (on my system) provides
+one tick approximately every 2ms and reduces system performance by about 10%.
+This can be reduced to increase accuracy at the cost of performance, or
+increased for the opposite effect.
+
+If method is "call", will profile based on function calls. Frequency is
+ignored.
+
+
+"time" may bias profiling somewhat towards large areas with "simple opcodes",
+as the profiling function (which introduces a certain amount of unavoidable
+overhead) will be called more often. This can be minimized by using a larger
+sample delay - the default should leave any error largely overshadowed by
+statistical noise. With a delay of 1000 I was able to achieve inaccuray of
+approximately 25%. Increasing the delay to 100000 left inaccuracy below my
+testing error.
+
+"call" may bias profiling heavily towards areas with many function calls.
+Testing found a degenerate case giving a figure inaccurate by approximately
+20,000%. (Yes, a multiple of 200.) This is, however, more directly comparable
+to common profilers (such as gprof) and also gives accurate function call
+counts, which cannot be retrieved from "time".
+
+I strongly recommend "time" mode, and it is now the default.
+
+== History ==
+
+2021-01-04 - Larry Deaton ( larry.deaton@dynetics.com )
+ Modified the profiling by "call" operation to ignore internal LUA functions
+ since LUA only provides debug hooks for the calling of the function and not
+ the return of the function. Without this change, the call stack is
+ continually growing.
+
+2008-09-16 - Time-based profiling and conversion to Lua 5.1
+ by Ben Wilhelm ( zorba-pepperfish@pavlovian.net ).
+ Added the ability to optionally choose profiling methods, along with a new
+ profiling method.
+
+Converted to Lua 5, a few improvements, and
+additional documentation by Tom Spilman ( tom@sickheadgames.com )
+
+Additional corrections and tidying by original author
+Daniel Silverstone ( dsilvers@pepperfish.net )
+
+== Status ==
+
+Daniel Silverstone is no longer using this code, and judging by how long it's
+been waiting for Lua 5.1 support, I don't think Tom Spilman is either. I'm
+perfectly willing to take on maintenance, so if you have problems or
+questions, go ahead and email me :)
+-- Ben Wilhelm ( zorba-pepperfish@pavlovian.net ) '
+
+== Copyright ==
+
+Lua profiler - Copyright Pepperfish 2002,2003,2004
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to
+do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+
+--]]
+
+
+--
+-- All profiler related stuff is stored in the top level table '_profiler'
+--
+_profiler = {}
+
+
+--
+-- newProfiler() creates a new profiler object for managing
+-- the profiler and storing state. Note that only one profiler
+-- object can be executing at one time.
+--
+function newProfiler(variant, sampledelay)
+ if _profiler.running then
+ print("Profiler already running.")
+ return
+ end
+
+ variant = variant or "time"
+
+ if variant ~= "time" and variant ~= "call" then
+ print("Profiler method must be 'time' or 'call'.")
+ return
+ end
+
+ local newprof = {}
+ for k,v in pairs(_profiler) do
+ newprof[k] = v
+ end
+ newprof.variant = variant
+ newprof.sampledelay = sampledelay or 100000
+ return newprof
+end
+
+
+--
+-- This function starts the profiler. It will do nothing
+-- if this (or any other) profiler is already running.
+--
+function _profiler.start(self)
+ if _profiler.running then
+ return
+ end
+ -- Start the profiler. This begins by setting up internal profiler state
+ _profiler.running = self
+ self.rawstats = {}
+ self.callstack = {}
+ if self.variant == "time" then
+ self.lastclock = os.clock()
+ debug.sethook( _profiler_hook_wrapper_by_time, "", self.sampledelay )
+ elseif self.variant == "call" then
+ debug.sethook( _profiler_hook_wrapper_by_call, "cr" )
+ else
+ print("Profiler method must be 'time' or 'call'.")
+ end
+end
+
+
+--
+-- This function stops the profiler. It will do nothing
+-- if a profiler is not running, and nothing if it isn't
+-- the currently running profiler.
+--
+function _profiler.stop(self)
+ if _profiler.running ~= self then
+ return
+ end
+ -- Stop the profiler.
+ debug.sethook( nil )
+ _profiler.running = nil
+end
+
+
+--
+-- Simple wrapper to handle the hook. You should not
+-- be calling this directly. Duplicated to reduce overhead.
+--
+function _profiler_hook_wrapper_by_call(action)
+ if _profiler.running == nil then
+ debug.sethook( nil )
+ end
+ _profiler.running:_internal_profile_by_call(action)
+end
+function _profiler_hook_wrapper_by_time(action)
+ if _profiler.running == nil then
+ debug.sethook( nil )
+ end
+ _profiler.running:_internal_profile_by_time(action)
+end
+
+
+--
+-- This is the main by-function-call function of the profiler and should not
+-- be called except by the hook wrapper
+--
+function _profiler._internal_profile_by_call(self,action)
+ -- Since we can obtain the 'function' for the item we've had call us, we
+ -- can use that...
+ local caller_info = debug.getinfo( 3 )
+ if caller_info == nil then
+ print "No caller_info"
+ return
+ end
+
+ if caller_info.short_src == "[C]" then
+ -- LMD -- These are builtin functions and for some reason the
+ -- debug hook does not catch the return of these functions.
+ -- So, for now, we are just going to skip them.
+ return
+ end
+
+ --SHG_LOG("[_profiler._internal_profile] "..(caller_info.name or "<nil>"))
+
+ -- Retrieve the most recent activation record...
+ local latest_ar = nil
+ if #self.callstack > 0 then
+ latest_ar = self.callstack[#self.callstack]
+ end
+
+ -- Are we allowed to profile this function?
+ local should_not_profile = 0
+ for k,v in pairs(self.prevented_functions) do
+ if k == caller_info.func then
+ should_not_profile = v
+ end
+ end
+ -- Also check the top activation record...
+ if latest_ar then
+ if latest_ar.should_not_profile == 2 then
+ should_not_profile = 2
+ end
+ end
+
+ -- Now then, are we in 'call' or 'return' ?
+ -- print("Profile:", caller_info.name, "SNP:", should_not_profile,
+ -- "Action:", action )
+ if action == "call" then
+ -- Making a call...
+ local this_ar = {}
+ this_ar.should_not_profile = should_not_profile
+ this_ar.parent_ar = latest_ar
+ this_ar.anon_child = 0
+ this_ar.name_child = 0
+ this_ar.children = {}
+ this_ar.children_time = {}
+ this_ar.clock_start = os.clock()
+ -- Last thing to do on a call is to insert this onto the ar stack...
+ table.insert( self.callstack, this_ar )
+ else
+ local this_ar = latest_ar
+ if this_ar == nil then
+ return -- No point in doing anything if no upper activation record
+ end
+
+ -- Right, calculate the time in this function...
+ this_ar.clock_end = os.clock()
+ this_ar.this_time = this_ar.clock_end - this_ar.clock_start
+
+ -- Now, if we have a parent, update its call info...
+ if this_ar.parent_ar then
+ this_ar.parent_ar.children[caller_info.func] =
+ (this_ar.parent_ar.children[caller_info.func] or 0) + 1
+ this_ar.parent_ar.children_time[caller_info.func] =
+ (this_ar.parent_ar.children_time[caller_info.func] or 0 ) +
+ this_ar.this_time
+ if caller_info.name == nil then
+ this_ar.parent_ar.anon_child =
+ this_ar.parent_ar.anon_child + this_ar.this_time
+ else
+ this_ar.parent_ar.name_child =
+ this_ar.parent_ar.name_child + this_ar.this_time
+ end
+ end
+ -- Now if we're meant to record information about ourselves, do so...
+ if this_ar.should_not_profile == 0 then
+ local inforec = self:_get_func_rec(caller_info.func,1)
+ inforec.count = inforec.count + 1
+ inforec.time = inforec.time + this_ar.this_time
+ inforec.anon_child_time = inforec.anon_child_time + this_ar.anon_child
+ inforec.name_child_time = inforec.name_child_time + this_ar.name_child
+ inforec.func_info = caller_info
+ for k,v in pairs(this_ar.children) do
+ inforec.children[k] = (inforec.children[k] or 0) + v
+ inforec.children_time[k] =
+ (inforec.children_time[k] or 0) + this_ar.children_time[k]
+ end
+ end
+
+ -- Last thing to do on return is to drop the last activation record...
+ table.remove( self.callstack, #self.callstack)
+ end
+end
+
+
+--
+-- This is the main by-time internal function of the profiler and should not
+-- be called except by the hook wrapper
+--
+function _profiler._internal_profile_by_time(self,action)
+ -- we do this first so we add the minimum amount of extra time to this call
+ local timetaken = os.clock() - self.lastclock
+
+ local depth = 3
+ local at_top = true
+ local last_caller
+ local caller = debug.getinfo(depth)
+ while caller do
+ if not caller.func then caller.func = "(tail call)" end
+ if self.prevented_functions[caller.func] == nil then
+ local info = self:_get_func_rec(caller.func, 1, caller)
+ info.count = info.count + 1
+ info.time = info.time + timetaken
+ if last_caller then
+ -- we're not the head, so update the "children" times also
+ if last_caller.name then
+ info.name_child_time = info.name_child_time + timetaken
+ else
+ info.anon_child_time = info.anon_child_time + timetaken
+ end
+ info.children[last_caller.func] =
+ (info.children[last_caller.func] or 0) + 1
+ info.children_time[last_caller.func] =
+ (info.children_time[last_caller.func] or 0) + timetaken
+ end
+ end
+ depth = depth + 1
+ last_caller = caller
+ caller = debug.getinfo(depth)
+ end
+
+ self.lastclock = os.clock()
+end
+
+
+--
+-- This returns a (possibly empty) function record for
+-- the specified function. It is for internal profiler use.
+--
+function _profiler._get_func_rec(self,func,force,info)
+ -- Find the function ref for 'func' (if force and not present, create one)
+ local ret = self.rawstats[func]
+ if ret == nil and force ~= 1 then
+ return nil
+ end
+ if ret == nil then
+ -- Build a new function statistics table
+ ret = {}
+ ret.func = func
+ ret.count = 0
+ ret.time = 0
+ ret.anon_child_time = 0
+ ret.name_child_time = 0
+ ret.children = {}
+ ret.children_time = {}
+ ret.func_info = info
+ self.rawstats[func] = ret
+ end
+ return ret
+end
+
+
+--
+-- This writes a profile report to the output file object. If
+-- sort_by_total_time is nil or false the output is sorted by
+-- the function time minus the time in it's children.
+--
+function _profiler.report( self, outfile, sort_by_total_time )
+
+ outfile:write
+ [[Lua Profile output created by profiler.lua. Copyright Pepperfish 2002+
+
+]]
+
+ -- This is pretty awful.
+ local terms = {}
+ if self.variant == "time" then
+ terms.capitalized = "Sample"
+ terms.single = "sample"
+ terms.pastverb = "sampled"
+ elseif self.variant == "call" then
+ terms.capitalized = "Call"
+ terms.single = "call"
+ terms.pastverb = "called"
+ else
+ assert(false)
+ end
+
+ local total_time = 0
+ local ordering = {}
+ for func,record in pairs(self.rawstats) do
+ table.insert(ordering, func)
+ end
+
+ if sort_by_total_time then
+ table.sort( ordering,
+ function(a,b) return self.rawstats[a].time > self.rawstats[b].time end
+ )
+ else
+ table.sort( ordering,
+ function(a,b)
+ local arec = self.rawstats[a]
+ local brec = self.rawstats[b]
+ local atime = arec.time - (arec.anon_child_time + arec.name_child_time)
+ local btime = brec.time - (brec.anon_child_time + brec.name_child_time)
+ return atime > btime
+ end
+ )
+ end
+
+ for i=1,#ordering do
+ local func = ordering[i]
+ local record = self.rawstats[func]
+ local thisfuncname = " " .. self:_pretty_name(func) .. " "
+ if string.len( thisfuncname ) < 42 then
+ thisfuncname =
+ string.rep( "-", (42 - string.len(thisfuncname))/2 ) .. thisfuncname
+ thisfuncname =
+ thisfuncname .. string.rep( "-", 42 - string.len(thisfuncname) )
+ end
+
+ total_time = total_time + ( record.time - ( record.anon_child_time +
+ record.name_child_time ) )
+ outfile:write( string.rep( "-", 19 ) .. thisfuncname ..
+ string.rep( "-", 19 ) .. "\n" )
+ outfile:write( terms.capitalized.." count: " ..
+ string.format( "%4d", record.count ) .. "\n" )
+ outfile:write( "Time spend total: " ..
+ string.format( "%4.3f", record.time ) .. "s\n" )
+ outfile:write( "Time spent in children: " ..
+ string.format("%4.3f",record.anon_child_time+record.name_child_time) ..
+ "s\n" )
+ local timeinself =
+ record.time - (record.anon_child_time + record.name_child_time)
+ outfile:write( "Time spent in self: " ..
+ string.format("%4.3f", timeinself) .. "s\n" )
+ outfile:write( "Time spent per " .. terms.single .. ": " ..
+ string.format("%4.5f", record.time/record.count) ..
+ "s/" .. terms.single .. "\n" )
+ outfile:write( "Time spent in self per "..terms.single..": " ..
+ string.format( "%4.5f", timeinself/record.count ) .. "s/" ..
+ terms.single.."\n" )
+
+ -- Report on each child in the form
+ -- Child <funcname> called n times and took a.bs
+ local added_blank = 0
+ for k,v in pairs(record.children) do
+ if self.prevented_functions[k] == nil or
+ self.prevented_functions[k] == 0
+ then
+ if added_blank == 0 then
+ outfile:write( "\n" ) -- extra separation line
+ added_blank = 1
+ end
+ outfile:write( "Child " .. self:_pretty_name(k) ..
+ string.rep( " ", 41-string.len(self:_pretty_name(k)) ) .. " " ..
+ terms.pastverb.." " .. string.format("%6d", v) )
+ outfile:write( " times. Took " ..
+ string.format("%4.2f", record.children_time[k] ) .. "s\n" )
+ end
+ end
+
+ outfile:write( "\n" ) -- extra separation line
+ outfile:flush()
+ end
+ outfile:write( "\n\n" )
+ outfile:write( "Total time spent in profiled functions: " ..
+ string.format("%5.3g",total_time) .. "s\n" )
+ outfile:write( [[
+
+END
+]] )
+ outfile:flush()
+end
+
+
+--
+-- This writes the profile to the output file object as
+-- loadable Lua source.
+--
+function _profiler.lua_report(self,outfile)
+ -- Purpose: Write out the entire raw state in a cross-referenceable form.
+ local ordering = {}
+ local functonum = {}
+ for func,record in pairs(self.rawstats) do
+ table.insert(ordering, func)
+ functonum[func] = #ordering
+ end
+
+ outfile:write(
+ "-- Profile generated by profiler.lua Copyright Pepperfish 2002+\n\n" )
+ outfile:write( "-- Function names\nfuncnames = {}\n" )
+ for i=1,#ordering do
+ local thisfunc = ordering[i]
+ outfile:write( "funcnames[" .. i .. "] = " ..
+ string.format("%q", self:_pretty_name(thisfunc)) .. "\n" )
+ end
+ outfile:write( "\n" )
+ outfile:write( "-- Function times\nfunctimes = {}\n" )
+ for i=1,#ordering do
+ local thisfunc = ordering[i]
+ local record = self.rawstats[thisfunc]
+ outfile:write( "functimes[" .. i .. "] = { " )
+ outfile:write( "tot=" .. record.time .. ", " )
+ outfile:write( "achild=" .. record.anon_child_time .. ", " )
+ outfile:write( "nchild=" .. record.name_child_time .. ", " )
+ outfile:write( "count=" .. record.count .. " }\n" )
+ end
+ outfile:write( "\n" )
+ outfile:write( "-- Child links\nchildren = {}\n" )
+ for i=1,#ordering do
+ local thisfunc = ordering[i]
+ local record = self.rawstats[thisfunc]
+ outfile:write( "children[" .. i .. "] = { " )
+ for k,v in pairs(record.children) do
+ if functonum[k] then -- non-recorded functions will be ignored now
+ outfile:write( functonum[k] .. ", " )
+ end
+ end
+ outfile:write( "}\n" )
+ end
+ outfile:write( "\n" )
+ outfile:write( "-- Child call counts\nchildcounts = {}\n" )
+ for i=1,#ordering do
+ local thisfunc = ordering[i]
+ local record = self.rawstats[thisfunc]
+ outfile:write( "children[" .. i .. "] = { " )
+ for k,v in record.children do
+ if functonum[k] then -- non-recorded functions will be ignored now
+ outfile:write( v .. ", " )
+ end
+ end
+ outfile:write( "}\n" )
+ end
+ outfile:write( "\n" )
+ outfile:write( "-- Child call time\nchildtimes = {}\n" )
+ for i=1,#ordering do
+ local thisfunc = ordering[i]
+ local record = self.rawstats[thisfunc];
+ outfile:write( "children[" .. i .. "] = { " )
+ for k,v in pairs(record.children) do
+ if functonum[k] then -- non-recorded functions will be ignored now
+ outfile:write( record.children_time[k] .. ", " )
+ end
+ end
+ outfile:write( "}\n" )
+ end
+ outfile:write( "\n\n-- That is all.\n\n" )
+ outfile:flush()
+end
+
+-- Internal function to calculate a pretty name for the profile output
+function _profiler._pretty_name(self,func)
+
+ -- Only the data collected during the actual
+ -- run seems to be correct.... why?
+ local info = self.rawstats[ func ].func_info
+ -- local info = debug.getinfo( func )
+
+ local name = ""
+ if info.what == "Lua" then
+ name = "L:"
+ end
+ if info.what == "C" then
+ name = "C:"
+ end
+ if info.what == "main" then
+ name = " :"
+ end
+
+ if info.name == nil then
+ name = name .. "<"..tostring(func) .. ">"
+ else
+ name = name .. info.name
+ end
+
+ if info.source then
+ name = name .. "@" .. info.source
+ else
+ if info.what == "C" then
+ name = name .. "@?"
+ else
+ name = name .. "@<string>"
+ end
+ end
+ name = name .. ":"
+ if info.what == "C" then
+ name = name .. "?"
+ else
+ name = name .. info.linedefined
+ end
+
+ return name
+end
+
+
+--
+-- This allows you to specify functions which you do
+-- not want profiled. Setting level to 1 keeps the
+-- function from being profiled. Setting level to 2
+-- keeps both the function and its children from
+-- being profiled.
+--
+-- BUG: 2 will probably act exactly like 1 in "time" mode.
+-- If anyone cares, let me (zorba) know and it can be fixed.
+--
+function _profiler.prevent(self, func, level)
+ self.prevented_functions[func] = (level or 1)
+end
+
+
+_profiler.prevented_functions = {
+ [_profiler.start] = 2,
+ [_profiler.stop] = 2,
+ [_profiler._internal_profile_by_time] = 2,
+ [_profiler._internal_profile_by_call] = 2,
+ [_profiler_hook_wrapper_by_time] = 2,
+ [_profiler_hook_wrapper_by_call] = 2,
+ [_profiler.prevent] = 2,
+ [_profiler._get_func_rec] = 2,
+ [_profiler.report] = 2,
+ [_profiler.lua_report] = 2,
+ [_profiler._pretty_name] = 2
+} \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/_lib/table.lua b/macros/luatex/latex/luacas/tex/_lib/table.lua
new file mode 100644
index 0000000000..70495d1479
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/_lib/table.lua
@@ -0,0 +1,185 @@
+-- Checks if two arrays are equal, starting at index i
+function ArrayEqual(a1, a2, i)
+ i = i or 1
+ while i <= math.max(#a1, #a2) do
+ if a1[i] ~= a2[i] then
+ return false
+ end
+ i = i + 1
+ end
+ return true
+end
+
+-- Checks if two arrays are equal, the first starting at index i, the second starting at index j
+function FancyArrayEqual(a1, a2, i, j)
+ i = i or 1
+ j = j or 1
+ while i <= math.max(#a1, #a2) or j <= math.max(#a1, #a2) do
+ if a1[i] ~= a2[j] then
+ return false
+ end
+ i = i + 1
+ j = j + 1
+ end
+ return true
+end
+
+-- Creates a copy of a table
+function Copy(orig)
+ local orig_type = type(orig)
+ local copy
+ if orig_type == 'table' then
+ copy = {}
+ for orig_key, orig_value in pairs(orig) do
+ copy[orig_key] = orig_value
+ end
+ copy = setmetatable(copy, getmetatable(orig))
+ else -- number, string, boolean, etc
+ copy = orig
+ end
+ return copy
+end
+
+-- Joins two arrays
+function JoinArrays(a1, a2)
+ local a = Copy(a1)
+ for index, value in ipairs(a2) do
+ a[index + #a1] = value
+ end
+ return a
+end
+
+-- Joins two arrays indexed from zero
+function JoinZeroArrays(a1, a2)
+ local a = Copy(a1)
+ a[#a1 + 1] = a2[0]
+ for index, value in ipairs(a2) do
+ a[index + #a1 + 1] = value
+ end
+ return a
+end
+
+-- Join two tables, using the second entry if a key appears in both tables
+function JoinTables(t1, t2)
+ local t = Copy(t1) or {}
+ for key, value in pairs(t2) do
+ t[key] = value
+ end
+ return t
+end
+
+-- Given an array, removes all occurances of that element from the array
+function Remove(a, e)
+ local r = Copy(a)
+ local found = false
+ for index, value in ipairs(r) do
+ if e == value then
+ found = true
+ end
+ if found then
+ r[index] = r[index + 1]
+ end
+ end
+ return r
+end
+
+-- Given an array, removes all elements in the second array from the first
+function RemoveAll(a1, a2)
+ local r = Copy(a1)
+ local removed = 0
+ for index, value in ipairs(r) do
+ if a2[value] then
+ removed = removed + 1
+ end
+ if removed then
+ r[index] = r[index + removed]
+ end
+ end
+ return r
+end
+
+-- Given an array of arrays, returns only the arrays that have no elements in common with the second array
+function RemoveAny(aa, a)
+ local toremove = {}
+ for _, v in ipairs(aa) do
+ for _, v1 in ipairs(v) do
+ for _, v2 in ipairs(a) do
+ if v1 == v2 then
+ toremove[#toremove+1] = v
+ goto endcheck
+ end
+ end
+ end
+ ::endcheck::
+ end
+ return RemoveAll(aa, toremove)
+end
+
+-- Converts an array to a string recursively
+function ToStringArray(t)
+ if string.sub(tostring(t), 1, 6) == "table:" then
+ local out = "{"
+ for index, value in ipairs(t) do
+ out = out .. ToStringArray(value)
+ if t[index + 1] then
+ out = out .. ", "
+ end
+ end
+ return out .. "}"
+ end
+ return tostring(t)
+end
+
+-- Converts a table to a string recursively
+function ToStringTable(t)
+ if string.sub(tostring(t), 1, 6) == "table:" then
+ local out = "{"
+ for index, value in pairs(t) do
+ out = out .. ToStringTable(index) .. " : " .. ToStringTable(value)
+ if t[index + 1] then
+ out = out .. ", "
+ end
+ end
+ return out .. "}"
+ end
+ return tostring(t)
+end
+
+-- Check if a table contains an element, and returns the index of that element if it does
+function Contains(t, e)
+ for index, value in pairs(t) do
+ if value == e then
+ return index
+ end
+ end
+ return false
+end
+
+-- Given an array a of unique elements, returns an array of the n-element subarrays of a
+function Subarrays(a, m)
+ local aout = {}
+ local l = 1
+ local newmax = {}
+
+ if(m <= 0) then
+ return {{}}, {0}
+ end
+
+ local rec, max = Subarrays(a, m - 1)
+
+ for recindex, set in pairs(rec) do
+ for index, element in pairs(a) do
+ if not set[element] and index > max[recindex] then
+ local new = Copy(set)
+ new[#new+1] = element
+ aout[l] = new
+ if not newmax[l] or index > newmax[l] then
+ newmax[l] = index
+ end
+ l = l + 1
+ end
+ end
+ end
+
+ return aout, newmax
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/_init.lua b/macros/luatex/latex/luacas/tex/algebra/_init.lua
new file mode 100644
index 0000000000..0a9b114cb9
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/_init.lua
@@ -0,0 +1,24 @@
+-- Loads algebra files in the correct order.
+require("_lib.table")
+
+require("core._init")
+
+require("algebra.ring")
+require("algebra.euclideandomain")
+require("algebra.field")
+require("algebra.polynomialring")
+require("algebra.integer")
+require("algebra.rational")
+require("algebra.integerquotientring")
+require("algebra.sqrtexpression")
+
+require("algebra.absexpression")
+require("algebra.equation")
+require("algebra.factorialexpression")
+require("algebra.logarithm")
+require("algebra.rootexpression")
+require("algebra.trigexpression")
+
+require("algebra.polynomialring.berlekampfactoring")
+require("algebra.polynomialring.zassenhausfactoring")
+require("algebra.polynomialring.decomposition") \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/absexpression.lua b/macros/luatex/latex/luacas/tex/algebra/absexpression.lua
new file mode 100644
index 0000000000..2bf0b6a7ef
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/absexpression.lua
@@ -0,0 +1,80 @@
+--- @class AbsExpression
+--- The absolute value of an expression.
+--- @field expression Expression
+AbsExpression = {}
+__AbsExpression = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new absolute value expression with the given expression.
+--- @param expression Expression
+--- @return AbsExpression
+function AbsExpression:new(expression)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ o.expression = expression
+
+ __o.__index = AbsExpression
+ __o.__tostring = function(a)
+ return '|' .. tostring(a.expression) .. '|'
+ end
+
+ o = setmetatable(o, __o)
+ return o
+end
+
+--- @return Expression
+function AbsExpression:evaluate()
+ if self.expression:isconstant() then
+ if self.expression >= Integer.zero() then
+ return self.expression
+ end
+ return -self.expression
+ end
+ return self
+end
+
+--- @return Expression
+function AbsExpression:autosimplify()
+ return AbsExpression(self.expression:autosimplify()):evaluate()
+end
+
+--- @return table<number, Expression>
+function AbsExpression:subexpressions()
+ return {self.expression}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return AbsExpression
+function AbsExpression:setsubexpressions(subexpressions)
+ return AbsExpression(subexpressions[1])
+end
+
+--- @param other Expression
+--- @return boolean
+function AbsExpression:order(other)
+ return FunctionExpression("abs", self.expression):order(other)
+end
+
+--- @return string
+function AbsExpression:tolatex()
+ return "\\left|" .. self.expression:tolatex() .. "\\right|"
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__AbsExpression.__index = CompoundExpression
+__AbsExpression.__call = AbsExpression.new
+AbsExpression = setmetatable(AbsExpression, __AbsExpression)
+
+----------------------
+-- Static constants --
+----------------------
+ABS = function (a)
+ return AbsExpression(a)
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/equation.lua b/macros/luatex/latex/luacas/tex/algebra/equation.lua
new file mode 100644
index 0000000000..4e81d5ca95
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/equation.lua
@@ -0,0 +1,183 @@
+--- @class Equation
+--- An expression that represents an equation of the form lhs = rhs.
+--- @field lhs Expression
+--- @field rhs Expression
+Equation = {}
+__Equation = {}
+
+--------------------------
+-- Static functionality --
+--------------------------
+
+--- Attempts to isolate the variable var in lhs by moving expressions to rhs. Ony performs a single step.
+--- @param lhs Expression
+--- @param rhs Expression
+--- @param var SymbolExpression
+--- @return Expression, Expression
+function Equation.isolatelhs(lhs, rhs, var)
+ if lhs:type() == BinaryOperation then
+ local stay = Integer.zero()
+ local switch = Integer.zero()
+ if lhs.operation == BinaryOperation.ADD then
+ for _, exp in ipairs(lhs:subexpressions()) do
+ if exp:freeof(var) then
+ switch = switch + exp
+ else
+ stay = stay + exp
+ end
+ end
+ if switch == Integer.zero() then
+ lhs = lhs:factor() -- TODO: Replace with collect for efficiency reasons
+ else
+ return stay:autosimplify(), (rhs - switch):autosimplify()
+ end
+ end
+ if lhs.operation == BinaryOperation.MUL then
+ stay = Integer.one()
+ switch = Integer.one()
+ for _, exp in ipairs(lhs:subexpressions()) do
+ if exp:freeof(var) then
+ switch = switch * exp
+ else
+ stay = stay * exp
+ end
+ end
+ return stay:autosimplify(), (rhs / switch):autosimplify()
+ end
+ if lhs.operation == BinaryOperation.POW then
+ if lhs:subexpressions()[1]:freeof(var) then
+ return lhs:subexpressions()[2]:autosimplify(), Logarithm(lhs:subexpressions()[1], rhs):autosimplify()
+ elseif lhs:subexpressions()[2]:freeof(var) then
+ return lhs:subexpressions()[1]:autosimplify(), (rhs ^ (Integer.one()/lhs:subexpressions()[2])):autosimplify()
+ end
+ end
+ elseif lhs:type() == Logarithm then
+ if lhs.base:freeof(var) then
+ return lhs.expression:autosimplify(), (lhs.base ^ rhs):autosimplify()
+ elseif lhs.expression:freeof(var) then
+ return lhs.base:autosimplify(), (lhs.expression ^ (Integer.one()/rhs)):autosimplify()
+ end
+ elseif lhs:type() == TrigExpression then
+ return lhs.expression:autosimplify(), TrigExpression(TrigExpression.INVERSES[lhs.name], rhs):autosimplify()
+ end
+
+ return lhs, rhs
+end
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new equation with the given expressions.
+--- @param lhs Expression
+--- @param rhs Expression
+--- @return Equation
+function Equation:new(lhs, rhs)
+
+ if lhs:type() == Equation or rhs:type() == Equation then
+ error("Sent parameter of wrong type: cannot nest equations or inequalities")
+ end
+
+ local o = {}
+ local __o = Copy(__ExpressionOperations) -- TODO: Ensure only one metatable for each instance of a class
+
+ o.lhs = lhs
+ o.rhs = rhs
+
+ __o.__index = Equation
+ __o.__tostring = function(a)
+ return tostring(a.lhs) .. ' = ' .. tostring(a.rhs)
+ end
+ __o.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always runs this anyway
+ if not b:type() == Equation then
+ return false
+ end
+ return a.lhs == b.lhs and a.rhs == b.rhs
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- Evaluation in this case just checks for structural equality, or guarenteed inequality in the case of constants
+--- @return Equation|boolean
+function Equation:evaluate()
+ if self.lhs == self.rhs then
+ return true -- TODO: Add Boolean Expressions
+ end
+ if self.lhs:isconstant() and self.rhs:isconstant() and self.lhs ~= self.rhs then
+ return false
+ end
+ return self
+end
+
+--- @return Equation|boolean
+function Equation:autosimplify()
+ local lhs = self.lhs:autosimplify()
+ local rhs = self.rhs:autosimplify()
+
+ return Equation(lhs, rhs):evaluate()
+end
+
+--- @return table<number, Expression>
+function Equation:subexpressions()
+ return {self.lhs, self.rhs}
+end
+
+--- Attempts to solve the equation for a particular variable.
+--- @param var SymbolExpression
+--- @return Equation
+function Equation:solvefor(var)
+ local lhs = self.lhs
+ local rhs = self.rhs
+
+ if lhs:freeof(var) and rhs:freeof(var) then
+ return self
+ end
+
+ -- Check for monovariate polynomial expressions
+ local root = (lhs - rhs):autosimplify()
+ local poly, status = root:expand():topolynomial()
+ if status then
+ -- TODO: Add Set expressions
+ return Equation(var, poly:roots()[1])
+ end
+
+ local newlhs, newrhs = root, Integer(0)
+ local oldlhs
+ while newlhs ~= var and oldlhs ~= newlhs do
+ oldlhs = newlhs
+ newlhs, newrhs = Equation.isolatelhs(newlhs, newrhs, var)
+ end
+
+ return Equation(newlhs, newrhs)
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return Equation
+function Equation:setsubexpressions(subexpressions)
+ return Equation(subexpressions[1], subexpressions[2])
+end
+
+--- @param other Expression
+--- @return boolean
+function Equation:order(other)
+ if other:isatomic() then
+ return false
+ end
+
+ return self.lhs:order(other)
+end
+
+--- @return string
+function Equation:tolatex()
+ return self.lhs:tolatex() .. '=' .. self.rhs:tolatex()
+end
+
+-----------------
+-- Inheritance --
+-----------------
+__Equation.__index = CompoundExpression
+__Equation.__call = Equation.new
+Equation = setmetatable(Equation, __Equation) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/euclideandomain.lua b/macros/luatex/latex/luacas/tex/algebra/euclideandomain.lua
new file mode 100644
index 0000000000..fab2c5c7c2
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/euclideandomain.lua
@@ -0,0 +1,63 @@
+--- @class EuclideanDomain
+--- Interface for an element of a euclidean domain.
+EuclideanDomain = {}
+__EuclideanDomain = {}
+
+----------------------
+-- Required methods --
+----------------------
+
+--- @param b EuclideanDomain
+--- @return EuclideanDomain, EuclideanDomain
+function EuclideanDomain:divremainder(b)
+ error("Called unimplemented method : divremainder()")
+end
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- @return boolean
+function EuclideanDomain:iscommutative()
+ return true
+end
+
+--------------------------
+-- Instance metamethods --
+--------------------------
+
+__EuclideanOperations = Copy(__RingOperations)
+
+-- Division with remainder
+-- Unfortunately, this can only return 1 result, so it returns the quotient - for the remainder use a % b, or a:divremainder(b)
+__EuclideanOperations.__idiv = function(a, b)
+ if(b == b:zero()) then
+ error("Cannot divide by zero.")
+ end
+ local aring, bring = a:getring(), b:getring()
+ local oring = Ring.resultantring(aring, bring)
+ if not oring then
+ error("Attempted to divide two elements of incompatable rings")
+ end
+ return a:inring(oring):divremainder(b:inring(oring))
+end
+
+__EuclideanOperations.__mod = function(a, b)
+ if(b == b:zero()) then
+ error("Cannot divide by zero.")
+ end
+ local aring, bring = a:getring(), b:getring()
+ local oring = Ring.resultantring(aring, bring)
+ if not oring then
+ error("Attempted to divide two elements of incompatable rings")
+ end
+ local _,q = a:inring(oring):divremainder(b:inring(oring))
+ return q
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__EuclideanDomain.__index = Ring
+EuclideanDomain = setmetatable(EuclideanDomain, __EuclideanDomain) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/factorialexpression.lua b/macros/luatex/latex/luacas/tex/algebra/factorialexpression.lua
new file mode 100644
index 0000000000..1c5b824ff7
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/factorialexpression.lua
@@ -0,0 +1,102 @@
+--- @class FactorialExpression
+--- The factorial of an expression.
+--- @field expression Expression
+FactorialExpression = {}
+__FactorialExpression = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new factorial expression with the given expression.
+--- @param expression Expression
+--- @return FactorialExpression
+function FactorialExpression:new(expression)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ o.expression = expression
+
+ __o.__index = FactorialExpression
+ __o.__tostring = function(a)
+ return '(' .. tostring(a.expression) .. ')!'
+ end
+
+ o = setmetatable(o, __o)
+ return o
+end
+
+--- @return Expression
+function FactorialExpression:evaluate()
+ if self.expression:type() == Integer then
+ if self.expression < Integer.zero() then
+ error("Aritmetic Error: Factorials of negative integers are not defined.")
+ end
+
+ if not FactorialExpression.LIMIT then
+ FactorialExpression.LIMIT = Integer(5000)
+ end
+
+ if self.expression > FactorialExpression.LIMIT then
+ return self
+ end
+ -- TODO: More efficient factorial computations.
+ local out = Integer.one()
+ local i = Integer.zero()
+ while i < self.expression do
+ i = i + Integer.one()
+ out = out * i
+ end
+ return out
+ end
+ return self
+end
+
+--- @return Expression
+function FactorialExpression:autosimplify()
+ return FactorialExpression(self.expression:autosimplify()):evaluate()
+end
+
+--- @return table<number, Expression>
+function FactorialExpression:subexpressions()
+ return {self.expression}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return AbsExpression
+function FactorialExpression:setsubexpressions(subexpressions)
+ return FactorialExpression(subexpressions[1])
+end
+
+--- @param other Expression
+--- @return boolean
+function FactorialExpression:order(other)
+ return FunctionExpression("fact", self.expression):order(other)
+end
+
+--- @return string
+function FactorialExpression:tolatex()
+ if self.expression:isatomic() then
+ return self.expression:tolatex() .. "!"
+ end
+ return "(" .. self.expression:tolatex() .. ")!"
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__FactorialExpression.__index = CompoundExpression
+__FactorialExpression.__call = FactorialExpression.new
+FactorialExpression = setmetatable(FactorialExpression, __FactorialExpression)
+
+----------------------
+-- Static constants --
+----------------------
+
+-- Do not attempt to compute factorials larger than this.
+FactorialExpression.LIMIT = Integer(5000)
+
+FACT = function (a)
+ return FactorialExpression(a)
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/field.lua b/macros/luatex/latex/luacas/tex/algebra/field.lua
new file mode 100644
index 0000000000..c845e94c55
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/field.lua
@@ -0,0 +1,69 @@
+--- @class Field
+--- Interface for an element of a field.
+Field = {}
+__Field = {}
+
+----------------------
+-- Required methods --
+----------------------
+
+--- @return Field
+function Field:div(b)
+ return self:mul(b:inv())
+end
+
+--- @return Field
+function Field:inv()
+ error("Called unimplemented method: inv()")
+end
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Field exponentiation based on the definition. Specific rings may implement more efficient methods.
+--- @return Field
+function Field:pow(n)
+ local base = self
+ if(n < Integer.zero()) then
+ n = -n
+ base = base:inv()
+ end
+ local k = Integer.zero()
+ local b = self.getring().one()
+ while k < n do
+ b = b.mul(base)
+ k = k + Integer.one()
+ end
+ return b
+end
+
+--------------------------
+-- Instance metamethods --
+--------------------------
+
+__FieldOperations = Copy(__EuclideanOperations)
+
+__FieldOperations.__div = function(a, b)
+ if not b.getring and not b:isconstant() then
+ return BinaryOperation.DIVEXP({a, b})
+ end
+
+ if(b == b:zero()) then
+ error("Arithmetic Error: Cannot divide by zero.")
+ end
+
+ local aring, bring = a:getring(), b:getring()
+ local oring = Ring.resultantring(aring, bring)
+ if not oring then
+ error("Attempted to divide two elements of incompatable rings")
+ end
+ return a:inring(oring):div(b:inring(oring))
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__Field.__index = EuclideanDomain
+Field = setmetatable(Field, __Field) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/integer.lua b/macros/luatex/latex/luacas/tex/algebra/integer.lua
new file mode 100644
index 0000000000..a3c54f498c
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/integer.lua
@@ -0,0 +1,952 @@
+--- @class Integer
+--- Represents an element of the ring of integers.
+--- @field self table<number, number>
+--- @field sign number
+Integer = {}
+__Integer = {}
+
+--------------------------
+-- Static functionality --
+--------------------------
+
+-- The length of each digit in base 10. 10^15 < 2^53 < 10^16, so 15 is the highest value that will work with double-percision numbers.
+-- For multiplication to work properly, however, this also must be even so we can take the square root of the digit size exactly.
+-- 10^14 is still larger than 2^26, so it is still efficient to do multiplication this way.
+Integer.DIGITLENGTH = 14
+-- The maximum size for a digit. While this doesn't need to be a power of 10, it makes implementing converting to and from strings much easier.
+Integer.DIGITSIZE = 10 ^ Integer.DIGITLENGTH
+-- Partition size for multiplying integers so we can get both the upper and lower bits of each digits
+Integer.PARTITIONSIZE = math.floor(math.sqrt(Integer.DIGITSIZE))
+
+--- Method for computing the gcd of two integers using Euclid's algorithm.
+--- @param a Integer
+--- @param b Integer
+--- @return Integer
+function Integer.gcd(a, b)
+ while b ~= Integer.zero() do
+ a, b = b, a%b
+ end
+ return a
+end
+
+--- Method for computing the gcd of two integers using Euclid's algorithm.
+--- Also returns Bezout's coefficients via extended gcd.
+--- @param a Integer
+--- @param b Integer
+--- @return Integer, Integer, Integer
+function Integer.extendedgcd(a, b)
+ local oldr, r = a, b
+ local olds, s = Integer.one(), Integer.zero()
+ local oldt, t = Integer.zero(), Integer.one()
+ while r ~= Integer.zero() do
+ local q = oldr // r
+ oldr, r = r, oldr - q*r
+ olds, s = s, olds - q*s
+ oldt, t = t, oldt - q*t
+ end
+ return oldr, olds, oldt
+end
+
+--- Method for computing the larger of two integers.
+--- Also returns the other integer for sorting purposes.
+--- @param a Integer
+--- @param b Integer
+--- @return Integer, Integer
+function Integer.max(a, b)
+ if a > b then
+ return a, b
+ end
+ return b, a
+end
+
+--- Method for computing the smaller of two integers.
+--- Also returns the other integer for sorting purposes.
+--- @param a Integer
+--- @param b Integer
+--- @return Integer, Integer
+function Integer.min(a, b)
+ if a < b then
+ return a, b
+ end
+ return b, a
+end
+
+--- Methods for computing the larger magnitude of two integers.
+--- Also returns the other integer for sorting purposes, and the number -1 if the two values were swapped, 1 if not.
+--- @param a Integer
+--- @param b Integer
+--- @return Integer, Integer, number
+function Integer.absmax(a, b)
+ if b:ltabs(a) then
+ return a, b, 1
+ end
+ return b, a, -1
+end
+
+-- Returns the ceiling of the log base (defaults to 10) of a.
+-- In other words, returns the least n such that base^n > a.
+--- @param a Integer
+--- @param base Integer
+--- @return Integer
+function Integer.ceillog(a, base)
+ base = base or Integer(10)
+ local k = Integer.zero()
+
+ while (base ^ k) < a do
+ k = k + Integer.one()
+ end
+
+ return k
+end
+
+--- Returns a ^ b (mod n). This should be used when a ^ b is potentially large.
+--- @param a Integer
+--- @param b Integer
+--- @param n Integer
+--- @return Integer
+function Integer.powmod(a, b, n)
+ if n == Integer.one() then
+ return Integer.zero()
+ else
+ local r = Integer.one()
+ a = a % n
+ while b > Integer.zero() do
+ if b % Integer(2) == Integer.one() then
+ r = (r * a) % n
+ end
+ a = (a ^ Integer(2)) % n
+ b = b // Integer(2)
+ end
+ return r
+ end
+end
+
+--- @return RingIdentifier
+local t = {ring=Integer}
+t = setmetatable(t, {__index = Integer, __eq = function(a, b)
+ return a["ring"] == b["ring"]
+end, __tostring = function(a)
+ return "ZZ"
+end})
+function Integer.makering()
+ return t
+end
+
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+-- 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
+ local out = ""
+ for i, digit in ipairs(a) do
+ local pre = tostring(math.floor(digit))
+ if i ~= #a then
+ while #pre ~= Integer.DIGITLENGTH do
+ pre = "0" .. pre
+ end
+ end
+ out = pre .. out
+ end
+ if a.sign == -1 then
+ out = "-" .. out
+ end
+ return out
+end
+__o.__div = function(a, b) -- Constructor for a rational number disguised as division
+ if not b.getring then
+ return BinaryOperation.DIVEXP({a, b})
+ end
+ if(a:getring() == Integer:getring() and b:getring() == Integer:getring()) then
+ return Rational(a, b)
+ 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.
+ return a + b / (Integer(10) ^ Integer.ceillog(b))
+end
+
+--- Creates a new integer given a string or number representation of the integer.
+--- @param n number|string|Integer
+--- @return Integer
+function Integer:new(n)
+ local o = {}
+ o = setmetatable(o, __o)
+
+ if not n then
+ o[1] = 0
+ o.sign = 0
+ return o
+ end
+
+ -- Can convert any floating-point number into an integer, though we generally only want to pass whole numbers into this.
+ -- This will only approximate very large floating point numbers to a small proportion of the total significant digits
+ -- After that the result will just be nonsense - strings should probably be used for big numbers
+ if type(n) == "number" then
+ n = math.floor(n)
+ if n == 0 then
+ o[1] = 0
+ o.sign = 0
+ else
+ if n < 0 then
+ n = -n
+ o.sign = -1
+ else
+ o.sign = 1
+ end
+ local i = 1
+ while n >= Integer.DIGITSIZE do
+ o[i] = n % Integer.DIGITSIZE
+ n = n // Integer.DIGITSIZE
+ i = i + 1
+ end
+ o[i] = n
+ end
+ -- Only works on strings that are exact (signed) integers
+ elseif type(n) == "string" then
+ if not tonumber(n) then
+ error("Sent parameter of wrong type: " .. n .. " is not an integer.")
+ end
+ if n == "0" then
+ o[1] = 0
+ o.sign = 0
+ else
+ local s = 1
+ if string.sub(n, 1, 1) == "-" then
+ s = s + 1
+ o.sign = -1
+ else
+ o.sign = 1
+ end
+
+ while string.sub(n, s, s) == "0" do
+ s = s + 1
+ end
+
+ local e = #n
+ local i = 1
+ while e > s + Integer.DIGITLENGTH - 1 do
+ o[i] = tonumber(string.sub(n, e - Integer.DIGITLENGTH + 1, e))
+ e = e - Integer.DIGITLENGTH
+ i = i + 1
+ end
+ o[i] = tonumber(string.sub(n, s, e)) or 0
+ end
+ -- Copying is expensive in Lua, so this constructor probably should only sparsely be called with an Integer argument.
+ elseif type(n) == "table" then
+ o = Copy(n)
+ else
+ error("Sent parameter of wrong type: Integer does not accept " .. type(n) .. ".")
+ end
+
+ return o
+end
+
+--- Returns the ring this object is an element of.
+--- @return RingIdentifier
+function Integer:getring()
+ return t
+end
+
+--- @param ring RingIdentifier
+--- @return Ring
+function Integer:inring(ring)
+ if ring == self:getring() then
+ return self
+ end
+
+ if ring == PolynomialRing:getring() then
+ return PolynomialRing({self:inring(ring.child)}, ring.symbol)
+ end
+
+ if ring == Rational:getring() then
+ if ring.child then
+ return Rational(self:inring(ring.child), self:inring(ring.child):one(), true)
+ end
+ return Rational(self, Integer.one(), true):inring(ring)
+ end
+
+ if ring == IntegerModN:getring() then
+ return IntegerModN(self, ring.modulus)
+ end
+
+ error("Unable to convert element to proper ring.")
+end
+
+--- @param b Integer
+--- @return Integer
+function Integer:add(b)
+ if self.sign == 1 and b.sign == -1 then
+ return self:usub(b, 1)
+ end
+ if self.sign == -1 and b.sign == 1 then
+ return self:usub(b, -1)
+ end
+
+ local sign = self.sign
+ if sign == 0 then
+ sign = b.sign
+ end
+ return self:uadd(b, sign)
+end
+
+--- Addition without sign so we don't have to create an entire new integer when switching signs.
+--- @param b Integer
+--- @param sign number
+--- @return Integer
+function Integer:uadd(b, sign)
+ local o = Integer()
+ o.sign = sign
+
+ local c = 0
+ local n = math.max(#self, #b)
+ for i = 1, n do
+ local s = (self[i] or 0) + (b[i] or 0) + c
+ if s >= Integer.DIGITSIZE then
+ o[i] = s - Integer.DIGITSIZE
+ c = 1
+ else
+ o[i] = s
+ c = 0
+ end
+ end
+ if c == 1 then
+ o[n + 1] = c
+ end
+ return o
+end
+
+--- @param b Integer
+--- @return Integer
+function Integer:sub(b)
+ if self.sign == 1 and b.sign == -1 then
+ return self:uadd(b, 1)
+ end
+ if self.sign == -1 and b.sign == 1 then
+ return self:uadd(b, -1)
+ end
+
+ local sign = self.sign
+ if sign == 0 then
+ sign = b.sign
+ end
+ return self:usub(b, sign)
+end
+
+-- Subtraction without sign so we don't have to create an entire new integer when switching signs.
+-- Uses subtraction by compliments.
+--- @param b Integer
+--- @param sign number
+--- @return Integer
+function Integer:usub(b, sign)
+ local a, b, swap = Integer.absmax(self, b)
+ local o = Integer()
+ o.sign = sign * swap
+
+ local c = 0
+ local n = #a
+ for i = 1, n do
+ local s = (a[i] or 0) + Integer.DIGITSIZE - 1 - (b[i] or 0) + c
+ if i == 1 then
+ s = s + 1
+ end
+ if s >= Integer.DIGITSIZE then
+ o[i] = s - Integer.DIGITSIZE
+ c = 1
+ else
+ o[i] = s
+ c = 0
+ end
+ end
+
+ -- Remove leading zero digits, since we want integer representations to be unique.
+ while o[n] == 0 do
+ o[n] = nil
+ n = n - 1
+ end
+
+ if not o[1] then
+ o[1] = 0
+ o.sign = 0
+ end
+
+ return o
+end
+
+--- @return Integer
+function Integer:neg()
+ local o = Integer()
+ o.sign = -self.sign
+ for i, digit in ipairs(self) do
+ o[i] = digit
+ end
+ return o
+end
+
+--- @param b Integer
+--- @return Integer
+function Integer:mul(b)
+ local o = Integer()
+ o.sign = self.sign * b.sign
+ if o.sign == 0 then
+ o[1] = 0
+ return o
+ end
+
+ -- Fast single-digit multiplication in the most common case
+ if #self == 1 and #b == 1 then
+ o[2], o[1] = self:mulone(self[1], b[1])
+
+ if o[2] == 0 then
+ o[2] = nil
+ end
+
+ return o
+ end
+
+ -- "Grade school" multiplication algorithm for numbers with small numbers of digits works faster than Karatsuba
+ local n = #self
+ local m = #b
+ o[1] = 0
+ o[2] = 0
+ for i = 2, n+m do
+ o[i + 1] = 0
+ for j = math.max(1, i-m), math.min(n, i-1) do
+ local u, l = self:mulone(self[j], b[i - j])
+ o[i - 1] = o[i - 1] + l
+ o[i] = o[i] + u
+ if o[i - 1] >= Integer.DIGITSIZE then
+ o[i - 1] = o[i - 1] - Integer.DIGITSIZE
+ o[i] = o[i] + 1
+ end
+ if o[i] >= Integer.DIGITSIZE then
+ o[i] = o[i] - Integer.DIGITSIZE
+ o[i + 1] = o[i + 1] + 1
+ end
+ end
+ end
+
+ -- Remove leading zero digits, since we want integer representations to be unique.
+ if o[n+m+1] == 0 then
+ o[n+m+1] = nil
+ end
+
+ if o[n+m] == 0 then
+ o[n+m] = nil
+ end
+
+ return o
+end
+
+--- Multiplies two single-digit numbers and returns two digits.
+--- @param a number
+--- @param b number
+--- @return number, number
+function Integer:mulone(a, b)
+ local P = Integer.PARTITIONSIZE
+
+ local a1 = a // P
+ local a2 = a % P
+ local b1 = b // P
+ local b2 = b % P
+
+ local u = a1 * b1
+ local l = a2 * b2
+
+ local m = ((a1 * b2) + (b1 * a2))
+ local mu = m // P
+ local ml = m % P
+
+ u = u + mu
+ l = l + ml * P
+
+ if l >= Integer.DIGITSIZE then
+ l = l - Integer.DIGITSIZE
+ u = u + 1
+ end
+
+ return u, l
+end
+
+--- Naive exponentiation is slow even for small exponents, so this uses binary exponentiation.
+--- @param b Integer
+--- @return Integer
+function Integer:pow(b)
+ if b < Integer.zero() then
+ return Integer.one() / (self ^ -b)
+ end
+
+ if b == Integer.zero() then
+ return Integer.one()
+ end
+
+ -- Fast single-digit exponentiation
+ if #self == 1 and #b == 1 then
+ local test = (self.sign * self[1]) ^ b[1]
+ if test < Integer.DIGITSIZE and test > -Integer.DIGITSIZE then
+ return Integer(test)
+ end
+ end
+
+ local x = self
+ local y = Integer.one()
+ while b > Integer.one() do
+ if b[1] % 2 == 0 then
+ x = x * x
+ b = b:divbytwo()
+ else
+ y = x * y
+ x = x * x
+ b = b:divbytwo()
+ end
+ end
+
+ return x * y
+end
+
+-- Fast integer division by two for binary exponentiation.
+--- @return Integer
+function Integer:divbytwo()
+ local o = Integer()
+ o.sign = self.sign
+ for i = #self, 1, -1 do
+ if self[i] % 2 == 0 then
+ o[i] = self[i] // 2
+ else
+ o[i] = self[i] // 2
+ if i ~= 1 then
+ o[i - 1] = self[i - 1] * 2
+ end
+ end
+ end
+ return o
+end
+
+--- Division with remainder over the integers. Uses the standard base 10 long division algorithm.
+--- @param b Integer
+--- @return Integer, Integer
+function Integer:divremainder(b)
+ if self >= Integer.zero() and b > self or self <= Integer.zero() and b < self then
+ return Integer.zero(), Integer(self)
+ end
+
+ if #self == 1 and #b == 1 then
+ return Integer((self[1]*self.sign) // (b[1]*b.sign)), Integer((self[1]*self.sign) % (b[1]*b.sign))
+ end
+
+ local Q = Integer()
+ local R = Integer()
+
+ Q.sign = self.sign * b.sign
+ R.sign = 1
+ local negativemod = false
+ if b.sign == -1 then
+ b.sign = -b.sign
+ negativemod = true
+ end
+
+ for i = #self, 1, -1 do
+ local s = tostring(math.floor(self[i]))
+ while i ~= #self and #s ~= Integer.DIGITLENGTH do
+ s = "0" .. s
+ end
+ Q[i] = 0
+ for j = 1, #s do
+ R = R:mulbyten()
+ R[1] = R[1] + tonumber(string.sub(s, j, j))
+ if R[1] > 0 then
+ R.sign = 1
+ end
+ while R >= b do
+ R = R - b
+ Q[i] = Q[i] + 10^(#s - j)
+ end
+ end
+ end
+
+ -- Remove leading zero digits, since we want integer representations to be unique.
+ while Q[#Q] == 0 do
+ Q[#Q] = nil
+ end
+
+ if negativemod then
+ R = -R
+ b.sign = -b.sign
+ elseif self.sign == -1 then
+ R = b - R
+ end
+
+ return Q, R
+end
+
+--- Fast in-place multiplication by ten for the division algorithm. This means the number IS MODIFIED by this method unlike the rest of the library.
+--- @return Integer
+function Integer:mulbyten()
+ local DIGITSIZE = Integer.DIGITSIZE
+ for i, _ in ipairs(self) do
+ self[i] = self[i] * 10
+ end
+ for i, _ in ipairs(self) do
+ if self[i] > DIGITSIZE then
+ local msd = self[i] // DIGITSIZE
+ if self[i+1] then
+ self[i+1] = self[i+1] + msd
+ else
+ self[i+1] = msd
+ end
+ self[i] = self[i] - DIGITSIZE*msd
+ end
+ end
+ return self
+end
+
+--- @param b Integer
+--- @return boolean
+function Integer:eq(b)
+ for i, digit in ipairs(self) do
+ if not b[i] or not (b[i] == digit) then
+ return false
+ end
+ end
+ return #self == #b and self.sign == b.sign
+end
+
+--- @param b Integer
+--- @return boolean
+function Integer:lt(b)
+ local selfsize = #self
+ local bsize = #b
+ if selfsize < bsize then
+ return b.sign == 1
+ end
+ if selfsize > bsize then
+ return self.sign == -1
+ end
+ local n = selfsize
+ while n > 0 do
+ if self[n]*self.sign < b[n]*b.sign then
+ return true
+ end
+ if self[n]*self.sign > b[n]*b.sign then
+ return false
+ end
+ n = n - 1
+ end
+ return false
+end
+
+--- Same as less than, but ignores signs.
+--- @param b Integer
+--- @return boolean
+function Integer:ltabs(b)
+ if #self < #b then
+ return true
+ end
+ if #self > #b then
+ return false
+ end
+ local n = #self
+ while n > 0 do
+ if self[n] < b[n] then
+ return true
+ end
+ if self[n] > b[n] then
+ return false
+ end
+ n = n - 1
+ end
+ return false
+end
+
+--- @param b Integer
+--- @return boolean
+function Integer:le(b)
+ local selfsize = #self
+ local bsize = #b
+ if selfsize < bsize then
+ return b.sign == 1
+ end
+ if selfsize > bsize then
+ return self.sign == -1
+ end
+ local n = selfsize
+ while n > 0 do
+ if self[n]*self.sign < b[n]*b.sign then
+ return true
+ end
+ if self[n]*self.sign > b[n]*b.sign then
+ return false
+ end
+ n = n - 1
+ end
+ return true
+end
+
+local zero = Integer:new(0)
+--- @return Integer
+function Integer:zero()
+ return zero
+end
+
+local one = Integer:new(1)
+--- @return Integer
+function Integer:one()
+ return one
+end
+
+--- Returns this integer as a floating point number. Can only approximate the value of large integers.
+--- @return number
+function Integer:asnumber()
+ local n = 0
+ for i, digit in ipairs(self) do
+ n = n + digit * Integer.DIGITSIZE ^ (i - 1)
+ end
+ return self.sign*math.floor(n)
+end
+
+--- Returns all positive divisors of the integer. Not guarenteed to be in any order.
+--- @return table<number, Integer>
+function Integer:divisors()
+ local primefactors = self:primefactorizationrec()
+ local divisors = {}
+
+ local terms = {}
+ for prime in pairs(primefactors) do
+ if prime == Integer(-1) then
+ primefactors[prime] = nil
+ end
+ terms[prime] = Integer.zero()
+ end
+
+ local divisor = Integer.one()
+
+ while true do
+ divisors[#divisors+1] = divisor
+ for prime, power in pairs(primefactors) do
+ if terms[prime] < power then
+ terms[prime] = terms[prime] + Integer.one()
+ divisor = divisor * prime
+ break
+ else
+ terms[prime] = Integer.zero()
+ divisor = divisor / (prime ^ power)
+ end
+ end
+ if divisor == Integer.one() then
+ break
+ end
+ end
+
+ return divisors
+end
+
+--- Returns whether this integer is a prime power, of the form p^a for prime p and positive integer a.
+--- If it is a prime power, also returns the prime and the power.
+--- @return boolean, Expression|nil, Expression|nil
+function Integer:isprimepower()
+ if self <= Integer.one() then
+ return false
+ end
+ local factorization = self:primefactorization()
+ if factorization:type() == BinaryOperation and #factorization:subexpressions() == 1 then
+ return true, factorization.expressions[1].expressions[2], factorization.expressions[1].expressions[1]
+ end
+ return false
+end
+
+--- Returns whether this integer is a perfect power, of the form a^b for positive integers a and b.
+--- If it is a prime power, also returns the prime and the power.
+--- @return boolean, Expression|nil, Expression|nil
+function Integer:isperfectpower()
+ if self <= Integer.one() then
+ return false
+ end
+ local factorization = self:primefactorization()
+ if factorization:type() ~= BinaryOperation then
+ return false
+ end
+ local power = Integer.zero()
+ for _, term in ipairs(factorization:subexpressions()) do
+ power = Integer.gcd(power, term.expressions[2])
+ if power == Integer.one() then
+ return false
+ end
+ end
+ local base = Integer.one()
+ for _, term in ipairs(factorization:subexpressions()) do
+ base = base * term.expressions[1] ^ (term.expressions[2] / power)
+ end
+ return true, base, power
+end
+
+--- Returns the prime factorization of this integer as a expression.
+--- @return Expression
+function Integer:primefactorization()
+ if not Integer.FACTORIZATIONLIMIT then
+ Integer.FACTORIZATIONLIMIT = Integer(Integer.DIGITSIZE)
+ end
+ if self > Integer.FACTORIZATIONLIMIT then
+ return self
+ end
+ local result = self:primefactorizationrec()
+ local mul = {}
+ local i = 1
+ for factor, degree in pairs(result) do
+ mul[i] = BinaryOperation.POWEXP({factor, degree})
+ i = i + 1
+ end
+ return BinaryOperation.MULEXP(mul):lock(Expression.NIL)
+end
+
+--- Recursive part of prime factorization using Pollard Rho.
+function Integer:primefactorizationrec()
+ if self < Integer.zero() then
+ return Integer.mergefactors({[Integer(-1)]=Integer.one()}, (-self):primefactorizationrec())
+ end
+ if self == Integer.one() then
+ return {[Integer.one()]=Integer.one()}
+ end
+ local result = self:findafactor()
+ if result == self then
+ return {[result]=Integer.one()}
+ end
+ local remaining = self / result
+ return Integer.mergefactors(result:primefactorizationrec(), remaining:primefactorizationrec())
+end
+
+
+function Integer.mergefactors(a, b)
+ local result = Copy(a)
+
+ for factor, degree in pairs(b) do
+ for ofactor, odegree in pairs(result) do
+ if factor == ofactor then
+ result[ofactor] = degree + odegree
+ goto continue
+ end
+ end
+ result[factor] = degree
+ ::continue::
+ end
+ return result
+end
+
+-- Return a non-trivial factor of n via Pollard Rho, or returns n if n is prime.
+function Integer:findafactor()
+ if self:isprime() then
+ return self
+ end
+
+ if self % Integer(2) == Integer.zero() then
+ return Integer(2)
+ end
+
+ if self % Integer(3) == Integer.zero() then
+ return Integer(3)
+ end
+
+ if self % Integer(5) == Integer.zero() then
+ return Integer(5)
+ end
+
+ local g = function(x)
+ local temp = Integer.powmod(x, Integer(2), self)
+ return temp
+ end
+
+ local xstart = Integer(2)
+ while xstart < self do
+ local x = xstart
+ local y = xstart
+ local d = Integer.one()
+ while d == Integer.one() do
+ x = g(x)
+ y = g(g(y))
+ d = Integer.gcd((x - y):abs(), self)
+ end
+
+ if d < self then
+ return d
+ end
+
+ xstart = xstart + Integer.one()
+ end
+end
+
+--- Uses Miller-Rabin to determine whether a number is prime up to a very large number.
+local smallprimes = {Integer:new(2), Integer:new(3), Integer:new(5), Integer:new(7), Integer:new(11), Integer:new(13), Integer:new(17),
+Integer:new(19), Integer:new(23), Integer:new(29), Integer:new(31), Integer:new(37), Integer:new(41), Integer:new(43), Integer:new(47)}
+
+function Integer:isprime()
+ if self % Integer(2) == Integer.zero() then
+ if self == Integer(2) then
+ return true
+ end
+ return false
+ end
+
+ if self == Integer.one() then
+ return false
+ end
+
+ for _, value in pairs(smallprimes) do
+ if value == self then
+ return true
+ end
+ end
+
+ local r = Integer.zero()
+ local d = self - Integer.one()
+ while d % Integer(2) == Integer.zero() do
+ r = r + Integer.one()
+ d = d / Integer(2)
+ end
+
+ for _, a in ipairs(smallprimes) do
+ local s = r
+ local x = Integer.powmod(a, d, self)
+ if x == Integer.one() or x == self - Integer.one() then
+ goto continue
+ end
+
+ while s > Integer.zero() do
+ x = Integer.powmod(x, Integer(2), self)
+ if x == self - Integer.one() then
+ goto continue
+ end
+ s = s - Integer.one()
+ end
+ do
+ return false
+ end
+ ::continue::
+ end
+
+ return true
+end
+
+--- Returns the absolute value of an integer.
+--- @return Integer
+function Integer:abs()
+ if self.sign >= 0 then
+ return Integer(self)
+ end
+ return -self
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__Integer.__index = EuclideanDomain
+__Integer.__call = Integer.new
+Integer = setmetatable(Integer, __Integer)
+
+----------------------
+-- Static constants --
+----------------------
+
+Integer.FACTORIZATIONLIMIT = Integer(Integer.DIGITSIZE) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/integerquotientring.lua b/macros/luatex/latex/luacas/tex/algebra/integerquotientring.lua
new file mode 100644
index 0000000000..4c2188c9e5
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/integerquotientring.lua
@@ -0,0 +1,197 @@
+--- @class IntegerModN
+--- Represents an element of the ring of integers mod n (this is also a field iff n is prime).
+--- @field element Integer
+--- @field modulus Integer
+
+IntegerModN = {}
+__IntegerModN = {}
+
+-- Metatable for ring objects.
+local __obj = {__index = IntegerModN, __eq = function(a, b)
+ return a["ring"] == b["ring"] and (a["modulus"] == b["modulus"] or a["modulus"] == nil or b["modulus"] == nil)
+end, __tostring = function(a)
+ if a.modulus then return "Z/Z" .. tostring(a.modulus) else return "(Generic Integer Mod Ring)" end
+end}
+
+--------------------------
+-- Static functionality --
+--------------------------
+
+--- Creates a new ring with the given modulus.
+--- @param modulus Integer
+--- @return RingIdentifier
+function IntegerModN.makering(modulus)
+ local t = {ring = IntegerModN}
+ t.modulus = modulus
+ t = setmetatable(t, __obj)
+ return t
+end
+
+-- Shorthand constructor for a ring with a particular modulus.
+function IntegerModN.R(modulus)
+ return IntegerModN.makering(modulus)
+end
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+-- So we don't have to copy the field operations each time
+local __o
+__o = Copy(__FieldOperations)
+
+__o.__index = IntegerModN
+__o.__tostring = function(a)
+ return tostring(a.element)
+end
+
+--- Creates a new integer i in Z/nZ.
+--- @param i Integer
+--- @param n Integer
+--- @return IntegerModN
+function IntegerModN:new(i, n)
+ local o = {}
+
+ if n:getring() ~= Integer:getring() or n < Integer.one() then
+ error("Argument error: modulus must be an integer greater than 0.")
+ end
+
+ o = setmetatable(o, __o)
+
+ if i < Integer.zero() or i >= n then
+ i = i % n
+ end
+
+ o.element = i
+ o.modulus = n
+
+ return o
+end
+
+--- @return RingIdentifier
+function IntegerModN:getring()
+ local t = {ring = IntegerModN}
+ if self then
+ t.modulus = self.modulus
+ end
+ t = setmetatable(t, __obj)
+ return t
+end
+
+--- @param ring RingIdentifier
+--- @return Ring
+function IntegerModN:inring(ring)
+ if ring == IntegerModN:getring() then
+ if ring.modulus then
+ return IntegerModN(self.element, ring.modulus)
+ end
+ return self
+ end
+
+ if ring == PolynomialRing:getring() then
+ return PolynomialRing({self:inring(ring.child)}, ring.symbol)
+ end
+
+ if ring == Rational:getring() and ring.symbol then
+ return Rational(self:inring(ring.child), self:inring(ring.child):one(), true)
+ end
+
+ if ring == Integer:getring() then
+ return self.element:inring(ring)
+ end
+
+ error("Unable to convert element to proper ring.")
+end
+
+--- @param b IntegerModN
+--- @return IntegerModN
+function IntegerModN:add(b)
+ return IntegerModN(self.element + b.element, self.modulus)
+end
+
+--- @return IntegerModN
+function IntegerModN:neg()
+ return IntegerModN(-self.element, self.modulus)
+end
+
+--- @param b IntegerModN
+--- @return IntegerModN
+function IntegerModN:mul(b)
+ return IntegerModN(self.element * b.element, self.modulus)
+end
+
+-- Overrides the generic power method with powmod.
+--- @param b IntegerModN
+--- @return IntegerModN
+function IntegerModN:pow(b)
+ return IntegerModN(Integer.powmod(self.element, b.element, self.modulus), self.modulus)
+end
+
+-- Returns the multiplicative inverse of this number if it exists.
+--- @return IntegerModN
+function IntegerModN:inv()
+ local r, t, _ = Integer.extendedgcd(self.element, self.modulus)
+
+ if r > Integer.one() then
+ error("Element does not have an inverse in this ring")
+ end
+
+ return IntegerModN(t, self.modulus)
+end
+
+--- @param b IntegerModN
+--- @return IntegerModN
+function IntegerModN:div(b)
+ return self:mul(b:inv())
+end
+
+--- @param b IntegerModN
+--- @return boolean
+function IntegerModN:eq(b)
+ return self.element == b.element
+end
+
+--- @param b IntegerModN
+--- @return boolean
+function IntegerModN:lt(b)
+ return self.element < b.element
+end
+
+--- @param b IntegerModN
+--- @return boolean
+function IntegerModN:le(b)
+ return self.element <= b.element
+end
+
+--- @return IntegerModN
+function IntegerModN:zero()
+ if not self or not self.modulus then
+ return Integer.zero()
+ end
+ return IntegerModN(Integer.zero(), self.modulus)
+end
+
+--- @return IntegerModN
+function IntegerModN:one()
+ if not self or not self.modulus then
+ return Integer.one()
+ end
+ return IntegerModN(Integer.one(), self.modulus)
+end
+
+--- @return string
+function IntegerModN:tolatex(mod)
+ mod = mod or false
+ if mod then
+ return self.element:tolatex() .. "\\bmod{" .. self.modulus:tolatex() .. "}"
+ else
+ return self.element:tolatex()
+ end
+end
+-----------------
+-- Inheritance --
+-----------------
+
+__IntegerModN.__index = Field
+__IntegerModN.__call = IntegerModN.new
+IntegerModN = setmetatable(IntegerModN, __IntegerModN) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/logarithm.lua b/macros/luatex/latex/luacas/tex/algebra/logarithm.lua
new file mode 100644
index 0000000000..07a9db84d1
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/logarithm.lua
@@ -0,0 +1,186 @@
+--- @class Logarithm
+--- An expression for the logarithm of an expression with respect to another.
+--- Currently, logarithms are not being evaluated since we are just doing symbolic computation.
+--- @field base Expression
+--- @field expression Expression
+Logarithm = {}
+__Logarithm = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new logarithm expression with the given symbol and expression.
+--- @param base Expression
+--- @param expression Expression
+--- @
+function Logarithm:new(base, expression)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ o.base = Copy(base)
+ o.expression = Copy(expression)
+
+ __o.__index = Logarithm
+ __o.__tostring = function(a)
+ return 'log(' .. tostring(base) .. ', ' .. tostring(expression) .. ')'
+ end
+ __o.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always runs this anyway
+ if not b:type() == Logarithm then
+ return false
+ end
+ return a.base == b.base and a.expression == b.expression
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- @return Expression
+function Logarithm:evaluate()
+
+ if (self.base:isconstant() and (self.base <= Integer.zero() or self.base == Integer.one())) or
+ (self.expression:isconstant() and self.expression <= Integer.zero()) then
+ error("Arithmetic error: division by zero")
+ end
+
+ if not self.base:isconstant() or not self.expression:isconstant() then
+ return self
+ end
+
+ local power = Integer.one()
+ local base = self.base
+ if base:type() == Integer then
+ local pp, b, p = base:isperfectpower()
+ if pp then
+ base = b
+ power = p
+ end
+ end
+
+ if base:type() == Rational then
+ local ppn, bn, pn = base.numerator:isperfectpower()
+ local ppd, bd, pd = base.denominator:isperfectpower()
+ if base.numerator == Integer.one() then
+ ppn = true
+ bn = Integer.one()
+ pn = pd
+ end
+ if ppn and ppd and pn == pd then
+ base = bn / bd
+ power = pn
+ end
+ end
+
+ local result = Integer.one()
+ local expression = self.expression
+ local sign = Integer.one()
+ if base < Integer.one() then
+ base = Integer.one() / base
+ sign = -sign
+ end
+
+
+ local current = base
+ while current < expression do
+ current = current * base
+ result = result + Integer.one()
+ end
+ if current == expression then
+ return sign * result / power
+ else
+ while current > expression do
+ current = current / base
+ result = result - Integer.one()
+ end
+ if current == expression then
+ return sign * result / power
+ end
+ end
+
+ return self
+end
+
+--- @return Expression
+function Logarithm:autosimplify()
+
+ local base = self.base:autosimplify()
+ local expression = self.expression:autosimplify()
+
+ local evaluated = Logarithm(base, expression):evaluate()
+ if evaluated:type() ~= Logarithm then
+ return evaluated
+ end
+
+ -- Uses the property that log(b, 1) = 0
+ if expression == Integer.one() then
+ return Integer.zero()
+ end
+
+ -- Uses the property that log(b, b) = 1
+ if expression == base then
+ return Integer.one()
+ end
+
+ -- Uses the propery that log(b, x^y) = y * log(b, x)
+ if expression.operation == BinaryOperation.POW then
+ return BinaryOperation.MULEXP({expression.expressions[2], Logarithm(base, expression.expressions[1])}):autosimplify()
+ end
+
+ if expression:type() == Rational and expression.numerator == Integer.one() then
+ return (-Logarithm(base, expression.denominator)):autosimplify()
+ end
+
+ -- Our expression cannot be simplified
+ return Logarithm(base, expression)
+end
+
+--- @return Expression
+function Logarithm:expand()
+ return Logarithm(self.base:expand(), self.expression:expand()):autosimplify()
+end
+
+--- @return table<number, Expression>
+function Logarithm:subexpressions()
+ return {self.base, self.expression}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return Logarithm
+function Logarithm:setsubexpressions(subexpressions)
+ return Logarithm(subexpressions[1], subexpressions[2])
+end
+
+--- @param other Expression
+--- @return boolean
+function Logarithm:order(other)
+ return FunctionExpression("log", {self.base, self.expression}):order(other)
+end
+
+--- @return string
+function Logarithm:tolatex()
+ if self.base == E then
+ return '\\ln\\mathopen{}\\left(' .. self.expression:tolatex() .. '\\right)'
+ end
+ return '\\log_' .. self.base:tolatex() .. '\\mathopen{}\\left(' .. self.expression:tolatex() .. '\\right)'
+end
+
+-----------------
+-- Inheritance --
+-----------------
+__Logarithm.__index = CompoundExpression
+__Logarithm.__call = Logarithm.new
+Logarithm = setmetatable(Logarithm, __Logarithm)
+
+----------------------
+-- Static constants --
+----------------------
+
+LOG = function(base, expression)
+ return Logarithm(base, expression)
+end
+
+LN = function(expression)
+ return Logarithm(E, expression)
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/polynomialring.lua b/macros/luatex/latex/luacas/tex/algebra/polynomialring.lua
new file mode 100644
index 0000000000..568c21c921
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/polynomialring.lua
@@ -0,0 +1,860 @@
+--- @class PolynomialRing
+--- Represents an element of a polynomial ring.
+--- @field coefficients table<number, Ring>
+--- @field symbol SymbolExpression
+--- @field ring RingIdentifier
+PolynomialRing = {}
+__PolynomialRing = {}
+
+-- Metatable for ring objects.
+local __obj = {__index = PolynomialRing, __eq = function(a, b)
+ return a["ring"] == b["ring"] and
+ (a["child"] == b["child"] or a["child"] == nil or b["child"] == nil) and
+ (a["symbol"] == b["symbol"] or a["child"] == nil or b["child"] == nil)
+end, __tostring = function(a)
+ if a.child and a.symbol then return tostring(a.child) .. "[" .. a.symbol .. "]" else return "(Generic Polynomial Ring)" end
+end}
+
+--------------------------
+-- Static functionality --
+--------------------------
+
+--- Creates a new ring with the given symbol and child ring.
+--- @param symbol SymbolExpression
+--- @param child RingIdentifier
+--- @return RingIdentifier
+function PolynomialRing.makering(symbol, child)
+ local t = {ring = PolynomialRing}
+ t.symbol = symbol
+ t.child = child
+ t = setmetatable(t, __obj)
+ return t
+end
+
+-- Shorthand constructor for a polynomial ring with integer or integer mod ring coefficients.
+function PolynomialRing.R(symbol, modulus)
+ if modulus then
+ return PolynomialRing.makering(symbol, IntegerModN.makering(modulus))
+ end
+ return PolynomialRing.makering(symbol, Integer.getring())
+end
+
+--- Returns the GCD of two polynomials in a ring, assuming both rings are euclidean domains.
+--- @param a PolynomialRing
+--- @param b PolynomialRing
+--- @return PolynomialRing
+function PolynomialRing.gcd(a, b)
+ if a.symbol ~= b.symbol then
+ error("Cannot take the gcd of two polynomials with different symbols")
+ end
+ while b ~= Integer.zero() do
+ a, b = b, a % b
+ end
+ return a // a:lc()
+end
+
+-- Returns the GCD of two polynomials in a ring, assuming both rings are euclidean domains.
+-- Also returns bezouts coefficients via extended gcd.
+--- @param a PolynomialRing
+--- @param b PolynomialRing
+--- @return PolynomialRing, PolynomialRing, PolynomialRing
+function PolynomialRing.extendedgcd(a, b)
+ local oldr, r = a, b
+ local olds, s = Integer.one(), Integer.zero()
+ local oldt, t = Integer.zero(), Integer.one()
+ while r ~= Integer.zero() do
+ local q = oldr // r
+ oldr, r = r, oldr - q*r
+ olds, s = s, olds - q*s
+ oldt, t = t, oldt - q*t
+ end
+ return oldr // oldr:lc(), olds // oldr:lc(), oldt // oldr:lc()
+end
+
+-- Returns the resultant of two polynomials in the same ring, whose coefficients are all part of a field.
+--- @param a PolynomialRing
+--- @param b PolynomialRing
+--- @return Field
+function PolynomialRing.resultant(a, b)
+
+ if a.ring == PolynomialRing.getring() or b.ring == PolynomialRing.getring() then
+ return PolynomialRing.resultantmulti(a, b)
+ end
+
+ local m, n = a.degree, b.degree
+ if n == Integer.zero() then
+ return b.coefficients[0]^m
+ end
+
+ local r = a % b
+ if r == Integer.zero() then
+ return r.coefficients[0]
+ end
+
+ local s = r.degree
+ local l = b:lc()
+
+ return Integer(-1)^(m*n) * l^(m-s) * PolynomialRing.resultant(b, r)
+end
+
+-- Returns the resultant of two polynomials in the same ring, whose coefficients are not part of a field.
+--- @param a PolynomialRing
+--- @param b PolynomialRing
+--- @return Ring
+function PolynomialRing.resultantmulti(a, b)
+ local m, n = a.degree, b.degree
+
+ if m < n then
+ return Integer(-1) ^ (m * n) * PolynomialRing.resultantmulti(b, a)
+ end
+ if n == Integer.zero() then
+ return b.coefficients[0]^m
+ end
+
+ local delta = m - n + Integer(1)
+ local _ , r = PolynomialRing.pseudodivide(a, b)
+ if r == Integer.zero() then
+ return r.coefficients[0]
+ end
+
+ local s = r.degree
+ local w = Integer(-1)^(m*n) * PolynomialRing.resultant(b, r)
+ local l = b:lc()
+ local k = delta * n - m + s
+ local f = l ^ k
+ return w // f
+end
+
+-- Given two polynomials a and b, returns a list of the remainders generated by the monic Euclidean algorithm.
+--- @param a PolynomialRing
+--- @param b PolynomialRing
+--- @return table<number, Ring>
+function PolynomialRing.monicgcdremainders(a, b)
+ if a.symbol ~= b.symbol then
+ error("Cannot take the gcd of two polynomials with different symbols")
+ end
+
+ local remainders = {a / a:lc(), b / b:lc()}
+ while true do
+ local q = remainders[#remainders - 1] // remainders[#remainders]
+ local c = remainders[#remainders - 1] - q*remainders[#remainders]
+ if c ~= Integer.zero() then
+ remainders[#remainders+1] = c/c:lc()
+ else
+ break
+ end
+ end
+
+ return remainders
+end
+
+-- Returns the partial fraction decomposition of the rational function g/f
+-- given g, f, and some (not nessecarily irreducible) factorization of f.
+-- If the factorization is omitted, the irreducible factorization is used.
+-- The degree of g must be less than the degree of f.
+--- @param g PolynomialRing
+--- @param f PolynomialRing
+--- @param ffactors Expression
+--- @return Expression
+function PolynomialRing.partialfractions(g, f, ffactors)
+
+ if g.degree >= f.degree then
+ error("Argument Error: The degree of g must be less than the degree of f.")
+ end
+
+ -- Converts f to a monic polynomial.
+ g = g * f:lc()
+ f = f / f:lc()
+
+ ffactors = ffactors or f:factor()
+
+ local expansionterms = {}
+
+ for _, factor in ipairs(ffactors.expressions) do
+ local k
+ local m
+ if factor.getring and factor:getring() == PolynomialRing:getring() then
+ m = factor
+ k = Integer.one()
+ elseif not factor:isconstant() then
+ m = factor.expressions[1]
+ k = factor.expressions[2]
+ end
+
+ if not factor:isconstant() then
+ -- Uses Chinese Remainder Theorem for each factor to determine the numerator of the term in the decomposition
+ local mk = m^k
+ local v = g % mk
+ local _, minv, _ = PolynomialRing.extendedgcd(f // mk, mk)
+ local c = v*minv % mk
+
+
+ if k == Integer.one() then
+ expansionterms[#expansionterms+1] = BinaryOperation.ADDEXP({BinaryOperation.DIVEXP({c, BinaryOperation.POWEXP({m, Integer.one()})})})
+ else
+ -- Uses the p-adic expansion of c to split terms with repeated roots.
+ local q = c
+ local r
+ local innerterms = {}
+ for i = k:asnumber(), 1, -1 do
+ q, r = q:divremainder(m)
+ innerterms[#innerterms+1] = BinaryOperation.DIVEXP({r, BinaryOperation.POWEXP({m, Integer(i)})})
+ end
+ expansionterms[#expansionterms+1] = BinaryOperation.ADDEXP(innerterms)
+ end
+ end
+ end
+
+ return BinaryOperation.ADDEXP(expansionterms)
+
+end
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+-- So we don't have to copy the Euclidean operations each time
+local __o = Copy(__EuclideanOperations)
+__o.__index = PolynomialRing
+__o.__tostring = function(a)
+ local out = ""
+ local loc = a.degree:asnumber()
+ while loc >= 0 do
+ if a.ring == PolynomialRing.getring() or (a.ring == Rational.getring() and a.ring.symbol) then
+ out = out .. "(" .. tostring(a.coefficients[loc]) .. ")" .. a.symbol .. "^" .. tostring(math.floor(loc)) .. "+"
+ else
+ out = out .. tostring(a.coefficients[loc]) .. a.symbol .. "^" .. tostring(math.floor(loc)) .. "+"
+ end
+ loc = loc - 1
+ end
+ return string.sub(out, 1, string.len(out) - 1)
+end
+__o.__div = function(a, b)
+ if not b.getring then
+ return BinaryOperation.DIVEXP({a, b})
+ end
+ if Ring.resultantring(a.ring, b:getring()) ~= Ring.resultantring(a:getring(), b:getring()) then
+ return a:div(b:inring(Ring.resultantring(a:getring(), b:getring())))
+ end
+ if b.ring and b:getring() == Rational:getring() and a.symbol == b.ring.symbol then
+ return a:inring(Ring.resultantring(a:getring(), b:getring())):div(b)
+ end
+ if a:getring() == b:getring() then
+ return Rational(a, b, true)
+ end
+ -- TODO: Fix this for arbitrary depth
+ if a:getring() == PolynomialRing:getring() and b:getring() == PolynomialRing:getring() and a.symbol == b.symbol then
+ local oring = Ring.resultantring(a:getring(), b:getring())
+ return Rational(a:inring(oring), b:inring(oring), true)
+ end
+ return BinaryOperation.DIVEXP({a, b})
+end
+
+function PolynomialRing:tolatex()
+ local out = ''
+ local loc = self.degree:asnumber()
+ if loc == 0 then
+ return self.coefficients[loc]: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
+ else
+ out = out .. self.symbol
+ end
+ if loc ~=1 then
+ out = out .. "^{" .. loc .. "}"
+ end
+ loc = loc -1
+ while loc >=0 do
+ local coeff = self.coefficients[loc]
+ if coeff == Integer.one() then
+ if loc == 0 then
+ out = out .. "+" .. coeff:tolatex()
+ goto skip
+ else
+ out = out .. "+"
+ goto continue
+ end
+ end
+ if coeff == Integer(-1) then
+ if loc == 0 then
+ out = out .. "-" .. coeff:neg():tolatex()
+ goto skip
+ else
+ out = out .. "-"
+ goto continue
+ end
+ end
+ if coeff < Integer.zero() then
+ out = out .. "-" .. coeff:neg():tolatex()
+ end
+ if coeff == Integer.zero() then
+ goto skip
+ end
+ if coeff > Integer.zero() then
+ out = out .. "+" .. coeff:tolatex()
+ end
+ ::continue::
+ if loc > 1 then
+ out = out .. self.symbol .. "^{" .. loc .. "}"
+ end
+ if loc == 1 then
+ out = out .. self.symbol
+ end
+ ::skip::
+ loc = loc-1
+ end
+ else
+ while loc >=0 do
+ if loc >=1 then
+ out = out .. self.coefficients[loc]:tolatex() .. self.symbol .. "^{" .. loc .. "} + "
+ else
+ out = out .. self.coefficients[loc]:tolatex() .. self.symbol .. "^{" .. loc .. "}"
+ end
+ loc = loc-1
+ end
+ end
+ return out
+end
+
+function PolynomialRing:isatomic()
+ --if self.degree >= Integer.one() then
+ -- return false
+ --else
+ return false
+ --end
+end
+--test
+
+-- 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)
+
+ if type(coefficients) ~= "table" then
+ error("Sent parameter of wrong type: Coefficients must be in an array")
+ end
+ o.coefficients = {}
+ o.degree = degree or Integer(-1)
+
+ if type(symbol) ~= "string" and not symbol.symbol then
+ error("Symbol must be a string")
+ end
+ o.symbol = symbol.symbol or symbol
+
+ -- Determines what ring the polynomial ring should have as its child
+ for index, coefficient in pairs(coefficients) do
+ if type(index) ~= "number" then
+ error("Sent parameter of wrong type: Coefficients must be in an array")
+ end
+ if not coefficient.getring then
+ error("Sent parameter of wrong type: Coefficients must be elements of a ring")
+ end
+ if not o.ring then
+ o.ring = coefficient:getring()
+ else
+ local newring = coefficient:getring()
+ local combinedring = Ring.resultantring(o.ring, newring)
+ if combinedring == newring then
+ o.ring = newring
+ elseif not o.ring == combinedring then
+ error("Sent parameter of wrong type: Coefficients must all be part of the same ring")
+ end
+ end
+ end
+
+ if not coefficients[0] then
+ -- Constructs the coefficients when a new polynomial is instantiated as an array
+ for index, coefficient in ipairs(coefficients) do
+ o.coefficients[index - 1] = coefficient
+ o.degree = o.degree + Integer.one()
+ end
+ else
+ -- Constructs the coefficients from an existing polynomial of coefficients
+ local loc = o.degree:asnumber()
+ while loc > 0 do
+ if not coefficients[loc] or coefficients[loc] == coefficients[loc]:zero() then
+ o.degree = o.degree - Integer.one()
+ else
+ break
+ end
+ loc = loc - 1
+ end
+
+ while loc >= 0 do
+ o.coefficients[loc] = coefficients[loc]
+ loc = loc - 1
+ end
+ end
+
+ -- Each value of the polynomial greater than its degree is implicitly zero
+ o.coefficients = setmetatable(o.coefficients, {__index = function (table, key)
+ return o:zeroc()
+ end})
+ return o
+end
+
+-- Returns the ring this object is an element of
+function PolynomialRing:getring()
+ local t = {ring = PolynomialRing}
+ if self then
+ t.child = self.ring
+ t.symbol = self.symbol
+ end
+ t = setmetatable(t, __obj)
+ return t
+end
+
+-- Explicitly converts this element to an element of another ring
+function PolynomialRing:inring(ring)
+
+ -- Faster equality check
+ if ring == self:getring() then
+ return self
+ end
+
+ if ring == Rational:getring() and ring.symbol then
+ return Rational(self:inring(ring.child), self:inring(ring.child):one(), true)
+ end
+
+ if ring.symbol == self.symbol then
+ local out = {}
+ for i = 0, self.degree:asnumber() do
+ out[i + 1] = self.coefficients[i]:inring(ring.child)
+ end
+ return PolynomialRing(out, self.symbol)
+ end
+
+ -- TODO: Allow re-ordering of polynomial rings, so from R[x][y] -> R[y][x] for instance
+ if ring == PolynomialRing:getring() then
+ return PolynomialRing({self:inring(ring.child)}, ring.symbol)
+ end
+
+ error("Unable to convert element to proper ring.")
+end
+
+
+-- Returns whether the ring is commutative
+function PolynomialRing:iscommutative()
+ return true
+end
+
+function PolynomialRing:add(b)
+ local larger
+
+ if self.degree > b.degree then
+ larger = self
+ else
+ larger = b
+ end
+
+ local new = {}
+ local loc = 0
+ while loc <= larger.degree:asnumber() do
+ new[loc] = self.coefficients[loc] + b.coefficients[loc]
+ loc = loc + 1
+ end
+
+ return PolynomialRing(new, self.symbol, larger.degree)
+end
+
+function PolynomialRing:neg()
+ local new = {}
+ local loc = 0
+ while loc <= self.degree:asnumber() do
+ new[loc] = -self.coefficients[loc]
+ loc = loc + 1
+ end
+ return PolynomialRing(new, self.symbol, self.degree)
+end
+
+function PolynomialRing:mul(b)
+ -- Grade-school multiplication is actually faster up to a very large polynomial size due to Lua's overhead.
+ local new = {}
+
+ local sd = self.degree:asnumber()
+ local bd = b.degree:asnumber()
+
+ for i = 0, sd+bd do
+ new[i] = self:zeroc()
+ for j = math.max(0, i-bd), math.min(sd, i) do
+ new[i] = new[i] + self.coefficients[j]*b.coefficients[i-j]
+ end
+ end
+ return PolynomialRing(new, self.symbol, self.degree + b.degree)
+ -- return PolynomialRing(PolynomialRing.mul_rec(self.coefficients, b.coefficients), self.symbol, self.degree + b.degree)
+end
+
+-- Performs Karatsuba multiplication without constructing new polynomials recursively
+function PolynomialRing.mul_rec(a, b)
+ if #a==0 and #b==0 then
+ return {[0]=a[0] * b[0], [1]=Integer.zero()}
+ end
+
+ local k = Integer.ceillog(Integer.max(Integer(#a), Integer(#b)) + Integer.one(), Integer(2))
+ local n = Integer(2) ^ k
+ local m = n / Integer(2)
+ local nn = n:asnumber()
+ local mn = m:asnumber()
+
+ local a0, a1, b0, b1 = {}, {}, {}, {}
+
+ for e = 0, mn - 1 do
+ a0[e] = a[e] or Integer.zero()
+ a1[e] = a[e + mn] or Integer.zero()
+ b0[e] = b[e] or Integer.zero()
+ b1[e] = b[e + mn] or Integer.zero()
+ end
+
+ local p1 = PolynomialRing.mul_rec(a1, b1)
+ local p2a = Copy(a0)
+ local p2b = Copy(b0)
+ for e = 0, mn - 1 do
+ p2a[e] = p2a[e] + a1[e]
+ p2b[e] = p2b[e] + b1[e]
+ end
+ local p2 = PolynomialRing.mul_rec(p2a, p2b)
+ local p3 = PolynomialRing.mul_rec(a0, b0)
+ local r = {}
+ for e = 0, mn - 1 do
+ p2[e] = p2[e] - p1[e] - p3[e]
+ r[e] = p3[e]
+ r[e + mn] = p2[e]
+ r[e + nn] = p1[e]
+ end
+ for e = mn, nn - 1 do
+ p2[e] = p2[e] - p1[e] - p3[e]
+ r[e] = r[e] + p3[e]
+ r[e + mn] = r[e + mn] + p2[e]
+ r[e + nn] = p1[e]
+ end
+
+ return r
+end
+
+-- Uses synthetic division.
+function PolynomialRing:divremainder(b)
+ local n, m = self.degree:asnumber(), b.degree:asnumber()
+
+ if m > n then
+ return self:zero(), self
+ end
+
+ local o = Copy(self.coefficients)
+ local lc = b:lc()
+ for i = n, m, -1 do
+ o[i] = o[i] / lc
+
+ if o[i] ~= self:zeroc() then
+ for j = 1, m do
+ o[i-j] = o[i-j] - b.coefficients[m - j] * o[i]
+ end
+ end
+ end
+
+ local q = {}
+ local r = {}
+ for i = 0, m-1 do
+ r[i] = o[i]
+ end
+
+ r[0] = r[0] or self:zeroc()
+
+ for i = m, #o do
+ q[i - m] = o[i]
+ end
+
+ return PolynomialRing(q, self.symbol, self.degree), PolynomialRing(r, self.symbol, Integer.max(Integer.zero(), b.degree-Integer.one()))
+end
+
+-- Performs polynomial pseudodivision of this polynomial by another in the same ring,
+-- and returns both the pseudoquotient and pseudoremainder.
+-- In the case where both coefficients are fields, this is equivalent to division with remainder.
+function PolynomialRing:pseudodivide(b)
+
+ local p = self:zero()
+ local s = self
+ local m = s.degree
+ local n = b.degree
+ local delta = Integer.max(m - n + Integer.one(), Integer.zero())
+
+ local lcb = b:lc()
+ local sigma = Integer.zero()
+
+ while m >= n and s ~= Integer.zero() do
+ local lcs = s:lc()
+ p = p * lcb + self:one():multiplyDegree((m-n):asnumber()) * lcs
+ s = s * lcb - b * self:one():multiplyDegree((m-n):asnumber()) * lcs
+ sigma = sigma + Integer.one()
+ m = s.degree
+ end
+
+ if delta - sigma == Integer.zero() then
+ return p,s
+ else
+ return lcb^(delta - sigma) * p, lcb^(delta - sigma) * s
+ end
+end
+
+-- Polynomial rings are never fields, but when dividing by a polynomial by a constant we may want to use / instead of //
+function PolynomialRing:div(b)
+ return self:divremainder(b)
+end
+
+function PolynomialRing:zero()
+ return self.coefficients[0]:zero():inring(self:getring())
+end
+
+function PolynomialRing:zeroc()
+ return self.coefficients[0]:zero()
+end
+
+function PolynomialRing:one()
+ return self.coefficients[0]:one():inring(self:getring())
+end
+
+function PolynomialRing:onec()
+ return self.coefficients[0]:one()
+end
+
+function PolynomialRing:eq(b)
+ for i=0,math.max(self.degree:asnumber(), b.degree:asnumber()) do
+ if self.coefficients[i] ~= b.coefficients[i] then
+ return false
+ end
+ end
+ return true
+end
+
+-- Returns the leading coefficient of this polynomial
+function PolynomialRing:lc()
+ return self.coefficients[self.degree:asnumber()]
+end
+
+--- @return boolean
+function PolynomialRing:isconstant()
+ return false
+end
+
+-- This expression is free of a symbol if and only if the symbol is not the symbol used to create the ring.
+function PolynomialRing:freeof(symbol)
+ return symbol.symbol ~= self.symbol
+end
+
+-- Replaces each expression in the map with its value.
+function PolynomialRing:substitute(map)
+ return self:tocompoundexpression():substitute(map)
+end
+
+-- Expands a polynomial expression. Polynomials are already in expanded form, so we just need to autosimplify.
+function PolynomialRing:expand()
+ return self:tocompoundexpression():autosimplify()
+end
+
+function PolynomialRing:autosimplify()
+ return self:tocompoundexpression():autosimplify()
+end
+
+-- Transforms from array format to an expression format.
+function PolynomialRing:tocompoundexpression()
+ local terms = {}
+ for exponent, coefficient in pairs(self.coefficients) do
+ terms[exponent + 1] = BinaryOperation(BinaryOperation.MUL, {coefficient:tocompoundexpression(),
+ BinaryOperation(BinaryOperation.POW, {SymbolExpression(self.symbol), Integer(exponent)})})
+ end
+ return BinaryOperation(BinaryOperation.ADD, terms)
+end
+
+-- Uses Horner's rule to evaluate a polynomial at a point
+function PolynomialRing:evaluateat(x)
+ local out = self:zeroc()
+ for i = self.degree:asnumber(), 1, -1 do
+ out = out + self.coefficients[i]
+ out = out * x
+ end
+ return out + self.coefficients[0]
+end
+
+-- Multiplies this polynomial by x^n
+function PolynomialRing:multiplyDegree(n)
+ local new = {}
+ for e = 0, n-1 do
+ new[e] = self:zeroc()
+ end
+ local loc = n
+ while loc <= self.degree:asnumber() + n do
+ new[loc] = self.coefficients[loc - n]
+ loc = loc + 1
+ end
+ return PolynomialRing(new, self.symbol, self.degree + Integer(n))
+end
+
+-- Returns the formal derivative of this polynomial
+function PolynomialRing:derivative()
+ if self.degree == Integer.zero() then
+ return PolynomialRing({self:zeroc()}, self.symbol, Integer(-1))
+ end
+ local new = {}
+ for e = 1, self.degree:asnumber() do
+ new[e - 1] = Integer(e) * self.coefficients[e]
+ end
+ return PolynomialRing(new, self.symbol, self.degree - Integer.one())
+end
+
+-- Returns the square-free factorization of a polynomial
+function PolynomialRing:squarefreefactorization()
+ local terms
+ if self.ring == Rational.getring() or self.ring == Integer.getring() then
+ terms = self:rationalsquarefreefactorization()
+ elseif self.ring == IntegerModN.getring() then
+ if not self.ring.modulus:isprime() then
+ error("Cannot compute a square-free factorization of a polynomial ring contructed from a ring that is not a field.")
+ end
+ terms = self:modularsquarefreefactorization()
+ end
+
+ local expressions = {self:lc()}
+ local j = 1
+ for index, term in ipairs(terms) do
+ if term.degree ~= Integer.zero() or term.coefficients[0] ~= Integer.one() then
+ j = j + 1
+ expressions[j] = BinaryOperation.POWEXP({term, Integer(index)})
+ end
+ end
+
+ return BinaryOperation.MULEXP(expressions)
+end
+
+-- Factors a polynomial into irreducible terms
+function PolynomialRing:factor()
+ -- Square-free factorization over an integral domain (so a polynomial ring constructed from a field)
+ local squarefree = self:squarefreefactorization()
+ local squarefreeterms = {}
+ local result = {squarefree.expressions[1]}
+ for i, expression in ipairs(squarefree.expressions) do
+ if i > 1 then
+ -- Converts square-free polynomials with rational coefficients to integer coefficients so Rational Roots / Zassenhaus can factor them
+ if expression.expressions[1].ring == Rational.getring() then
+ local factor, integerpoly = expression.expressions[1]:rationaltointeger()
+ result[1] = result[1] * factor ^ expression.expressions[2]
+ squarefreeterms[i - 1] = integerpoly
+ else
+ squarefreeterms[i - 1] = expression.expressions[1]
+ end
+ end
+ end
+
+ for i, expression in ipairs(squarefreeterms) do
+ local terms
+ if expression.ring == Integer.getring() then
+ -- Factoring over the integers first uses the rational roots test to factor out monomials (for efficiency purposes)
+ local remaining, factors = expression:rationalroots()
+ terms = factors
+ -- Then applies the Zassenhaus algorithm if there entire polynomial has not been factored into monomials
+ if remaining ~= Integer.one() then
+ remaining = remaining:zassenhausfactor()
+ for _, exp in ipairs(remaining) do
+ terms[#terms+1] = exp
+ end
+ end
+ end
+ if expression.ring == IntegerModN.getring() then
+ -- Berlekamp factorization is used for rings with integers mod a prime as coefficients
+ terms = expression:berlekampfactor()
+ end
+ for _, factor in ipairs(terms) do
+ result[#result+1] = BinaryOperation.POWEXP({factor, squarefree.expressions[i + 1].expressions[2]})
+ end
+ end
+ return BinaryOperation.MULEXP(result)
+end
+
+-- Uses the Rational Root test to factor out monomials of a square-free polynomial.
+function PolynomialRing:rationalroots()
+ local remaining = self
+ local roots = {}
+ if self.coefficients[0] == Integer.zero() then
+ roots[1] = PolynomialRing({Integer.zero(), Integer.one()}, self.symbol)
+ remaining = remaining // roots[1]
+ end
+ -- This can be slower than Zassenhaus if the digits are large enough, since factoring integers is slow
+ -- if self.coefficients[0] > Integer(Integer.DIGITSIZE - 1) or self:lc() > Integer(Integer.DIGITSIZE - 1) then
+ -- return remaining, roots
+ -- end
+ while remaining ~= Integer.one() do
+ :: nextfactor ::
+ local a = remaining.coefficients[0]
+ local b = remaining:lc()
+ local afactors = a:divisors()
+ local bfactors = b:divisors()
+ for _, af in ipairs(afactors) do
+ for _, bf in ipairs(bfactors) do
+ local testroot = Rational(af, bf, true)
+ if remaining:evaluateat(testroot) == Integer.zero() then
+ roots[#roots+1] = PolynomialRing({-testroot.numerator, testroot.denominator}, self.symbol)
+ remaining = remaining // roots[#roots]
+ goto nextfactor
+ end
+ if remaining:evaluateat(-testroot) == Integer.zero() then
+ roots[#roots+1] = PolynomialRing({testroot.numerator, testroot.denominator}, self.symbol)
+ remaining = remaining // roots[#roots]
+ goto nextfactor
+ end
+ end
+ end
+ break
+ end
+
+ return remaining, roots
+end
+
+-- Returns a list of roots of the polynomial, simplified up to cubics.
+function PolynomialRing:roots()
+ local roots = {}
+ local factorization = self:factor()
+
+ for i, factor in ipairs(factorization.expressions) do
+ if i > 1 then
+ local decomp = factor.expressions[1]:decompose()
+ for _, poly in ipairs(decomp) do
+ if poly.degree > Integer(3) then
+ table.insert(roots,RootExpression(factor.expressions[1]))
+ goto nextfactor
+ end
+ end
+ local factorroots = RootExpression(decomp[#decomp]):autosimplify()
+ if factorroots == true then
+ return true
+ end
+ if factorroots == false then
+ goto nextfactor
+ end
+ local replaceroots = {}
+ for j = #decomp - 1,1,-1 do
+ for _, root in ipairs(factorroots) do
+ local temp = RootExpression(decomp[j]):autosimplify(root)
+ if temp == true then
+ return true
+ end
+ if factorroots == false then
+ goto nextfactor
+ end
+ replaceroots = JoinArrays(replaceroots, temp)
+ end
+ factorroots = replaceroots
+ end
+ roots = JoinArrays(roots, factorroots)
+ end
+ end
+ ::nextfactor::
+ return roots
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__PolynomialRing.__index = Ring
+__PolynomialRing.__call = PolynomialRing.new
+PolynomialRing = setmetatable(PolynomialRing, __PolynomialRing) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/polynomialring/berlekampfactoring.lua b/macros/luatex/latex/luacas/tex/algebra/polynomialring/berlekampfactoring.lua
new file mode 100644
index 0000000000..feabb61a3f
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/polynomialring/berlekampfactoring.lua
@@ -0,0 +1,187 @@
+-- Methods related to the Berlekamp factoring algorithm.
+
+-- Square-free factorization in the modular field Zp.
+function PolynomialRing:modularsquarefreefactorization()
+ local monic = self / self:lc()
+ local terms = {}
+ terms[0] = PolynomialRing.gcd(monic, monic:derivative())
+ local b = monic // terms[0]
+ local c = monic:derivative() // terms[0]
+ local d = c - b:derivative()
+ local i = 1
+ while b ~= Integer.one() do
+ terms[i] = PolynomialRing.gcd(b, d)
+ b, c = b // terms[i], d // terms[i]
+ i = i + 1
+ d = c - b:derivative()
+ end
+
+ if not (terms[i-1]:derivative().degree == Integer.zero() and terms[i-1]:derivative().coefficients[0] == Integer.zero()) then
+ return terms
+ end
+
+ local recursiveterms = terms[i-1]:collapseterms(self.ring.modulus):modularsquarefreefactorization()
+ for k, poly in ipairs(recursiveterms) do
+ recursiveterms[k] = poly:expandterms(self.ring.modulus)
+ end
+ return JoinArrays(terms, recursiveterms)
+end
+
+-- Returns a new polnomial consisting of every nth term of the old one - helper method for square-free factorization
+function PolynomialRing:collapseterms(n)
+ local new = {}
+ local loc = 0
+ local i = 0
+ local nn = n:asnumber()
+ while loc <= self.degree:asnumber() do
+ new[i] = self.coefficients[loc]
+ loc = loc + nn
+ i = i + 1
+ end
+
+ return PolynomialRing(new, self.symbol, self.degree // n)
+end
+
+-- Returns a new polnomial consisting of every nth term of the old one - helper method for square-free factorization
+function PolynomialRing:expandterms(n)
+ local new = {}
+ local loc = 0
+ local i = 0
+ local nn = n:asnumber()
+ while i <= self.degree:asnumber() do
+ new[loc] = self.coefficients[i]
+ for j = 1, nn do
+ new[loc + j] = IntegerModN(Integer.zero(), n)
+ end
+ loc = loc + nn
+ i = i + 1
+ end
+
+ return PolynomialRing(new, self.symbol, self.degree * n)
+end
+
+-- Uses Berlekamp's Algorithm to factor polynomials in mod p
+function PolynomialRing:berlekampfactor()
+ if self.degree == 0 or self.degree == 1 then
+ return {self}
+ end
+
+ local R = self:RMatrix()
+ local S = self:auxillarybasis(R)
+ if #S == 1 then
+ return {self}
+ end
+ return self:findfactors(S)
+end
+
+-- Gets the R Matrix for Berlekamp factorization
+function PolynomialRing:RMatrix()
+ local R = {}
+ for i = 1, self.degree:asnumber() do
+ R[i] = {}
+ end
+ for i = 0, self.degree:asnumber()-1 do
+ local remainder = PolynomialRing({IntegerModN(Integer.one(), self.ring.modulus)}, self.symbol):multiplyDegree(self.ring.modulus:asnumber()*i) % self
+ for j = 0, self.degree:asnumber()-1 do
+ R[j + 1][i + 1] = remainder.coefficients[j]
+ if j == i then
+ R[j + 1][i + 1] = R[j + 1][i + 1] - IntegerModN(Integer.one(), self.ring.modulus)
+ end
+ end
+ end
+ return R
+end
+
+-- Creates an auxillary basis using the R matrix
+function PolynomialRing:auxillarybasis(R)
+ local P = {}
+ local n = self.degree:asnumber()
+ for i = 1, n do
+ P[i] = 0
+ end
+ S = {}
+ local q = 1
+ for j = 1, n do
+ local i = 1
+ local pivotfound = false
+ while not pivotfound and i <= n do
+ if R[i][j] ~= self:zeroc() and P[i] == 0 then
+ pivotfound = true
+ else
+ i = i + 1
+ end
+ end
+ if pivotfound then
+ P[i] = j
+ local a = R[i][j]:inv()
+ for l = 1, n do
+ R[i][l] = a * R[i][l]
+ end
+ for k = 1, n do
+ if k ~= i then
+ local f = R[k][j]
+ for l = 1, n do
+ R[k][l] = R[k][l] - f*R[i][l]
+ end
+ end
+ end
+ else
+ local s = {}
+ s[j] = self:onec()
+ for l = 1, j - 1 do
+ local e = 0
+ i = 1
+ while e == 0 and i <= n do
+ if l == P[i] then
+ e = i
+ else
+ i = i + 1
+ end
+ end
+ if e > 0 then
+ local c = -R[e][j]
+ s[l] = c
+ else
+ s[l] = self:zeroc()
+ end
+ end
+ S[#S+1] = PolynomialRing(s, self.symbol)
+ end
+ end
+ return S
+end
+
+-- Uses the auxilary basis to find the irreirrducible factors of the polynomial.
+function PolynomialRing:findfactors(S)
+ local r = #S
+ local p = self.ring.modulus
+ local factors = {self}
+ for k = 2,r do
+ local b = S[k]
+ local old_factors = Copy(factors)
+ for i = 1,#old_factors do
+ local w = old_factors[i]
+ local j = 0
+ while j <= p:asnumber() - 1 do
+ local g = PolynomialRing.gcd(b-IntegerModN(Integer(j), p), w)
+ if g == Integer.one() then
+ j = j + 1
+ elseif g == w then
+ j = p:asnumber()
+ else
+ factors = Remove(factors, w)
+ local q = w // g
+ factors[#factors+1] = g
+ factors[#factors+1] = q
+ if #factors == r then
+ return factors
+ else
+ j = j + 1
+ w = q
+ end
+ end
+
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/polynomialring/decomposition.lua b/macros/luatex/latex/luacas/tex/algebra/polynomialring/decomposition.lua
new file mode 100644
index 0000000000..84f2af1efc
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/polynomialring/decomposition.lua
@@ -0,0 +1,93 @@
+-- Methods related to polynomial decomposition.
+
+-- Returns a list of polynomials that form a complete decomposition of a polynomial.
+function PolynomialRing:decompose()
+ local U = self - self.coefficients[0]
+ local S = U:divisors()
+ local decomposition = {}
+ local C = PolynomialRing({Integer.zero(), Integer.one()}, self.symbol)
+ local finalcomponent
+
+ while S[1] do
+ local w = S[1]
+ for _, poly in ipairs(S) do
+ if poly.degree < w.degree then
+ w = poly
+ end
+ end
+ S = Remove(S, w)
+ if C.degree < w.degree and w.degree < self.degree and self.degree % w.degree == Integer.zero() then
+ local g = w:polyexpand(C, self.symbol)
+ local R = self:polyexpand(w, self.symbol)
+ if g.degree == Integer.zero() and R.degree == Integer.zero() then
+ g.symbol = self.symbol
+ decomposition[#decomposition+1] = g.coefficients[0]
+ decomposition[#decomposition].symbol = self.symbol
+ C = w
+ finalcomponent = R.coefficients[0]
+ end
+ end
+ end
+
+ if not decomposition[1] then
+ return {self}
+ end
+
+ finalcomponent.symbol = self.symbol
+ decomposition[#decomposition+1] = finalcomponent
+ return decomposition
+end
+
+-- Returns a list of all monic divisors of positive degree of the polynomial, assuming the polynomial ring is a Euclidean Domain.
+function PolynomialRing:divisors()
+ local factors = self:factor()
+ -- Converts each factor to a monic factor (we don't need to worry updating the constant term)
+ for i, factor in ipairs(factors.expressions) do
+ if i > 1 then
+ factor.expressions[1] = factor.expressions[1] / factor.expressions[1]:lc()
+ end
+ end
+
+ local terms = {}
+ for i, _ in ipairs(factors.expressions) do
+ if i > 1 then
+ terms[i] = Integer.zero()
+ end
+ end
+
+ local divisors = {}
+ local divisor = PolynomialRing({self:onec()}, self.symbol)
+ while true do
+ for i, factor in ipairs(factors.expressions) do
+ if i > 1 then
+ local base = factor.expressions[1]
+ local power = factor.expressions[2]
+ if terms[i] < power then
+ terms[i] = terms[i] + Integer.one()
+ divisor = divisor * base
+ break
+ else
+ terms[i] = Integer.zero()
+ divisor = divisor // (base ^ power)
+ end
+ end
+ end
+ if divisor == Integer.one() then
+ break
+ end
+ divisors[#divisors+1] = divisor
+ end
+
+ return divisors
+
+end
+
+-- Polynomial expansion as a subroutine of decomposition.
+function PolynomialRing:polyexpand(v, x)
+ local u = self
+ if u == Integer.zero() then
+ return Integer.zero()
+ end
+ local q,r = u:divremainder(v)
+ return PolynomialRing({PolynomialRing({Integer.zero(), Integer.one()}, "_")}, x) * q:polyexpand(v, x) + r
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/polynomialring/zassenhausfactoring.lua b/macros/luatex/latex/luacas/tex/algebra/polynomialring/zassenhausfactoring.lua
new file mode 100644
index 0000000000..be19298e64
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/polynomialring/zassenhausfactoring.lua
@@ -0,0 +1,220 @@
+-- Methods related to the Zassenhaus factorization algorithm.
+
+
+-- Square-free factorization in the rational field.
+function PolynomialRing:rationalsquarefreefactorization(keeplc)
+ local monic = self / self:lc()
+ local terms = {}
+ terms[0] = PolynomialRing.gcd(monic, monic:derivative())
+ local b = monic // terms[0]
+ local c = monic:derivative() // terms[0]
+ local d = c - b:derivative()
+ local i = 1
+ while b.degree ~= Integer.zero() or b.coefficients[0] ~= Integer.one() do
+ terms[i] = PolynomialRing.gcd(b, d)
+ b, c = b // terms[i], d // terms[i]
+ i = i + 1
+ d = c - b:derivative()
+ end
+ if keeplc and terms[1] then
+ terms[1] = terms[1] * self:lc()
+ end
+ return terms
+end
+
+-- Factors the largest possible constant out of a polynomial whos underlying ring is a Euclidean domain but not a field
+function PolynomialRing:factorconstant()
+ local gcd = Integer.zero()
+ for i = 0, self.degree:asnumber() do
+ gcd = self.ring.gcd(gcd, self.coefficients[i])
+ end
+ if gcd == Integer.zero() then
+ return Integer.one(), self
+ end
+ return gcd, self / gcd
+end
+
+-- Converts a polynomial in the rational polynomial ring to the integer polynomial ring
+function PolynomialRing:rationaltointeger()
+ local lcm = Integer.one()
+ for i = 0, self.degree:asnumber() do
+ if self.coefficients[i]:getring() == Rational:getring() then
+ lcm = lcm * self.coefficients[i].denominator / Integer.gcd(lcm, self.coefficients[i].denominator)
+ end
+ end
+ return Integer.one() / lcm, self * lcm
+end
+
+-- Uses Zassenhaus's Algorithm to factor sqaure-free polynomials over the intergers
+function PolynomialRing:zassenhausfactor()
+
+ -- Creates a monic polynomial V with related roots
+ local V = {}
+ local n = self.degree:asnumber()
+ local l = self:lc()
+ for i = 0, n - 1 do
+ V[i] = l ^ Integer(n - 1 - i) * self.coefficients[i]
+ end
+ V[n] = Integer.one()
+ V = PolynomialRing(V, "y", self.degree)
+
+ -- Performs Berlekamp Factorization in a sutable prime base
+ local p = V:findprime()
+ local S = V:inring(PolynomialRing.R("y", p)):berlekampfactor()
+
+ -- If a polynomial is irreducible with coefficients in mod p, it is also irreducible over the integers
+ if #S == 1 then
+ return {self}
+ end
+
+ -- Performs Hensel lifting on the factors mod p
+ local k = V:findmaxlifts(p)
+ local W = V:henselift(S, k)
+ local M = {}
+
+ -- Returns the solutions back to the original from the monic transformation
+ for i, factor in ipairs(W) do
+ local w = {}
+ for j = 0, factor.degree:asnumber() do
+ w[j] = factor.coefficients[j]:inring(Integer.getring()) * l ^ Integer(j)
+ end
+ _, M[i] = PolynomialRing(w, self.symbol, factor.degree):factorconstant()
+ end
+
+ return M
+
+end
+
+-- Finds the smallest prime such that this polynomial with coefficients in mod p is square-free
+function PolynomialRing:findprime()
+
+ local smallprimes = {Integer(2), Integer(3), Integer(5), Integer(7), Integer(11), Integer(13), Integer(17), Integer(19), Integer(23),
+ Integer(29), Integer(31), Integer(37), Integer(41), Integer(43), Integer(47), Integer(53), Integer(59)}
+
+ for _, p in pairs(smallprimes) do
+ local P = PolynomialRing({IntegerModN(Integer.one(), p)}, self.symbol)
+ local s = self:inring(P:getring())
+ if PolynomialRing.gcd(s, s:derivative()) == P then
+ return p
+ end
+ end
+
+ error("Execution error: No suitable prime found for factoring.")
+end
+
+-- Finds the maximum number of times Hensel Lifting will be applied to raise solutions to the appropriate power
+function PolynomialRing:findmaxlifts(p)
+ local n = self.degree:asnumber()
+ local h = self.coefficients[0]
+ for i=0 , n do
+ if self.coefficients[i] > h then
+ h = self.coefficients[i]
+ end
+ end
+
+ local B = 2^n * math.sqrt(n) * h:asnumber()
+ return Integer(math.ceil(math.log(2*B, p:asnumber())))
+end
+
+-- Uses Hensel lifting on the factors of a polynomial S mod p to find them in the integers
+function PolynomialRing:henselift(S, k)
+ local p = S[1].ring.modulus
+ if k == Integer.one() then
+ return self:truefactors(S, p, k)
+ end
+ G = self:genextendsigma(S)
+ local V = S
+ for j = 2, k:asnumber() do
+ local Vp = V[1]:inring(PolynomialRing.R("y"))
+ for i = 2, #V do
+ Vp = Vp * V[i]:inring(PolynomialRing.R("y"))
+ end
+ local E = self - Vp:inring(PolynomialRing.R("y"))
+ if E == Integer.zero() then
+ return V
+ end
+ E = E:inring(PolynomialRing.R("y", p ^ Integer(j))):inring(PolynomialRing.R("y"))
+ F = E / p ^ (Integer(j) - Integer.one())
+ R = self:genextendR(V, G, F)
+ local Vnew = {}
+ for i, v in ipairs(V) do
+ local vnew = v:inring(PolynomialRing.R("y", p ^ Integer(j)))
+ local rnew = R[i]:inring(PolynomialRing.R("y", p ^ Integer(j)))
+ Vnew[i] = vnew + (p) ^ (Integer(j) - Integer.one()) * rnew
+ end
+ V = Vnew
+ end
+ return self:truefactors(V, p, k)
+end
+
+-- Gets a list of sigma polynomials for use in hensel lifting
+function PolynomialRing:genextendsigma(S)
+ local v = S[1] * S[2]
+ local _, A, B = PolynomialRing.extendedgcd(S[2], S[1])
+ local SIGMA = {A, B}
+ for i, _ in ipairs(S) do
+ if i >= 3 then
+ v = v * S[i]
+ local sum = SIGMA[1] * (v // S[1])
+ for j = 2, i-1 do
+ sum = sum + SIGMA[j] * (v // S[j])
+ end
+ _, A, B = PolynomialRing.extendedgcd(sum, v // S[i])
+ for j = 1, i-1 do
+ SIGMA[j] = SIGMA[j] * A
+ end
+ SIGMA[i] = B
+ end
+ end
+
+ return SIGMA
+end
+
+-- Gets a list of r polynomials for use in hensel lifting
+function PolynomialRing:genextendR(V, G, F)
+ R = {}
+ for i, v in ipairs(V) do
+ local pring = G[1]:getring()
+ R[i] = F:inring(pring) * G[i] % v:inring(pring)
+ end
+ return R
+end
+
+-- Updates factors of the polynomial to the correct ones in the integer ring
+function PolynomialRing:truefactors(l, p, k)
+ local U = self
+ local L = l
+ local factors = {}
+ local m = 1
+ while m <= #L / 2 do
+ local C = Subarrays(L, m)
+ while #C > 0 do
+ local t = C[1]
+ local prod = t[1]
+ for i = 2, #t do
+ prod = prod * t[i]
+ end
+ local T = prod:inring(PolynomialRing.R("y", p ^ k)):inring(PolynomialRing.R("y"))
+ -- Convert to symmetric representation - this is the only place it actually matters
+ for i = 0, T.degree:asnumber() do
+ if T.coefficients[i] > p ^ k / Integer(2) then
+ T.coefficients[i] = T.coefficients[i] - p^k
+ end
+ end
+ local Q, R = U:divremainder(T)
+ if R == Integer.zero() then
+ factors[#factors+1] = T
+ U = Q
+ L = RemoveAll(L, t)
+ C = RemoveAny(C, t)
+ else
+ C = Remove(C, t)
+ end
+ end
+ m = m + 1
+ end
+ if U ~= Integer.one() then
+ factors[#factors+1] = U
+ end
+ return factors
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/rational.lua b/macros/luatex/latex/luacas/tex/algebra/rational.lua
new file mode 100644
index 0000000000..811909a948
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/rational.lua
@@ -0,0 +1,241 @@
+--- @class Rational
+--- Represents an element of the field of rational numbers or rational functions.
+--- @field numerator Ring
+--- @field denominator Ring
+--- @field ring RingIdentifier
+Rational = {}
+__Rational = {}
+
+
+--------------------------
+-- Static functionality --
+--------------------------
+
+-- Metatable for ring objects.
+local __obj = {__index = Rational, __eq = function(a, b)
+ return a["ring"] == b["ring"] and
+ (a["child"] == b["child"] or a["child"] == nil or b["child"] == nil) and
+ (a["symbol"] == b["symbol"] or a["child"] == nil or b["child"] == nil)
+end, __tostring = function(a)
+ if a.symbol then
+ return tostring(a.child.child) .. "(" .. a.symbol .. ")"
+ end
+ if a.child then
+ return "QQ"
+ end
+ return "(Generic Fraction Field)"
+ end}
+
+--- @param symbol SymbolExpression
+--- @param child RingIdentifier
+--- @return RingIdentifier
+function Rational.makering(symbol, child)
+ local t = {ring = Rational}
+ t.symbol = symbol
+ t.child = child
+ t = setmetatable(t, __obj)
+ return t
+end
+
+--- Converts a string of the form -?[0-9]+ or -?[0-9]+\/[0-9]+ to a rational number.
+--- @param str string
+--- @return Rational|Integer
+function Rational.fromstring(str)
+ local divloc = string.find(str, "/");
+ if not divloc then
+ return Integer(str)
+ end
+ return Rational(Integer(string.sub(str, 1, divloc - 1)), Integer(string.sub(str, divloc + 1, #str)))
+end
+
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+-- So we don't have to copy the field operations each time.
+local __o = Copy(__FieldOperations)
+__o.__index = Rational
+__o.__tostring = function(a)
+ if a.ring.symbol then
+ return "(" .. tostring(a.numerator)..")/("..tostring(a.denominator) .. ")"
+ end
+ return tostring(a.numerator).."/"..tostring(a.denominator)
+end
+
+--- Creates a new rational given a numerator and denominator that are part of the same ring.
+--- Rational numbers are represented uniquely.
+--- @param n Ring
+--- @param d Ring
+--- @param keep boolean
+function Rational:new(n, d, keep)
+ local o = {}
+ o = setmetatable(o, __o)
+
+ if n:getring() == PolynomialRing.getring() then
+ o.symbol = n.symbol
+ end
+
+ if d:getring() == PolynomialRing.getring() then
+ o.symbol = d.symbol
+ end
+
+ if d == Integer(0) then
+ error("Arithmetic error: division by zero")
+ end
+
+ n = n or Integer.zero()
+ d = d or Integer.one()
+ o.numerator = n
+ o.denominator = d
+ o:reduce()
+
+ if (not keep) and o.denominator == Integer.one() or (not keep) and o.numerator == Integer.zero() then
+ return o.numerator
+ end
+
+ return o
+end
+
+--- Reduces a rational expression to standard form. This method mutates its object.
+function Rational:reduce()
+ if self.numerator:getring() == Integer.getring() then
+ if self.denominator < Integer.zero() then
+ self.denominator = -self.denominator
+ self.numerator = -self.numerator
+ end
+ 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
+ self.numerator = self.numerator/lc
+ 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
+
+
+--- @return RingIdentifier
+function Rational:getring()
+ local t = {ring=Rational}
+ if self then
+ t.child = self.ring
+ t.symbol = self.symbol
+ end
+ t = setmetatable(t, __obj)
+ return t
+end
+
+--- @param ring RingIdentifier
+--- @return Ring
+function Rational:inring(ring)
+ if ring == self:getring() then
+ return self
+ end
+
+ if ring == Rational:getring() and ring.symbol then
+ if not self:getring().symbol then
+ return Rational(self:inring(ring.child), self:inring(ring.child):one(), true)
+ end
+ return Rational(self.numerator:inring(ring.child), self.denominator:inring(ring.child), true)
+ end
+
+ if ring == PolynomialRing:getring() then
+ return PolynomialRing({self:inring(ring.child)}, ring.symbol)
+ end
+
+ error("Unable to convert element to proper ring.")
+end
+
+--- @return boolean
+function Rational:isconstant()
+ if self.symbol then
+ return false
+ end
+ return true
+end
+
+--- @return Expression
+function Rational:tocompoundexpression()
+ return BinaryOperation(BinaryOperation.DIV, {self.numerator:tocompoundexpression(), self.denominator:tocompoundexpression()})
+end
+
+--- Returns this rational as a floating point number. Can only approximate the value of most rationals.
+--- @return number
+function Rational:asnumber()
+ return self.numerator:asnumber() / self.denominator:asnumber()
+end
+
+function Rational:add(b)
+ return Rational(self.numerator * b.denominator + self.denominator * b.numerator, self.denominator * b.denominator)
+end
+
+function Rational:neg()
+ return Rational(-self.numerator, self.denominator, true)
+end
+
+function Rational:mul(b)
+ return Rational(self.numerator * b.numerator, self.denominator * b.denominator)
+end
+
+-- function Rational:inv(b)
+-- return Rational(self.numerator * b.numerator, self.denominator * b.denominator)
+-- end
+
+function Rational:pow(b)
+ return (self.numerator ^ b) / (self.denominator ^ b)
+end
+
+function Rational:div(b)
+ return Rational(self.numerator * b.denominator, self.denominator * b.numerator)
+end
+
+function Rational:eq(b)
+ return self.numerator == b.numerator and self.denominator == b.denominator
+end
+
+function Rational:lt(b)
+ if self.numerator < Integer.zero() and b.numerator > Integer.zero() then
+ return true
+ end
+ if self.numerator > Integer.zero() and b.numerator < Integer.zero() then
+ return false
+ end
+
+ if (self.numerator >= Integer.zero() and b.numerator >= Integer.zero()) or (self.numerator <= Integer.zero() and b.numerator <= Integer.zero()) then
+ return self.numerator * b.denominator < self.denominator * b.numerator
+ end
+ return self.numerator * b.denominator > self.denominator * b.numerator
+end
+
+function Rational:le(b)
+ return self:eq(b) or self:lt(b)
+end
+
+function Rational:zero()
+ return Integer.zero()
+end
+
+function Rational:one()
+ return Integer.one()
+end
+
+function Rational:tolatex()
+ if string.sub(self.numerator:tolatex(),1,1) == '-' then
+ return "- \\frac{" .. string.sub(self.numerator:tolatex(),2,-1) .. "}{" .. self.denominator:tolatex() .. "}"
+ end
+ return "\\frac{" .. self.numerator:tolatex() .."}{".. self.denominator:tolatex().. "}"
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__Rational.__index = Field
+__Rational.__call = Rational.new
+Rational = setmetatable(Rational, __Rational) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/ring.lua b/macros/luatex/latex/luacas/tex/algebra/ring.lua
new file mode 100644
index 0000000000..4903b9c912
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/ring.lua
@@ -0,0 +1,326 @@
+--- @class Ring
+--- Interface for an element of a ring with unity.
+Ring = {}
+__Ring = {}
+
+--------------------------
+-- Static functionality --
+--------------------------
+
+--- Determines which ring the output of a binary operation with inputs in ring1 and ring2 should be, if such a ring exists.
+--- If one of the rings is a subring of another ring, the result should be one of the two rings.
+--- @param ring1 RingIdentifier
+--- @param ring2 RingIdentifier
+--- @return RingIdentifier
+function Ring.resultantring(ring1, ring2)
+ if ring1 == ring2 then
+ return ring1
+ end
+
+ if ((ring1 == PolynomialRing.getring() and ring2 == Rational.getring()) or
+ (ring2 == PolynomialRing.getring() and ring1 == Rational.getring()))
+ and ring1.symbol == ring2.symbol then
+ return Rational.makering(ring1.symbol, Ring.resultantring(ring1.child, ring2.child))
+ end
+
+ if ring1 == PolynomialRing.getring() or ring2 == PolynomialRing.getring() then
+ if ring1 == ring2.child then
+ return ring2
+ end
+ if ring2 == ring1.child then
+ return ring1
+ end
+
+ if ring1 == PolynomialRing.getring() and ring2 == PolynomialRing.getring() and ring1.symbol == ring2.symbol then
+ return PolynomialRing.makering(ring1.symbol, Ring.resultantring(ring1.child, ring2.child))
+ end
+
+ -- If none of the above conditions are satisfied, recusion is a pain, so we just strip all of the variables off of both rings.
+ -- TODO: Make this properly recursive, or just use a multivariable polynomial ring class
+ local symbols = {}
+ while ring1 == PolynomialRing.getring() do
+ symbols[#symbols+1] = ring1.symbol
+ ring1 = ring1.child
+ end
+ while ring2 == PolynomialRing.getring() do
+ if not Contains(symbols, ring2.symbol) then
+ symbols[#symbols+1] = ring2.symbol
+ end
+ ring2 = ring2.child
+ end
+ local ring = Ring.resultantring(ring1, ring2)
+
+ if ring == Rational.getring() and Contains(symbols, ring.symbol) then
+ symbols = Remove(symbols, ring.symbol)
+ end
+ for i = #symbols, 1, -1 do
+ ring = PolynomialRing.makering(symbols[i], ring)
+ end
+ return ring
+ end
+
+ if ring1 == Integer.getring() then
+ if ring2 == Integer.getring() then
+ return ring2
+ end
+
+ if ring2 == Rational.getring() then
+ return ring2
+ end
+
+ if ring2 == IntegerModN.getring() then
+ return ring2
+ end
+ end
+
+ if ring1 == Rational.getring() then
+ if ring2 == Integer.getring() then
+ return ring1
+ end
+
+ if ring2 == Rational.getring() then
+ if not ring1.symbol then
+ return Rational.makering(ring2.symbol, Ring.resultantring(ring1, ring2.child))
+ end
+ if not ring2.symbol then
+ return Rational.makering(ring1.symbol, Ring.resultantring(ring1.child, ring2))
+ end
+ if ring1.symbol and ring2.symbol and ring1.symbol == ring2.symbol then
+ return Rational.makering(ring1.symbol, Ring.resultantring(ring1.child, ring2.child))
+ end
+ return ring2
+ end
+
+ if ring2 == IntegerModN.getring() then
+ return nil
+ end
+ end
+
+ if ring1 == IntegerModN.getring() then
+ if ring2 == Integer.getring() then
+ return ring1
+ end
+
+ if ring2 == Rational.getring() then
+ return nil
+ end
+
+ if ring2 == IntegerModN.getring() then
+ return IntegerModN.makering(Integer.gcd(ring1.modulus, ring2.modulus))
+ end
+ end
+
+ return nil
+end
+
+--- Returns a particular instantiation of a ring.
+--- Does the same thing as getring() if there is only one possible ring for a class, i.e., the integers and rationals.
+--- @return RingIdentifier
+function Ring.makering()
+ error("Called unimplemented method : makering()")
+end
+
+----------------------
+-- Required methods --
+----------------------
+
+--- Returns the ring this element is part of.
+--- @return RingIdentifier
+function Ring:getring()
+ error("Called unimplemented method : getring()")
+end
+
+--- Explicitly converts this element to an element of another ring.
+--- @param ring RingIdentifier
+--- @return Ring
+function Ring:inring(ring)
+ error("Called unimplemented method : in()")
+end
+
+--- Returns whether the ring is commutative.
+--- @return boolean
+function Ring:iscommutative()
+ error("Called unimplemented method : iscommutative()")
+end
+
+--- @return Ring
+function Ring:add(b)
+ error("Called unimplemented method : add()")
+end
+
+--- @return Ring
+function Ring:sub(b)
+ return(self:add(b:neg()))
+end
+
+--- @return Ring
+function Ring:neg()
+ error("Called unimplemented method : neg()")
+end
+
+--- @return Ring
+function Ring:mul(b)
+ error("Called unimplemented method : mul()")
+end
+
+--- Ring exponentiation by definition. Specific rings may implement more efficient methods.
+--- @return Ring
+function Ring:pow(n)
+ if(n < Integer.zero()) then
+ error("Execution error: Negative exponentiation is undefined over general rings")
+ end
+ local k = Integer.zero()
+ local b = self:one()
+ while k < n do
+ b = b * self
+ k = k + Integer.one()
+ end
+ return b
+end
+
+--- @return boolean
+function Ring:eq(b)
+ error("Execution error: Ring does not have a total order")
+end
+
+--- @return boolean
+function Ring:lt(b)
+ error("Execution error: Ring does not have a total order")
+end
+
+--- @return boolean
+function Ring:le(b)
+ error("Execution error: Ring does not have a total order")
+end
+
+--- The additive identitity of the ring.
+--- @return Ring
+function Ring:zero()
+ error("Called unimplemented method : zero()")
+end
+
+--- The multiplicative identitity of the ring.
+--- @return Ring
+function Ring:one()
+ error("Called unimplemented method : one()")
+end
+
+--------------------------
+-- Instance metamethods --
+--------------------------
+__RingOperations = {}
+
+-- Each of these methods just handles coverting each element in the ring to an instance of the proper ring, if possible,
+-- then passing the arguments to the function in a specific ring.
+
+__RingOperations.__unm = function(a)
+ return a:neg()
+end
+
+__RingOperations.__add = function(a, b)
+ if not b.getring then
+ return BinaryOperation.ADDEXP({a, b})
+ end
+
+ local aring, bring = a:getring(), b:getring()
+ local oring = Ring.resultantring(aring, bring)
+ if not oring then
+ error("Attempted to add two elements of incompatable rings")
+ end
+ return a:inring(oring):add(b:inring(oring))
+end
+
+__RingOperations.__sub = function(a, b)
+ if not b.getring then
+ return BinaryOperation.SUBEXP({a, b})
+ end
+
+ local aring, bring = a:getring(), b:getring()
+ local oring = Ring.resultantring(aring, bring)
+ if not oring then
+ error("Attempted to subtract two elements of incompatable rings")
+ end
+ return a:inring(oring):sub(b:inring(oring))
+end
+
+-- Allows for multiplication by writing two expressions next to each other.
+__RingOperations.__call = function (a, b)
+ return a * b
+end
+
+__RingOperations.__mul = function(a, b)
+ if not b.getring then
+ return BinaryOperation.MULEXP({a, b})
+ end
+
+ local aring, bring = a:getring(), b:getring()
+ local oring = Ring.resultantring(aring, bring)
+ if not oring then
+ error("Attempted to muliply two elements of incompatable rings")
+ end
+ return a:inring(oring):mul(b:inring(oring))
+end
+
+__RingOperations.__pow = function(a, n)
+ if (not n.getring) or (n.getring and n:getring().ring ~= Integer) then
+ return BinaryOperation.POWEXP({a, n})
+ end
+
+ -- if a == a:zero() and n == Integer.zero() then
+ -- error("Cannot raise 0 to the power of 0")
+ -- end
+
+ return a:pow(n)
+end
+
+-- Comparison operations assume, of course, that the ring operation is equipped with a total order
+-- All elements of all rings need these metamethods, since in Lua comparisons on tables only fire if both objects have the table
+__RingOperations.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always runs this anyway
+ if not a.getring or not b.getring then
+ return false
+ end
+ local aring, bring = a:getring(), b:getring()
+ if aring == bring then
+ return a:eq(b)
+ end
+ local oring = Ring.resultantring(aring, bring)
+ if not oring then
+ error("Attempted to compare two elements of incompatable rings")
+ end
+ return a:inring(oring):eq(b:inring(oring))
+end
+
+__RingOperations.__lt = function(a, b)
+ local aring, bring = a:getring(), b:getring()
+ if aring == bring then
+ return a:lt(b)
+ end
+ local oring = Ring.resultantring(aring, bring)
+ if not oring then
+ error("Attempted to compare two elements of incompatable rings")
+ end
+ return a:inring(oring):lt(b:inring(oring))
+end
+
+__RingOperations.__le = function(a, b)
+ local aring, bring = a:getring(), b:getring()
+ if aring == bring then
+ return a:le(b)
+ end
+ local oring = Ring.resultantring(aring, bring)
+ if not oring then
+ error("Attempted to compare two elements of incompatable rings")
+ end
+ return a:inring(oring):le(b:inring(oring))
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__Ring.__index = ConstantExpression
+Ring = setmetatable(Ring, __Ring)
+
+--- Used for comparing and converting between rings.
+--- @class RingIdentifier
diff --git a/macros/luatex/latex/luacas/tex/algebra/rootexpression.lua b/macros/luatex/latex/luacas/tex/algebra/rootexpression.lua
new file mode 100644
index 0000000000..a66182579a
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/rootexpression.lua
@@ -0,0 +1,135 @@
+--- @class RootExpression
+--- An expression that represents the solutions to expression = 0.
+--- @field expression Expression
+RootExpression = {}
+__RootExpression = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new root expression with the given expression.
+--- @param expression Expression
+--- @return RootExpression
+function RootExpression:new(expression)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ o.expression = Copy(expression)
+
+ __o.__index = RootExpression
+ __o.__tostring = function(a)
+ return 'Root Of: (' .. tostring(a.expression) .. ')'
+ end
+ __o.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always rungs this anyway
+ if not b:type() == RootExpression then
+ return false
+ end
+ return a.expression == b.expression
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- @return Expression
+function RootExpression:autosimplify(subpart)
+ local simplified = self.expression:autosimplify()
+ local simplified, ispoly = simplified:topolynomial()
+
+ if simplified:isconstant() then
+ -- 0 = 0 is always true (obviously).
+ return simplified == simplified:zero()
+ end
+
+ if ispoly then
+ if simplified.degree == Integer.zero() then
+ return simplified == simplified:zero()
+ end
+ if simplified.degree == Integer.one() then
+ return {-simplified.coefficients[0] / simplified.coefficients[1]}
+ end
+ if simplified.degree == Integer(2) then
+ local a = simplified.coefficients[2]
+ local b = simplified.coefficients[1]
+ local c = simplified.coefficients[0]
+ -- This is a hack until we can get more expression manipulation working, but that's okay.
+ if subpart then
+ c = (c - subpart):autosimplify()
+ end
+ return {((-b + sqrt(b^Integer(2) - Integer(4) * a * c)) / (Integer(2) * a)):autosimplify(),
+ ((-b - sqrt(b^Integer(2) - Integer(4) * a * c)) / (Integer(2) * a)):autosimplify()}
+ end
+ if simplified.degree == Integer(3) then
+ local a = simplified.coefficients[3]
+ local b = simplified.coefficients[2]
+ local c = simplified.coefficients[1]
+ local d = simplified.coefficients[0]
+ -- This is a hack until we can get more expression manipulation working, but that's okay.
+ if subpart then
+ d = (d - subpart):autosimplify()
+ end
+
+ local delta0 = (b^Integer(2) - Integer(3)*a*c):autosimplify()
+ local delta1 = (Integer(2) * b^Integer(3) - Integer(9)*a*b*c+Integer(27)*a^Integer(2)*d):autosimplify()
+
+ local C = sqrt((delta1 + sqrt(delta1 ^ Integer(2) - Integer(4) * delta0 ^ Integer(3))) / Integer(2), Integer(3)):autosimplify()
+
+ if C == Integer.zero() then
+ C = sqrt((delta1 - sqrt(delta1 ^ Integer(2) - Integer(4) * delta0 ^ Integer(3))) / Integer(2), Integer(3)):autosimplify()
+ end
+
+ if C == Integer.zero() then
+ C = (-b/(Integer(3)*a)):autosimplify()
+ end
+
+ local eta = ((Integer(-1) + sqrt(Integer(-3))) / Integer(2)):autosimplify()
+
+ return {((-Integer.one() / (Integer(3) * a)) * (b + C + delta0 / C)):autosimplify(),
+ ((-Integer.one() / (Integer(3) * a)) * (b + C*eta + delta0 / (C*eta))):autosimplify(),
+ ((-Integer.one() / (Integer(3) * a)) * (b + C*eta^Integer(2) + delta0 / (C*eta^Integer(2)))):autosimplify()}
+ end
+ end
+ if ispoly then
+ simplified = simplified:autosimplify()
+ end
+ if subpart then
+ simplified = (simplified - subpart):autosimplify()
+ end
+ return {RootExpression(simplified)}
+end
+
+--- @return table<number, Expression>
+function RootExpression:subexpressions()
+ return {self.expression}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return RootExpression
+function RootExpression:setsubexpressions(subexpressions)
+ return RootExpression(subexpressions[1])
+end
+
+--- @param other Expression
+--- @return boolean
+function RootExpression:order(other)
+ --- TODO: Fix ordering on new expression types
+ if other:type() ~= RootExpression then
+ return false
+ end
+
+ return self.expression:order(other.expression)
+end
+
+--- @return string
+function RootExpression:tolatex()
+ return '\\operatorname{RootOf}\\left(' .. self.expression:tolatex() .. '\\right)'
+end
+
+-----------------
+-- Inheritance --
+-----------------
+__RootExpression.__index = CompoundExpression
+__RootExpression.__call = RootExpression.new
+RootExpression = setmetatable(RootExpression, __RootExpression) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/sqrtexpression.lua b/macros/luatex/latex/luacas/tex/algebra/sqrtexpression.lua
new file mode 100644
index 0000000000..bc96f4e1d8
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/sqrtexpression.lua
@@ -0,0 +1,196 @@
+--- @class SqrtExpression
+--- 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 = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new sqrt expression with the given expression.
+--- @param expression Expression
+--- @param root Integer
+--- @return SqrtExpression
+function SqrtExpression:new(expression, root)
+ root = root or Integer(2)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ o.expression = Copy(expression)
+ o.root = root
+
+ __o.__index = SqrtExpression
+ __o.__tostring = function(a)
+ return tostring(a.expression) .. ' ^ (1/' .. tostring(a.root) .. ')'
+ end
+ __o.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always rungs this anyway
+ if not b:type() == SqrtExpression then
+ return false
+ end
+ return a.expression == b.expression and a.root == b.root
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+
+--- @return table<number, Expression>
+function SqrtExpression:subexpressions()
+ return {self.expression}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return SqrtExpression
+function SqrtExpression:setsubexpressions(subexpressions)
+ return SqrtExpression(subexpressions[1], self.root)
+end
+
+--- @param other Expression
+--- @return boolean
+function SqrtExpression:order(other)
+ return self:topower():order(other)
+end
+
+function SqrtExpression:topower()
+ local exponent = BinaryOperation(BinaryOperation.DIV,{Integer.one(),self.root}):autosimplify()
+ local base = self.expression
+ return BinaryOperation(BinaryOperation.POW,{base,exponent}):autosimplify()
+end
+
+function SqrtExpression:autosimplify()
+ local expression = self.expression:autosimplify()
+ local root = self.root:autosimplify()
+
+ if root == Integer.one() then
+ return expression
+ end
+
+ if root:type() == Rational then
+ return SqrtExpression(BinaryOperation(BinaryOperation.POW,{expression,root.denominator}):autosimplify(), root.numerator):autosimplify()
+ end
+
+ if not root:isconstant() then
+ return BinaryOperation(BinaryOperation.POW,{expression,Integer.one() / root}):autosimplify()
+ end
+
+ if not expression:isconstant() then
+ if expression.operation == BinaryOperation.MUL and expression.expressions[1]:isconstant() then
+ local coeff = SqrtExpression(expression.expressions[1],root):autosimplify()
+ expression.expressions[1] = BinaryOperation(BinaryOperation.MUL,{Integer.one()})
+ expression = expression:autosimplify()
+ local sqrtpart = SqrtExpression(expression,root):autosimplify()
+ local result = coeff*sqrtpart
+ return result:autosimplify()
+ end
+ return BinaryOperation(BinaryOperation.POW,{expression,Integer.one() / root}):autosimplify()
+ end
+
+ if expression:type() == Rational then
+ local result = BinaryOperation(BinaryOperation.MUL, {SqrtExpression(expression.numerator,root):autosimplify(),BinaryOperation(BinaryOperation.POW,{SqrtExpression(expression.denominator,root):autosimplify(),Integer(-1)})})
+ return result:autosimplify()
+ end
+
+ if expression:type() == Integer then
+ if expression == Integer.zero() then
+ return Integer.zero()
+ end
+ if expression == Integer.one() then
+ return Integer.one()
+ end
+ if expression < Integer.zero() then
+ if root == Integer(2) then
+ local result = SqrtExpression(expression:neg(),root):autosimplify()
+ result = I*result
+ return result:autosimplify()
+ end
+ if root % Integer(2) == Integer.one() then
+ local result = SqrtExpression(expression:neg(),root):autosimplify()
+ result = -result
+ return result:autosimplify()
+ end
+ end
+ local primes = expression:primefactorization()
+ local coeffresult = {}
+ local exprresult = {}
+ local reduction = root
+ for _, term in ipairs(primes.expressions) do
+ local primepower = term.expressions[2]
+ reduction = Integer.gcd(primepower,reduction)
+ if reduction == Integer.one() then
+ goto skip
+ end
+ end
+ ::skip::
+ local newroot = root / reduction
+ for index, term in ipairs(primes.expressions) do
+ local prime = term.expressions[1]
+ local primepower = term.expressions[2] / reduction
+ local coeffpower = primepower // newroot
+ coeffresult[index] = prime ^ coeffpower
+ local exprpower = primepower - coeffpower*newroot
+ exprresult[index] = prime ^ exprpower
+ end
+ local newexpression = BinaryOperation(BinaryOperation.MUL,exprresult):autosimplify()
+ local coeff = BinaryOperation(BinaryOperation.MUL,coeffresult):autosimplify()
+ if coeff == Integer.one() then
+ if reduction == Integer.one() then
+ goto stop
+ end
+ return SqrtExpression(newexpression,newroot)
+ end
+ if newroot == Integer.one() then
+ return coeff
+ end
+ return BinaryOperation(BinaryOperation.MUL,{coeff,SqrtExpression(newexpression,newroot)}):autosimplify()
+ end
+ ::stop::
+
+ if expression.operation == BinaryOperation.POW and expression.expressions[2]:type() == Integer then
+ local exponent = expression.expressions[2]
+ local power = exponent // root
+ local newexponent = (exponent / root) - power
+ local coeff = expression.expressions[1] ^ power
+ coeff = coeff:evaluate()
+ if newexponent == Integer.zero() then
+ return coeff
+ else
+ local num = newexponent.numerator
+ local den = newexponent.denominator
+ local newexpression = expression ^ num
+ newexpression = newexpression:autosimplify()
+ local result = coeff * SqrtExpression(newexpression,den)
+ return result
+ end
+ end
+
+ return SqrtExpression(expression,root)
+end
+
+function SqrtExpression:tolatex()
+ local printout = '\\sqrt'
+ if self.root == Integer(2) then
+ printout = printout .. '{' .. self.expression:tolatex() .. '}'
+ else
+ printout = printout .. '[' .. self.root:tolatex() .. ']' .. '{' .. self.expression:tolatex() .. '}'
+ end
+ return printout
+end
+
+
+-----------------
+-- Inheritance --
+-----------------
+__SqrtExpression.__index = CompoundExpression
+__SqrtExpression.__call = SqrtExpression.new
+SqrtExpression = setmetatable(SqrtExpression, __SqrtExpression)
+
+----------------------
+-- Static constants --
+----------------------
+
+sqrt = function(expression, root)
+ return SqrtExpression(expression, root)
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/algebra/trigexpression.lua b/macros/luatex/latex/luacas/tex/algebra/trigexpression.lua
new file mode 100644
index 0000000000..307bfe1737
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/algebra/trigexpression.lua
@@ -0,0 +1,355 @@
+--- @class TrigExpression
+--- Represents a trigonometric function from one expression to another.
+--- @field name string
+--- @field expression Expression
+TrigExpression = {}
+__TrigExpression = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new trig expression with the given name and expression.
+--- @param name string|SymbolExpression
+--- @param expression Expression
+--- @return TrigExpression
+function TrigExpression:new(name, expression)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ if not TrigExpression.NAMES[name] then
+ error("Argument error: " .. name .. " is not the name of a trigonometric function.")
+ end
+
+ o.name = name
+ o.expression = expression
+ o.expressions = {expression}
+ if expression:isatomic() then
+ o.variables = {expression}
+ else
+ o.variables = {SymbolExpression('x')}
+ end
+ o.derivatives = {Integer.zero()}
+
+ __o.__index = TrigExpression
+ __o.__tostring = function(a)
+ return tostring(a.name) .. '(' .. tostring(a.expression) .. ')'
+ end
+ __o.__eq = function(a, b)
+ -- if b:type() == FunctionExpression then
+ -- return a:tofunction() == b
+ -- end
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always runs this anyway
+ if not b:type() == TrigExpression then
+ return false
+ end
+ return a.name == b.name and a.expression == b.expression
+ end
+
+ o = setmetatable(o, __o)
+ return o
+end
+
+--- @return TrigExpression
+function TrigExpression:evaluate()
+ local expression = self.expression:autosimplify()
+
+ if expression == Integer.zero() then
+ if self.name == "cos" or self.name == "sec" then
+ return Integer.one()
+ end
+ if self.name == "sin" or self.name == "tan" then
+ return Integer.zero()
+ end
+ if self.name == "arctan" or self.name == "arcsin" then
+ return Integer.zero()
+ end
+ if self.name == "arccos" or self.name == "arccot" then
+ return PI / Integer(2)
+ end
+ end
+
+ if expression == PI then
+ if self.name == "cos" or self.name == "sec" then
+ return Integer(-1)
+ end
+ if self.name == "sin" or self.name == "tan" then
+ return Integer.zero()
+ end
+ end
+
+ if expression:ismulratlPI() then
+ local coeff = expression.expressions[1]
+ if TrigExpression.COSVALUES[tostring(coeff)] ~= nil then
+ if self.name == "cos" then
+ return TrigExpression.COSVALUES[tostring(coeff)]:autosimplify()
+ end
+ if self.name == "sin" then
+ local sign = Integer.one()
+ if coeff > Integer.one() then
+ sign = Integer(-1)
+ end
+ return (sign*sqrt(Integer.one()-cos(expression)^Integer(2))):autosimplify()
+ end
+ if self.name == "tan" then
+ return (sin(expression) / cos(expression)):autosimplify()
+ end
+ if self.name == "sec" then
+ return (Integer.one() / cos(expression)):autosimplify()
+ end
+ if self.name == "csc" then
+ return (Integer.one() / sin(expression)):autosimplify()
+ end
+ if self.name == "cot" then
+ return (cos(expression) / sin(expression)):autosimplify()
+ end
+ end
+ end
+
+ if TrigExpression.ACOSVALUES[tostring(expression)] ~= nil then
+ if self.name == "arccos" then
+ return TrigExpression.ACOSVALUES[tostring(expression)]:autosimplify()
+ end
+ if self.name == "arcsin" then
+ if expression == Integer(-1) then
+ return TrigExpression.ACOSVALUES["-1"]:autosimplify()
+ elseif expression.expressions and expression.expressions[1] == Integer(-1) then
+ local expr = (Integer(-1)*sqrt(Integer.one() - expression ^ Integer(2))):autosimplify()
+ return TrigExpression.ACOSVALUES[tostring(expr)]:autosimplify()
+ else
+ local expr = (sqrt(Integer.one() - expression ^ Integer(2))):autosimplify()
+ return TrigExpression.ACOSVALUES[tostring(expr)]:autosimplify()
+ end
+ end
+ end
+
+ if self.name == "arctan" and TrigExpression.ATANVALUES[tostring(expression)] ~= nil then
+ return TrigExpression.ATANVALUES[tostring(expression)]:autosimplify()
+ end
+
+ return self
+end
+
+--- checks if expression is a rational multiple of pi
+--- @return boolean
+function Expression:ismulratlPI()
+ if self.operation == BinaryOperation.MUL and #self.expressions == 2 and (self.expressions[1]:type() == Integer or self.expressions[1]:type() == Rational) and self.expressions[2] == PI then
+ return true
+ end
+
+ return false
+end
+
+--- @return TrigExpression
+function TrigExpression:autosimplify()
+ local expression = self.expression:autosimplify()
+
+ -- even and odd properties of trig functions
+ if (self.name == "sin" or self.name == "tan" or self.name == "csc" or self.name == "cot") and
+ expression.operation == BinaryOperation.MUL and expression.expressions[1]:isconstant() and expression.expressions[1] < Integer(0) then
+ return (-Integer.one() * TrigExpression(self.name, -expression)):autosimplify()
+ end
+
+ if (self.name == "cos" or self.name == "sec") and
+ expression.operation == BinaryOperation.MUL and expression.expressions[1]:isconstant() and expression.expressions[1] < Integer(0) then
+ expression = (-expression):autosimplify()
+ end
+
+ -- uses periodicity of sin and cos and friends
+ if self.name == "sin" or self.name == "cos" or self.name == "csc" or self.name == "sec" then
+ if expression == Integer.zero() or expression == PI then
+ goto skip
+ end
+ if expression.operation ~= BinaryOperation.ADD then
+ expression = BinaryOperation(BinaryOperation.ADD,{expression})
+ end
+ for index,component in ipairs(expression.expressions) do
+ if component:ismulratlPI() then
+ local coeff = component.expressions[1]
+ if coeff:type() == Integer then
+ coeff = coeff % Integer(2)
+ coeff = coeff:autosimplify()
+ end
+ if coeff:type() == Rational then
+ local n = coeff.numerator
+ local d = coeff.denominator
+ local m = {n:divremainder(d)}
+ coeff = (m[1] % Integer(2)) + m[2]/d
+ coeff = coeff:autosimplify()
+ end
+ expression.expressions[index].expressions[1] = coeff
+ end
+ expression = expression:autosimplify()
+ end
+ ::skip::
+ end
+
+ -- uses periodicity of tan and cot
+ if self.name == "tan" or self.name == "cot" then
+ if expression == Integer.zero() or expression == PI then
+ goto skip
+ end
+ if expression.operation ~= BinaryOperation.ADD then
+ expression = BinaryOperation(BinaryOperation.ADD,{expression})
+ end
+ for index,component in ipairs(expression.expressions) do
+ if component:ismulratlPI() then
+ local coeff = component.expressions[1]
+ if coeff:type() == Integer then
+ coeff = Integer.zero()
+ end
+ if coeff:type() == Rational then
+ local n = coeff.numerator
+ local d = coeff.denominator
+ local m = {n:divremainder(d)}
+ coeff = m[2]/d
+ coeff = coeff:autosimplify()
+ end
+ expression.expressions[index].expressions[1] = coeff
+ end
+ if component == PI then
+ expression.expressions[index] = Integer.zero()
+ end
+ end
+ expression = expression:autosimplify()
+ ::skip::
+ end
+
+ return TrigExpression(self.name, expression):evaluate()
+end
+
+--- @return table<number, Expression>
+function TrigExpression:subexpressions()
+ return {self.expression}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return TrigExpression
+function TrigExpression:setsubexpressions(subexpressions)
+ return TrigExpression(self.name, subexpressions[1])
+end
+
+-- function TrigExpression:freeof(symbol)
+-- return self.expression:freeof(symbol)
+-- end
+
+-- function TrigExpression:substitute(map)
+-- for expression, replacement in pairs(map) do
+-- if self == expression then
+-- return replacement
+-- end
+-- end
+-- return TrigExpression(self.name, self.expression:substitute(map))
+-- end
+
+-- function TrigExpression:order(other)
+-- return self:tofunction():order(other)
+-- end
+
+-- function TrigExpression:tofunction()
+-- return FunctionExpression(self.name, {self.expression}, true)
+-- end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__TrigExpression.__index = FunctionExpression
+__TrigExpression.__call = TrigExpression.new
+TrigExpression = setmetatable(TrigExpression, __TrigExpression)
+
+----------------------
+-- Static constants --
+----------------------
+TrigExpression.NAMES = {sin=1, cos=2, tan=3, csc=4, sec=5, cot=6,
+ arcsin=7, arccos=8, arctan=9, arccsc=10, arcsec=11, arccot=12}
+
+TrigExpression.INVERSES = {sin="arcsin", cos="arccos", tan="arctan", csc="arccsc", sec="arcsec", cot="arccot",
+ arcsin="sin", arccos="cos", arctan="tan", arccsc="csc", arcsec="sec", arccot="cot"}
+
+TrigExpression.COSVALUES = {
+ ["0"] = Integer.one(),
+ ["1/6"] = sqrt(Integer(3))/Integer(2),
+ ["1/4"] = sqrt(Integer(2))/Integer(2),
+ ["1/3"] = Integer.one()/Integer(2),
+ ["1/2"] = Integer.zero(),
+ ["2/3"] = -Integer.one()/Integer(2),
+ ["3/4"] = -sqrt(Integer(2))/Integer(2),
+ ["5/6"] = -sqrt(Integer(3))/Integer(2),
+ ["1"] = -Integer.one(),
+ ["7/6"] = -sqrt(Integer(3))/Integer(2),
+ ["5/4"] = -sqrt(Integer(2))/Integer(2),
+ ["4/3"] = -Integer.one()/Integer(2),
+ ["3/2"] = Integer.zero(),
+ ["5/3"] = Integer.one()/Integer(2),
+ ["7/4"] = sqrt(Integer(2))/Integer(2),
+ ["11/6"] = sqrt(Integer(3))/Integer(2),
+}
+TrigExpression.ACOSVALUES = {
+ ["1"] = Integer.zero(),
+ ["(1/2 * sqrt(3,2))"] = PI * Integer(6) ^ Integer(-1),
+ ["(1/2 * sqrt(2,2))"] = PI * Integer(4) ^ Integer(-1),
+ ["1/2"] = PI * Integer(3) ^ Integer(-1),
+ ["0"] = PI * Integer(2) ^ Integer(-1),
+ ["-1/2"] = PI * Integer(2) * Integer(3) ^ Integer(-1),
+ ["(-1/2 * sqrt(2,2))"]= PI * Integer(3) * Integer(4) ^ Integer(-1),
+ ["(-1/2 * sqrt(3,2))"]= PI * Integer(5) * Integer(6) ^ Integer(-1),
+ ["-1"] = Integer(-1)*PI,
+}
+TrigExpression.ATANVALUES = {
+ ["(-1 * sqrt(3,2))"] = Integer(-1) * PI * Integer(3) ^ Integer(-1),
+ ["-1"] = Integer(-1) * PI * Integer(4) ^ Integer(-1),
+ ["(-1/3 * sqrt(3,2))"] = Integer(-1) * Integer(6) ^ Integer(-1),
+ ["0"] = Integer.zero(),
+ ["(1/3 * sqrt(3,2))"] = PI * Integer(6) ^ Integer(-1),
+ ["1"] = PI * Integer(4) ^ Integer(-1),
+ ["sqrt(3,2)"] = PI * Integer(3) ^ Integer(-1)
+}
+
+SIN = function (a)
+ return TrigExpression("sin", a)
+end
+
+COS = function (a)
+ return TrigExpression("cos", a)
+end
+
+TAN = function (a)
+ return TrigExpression("tan", a)
+end
+
+CSC = function (a)
+ return TrigExpression("csc", a)
+end
+
+SEC = function (a)
+ return TrigExpression("sec", a)
+end
+
+COT = function (a)
+ return TrigExpression("cot", a)
+end
+
+ARCSIN = function (a)
+ return TrigExpression("arcsin", a)
+end
+
+ARCCOS = function (a)
+ return TrigExpression("arccos", a)
+end
+
+ARCTAN = function (a)
+ return TrigExpression("arctan", a)
+end
+
+ARCCSC = function (a)
+ return TrigExpression("arccsc", a)
+end
+
+ARCSEC = function (a)
+ return TrigExpression("arcsec", a)
+end
+
+ARCCOT = function (a)
+ return TrigExpression("arccot", a)
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/calculus/_init.lua b/macros/luatex/latex/luacas/tex/calculus/_init.lua
new file mode 100644
index 0000000000..59ba1bbcc8
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/calculus/_init.lua
@@ -0,0 +1,6 @@
+-- Loads calculus files in the correct order.
+require("algebra._init")
+
+require("calculus.derivativeexpression")
+require("calculus.integralexpression")
+require("calculus.diffexpression")
diff --git a/macros/luatex/latex/luacas/tex/calculus/derivativeexpression.lua b/macros/luatex/latex/luacas/tex/calculus/derivativeexpression.lua
new file mode 100644
index 0000000000..b757759c4c
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/calculus/derivativeexpression.lua
@@ -0,0 +1,265 @@
+--- @class DerivativeExpression
+--- An expression for a single-variable derivative of an expression.
+--- @field symbol SymbolExpression
+--- @field expression Expression
+DerivativeExpression = {}
+__DerivativeExpression = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+-- Creates a new single-variable derivative operation with the given symbol and expression.
+--- @param expression Expression
+--- @param symbol Symbol
+--- @return DerivativeExpression
+function DerivativeExpression:new(expression, symbol)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ o.expression = Copy(expression)
+ o.symbol = symbol or SymbolExpression("x")
+
+ __o.__index = DerivativeExpression
+ __o.__tostring = function(a)
+ return '(d/d' .. tostring(a.symbol) .. " " .. tostring(a.expression) .. ')'
+ end
+ __o.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always runs this anyway
+ if not b:type() == DerivativeExpression then
+ return false
+ end
+ return a.symbol == b.symbol and a.expression == b.expression
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- @return Expression
+function DerivativeExpression:evaluate()
+ local exp = self.expression
+
+ -- The derivative of a constant is 0
+ if exp:isconstant() then
+ return Integer.zero()
+ end
+
+ -- The derivative of a symbol is either 1 or 0
+ if exp:type() == SymbolExpression then
+ if self.symbol == exp then
+ return Integer.one()
+ end
+ return Integer.zero()
+ end
+
+ -- Chain rule for arbitrary functions
+
+ if exp:type() == FunctionExpression then
+ local results = {}
+ for index,expression in ipairs(exp.expressions) do
+ local dout = FunctionExpression(exp.name,exp.expressions,exp.derivatives)
+ dout.variables = exp.variables
+ dout.derivatives[index] = dout.derivatives[index]+Integer.one()
+ local dinn = DerivativeExpression(expression,self.symbol):evaluate()
+ results[index] = dout*dinn
+ end
+ return BinaryOperation(BinaryOperation.ADD,results):autosimplify()
+ end
+
+ --if exp:type() == FunctionExpression then
+ -- local results = {}
+ -- for index,expression in ipairs(exp.expressions) do
+ -- local inn = DerivativeExpression(expression,self.symbol):autosimplify()
+ -- local out = Copy(exp)
+ -- out.orders[index] = out.orders[index] + Integer.one()
+ -- local result = inn*out
+ -- table.insert(results,result)
+ -- end
+ -- return BinaryOperation(BinaryOperation.ADD,results):autosimplify()
+ --end
+
+ --if exp:type() == FunctionExpression then
+ -- if exp.expressions[2] then
+ -- return self
+ -- end
+ -- return DerivativeExpression(exp.expressions[1], self.symbol) * FunctionExpression(exp.name, exp.expressions, exp.orders[1] + Integer.one(), exp.variables[1]):autosimplify()
+ --end
+
+ -- Chain rule for trig functions
+ if exp:type() == TrigExpression then
+ local internal = DerivativeExpression(exp.expression, self.symbol)
+
+ if exp.name == "sin" then
+ return (internal * COS(exp.expression)):autosimplify()
+ end
+ if exp.name == "cos" then
+ return (internal * -SIN(exp.expression)):autosimplify()
+ end
+ if exp.name == "tan" then
+ return (internal * SEC(exp.expression)^Integer(2)):autosimplify()
+ end
+ if exp.name == "csc" then
+ return (internal * -CSC(exp.expression)*COT(exp.expression)):autosimplify()
+ end
+ if exp.name == "sec" then
+ return (internal * -SEC(exp.expression)*TAN(exp.expression)):autosimplify()
+ end
+ if exp.name == "cot" then
+ return (internal * -CSC(exp.expression)^Integer(2)):autosimplify()
+ end
+ if exp.name == "arcsin" then
+ return (internal / (Integer(1)-exp.expression^Integer(2))^(Integer(1)/Integer(2))):autosimplify()
+ end
+ if exp.name == "arccos" then
+ return (-internal / (Integer(1)-exp.expression^Integer(2))^(Integer(1)/Integer(2))):autosimplify()
+ end
+ if exp.name == "arctan" then
+ return (internal / (Integer(1)+exp.expression^Integer(2))):autosimplify()
+ end
+ if exp.name == "arccsc" then
+ return (-internal / (ABS(exp.expression) * (Integer(1)-exp.expression^Integer(2))^(Integer(1)/Integer(2)))):autosimplify()
+ end
+ if exp.name == "arcsec" then
+ return (internal / (ABS(exp.expression) * (Integer(1)-exp.expression^Integer(2))^(Integer(1)/Integer(2)))):autosimplify()
+ end
+ if exp.name == "arccot" then
+ return (-internal / (Integer(1)+exp.expression^Integer(2))):autosimplify()
+ end
+ end
+
+ -- TODO: Piecewise functions
+ if self:type() == AbsExpression then
+ return DerivativeExpression(self.expression, self.symbol):autosimplify()
+ end
+
+ -- Uses linearity of derivatives to evaluate sum expressions
+ if exp.operation == BinaryOperation.ADD then
+ local parts = {}
+ for i, expression in pairs(exp.expressions) do
+ parts[i] = DerivativeExpression(expression, self.symbol)
+ end
+ return BinaryOperation(BinaryOperation.ADD, parts):autosimplify()
+ end
+
+ -- Uses product rule to evaluate product expressions
+ if exp.operation == BinaryOperation.MUL then
+ local sums = {}
+ for i, expression in pairs(exp.expressions) do
+ local products = {}
+ for j, innerexpression in pairs(exp.expressions) do
+ if i ~= j then
+ products[j] = innerexpression
+ else
+ products[j] = DerivativeExpression(innerexpression, self.symbol)
+ end
+ end
+ sums[i] = BinaryOperation(BinaryOperation.MUL, products)
+ end
+ return BinaryOperation(BinaryOperation.ADD, sums):autosimplify()
+ end
+
+ -- Uses the generalized power rule to evaluate power expressions
+ if exp.operation == BinaryOperation.POW then
+ local base = exp.expressions[1]
+ local exponent = exp.expressions[2]
+
+ return BinaryOperation.MULEXP({
+ BinaryOperation.POWEXP({base, exponent}),
+ BinaryOperation.ADDEXP({
+ BinaryOperation.MULEXP({
+ DD(base, self.symbol),
+ BinaryOperation.DIVEXP({exponent, base})}),
+ BinaryOperation.MULEXP({
+ DD(exponent, self.symbol),
+ LN(base)})})
+ }):autosimplify()
+ end
+
+ if exp:type() == Logarithm then
+ local base = exp.base
+ local expression = exp.expression
+
+ return BinaryOperation.SUBEXP({
+ BinaryOperation.DIVEXP({DD(expression, self.symbol),
+ BinaryOperation.MULEXP({expression, LN(base)})}),
+
+ BinaryOperation.DIVEXP({
+ BinaryOperation.MULEXP({LN(expression), DD(base, self.symbol)}),
+ BinaryOperation.MULEXP({BinaryOperation.POWEXP({LN(base), Integer(2)}), base})
+ })
+
+ }):autosimplify()
+ end
+
+ return self
+end
+
+--- @return Expression
+function DerivativeExpression:autosimplify()
+ return DerivativeExpression(self.expression:autosimplify(), self.symbol):evaluate()
+end
+
+--- @return table<number, Expression>
+function DerivativeExpression:subexpressions()
+ return {self.expression}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return DerivativeExpression
+function DerivativeExpression:setsubexpressions(subexpressions)
+ return DerivativeExpression(subexpressions[1], self.symbol)
+end
+
+-- function DerivativeExpression:freeof(symbol)
+-- return self.symbol.freeof(symbol) and self.expression:freeof(symbol)
+-- end
+
+-- Substitutes each expression for a new one.
+-- function DerivativeExpression:substitute(map)
+-- for expression, replacement in pairs(map) do
+-- if self == expression then
+-- return replacement
+-- end
+-- end
+-- -- Typically, we only perform substitution on autosimplified expressions, so this won't get called. May give strange results, i.e.,
+-- -- substituting and then evaluating the derivative may not return the same thing as evaluating the derivative and then substituting.
+-- return DerivativeExpression(self.expression:substitute(map), self.symbol)
+-- end
+
+--- @param other Expression
+--- @return boolean
+function DerivativeExpression:order(other)
+ if other:type() == IntegralExpression then
+ return true
+ end
+
+ if other:type() ~= DerivativeExpression then
+ return false
+ end
+
+ if self.symbol ~= other.symbol then
+ return self.symbol:order(other.symbol)
+ end
+
+ return self.expression:order(other.expression)
+end
+
+--- @return string
+function DerivativeExpression:tolatex()
+ return '\\frac{d}{d' .. self.symbol:tolatex() .. '}\\left(' .. self.expression:tolatex() .. '\\right)'
+end
+
+-----------------
+-- Inheritance --
+-----------------
+__DerivativeExpression.__index = CompoundExpression
+__DerivativeExpression.__call = DerivativeExpression.new
+DerivativeExpression = setmetatable(DerivativeExpression, __DerivativeExpression)
+
+----------------------
+-- Static constants --
+----------------------
+DD = function(expression, symbol)
+ return DerivativeExpression(expression, symbol)
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/calculus/diffexpression.lua b/macros/luatex/latex/luacas/tex/calculus/diffexpression.lua
new file mode 100644
index 0000000000..df1dc90f33
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/calculus/diffexpression.lua
@@ -0,0 +1,190 @@
+--- @class DiffExpression
+--- An expression for a multi-variable higher-order derivatives of an expression.
+--- @field symbols SymbolExpression
+--- @field expression Expression
+
+DiffExpression = {}
+__DiffExpression = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+-- Creates a new derivative operation with the given symbols and expression.
+--- @param expression Expression
+--- @param symbols table<number, Symbol>
+--- @return DiffExpression
+function DiffExpression:new(expression,symbols)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ o.symbols = Copy(symbols)
+ o.degree = #o.symbols
+ o.expression = Copy(expression)
+
+ __o.__tostring = function(a)
+ local varlist = '(d'
+ if a.degree == 1 then
+ varlist = varlist .. '/d' .. tostring(a.symbols[1]) .. " " .. tostring(a.expression) .. ')'
+ end
+ if a.degree > 1 then
+ varlist = varlist .. '^' .. tostring(a.degree) .. '/'
+ local varnum = 1
+ for index = 1, #a.symbols do
+ local var = a.symbols[#a.symbols-index+1]
+ if a.symbols[#a.symbols-index] == var then
+ varnum = varnum + 1
+ goto nextvar
+ end
+ if a.symbols[#a.symbols-index] ~=var then
+ if varnum == 1 then
+ varlist = varlist .. 'd' .. tostring(var)
+ else
+ varlist = varlist .. 'd' .. tostring(var) .. '^' .. tostring(varnum)
+ end
+ varnum = 1
+ end
+ ::nextvar::
+ end
+ varlist = varlist .. " " .. tostring(a.expression) .. ')'
+ end
+ return varlist
+ end
+
+ __o.__index = DiffExpression
+ __o.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always rungs this anyway
+ if not b:type() == DiffExpression then
+ return false
+ end
+ if a.expression ~= b.expression then
+ return false
+ end
+ local loc = 1
+ while a.symbols[loc] or b.symbols[loc] do
+ if not a.symbols[loc] or not b.symbols[loc] or
+ (a.symbols[loc] ~= b.symbols[loc]) then
+ return false
+ end
+ loc = loc + 1
+ end
+ return true
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- @return Expression
+function DiffExpression:evaluate()
+ local exp = self.expression
+
+ for _,var in ipairs(self.symbols) do
+ exp = DerivativeExpression(exp,var):evaluate()
+ end
+ return exp
+end
+
+--- @return Expression
+function DiffExpression:autosimplify()
+ return DiffExpression(self.expression:autosimplify(), self.symbols):evaluate()
+end
+
+
+--- @return table<number, Expression>
+function DiffExpression:subexpressions()
+ return {self.expression}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return DiffExpression
+function DiffExpression:setsubexpressions(subexpressions)
+ return DiffExpression(subexpressions[1], self.symbols)
+end
+
+--- @param other Expression
+--- @return boolean
+function DiffExpression:order(other)
+ if other:type() == IntegralExpression then
+ return true
+ end
+
+ if other:type() ~= DiffExpression then
+ return false
+ end
+
+ if self.degree > other.degree then
+ return false
+ end
+
+ if self.degree < other.degree then
+ return true
+ end
+
+ return self.expression:order(other.expression)
+end
+
+--- @return string
+function DiffExpression:tolatex()
+ local varlist = '\\frac{'
+ if self.degree == 1 then
+ varlist = varlist .. 'd}{d' .. self.symbols[1]:tolatex() .. '}\\left(' .. self.expression:tolatex() .. '\\right)'
+ end
+ if self.degree > 1 then
+ local cvarlist = {}
+ local count = 1
+ for index, var in ipairs(self.symbols) do
+ if var == self.symbols[index+1] then
+ count = count + 1
+ else
+ table.insert(cvarlist,{var,count})
+ count = 1
+ end
+ end
+ if #cvarlist == 1 then
+ varlist = varlist .. 'd^{' .. self.degree .. '}}{' .. 'd' .. cvarlist[1][1]:tolatex() .. '^{' .. self.degree .. '}'
+ end
+ if #cvarlist > 1 then
+ varlist = varlist .. '\\partial^{' .. self.degree .. '}}{'
+ for index, varnum in ipairs(cvarlist) do
+ var = cvarlist[#cvarlist - index+1][1]
+ num = cvarlist[#cvarlist - index+1][2]
+ if num == 1 then
+ varlist = varlist .. '\\partial ' .. var:tolatex()
+ else
+ varlist = varlist .. '\\partial ' .. var:tolatex() .. '^{' .. num .. '}'
+ end
+ end
+ end
+ varlist = varlist .. '} \\left(' .. self.expression:tolatex() .. '\\right)'
+ end
+ return varlist
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__DiffExpression.__index = CompoundExpression
+__DiffExpression.__call = DiffExpression.new
+DiffExpression = setmetatable(DiffExpression, __DiffExpression)
+
+----------------------
+-- Static constants --
+----------------------
+
+diff = function(expression,...)
+ local symbols = {}
+ for i = 1, select("#",...) do
+ local var = select(i,...)
+ if #var == 0 then
+ table.insert(symbols,var)
+ end
+ if #var > 0 then
+ for index=1, RR(var[2]) do
+ table.insert(symbols,var[1])
+ end
+ end
+ end
+ return DiffExpression(expression,symbols)
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/calculus/integralexpression.lua b/macros/luatex/latex/luacas/tex/calculus/integralexpression.lua
new file mode 100644
index 0000000000..5267127264
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/calculus/integralexpression.lua
@@ -0,0 +1,934 @@
+--- @class IntegralExpression
+--- An expression for the integral of an expression.
+--- @field symbol SymbolExpression
+--- @field expression Expression
+--- @field upper Expression
+--- @field lower Expression
+--- @field attempts table<number, Expression>
+--- @field results table<number, Expression>
+--- @field enhancedsubstitution Integer
+--- @field recursive boolean
+
+IntegralExpression = {}
+__IntegralExpression = {}
+
+
+--------------------------
+-- Static functionality --
+--------------------------
+
+--- Recursive part of the indefinite integral operator. Returns nil if the expression could not be integrated.
+--- We switch to prodcedural programming here because it is more natural.
+--- @param integral IntegralExpression
+--- @return Expression|nil
+function IntegralExpression.integrate(integral)
+ integral.expression = integral.expression:autosimplify()
+
+ if not integral.recursive and #integral.attempts > 0 then
+ return Copy(integral:lock(Expression.NIL, true))
+ end
+
+ -- print(integral.expression)
+
+ local F = IntegralExpression.table(integral)
+ if F then return F end
+
+ -- If we see the same integrand again, and hasn't been solved already, then the integral can't be solved
+ local resultindex = Contains(integral.attempts, integral.expression)
+ if resultindex then
+ return integral.results[resultindex]
+ end
+ local newindex = #integral.attempts+1
+ integral.attempts[newindex] = integral.expression
+
+ -- print("Evalutaing: " .. tostring(integral.expression))
+
+ F = IntegralExpression.linearproperties(integral)
+ if F then
+ -- print("Linear Properties")
+ integral.results[newindex] = F
+ return F
+ end
+
+ -- local exp = integral.expression
+ -- local sym = integral.symbol
+ -- local es = integral.enhancedsubstitution
+ -- integral.enhancedsubstitution = Integer.zero()
+ F = IntegralExpression.substitutionmethod(integral)
+ if F then
+ -- print("u-Substitution")
+ integral.results[newindex] = F
+ return F
+ end
+ -- integral.expression = exp
+ -- integral.symbol = sym
+ -- integral.enhancedsubstitution = es
+
+
+ F = IntegralExpression.rationalfunction(integral)
+ if F then
+ -- print("Rational Function")
+ integral.results[newindex] = F
+ return F
+ end
+
+ F = IntegralExpression.partsmethod(integral)
+ if F then
+ -- print("Parts")
+ integral.results[newindex] = F
+ return F
+ end
+
+ F = IntegralExpression.eulersformula(integral)
+ if F then
+ -- print("Euler's formula")
+ integral.results[newindex] = F
+ return F
+ end
+
+ local expanded = integral.expression:expand()
+ if integral.expression ~= expanded then
+ integral.expression = expanded
+ F = IntegralExpression.integrate(integral)
+ if F then
+ -- print("Expanded")
+ integral.results[newindex] = F
+ return F
+ end
+ end
+
+ expanded = (Integer.one()/((Integer.one()/integral.expression):autosimplify():expand())):autosimplify()
+ if integral.expression ~= expanded then
+ integral.expression = expanded
+ F = IntegralExpression.integrate(integral)
+ if F then
+ -- print("Inverse Expanded")
+ integral.results[newindex] = F
+ return F
+ end
+ end
+
+ F = IntegralExpression.enhancedsubstitutionmethod(integral)
+ if F then
+ -- print("Enhanced u-Substitution")
+ integral.results[newindex] = F
+ return F
+ end
+
+ return nil
+end
+
+--- A table of basic integrals, returns nil if the integrand isn't in the table.
+--- @param integral IntegralExpression
+--- @return Expression|nil
+function IntegralExpression.table(integral)
+ local integrand = integral.expression
+ local symbol = integral.symbol
+
+ -- Constant integrand rule - int(c, x) = c*x
+ if integrand:freeof(symbol) then
+ return integrand*symbol
+ end
+
+ if integrand:type() == SymbolExpression then
+
+ -- int(x, x) = x^2/2
+ if integrand == symbol then
+ return integrand ^ Integer(2) / Integer(2)
+ end
+
+ -- Constant integrand rule again
+ return integrand*symbol
+ end
+
+ if integrand:type() == BinaryOperation then
+
+ if integrand.operation == BinaryOperation.POW then
+ -- int(1/x, x) = ln(x)
+ if integrand.expressions[1] == symbol and integrand.expressions[2] == Integer(-1) then
+ return LN(symbol)
+ end
+
+ -- Cavalieri's formula - int(x^n, x) = x^(n+1)/(n+1)
+ if integrand.expressions[1] == symbol and integrand.expressions[2]:freeof(symbol) then
+ return symbol ^ (integrand.expressions[2] + Integer.one()) / (integrand.expressions[2] + Integer.one())
+ end
+
+ -- int(n^x, x) = n^x/ln(n)
+ if integrand.expressions[1]:freeof(symbol) and integrand.expressions[2] == symbol then
+ return integrand / LN(integrand.expressions[1])
+ end
+
+ -- int(csc(x)^2, x) = -cot(x)
+ if integrand.expressions[1] == CSC(symbol) and integrand.expressions[2] == Integer(2) then
+ return -COT(symbol)
+ end
+
+ -- int(sec(x)^2, x) = tan(x)
+ if integrand.expressions[1] == SEC(symbol) and integrand.expressions[2] == Integer(2) then
+ return TAN(symbol)
+ end
+ end
+
+ if integrand.operation == BinaryOperation.MUL and #integrand.expressions == 2 then
+ -- int(tan(x)sec(x), x) = sec(x)
+ if integrand.expressions[1] == TAN(symbol) and integrand.expressions[2] == SEC(symbol) then
+ return SEC(symbol)
+ end
+
+ -- int(csc(x)cot(x), x) = -csc(x)
+ if integrand.expressions[1] == CSC(symbol) and integrand.expressions[2] == COT(symbol) then
+ return -CSC(symbol)
+ end
+ end
+
+ return nil
+ end
+
+ if integrand:type() == Logarithm then
+ -- int(log_n(x), x) = (x*ln(x)-x)/ln(n)
+ if integrand.base:freeof(symbol) and integrand.expression == symbol then
+ return (symbol * LN(symbol) - symbol) / LN(integrand.base)
+ end
+
+ return nil
+ end
+
+ if integrand:type() == TrigExpression then
+ if integrand == SIN(symbol) then
+ return -COS(symbol)
+ end
+
+ if integrand == COS(symbol) then
+ return SIN(symbol)
+ end
+
+ if integrand == TAN(symbol) then
+ return -LN(COS(symbol))
+ end
+
+ if integrand == CSC(symbol) then
+ return -LN(CSC(symbol)+COT(symbol))
+ end
+
+ if integrand == SEC(symbol) then
+ return LN(SEC(symbol) + TAN(symbol))
+ end
+
+ if integrand == COT(symbol) then
+ return LN(SIN(symbol))
+ end
+
+ if integrand == ARCSIN(symbol) then
+ return symbol*ARCSIN(symbol) + (Integer.one()-symbol^(Integer(2)))^(Integer.one()/Integer(2))
+ end
+
+ if integrand == ARCCOS(symbol) then
+ return symbol*ARCCOS(symbol) - (Integer.one()-symbol^(Integer(2)))^(Integer.one()/Integer(2))
+ end
+
+ if integrand == ARCTAN(symbol) then
+ return symbol*ARCTAN(symbol) - (Integer.one()/Integer(2))*LN(Integer.one()+symbol^Integer(2))
+ end
+
+ if integrand == ARCCSC(symbol) then
+ return symbol*ARCCSC(symbol) + LN(symbol*(Integer.one()+(Integer.one()-symbol^(Integer(-2)))^(Integer.one()/Integer(2))))
+ end
+
+ if integrand == ARCSEC(symbol) then
+ return symbol*ARCSEC(symbol) - LN(symbol*(Integer.one()+(Integer.one()-symbol^(Integer(-2)))^(Integer.one()/Integer(2))))
+ end
+
+ if integrand == ARCCOT(symbol) then
+ return symbol*ARCCOT(symbol) + (Integer.one()/Integer(2))*LN(Integer.one()+symbol^Integer(2))
+ end
+ end
+
+ return nil
+end
+
+--- Uses linearity to break up the integral and integrate each piece.
+--- @param integral IntegralExpression
+--- @return Expression|nil
+function IntegralExpression.linearproperties(integral)
+ local expression = integral.expression
+ local symbol = integral.symbol
+ local es = integral.enhancedsubstitution
+
+ if expression:type() == BinaryOperation then
+ if expression.operation == BinaryOperation.MUL then
+ local freepart = Integer.one()
+ local variablepart = Integer.one()
+ for _, term in ipairs(expression.expressions) do
+ if term:freeof(symbol) then
+ freepart = freepart*term
+ else
+ variablepart = variablepart*term
+ end
+ end
+ if freepart == Integer.one() then
+ return nil
+ end
+ integral.expression = variablepart
+ integral.symbol = symbol
+ integral.enhancedsubstitution = es
+ local F = IntegralExpression.integrate(integral)
+ if F then
+ return freepart*F
+ end
+ return nil
+ end
+
+ if expression.operation == BinaryOperation.ADD then
+ local sum = Integer.zero()
+ for _, term in ipairs(expression.expressions) do
+ integral.expression = term
+ integral.symbol = symbol
+ integral.enhancedsubstitution = es
+ local F = IntegralExpression.integrate(integral)
+ if F then
+ sum = sum + F
+ else
+ return nil
+ end
+
+ end
+ return sum
+ end
+ end
+
+ return nil
+end
+
+--- Attempts u-substitutions to evaluate the integral.
+--- @param integral IntegralExpression
+--- @return Expression|nil
+function IntegralExpression.substitutionmethod(integral)
+ local expression = integral.expression
+ local symbol = integral.symbol
+ local es = integral.enhancedsubstitution
+
+ local P = IntegralExpression.trialsubstitutions(expression)
+ local F = nil
+ local i = 1
+
+ while not F and i <= #P do
+ local g = P[i]
+ if g ~= symbol and not g:freeof(symbol) then
+ local subsymbol = SymbolExpression("u")
+ if symbol == SymbolExpression("u") then
+ subsymbol = SymbolExpression("v")
+ end
+ local u = (expression / (DerivativeExpression(g, symbol))):autosimplify()
+ u = u:substitute({[g]=subsymbol}):autosimplify()
+
+ --factor u and cancel like non-constant terms
+ u = u:factor():autosimplify()
+
+ if u:freeof(symbol) then
+ integral.expression = u
+ integral.symbol = subsymbol
+ integral.enhancedsubstitution = es
+ F = IntegralExpression.integrate(integral)
+ if F then
+ if integral.recursive then
+ F = F:substitute({[subsymbol]=g})
+ end
+ return F
+ end
+ end
+ end
+ i = i + 1
+ end
+
+ return F
+end
+
+--- Attempts u-substitutions to evaluate the integral, including solving for the original variable and substituting the result into the expression.
+--- @param integral IntegralExpression
+--- @return Expression|nil
+function IntegralExpression.enhancedsubstitutionmethod(integral)
+ local expression = integral.expression
+ local symbol = integral.symbol
+ local es = integral.enhancedsubstitution
+
+ local P = IntegralExpression.trialsubstitutions(expression)
+ local F = nil
+ local i = 1
+
+ while not F and i <= #P do
+ local g = P[i]
+ if g ~= symbol and not g:freeof(symbol) then
+ local subsymbol = SymbolExpression("u")
+ if symbol == SymbolExpression("u") then
+ subsymbol = SymbolExpression("v")
+ end
+ local u = (expression / (DerivativeExpression(g, symbol))):autosimplify()
+ u = u:substitute({[g]=subsymbol}):autosimplify()
+
+ --factor u and cancel like non-constant terms
+ u = u:factor():autosimplify()
+
+ if integral.enhancedsubstitution > Integer.zero() then
+ local f = Equation(subsymbol, g):solvefor(symbol)
+ if f.lhs == symbol then
+ u = u:substitute({[symbol]=f.rhs}):autosimplify()
+ integral.expression = u
+ integral.symbol = subsymbol
+ integral.enhancedsubstitution = integral.enhancedsubstitution - Integer.one()
+ F = IntegralExpression.integrate(integral)
+ if F then
+ if integral.recursive then
+ F = F:substitute({[subsymbol]=g})
+ end
+ return F
+ end
+ integral.enhancedsubstitution = integral.enhancedsubstitution + Integer.one()
+ end
+ end
+ end
+ i = i + 1
+ end
+
+ return F
+end
+
+--- Generates a list of possible u-substitutions to attempt
+--- @param expression Expression
+--- @return table<number, Expression>
+function IntegralExpression.trialsubstitutions(expression)
+ local substitutions = {}
+
+ -- Recursive part - evaluates each term in a product.
+ if expression:type() == BinaryOperation and expression.operation == BinaryOperation.MUL then
+ substitutions[#substitutions+1] = expression
+ for _, term in ipairs(expression.expressions) do
+ substitutions = JoinArrays(substitutions, IntegralExpression.trialsubstitutions(term))
+ end
+ end
+
+ --Recursive part - evaluates each term in a sum.
+ if expression:type() == BinaryOperation and expression.operation == BinaryOperation.ADD then
+ substitutions[#substitutions+1] = expression
+ for _,term in ipairs(expression.expressions) do
+ substitutions = JoinArrays(substitutions, IntegralExpression.trialsubstitutions(term))
+ end
+ end
+
+ -- Function forms and arguments of function forms (includes a recursive part)
+ if expression:type() == TrigExpression or expression:type() == Logarithm then
+ substitutions[#substitutions+1] = expression
+ if not expression.expression:isatomic() then
+ substitutions[#substitutions+1] = expression.expression
+ end
+ substitutions = JoinArrays(substitutions, IntegralExpression.trialsubstitutions(expression.expression))
+ end
+
+ -- Bases and exponents of powers
+ if expression:type() == BinaryOperation and expression.operation == BinaryOperation.POW then
+ substitutions[#substitutions+1] = expression
+ -- Atomic expressions are technically valid substitutions, but they won't be useful
+ if not expression.expressions[1]:isatomic() then
+ --substitutions[#substitutions+1] = expression.expressions[1]
+ substitutions = JoinArrays(substitutions, IntegralExpression.trialsubstitutions(expression.expressions[1]))
+ end
+ if not expression.expressions[2]:isatomic() then
+ --substitutions[#substitutions+1] = expression.expressions[2]
+ substitutions = JoinArrays(substitutions, IntegralExpression.trialsubstitutions(expression.expressions[2]))
+ end
+ end
+
+ return substitutions
+end
+
+
+--- Uses Lazard, Rioboo, Rothstein, and Trager's method to integrate rational functions.
+--- This is mostly to try to avoid factoring and finding the roots of the full denominator whenever possible.
+--- @param integral IntegralExpression
+--- @return Expression|nil
+function IntegralExpression.rationalfunction(integral)
+ local expression = integral.expression
+ local symbol = integral.symbol
+ local es = integral.enhancedsubstitution
+
+ -- Type checking and conversion to polynomial type.
+ local f, g, fstat, gstat
+ if expression:type() == BinaryOperation and expression.operation == BinaryOperation.POW and expression.expressions[2] == Integer(-1) then
+ g, gstat = expression.expressions[1]:topolynomial()
+ if not gstat then
+ return nil
+ end
+ f = PolynomialRing({Integer.one()}, g.symbol)
+ else
+ if expression:type() ~= BinaryOperation or expression.operation ~= BinaryOperation.MUL or expression.expressions[3] then
+ return nil
+ end
+ if expression.expressions[2]:type() == BinaryOperation and expression.expressions[2].operation == BinaryOperation.POW and expression.expressions[2].expressions[2] == Integer(-1) then
+ if expression.expressions[1].topolynomial ~=nil and expression.expressions[2].expressions[1].topolynomial ~=nil then
+ f, fstat = expression.expressions[1]:topolynomial()
+ g, gstat = expression.expressions[2].expressions[1]:topolynomial()
+ end
+ elseif expression.expressions[1]:type() == BinaryOperation and expression.expressions[1].operation == BinaryOperation.POW and expression.expressions[1].expressions[2] == Integer(-1) then
+ if expression.expressions[2].topolynomial ~= nil and expression.expressions[1].expressions[1].topolynomial ~= nil then
+ f, fstat = expression.expressions[2]:topolynomial()
+ g, gstat = expression.expressions[1].expressions[1]:topolynomial()
+ end
+ else
+ return nil
+ end
+
+ if not fstat or not gstat or f.symbol ~= symbol.symbol or g.symbol ~= symbol.symbol then
+ return nil
+ end
+ end
+
+ -- Explicit handling of degree 1 or less over a binomial.
+ do
+ local disc = g.coefficients[1]*g.coefficients[1]-Integer(4)*g.coefficients[2]*g.coefficients[0]
+ if f.degree <= Integer.one() and g.degree == Integer(2) and disc < Integer.zero() then
+ return (f.coefficients[1] * LN(g.coefficients[0] + g.coefficients[1] * symbol + g.coefficients[2] * symbol ^ Integer(2))/(Integer(2) * g.coefficients[2]) + (Integer(2)*f.coefficients[0]*g.coefficients[2] - f.coefficients[1]*g.coefficients[1]) / (g.coefficients[2] * sqrt(Integer(4)*g.coefficients[0]*g.coefficients[2] - g.coefficients[1] ^ Integer(2))) * ARCTAN((Integer(2)*g.coefficients[2]*symbol+g.coefficients[1]) / sqrt(Integer(4)*g.coefficients[0]*g.coefficients[2]-g.coefficients[1] ^ Integer(2)))):autosimplify()
+ end
+ end
+
+ -- If the polynomials are not relatively prime, divides out the common factors.
+ local gcd = PolynomialRing.gcd(f, g)
+ if gcd ~= Integer.one() then
+ f, g = f // gcd, g // gcd
+ end
+
+ -- Seperates out the polynomial part and rational part and integrates the polynomial part.
+ local q, h = f:divremainder(g)
+ integral.expression = q
+ integral.symbol = symbol
+ integral.enhancedsubstitution = es
+ U = IntegralExpression.integrate(integral)
+
+ if h == Integer.zero() then
+ return U
+ end
+
+ -- Performs partial fraction decomposition into square-free denominators on the rational part.
+ local gg = g:squarefreefactorization()
+ local pfd = PolynomialRing.partialfractions(h, g, gg)
+
+ -- Hermite reduction.
+ local V = Integer.zero()
+ for _, term in ipairs(pfd.expressions) do
+ local i = #term.expressions
+ if i > 1 then
+ for j = 1, i-1 do
+ local n = term.expressions[j].expressions[1]
+ local d = term.expressions[j].expressions[2].expressions[1]
+ local p = term.expressions[j].expressions[2].expressions[2]
+
+ local _, s, t = PolynomialRing.extendedgcd(d, d:derivative())
+ s = s * n
+ t = t * n
+ V = V - t / ((p-Integer.one()) * BinaryOperation.POWEXP({d, p-Integer.one()}))
+ term.expressions[j+1].expressions[1] = term.expressions[j+1].expressions[1] + s + t:derivative() / (p-Integer.one())
+ end
+ end
+ end
+
+ --Lazard-Rioboo-Trager method.
+ local W = Integer.zero()
+ for _, term in ipairs(pfd.expressions) do
+ local a = term.expressions[#term.expressions].expressions[1]
+ local b = term.expressions[1].expressions[2].expressions[1]
+ local y = a - b:derivative() * PolynomialRing({Integer.zero(), Integer.one()}, "_")
+ local r = PolynomialRing.resultant(b, y)
+
+
+ local rr = r:squarefreefactorization()
+ local remainders = PolynomialRing.monicgcdremainders(b, y)
+ for pos, factor in ipairs(rr.expressions) do
+ if pos > 1 then
+ local re = factor.expressions[1]
+ local e = factor.expressions[2]
+ local roots = re:roots()
+ for _, root in ipairs(roots) do
+ local w
+ for _, remainder in ipairs(remainders) do
+ if remainder.degree == e then
+ w = remainder
+ break
+ end
+ end
+ W = W + root*LN(w:substitute({[SymbolExpression("_")] = root}))
+ end
+ end
+ end
+ end
+
+ return U + V + W
+end
+
+
+--- Attempts integration by parts for expressions with a polynomial factor in them. Other product expressions use Euler's formula.
+--- @param integral IntegralExpression
+--- @return Expression|nil
+function IntegralExpression.partsmethod(integral)
+ local expression = integral.expression
+ local symbol = integral.symbol
+ local es = integral.enhancedsubstitution
+
+ if expression:type() ~= BinaryOperation or expression.operation ~= BinaryOperation.MUL then
+ return
+ end
+
+ local u
+ local vp = Integer.one()
+ --looking for ILATE
+ for _, exp in ipairs(expression:subexpressions()) do
+ if exp:type() == TrigExpression and (exp.name == "arctan" or exp.name == "arccos" or exp.name == "arcsin" or exp.name == "arccot" or exp.name == "arcsec" or exp.name == "arccsc") then
+ u = exp
+ else
+ vp = vp * exp
+ end
+ end
+
+ if not u or u:freeof(symbol) then
+ goto skipI
+ else
+ vp = vp:autosimplify()
+ end
+
+ --if vp:type() == Logarithm or vp.topolynomial or (vp:type() == TrigExpression and (vp.name == "cos" or vp.name == "sin")) or (vp.operation == BinaryOperation.POW and vp.expressions[1]:freeof(symbol)) then
+ if select(2,vp:topolynomial()) then
+ integral.expression = vp
+ integral.symbol = symbol
+ integral.enhancedsubstitution = es
+ local v = IntegralExpression.integrate(integral)
+ if not v then
+ goto skipI
+ end
+
+ local up = DerivativeExpression(u, symbol):autosimplify()
+
+ integral.expression = v*up
+ integral.symbol = symbol
+ integral.enhancedsubstitution = es
+ local vup = IntegralExpression.integrate(integral)
+ if not vup then
+ goto skipI
+ end
+
+ local result = u*v - vup
+
+ return result:autosimplify()
+ end
+ ::skipI::
+
+ local u
+ local vp = Integer.one()
+ --looking for LATE
+ for _, exp in ipairs(expression:subexpressions()) do
+ if exp:type() == Logarithm then
+ u = exp
+ else
+ vp = vp * exp
+ end
+ end
+
+ if not u or u:freeof(symbol) then
+ goto skipL
+ else
+ vp = vp:autosimplify()
+ end
+
+ --if vp.topolynomial or (vp:type() == TrigExpression and (vp.name == "cos" or vp.name == "sin")) or (vp.operation == BinaryOperation.POW and vp.expressions[1]:freeof(symbol)) then
+ if select(2,vp:topolynomial()) then
+ integral.expression = vp
+ integral.symbol = symbol
+ integral.enhancedsubstitution = es
+ local v = IntegralExpression.integrate(integral)
+ if not v then
+ goto skipL
+ end
+
+ local up = DerivativeExpression(u, symbol):autosimplify()
+
+ integral.expression = v*up
+ integral.symbol = symbol
+ integral.enhancedsubstitution = es
+ local vup = IntegralExpression.integrate(integral)
+ if not vup then
+ goto skipL
+ end
+
+ local result = u*v - vup
+
+ return result:autosimplify()
+ end
+ ::skipL::
+
+ local u
+ local vp = Integer.one()
+ --looking for ATE
+ for _, exp in ipairs(expression:subexpressions()) do
+ local _, bool = exp:topolynomial()
+ if bool then
+ u = exp
+ else
+ vp = vp * exp
+ end
+ end
+
+ if not u or u:freeof(symbol) then
+ return
+ else
+ vp = vp:autosimplify()
+ end
+
+ if (vp:type() == TrigExpression and (vp.name == "cos" or vp.name == "sin")) or (vp.operation == BinaryOperation.POW and vp.expressions[1]:freeof(symbol)) then
+ local results = {}
+ while u ~= Integer.zero() do
+ integral.expression = vp
+ integral.symbol = symbol
+ integral.enhancedsubstitution = es
+ local v = IntegralExpression.integrate(integral):unlock():autosimplify()
+ if not v then
+ return
+ end
+ local up = DerivativeExpression(u, symbol):autosimplify()
+
+ if not integral.recursive then
+ return (u*v - IntegralExpression(v*up, symbol):nonrecursive():lock(Expression.NIL, true)):autosimplify()
+ end
+
+ results[#results+1] = u*v
+ u = up
+ vp = v
+ end
+
+ local result = results[#results]
+ for i=#results-1,1,-1 do
+ result = results[i] - result
+ end
+
+ return result:autosimplify()
+ end
+end
+
+--- Attempts integration using Euler's formula and kind. Alternative for integration by parts for many expressions.
+--- @param integral IntegralExpression
+--- @return Expression|nil
+function IntegralExpression.eulersformula(integral)
+ local expression = integral.expression
+ local symbol = integral.symbol
+ local es = integral.enhancedsubstitution
+
+ local new = expression:substitute({[COS(symbol)] = (E^(I*symbol) + E^(-I*symbol))/Integer(2),
+ [SIN(symbol)] = (E^(I*symbol) - E^(-I*symbol))/(Integer(2)*I)})
+
+ if new == expression then
+ return
+ end
+
+ integral.expression = new:autosimplify():expand()
+ integral.symbol = symbol
+ integral.enhancedsubstitution = es
+ local complexresult = IntegralExpression.integrate(integral)
+ if not complexresult then
+ return
+ end
+
+ -- TODO: Proper complex number conversion methods
+ local function converttorectangular(exp)
+ exp = exp:expand()
+ local results = {}
+ for index, sub in ipairs(exp:subexpressions()) do
+ results[index] = converttorectangular(sub)
+ end
+ local converted = exp:setsubexpressions(results)
+
+ if converted.operation == BinaryOperation.POW and converted.expressions[1] == E and converted.expressions[2].operation == BinaryOperation.MUL then
+ local ipart
+ local rest = Integer.one()
+ for _, factor in ipairs(converted.expressions[2]:subexpressions()) do
+ if factor == I then
+ ipart = true
+ else
+ rest = rest * factor
+ end
+ end
+ if ipart then
+ return (COS(rest) + I*SIN(rest)):autosimplify()
+ end
+ end
+
+ return converted
+ end
+
+ return converttorectangular(complexresult:autosimplify()):expand():autosimplify()
+
+end
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new integral operation with the given symbol and expression.
+--- @param expression Expression
+--- @param symbol SymbolExpression
+--- @param lower Expression
+--- @param upper Expression
+function IntegralExpression:new(expression, symbol, lower, upper)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ if not symbol or not expression then
+ error("Send wrong number of parameters: integrals must have a variable to integrate with respect to and an expression to integrate.")
+ end
+
+ if lower and not upper then
+ error("Send wrong number of parameters: definite integrals must have an upper and a lower bound.")
+ end
+
+ o.symbol = symbol
+ o.expression = Copy(expression)
+ o.upper = Copy(upper)
+ o.lower = Copy(lower)
+ o.recursive = true
+
+ o.attempts = {}
+ o.results = {}
+ o.enhancedsubstitution = IntegralExpression.ENHANCEDSUBSTITUTIONRECURSIONLIMIT
+
+ __o.__index = IntegralExpression
+ __o.__tostring = function(a)
+ if a:isdefinite() then
+ return 'int(' .. tostring(a.expression) .. ", " .. tostring(a.symbol) .. ", ".. tostring(a.lower) .. ', ' .. tostring(a.upper) .. ')'
+ end
+ return 'int(' .. tostring(a.expression) .. ", " .. tostring(a.symbol) .. ')'
+ end
+ __o.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always rungs this anyway
+ if not b:type() == IntegralExpression then
+ return false
+ end
+ return a.symbol == b.symbol and a.expression == b.expression and a.upper == b.upper and a.lower == b.lower
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- Returns true if the integral is definite, i.e., has an upper and lower bound.
+--- @return boolean
+function IntegralExpression:isdefinite()
+ return self.upper ~= nil
+end
+
+--- Sets the integral to not autosimplify other integral expressions that are produced by the integration process.
+--- THIS METHOD MUTATES THE OBJECT IT IS CALLED ON.
+function IntegralExpression:nonrecursive()
+ self.recursive = false
+ return self
+end
+
+--- @return Expression
+function IntegralExpression:autosimplify()
+ local arg = IntegralExpression(self.expression, self.symbol)
+ local integrated = IntegralExpression.integrate(arg)
+
+ -- Our expression could not be integrated.
+ if not integrated then
+ return self
+ end
+
+ if not self.recursive then
+ return integrated:autosimplify():unlock(true)
+ end
+
+ if self:isdefinite() then
+ return (integrated:substitute({[self.symbol]=self.upper}) - integrated:substitute({[self.symbol]=self.lower})):autosimplify()
+ end
+
+ return integrated:autosimplify()
+end
+
+
+--- @return table<number, Expression>
+function IntegralExpression:subexpressions()
+ if self:isdefinite() then
+ return {self.expression, self.symbol, self.lower, self.upper}
+ end
+
+ return {self.expression, self.symbol}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return IntegralExpression
+function IntegralExpression:setsubexpressions(subexpressions)
+ local out = IntegralExpression(subexpressions[1], subexpressions[2], subexpressions[3], subexpressions[4])
+
+ return out;
+end
+
+-- function IntegralExpression:freeof(symbol)
+-- if self:isdefinite() then
+-- return self.expression:freeof(symbol) and self.upper:freeof(symbol) and self.lower:freeof(symbol)
+-- end
+-- return self.expression:freeof(symbol)
+-- end
+
+-- -- Substitutes each expression for a new one.
+-- function IntegralExpression:substitute(map)
+-- for expression, replacement in pairs(map) do
+-- if self == expression then
+-- return replacement
+-- end
+-- end
+-- -- Typically, we only perform substitution on autosimplified expressions, so this won't get called. May give strange results, i.e.,
+-- -- substituting and then evaluating the integral may not return the same thing as evaluating the integral and then substituting.
+-- if self:isdefinite() then
+-- return IntegralExpression(self.symbol, self.expression:substitute(map), self.upper:substitute(map), self.lower:substitute(map))
+-- end
+-- return IntegralExpression(self.symbol, self.expression:substitute(map))
+-- end
+
+--- @param other Expression
+--- @return boolean
+function IntegralExpression:order(other)
+ if other:type() ~= IntegralExpression then
+ return false
+ end
+
+ if self.symbol ~= other.symbol then
+ return self.symbol:order(other.symbol)
+ end
+
+ return self.expression:order(other.expression)
+end
+
+--- @return string
+function IntegralExpression:tolatex()
+ if self:isdefinite() then
+ return '\\int_{' .. self.lower:tolatex() .. '}^{' .. self.upper:tolatex() .. '}{' .. self.expression:tolatex() .. '\\mathop{d' .. self.symbol:tolatex() .. '}}'
+ end
+ return '\\int{' .. self.expression:tolatex() .. '\\mathop{d' .. self.symbol:tolatex() .. '}}'
+end
+
+
+-----------------
+-- Inheritance --
+-----------------
+__IntegralExpression.__index = CompoundExpression
+__IntegralExpression.__call = IntegralExpression.new
+IntegralExpression = setmetatable(IntegralExpression, __IntegralExpression)
+
+----------------------
+-- Static constants --
+----------------------
+INT = function(symbol, expression, lower, upper)
+ return IntegralExpression(symbol, expression, lower, upper)
+end
+
+----------------------
+-- Static constants --
+----------------------
+
+-- Limit for the maximum number of full u-subs to attempts for any integral.
+-- This should be low, since integrals are highly unlikely to need more than 1 or 2 u-subs, and gives exponentially worse performance the higher the number is.
+IntegralExpression.ENHANCEDSUBSTITUTIONRECURSIONLIMIT = Integer(2) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/_init.lua b/macros/luatex/latex/luacas/tex/core/_init.lua
new file mode 100644
index 0000000000..cfd640a214
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/_init.lua
@@ -0,0 +1,16 @@
+-- Loads core files in the correct order.
+
+require("core.expression")
+require("core.atomicexpression")
+require("core.compoundexpression")
+require("core.constantexpression")
+require("core.symbolexpression")
+require("core.binaryoperation")
+require("core.functionexpression")
+
+
+require("core.binaryoperation.power")
+require("core.binaryoperation.product")
+require("core.binaryoperation.sum")
+require("core.binaryoperation.quotient")
+require("core.binaryoperation.difference") \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/atomicexpression.lua b/macros/luatex/latex/luacas/tex/core/atomicexpression.lua
new file mode 100644
index 0000000000..6cc6e4f58e
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/atomicexpression.lua
@@ -0,0 +1,70 @@
+--- @class AtomicExpression
+--- Interface for an atomic mathematical expression that has no sub-expressions.
+AtomicExpression = {}
+__AtomicExpression = {}
+
+
+----------------------
+-- Required methods --
+----------------------
+
+--- Converts an atomic expression to its equivalent compound expression, if it has one.
+--- @return Expression
+function AtomicExpression:tocompoundexpression()
+ return self
+end
+
+----------------------
+-- Instance methods --
+----------------------
+
+--- @return AtomicExpression
+function AtomicExpression:evaluate()
+ return self
+end
+
+--- @return AtomicExpression
+function AtomicExpression:autosimplify()
+ return self
+end
+
+--- @return table<number, Expression>
+function AtomicExpression:subexpressions()
+ return {}
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return AtomicExpression
+function AtomicExpression:setsubexpressions(subexpressions)
+ return self
+end
+
+--- @param map table<Expression, Expression>
+--- @return Expression
+function AtomicExpression:substitute(map)
+ for expression, replacement in pairs(map) do
+ if self == expression then
+ return replacement
+ end
+ end
+ return self
+end
+
+--- @return boolean
+function AtomicExpression:isatomic()
+ return true
+end
+
+--- @return string
+function AtomicExpression:tolatex()
+ -- Most atomic expressions should have the same __tostring as LaTeX's output
+ return tostring(self)
+end
+
+
+-----------------
+-- Inheritance --
+-----------------
+
+__AtomicExpression.__index = Expression
+AtomicExpression = setmetatable(AtomicExpression, __AtomicExpression) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/binaryoperation.lua b/macros/luatex/latex/luacas/tex/core/binaryoperation.lua
new file mode 100644
index 0000000000..708a6c1540
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/binaryoperation.lua
@@ -0,0 +1,800 @@
+--- @class BinaryOperation
+--- Represents a binary operation with two inputs and one output.
+--- Represents a generic function that takes zero or more expressions as inputs.
+--- @field name string
+--- @field operation function
+--- @field expressions table<number, Expression>
+BinaryOperation = {}
+__BinaryOperation = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new binary operation with the given operation.
+--- @param operation function
+--- @param expressions table<number, Expression>
+--- @return BinaryOperation
+function BinaryOperation:new(operation, expressions)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ if type(operation) ~= "function" then
+ error("Sent parameter of wrong type: operation must be a function")
+ end
+
+ if type(expressions) ~= "table" then
+ error("Sent parameter of wrong type: expressions must be an array")
+ end
+
+ o.name = BinaryOperation.DEFAULT_NAMES[operation]
+ o.operation = operation
+ o.expressions = Copy(expressions)
+
+ if BinaryOperation.COMMUTATIVITY[operation] then
+ function o:iscommutative()
+ return true
+ end
+ else
+ function o:iscommutative()
+ return false
+ end
+ end
+
+ if not o:iscommutative() and o.operation ~= BinaryOperation.SUB and #o.expressions ~= 2 then
+ error("Sent parameter of wrong type: noncommutative operations cannot have an arbitrary number of paramaters")
+ end
+
+ __o.__index = BinaryOperation
+ __o.__tostring = function(a)
+ local expressionnames = ''
+ for index, expression in ipairs(a.expressions) do
+ if index == 1 and not a.expressions[index + 1] then
+ expressionnames = expressionnames .. a.name .. ' '
+ end
+ if index > 1 then
+ expressionnames = expressionnames .. ' '
+ end
+ if expression:isatomic() and not (a.operation == BinaryOperation.POW and expression:type() == Rational) then
+ expressionnames = expressionnames .. tostring(expression)
+ else
+ expressionnames = expressionnames .. '(' .. tostring(expression) .. ')'
+ end
+ if a.expressions[index + 1] then
+ expressionnames = expressionnames .. ' ' .. a.name
+ end
+ end
+ return expressionnames
+ end
+ __o.__eq = function(a, b)
+ -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always runs this anyway
+ if not a.operation or not b.operation then
+ return false
+ end
+ local loc = 1
+ while a.expressions[loc] or b.expressions[loc] do
+ if not a.expressions[loc] or not b.expressions[loc] or
+ (a.expressions[loc] ~= b.expressions[loc]) then
+ return false
+ end
+ loc = loc + 1
+ end
+ return a.operation == b.operation
+ end
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- @return Expression
+function BinaryOperation:evaluate()
+ local results = {}
+ local reducible = true
+ for index, expression in ipairs(self:subexpressions()) do
+ results[index] = expression:evaluate()
+ if not results[index]:isconstant() then
+ reducible = false
+ end
+ end
+ if not reducible then
+ return BinaryOperation(self.operation, results)
+ end
+
+ if not self.expressions[1] then
+ error("Execution error: cannot perform binary operation on zero expressions")
+ end
+
+ local result = results[1]
+ for index, expression in ipairs(results) do
+ if not (index == 1) then
+ result = self.operation(result, expression)
+ end
+ end
+ return result
+end
+
+--- @return Expression
+function BinaryOperation:autosimplify()
+ local results = {}
+ for index, expression in ipairs(self:subexpressions()) do
+ results[index] = expression:autosimplify()
+ end
+ local simplified = BinaryOperation(self.operation, results)
+ if simplified.operation == BinaryOperation.POW then
+ return simplified:simplifypower()
+ end
+ if simplified.operation == BinaryOperation.MUL then
+ return simplified:simplifyproduct()
+ end
+ if simplified.operation == BinaryOperation.ADD then
+ return simplified:simplifysum()
+ end
+ if simplified.operation == BinaryOperation.DIV then
+ return simplified:simplifyquotient()
+ end
+ if simplified.operation == BinaryOperation.SUB then
+ return simplified:simplifydifference()
+ end
+ return simplified
+end
+
+--- @return table<number, Expression>
+function BinaryOperation:subexpressions()
+ return self.expressions
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return BinaryOperation
+function BinaryOperation:setsubexpressions(subexpressions)
+ return BinaryOperation(self.operation, subexpressions)
+end
+
+--- @return Expression
+function BinaryOperation:expand()
+ local results = {}
+ for index, expression in ipairs(self:subexpressions()) do
+ results[index] = expression:expand()
+ end
+ local expanded = BinaryOperation(self.operation, results)
+ if expanded.operation == BinaryOperation.MUL then
+ local allsums = BinaryOperation(BinaryOperation.ADD, {Integer.one()})
+ for _, expression in ipairs(expanded.expressions) do
+ allsums = allsums:expand2(expression)
+ end
+ return allsums:autosimplify()
+ end
+ if expanded.operation == BinaryOperation.POW and expanded.expressions[2]:type() == Integer then
+ if expanded.expressions[1]:type() ~= BinaryOperation then
+ return expanded:autosimplify()
+ end
+ local exp = BinaryOperation.MULEXP({Integer.one()})
+ local pow = expanded.expressions[2]:asnumber()
+ for _ = 1, math.abs(pow) do
+ exp = exp:expand2(expanded.expressions[1])
+ if _ > 1 then
+ exp = exp:autosimplify()
+ end
+ end
+ if pow < 0 then
+ exp = exp^Integer(-1)
+ end
+ return exp
+ end
+ if expanded.operation == BinaryOperation.POW and expanded.expressions[2].operation == BinaryOperation.ADD then
+ local exp = {}
+ for i = 1, #expanded.expressions[2].expressions do
+ exp[#exp+1] = (expanded.expressions[1]^expanded.expressions[2].expressions[i]):autosimplify()
+ end
+ return BinaryOperation.MULEXP(exp)
+ end
+ return expanded:autosimplify()
+end
+
+--- Helper for expand - multiplies two addition expressions.
+--- @return Expression
+function BinaryOperation:expand2(other)
+ local result = {}
+ for _, expression in ipairs(self:subexpressions()) do
+ if other:type() == BinaryOperation and other.operation == BinaryOperation.ADD then
+ for _, expression2 in ipairs(other.expressions) do
+ result[#result+1] = expression * expression2
+ end
+ else
+ result[#result+1] = expression * other
+ end
+ end
+ return BinaryOperation(BinaryOperation.ADD, result)
+end
+
+--- @return Expression
+function BinaryOperation:factor()
+ local results = {}
+
+ -- Recursively factors sub-expressions
+ for index, expression in ipairs(self:subexpressions()) do
+ results[index] = expression:factor()
+ end
+
+ -- Attempts to factor expressions as monovariate polynomials
+ local factoredsubs = BinaryOperation(self.operation, results)
+ local subs = factoredsubs:getsubexpressionsrec()
+ for index, sub in ipairs(subs) do
+ local substituted = factoredsubs:substitute({[sub]=SymbolExpression("_")}):autosimplify()
+ local polynomial, result = substituted:topolynomial()
+ if result then
+ local factored = polynomial:factor():autosimplify()
+ if factored ~= substituted then
+ return factored:substitute({[SymbolExpression("_")]=sub})
+ end
+ end
+ end
+
+ -- Pulls common sub-expressions out of sum expressions
+ if self.operation == BinaryOperation.ADD then
+ local gcf
+ for _, expression in ipairs(factoredsubs:subexpressions()) do
+ if expression.operation ~= BinaryOperation.MUL then
+ expression = BinaryOperation.MULEXP({expression})
+ end
+ if not gcf then
+ gcf = expression
+ else
+ local newgcf = Integer.one()
+ for _, gcfterm in ipairs(gcf:subexpressions()) do
+ local gcfpower = Integer.one()
+ if gcfterm:type() == BinaryOperation and gcfterm.operation == BinaryOperation.POW and gcfterm.expressions[2]:type() == Integer then
+ gcfpower = gcfterm.expressions[2]
+ gcfterm = gcfterm.expressions[1]
+ end
+ for _, term in ipairs(expression:subexpressions()) do
+ local power = Integer.one()
+ if term:type() == BinaryOperation and term.operation == BinaryOperation.POW and term.expressions[2]:type() == Integer then
+ power = term.expressions[2]
+ term = term.expressions[1]
+ end
+ if term == gcfterm then
+ newgcf = newgcf * term^Integer.min(power, gcfpower)
+ end
+ end
+ end
+ gcf = newgcf
+ end
+ end
+ if gcf:type() ~= Integer then
+ local out = Integer.zero()
+ for _, expression in ipairs(factoredsubs:subexpressions()) do
+ out = out + expression/gcf
+ end
+ out = gcf*(out:autosimplify():factor())
+ return out:autosimplify()
+ end
+ end
+
+ return factoredsubs
+end
+
+--- @return Expression
+function BinaryOperation:combine()
+ local den, num, aux, mul, input = {}, {}, {}, {}, self:autosimplify():expand()
+ if input.operation ~= BinaryOperation.ADD then
+ return input
+ end
+ for _, expr in ipairs(input.expressions) do
+ local numpart, denpart = Integer.one(), Integer.one()
+ if expr.operation == BinaryOperation.POW and expr.expressions[2]:type() == Integer and expr.expressions[2] < Integer.zero() then
+ denpart = denpart*expr.expressions[1] ^ expr.expressions[2]:neg()
+ for index,term in ipairs(den) do
+ if expr.expressions[1] == den[index] then
+ if expr.expressions[2]:neg() > mul[index] then
+ mul[index] = expr.expressions[2]:neg()
+ goto continue
+ else
+ goto continue
+ end
+ end
+ end
+ table.insert(den,expr.expressions[1])
+ table.insert(mul,expr.expressions[2]:neg())
+ ::continue::
+ end
+ if expr.operation == BinaryOperation.MUL then
+ for _,subexpr in ipairs(expr.expressions) do
+ if subexpr.operation == BinaryOperation.POW and subexpr.expressions[2]:type() == Integer and subexpr.expressions[2] < Integer.zero() then
+ denpart = denpart*subexpr.expressions[1] ^ subexpr.expressions[2]:neg()
+ for index,term in ipairs(den) do
+ if subexpr.expressions[1] == den[index] then
+ if subexpr.expressions[2]:neg() > mul[index] then
+ mul[index] = subexpr.expressions[2]:neg()
+ goto continue
+ else
+ goto continue
+ end
+ end
+ end
+ table.insert(den,subexpr.expressions[1])
+ table.insert(mul,subexpr.expressions[2]:neg())
+ ::continue::
+ else
+ numpart = numpart*subexpr
+ end
+ end
+ end
+ if expr.operation ~= BinaryOperation.POW and expr.operation ~= BinaryOperation.MUL then
+ numpart = expr
+ end
+ table.insert(num,numpart)
+ table.insert(aux,denpart)
+ end
+ local denominator = Integer.one()
+ local numerator = Integer.zero()
+ for index,expr in ipairs(den) do
+ denominator = denominator*den[index] ^ mul[index]
+ end
+ denominator = denominator:autosimplify()
+ for index,expr in ipairs(num) do
+ local uncommon = denominator/aux[index]
+ uncommon = uncommon:factor():simplify()
+ numerator = numerator + expr*uncommon
+ end
+ numerator = numerator:simplify():factor()
+ if denominator == Integer.one() then
+ return numerator
+ else
+ return numerator/denominator
+ end
+end
+
+--- @param collect Expression
+--- @return Expression
+function BinaryOperation:collect(collect)
+ -- Constant expressions cannot be collected
+ if collect:isconstant() then
+ return self
+ end
+
+ -- Recusively collect subexpressions
+ local results = {}
+ for index, expression in ipairs(self:subexpressions()) do
+ results[index] = expression:collect(collect)
+ end
+ local collected = BinaryOperation(self.operation, results)
+
+ if not (collected.operation == BinaryOperation.ADD) then
+ return collected:autosimplify()
+ end
+
+ local coefficients = {}
+
+ -- TODO: Add an expression map class
+ setmetatable(coefficients, {__index =
+ function(table, key)
+ local out = rawget(table, tostring(key))
+ return out or Integer.zero()
+ end,
+ __newindex =
+ function (table, key, value)
+ rawset(table, tostring(key), value)
+ end
+ })
+
+ -- Finds all instances of a constant power of the expression to be collected, and maps each power to all terms it is multiplied by
+ for _, expression in ipairs(collected:subexpressions()) do
+ if expression == collect then
+ coefficients[Integer.one()] = coefficients[Integer.one()] + Integer.one()
+ elseif expression.operation == BinaryOperation.POW and expression:subexpressions()[1] == collect and expression:subexpressions()[2]:isconstant() then
+ coefficients[expression:subexpressions()[2]] = coefficients[expression:subexpressions()[2]] + Integer.one()
+ elseif collect:type() == BinaryOperation and collect.operation == BinaryOperation.POW and
+ expression.operation == BinaryOperation.POW and expression:subexpressions()[1] == collect:subexpressions()[1] then
+ -- Handle the fact that autosimplify turns (a^x^n -> a^(xn)), this is needed if the term to collect is itself an exponential
+ local power = (expression:subexpressions()[2] / collect:subexpressions()[2]):autosimplify()
+ if power:isconstant() then
+ coefficients[power] = coefficients[power] + Integer.one()
+ else
+ coefficients[Integer.zero()] = coefficients[Integer.zero()] + expression
+ end
+ elseif expression.operation == BinaryOperation.MUL then
+ local varpart
+ local coeffpart = Integer.one()
+ for _, term in ipairs(expression:subexpressions()) do
+ if term == collect then
+ varpart = Integer.one()
+ elseif (term.operation == BinaryOperation.POW and term:subexpressions()[1] == collect and term:subexpressions()[2]:isconstant()) then
+ varpart = term:subexpressions()[2]
+ elseif collect:type() == BinaryOperation and collect.operation == BinaryOperation.POW and
+ term.operation == BinaryOperation.POW and term:subexpressions()[1] == collect:subexpressions()[1] then
+ local power = (term:subexpressions()[2] / collect:subexpressions()[2]):autosimplify()
+ if power:isconstant() then
+ varpart = power
+ end
+ else
+ coeffpart = coeffpart * term
+ end
+ end
+ if varpart then
+ coefficients[varpart] = coefficients[varpart] + coeffpart
+ else
+ coefficients[Integer.zero()] = coefficients[Integer.zero()] + expression
+ end
+ else
+ coefficients[Integer.zero()] = coefficients[Integer.zero()] + expression
+ end
+
+
+ end
+
+ local out = Integer.zero()
+ for index, value in pairs(coefficients) do
+ out = out + collect ^ Rational.fromstring(index) * value
+ end
+
+ return out:autosimplify()
+end
+
+--- @param other Expression
+--- @return boolean
+function BinaryOperation:order(other)
+ if other:isconstant() then
+ return false
+ end
+
+ if other:isatomic() then
+ if self.operation == BinaryOperation.POW then
+ return self:order(BinaryOperation(BinaryOperation.POW, {other, Integer.one()}))
+ end
+
+ if self.operation == BinaryOperation.MUL then
+ return self:order(BinaryOperation(BinaryOperation.MUL, {other}))
+ end
+
+ if self.operation == BinaryOperation.ADD then
+ return self:order(BinaryOperation(BinaryOperation.ADD, {other}))
+ end
+ end
+
+ if self.operation == BinaryOperation.POW and other.operation == BinaryOperation.POW then
+ if self.expressions[1] ~= other.expressions[1] then
+ return self.expressions[1]:order(other.expressions[1])
+ end
+ return self.expressions[2]:order(other.expressions[2])
+ end
+
+ if (self.operation == BinaryOperation.MUL and other.operation == BinaryOperation.MUL) or
+ (self.operation == BinaryOperation.ADD and other.operation == BinaryOperation.ADD) then
+ local k = 0
+ while #self.expressions - k > 0 and #other.expressions - k > 0 do
+ if self.expressions[#self.expressions - k] ~= other.expressions[#other.expressions - k] then
+ return self.expressions[#self.expressions - k]:order(other.expressions[#other.expressions - k])
+ end
+ k = k + 1
+ end
+ return #self.expressions < #other.expressions
+ end
+
+ if (self.operation == BinaryOperation.MUL) and (other.operation == BinaryOperation.POW or other.operation == BinaryOperation.ADD) then
+ return self:order(BinaryOperation(BinaryOperation.MUL, {other}))
+ end
+
+ if (self.operation == BinaryOperation.POW) and (other.operation == BinaryOperation.MUL) then
+ return BinaryOperation(BinaryOperation.MUL, {self}):order(other)
+ end
+
+ if (self.operation == BinaryOperation.POW) and (other.operation == BinaryOperation.ADD) then
+ return self:order(BinaryOperation(BinaryOperation.POW, {other, Integer.one()}))
+ end
+
+ if (self.operation == BinaryOperation.ADD) and (other.operation == BinaryOperation.MUL) then
+ return BinaryOperation(BinaryOperation.MUL, {self}):order(other)
+ end
+
+ if (self.operation == BinaryOperation.ADD) and (other.operation == BinaryOperation.POW) then
+ return BinaryOperation(BinaryOperation.POW, {self, Integer.one()}):order(other)
+ end
+
+ if other:type() == FunctionExpression or other:type() == TrigExpression or other:type() == Logarithm then
+ if self.operation == BinaryOperation.ADD or self.operation == BinaryOperation.MUL then
+ return self:order(BinaryOperation(self.operation, {other}))
+ end
+
+ if self.operation == BinaryOperation.POW then
+ return self:order(other^Integer.one())
+ end
+ end
+
+ return true
+end
+
+--- Returns whether the binary operation is commutative.
+--- @return boolean
+function BinaryOperation:iscommutative()
+ error("Called unimplemented method: iscommutative()")
+end
+
+--- @return PolynomialRing, boolean
+function BinaryOperation:topolynomial()
+ local addexp = self
+ if not self.operation or self.operation ~= BinaryOperation.ADD then
+ addexp = BinaryOperation(BinaryOperation.ADD, {self})
+ end
+
+ local poly = {}
+ local degree = 0
+ local symbol
+ for _, expression in ipairs(addexp.expressions) do
+ local coefficient
+ local sym
+ local power
+ -- Expressions of the form c
+ if expression:isconstant() then
+ coefficient = expression
+ power = 0
+ -- Expressions of the form x
+ elseif expression:type() == SymbolExpression then
+ coefficient = Integer.one()
+ sym = expression.symbol
+ power = 1
+ -- Expressions of the form c*x
+ elseif expression.operation and expression.operation == BinaryOperation.MUL and #expression.expressions == 2
+ and expression.expressions[1]:isconstant() and expression.expressions[2]:type() == SymbolExpression then
+
+ coefficient = expression.expressions[1]
+ sym = expression.expressions[2].symbol
+ power = 1
+ -- Expressions of the form c*x^n (totally not confusing)
+ elseif expression.operation and expression.operation == BinaryOperation.MUL and #expression.expressions == 2
+ and expression.expressions[1]:isconstant() and expression.expressions[2].operation and
+ expression.expressions[2].operation == BinaryOperation.POW and #expression.expressions[2].expressions == 2
+ and expression.expressions[2].expressions[1]:type() == SymbolExpression and expression.expressions[2].expressions[2].getring
+ and expression.expressions[2].expressions[2]:getring() == Integer.getring() and expression.expressions[2].expressions[2] > Integer.zero() then
+
+ coefficient = expression.expressions[1]
+ sym = expression.expressions[2].expressions[1].symbol
+ power = expression.expressions[2].expressions[2]:asnumber()
+ -- Expressions of the form x^n
+ elseif expression.operation and expression.operation == BinaryOperation.POW and #expression.expressions == 2
+ and expression.expressions[1]:type() == SymbolExpression and expression.expressions[2].getring
+ and expression.expressions[2]:getring() == Integer.getring() and expression.expressions[2] > Integer.zero() then
+
+ coefficient = Integer.one()
+ sym = expression.expressions[1].symbol
+ power = expression.expressions[2]:asnumber()
+ else
+ return self, false
+ end
+
+ if symbol and sym and symbol ~= sym then
+ return self, false
+ end
+ if not symbol then
+ symbol = sym
+ end
+ poly[power + 1] = coefficient
+ if power > degree then
+ degree = power
+ end
+ end
+
+ for i = 1,degree+1 do
+ poly[i] = poly[i] or Integer.zero()
+ end
+
+ return PolynomialRing(poly, symbol), true
+end
+
+function BinaryOperation:tolatex()
+ if self.operation == BinaryOperation.POW then
+ if self.expressions[2]:type() == Integer and self.expressions[2] < Integer.zero() then
+ local base = self.expressions[1]
+ local exponent = self.expressions[2]
+ if exponent == Integer(-1) then
+ return "\\frac{1}{" .. base:tolatex() .. "}"
+ else
+ if base:isatomic() then
+ return "\\frac{1}{" .. base:tolatex() .. "^{" .. exponent:neg():tolatex() .. "}}"
+ else
+ return "\\frac{1}{\\left(" .. base:tolatex() .. "\\right)^{" .. exponent:neg():tolatex() .. "}}"
+ end
+ end
+ end
+ if self.expressions[1]:isatomic() then
+ if self.expressions[2]:isconstant() and self.expressions[2]:getring() == Rational:getring() and self.expressions[2].numerator == Integer.one() then
+ if self.expressions[2].denominator == Integer(2) then
+ return "\\sqrt{" .. self.expressions[1]:tolatex() .. '}'
+ end
+ return "\\sqrt[" .. self.expressions[2].denominator:tolatex() .. ']{' .. self.expressions[1]:tolatex() .. '}'
+ end
+ return self.expressions[1]:tolatex() .. '^{' .. self.expressions[2]:tolatex() .. '}'
+ else
+ if self.expressions[2]:isconstant() and self.expressions[2]:getring() == Rational:getring() and self.expressions[2].numerator == Integer.one() then
+ if self.expressions[2].denominator == Integer(2) then
+ return "\\sqrt{" .. self.expressions[1]:tolatex() .. '}'
+ end
+ return "\\sqrt[" .. self.expressions[2].denominator:tolatex() .. ']{' .. self.expressions[1]:tolatex() .. '}'
+ end
+ return "\\left(" .. self.expressions[1]:tolatex() .. "\\right)" .. '^{' .. self.expressions[2]:tolatex() .. '}'
+ end
+ end
+ if self.operation == BinaryOperation.MUL then
+ local sign = ''
+ local out = ''
+ local denom = ''
+ if self:autosimplify():isconstant() then
+ for index, expression in ipairs(self.expressions) do
+ if index == 1 then
+ out = out .. expression:tolatex()
+ else
+ out = out .. "\\cdot " .. expression:tolatex()
+ end
+ end
+ return out
+ end
+ if #self.expressions == 2 and self.expressions[2]:type() == BinaryOperation and self.expressions[2].operation == BinaryOperation.POW and self.expressions[2].expressions[2] == -Integer.one() then
+ out = '\\frac{' .. self.expressions[1]:tolatex() .. '}{' .. self.expressions[2].expressions[1]:tolatex() .. '}'
+ return out
+ end
+ for _, expression in ipairs(self.expressions) do
+ if expression:type() == BinaryOperation then
+ if expression.operation == BinaryOperation.POW and expression.expressions[2]:isconstant() and expression.expressions[2] < Integer.zero() then
+ local reversed = (Integer.one() / expression):autosimplify()
+ if reversed.operation == BinaryOperation.ADD or expression.operation == BinaryOperation.SUB then
+ denom = denom .. '\\left('.. reversed:tolatex() .. '\\right)'
+ else
+ denom = denom .. reversed:tolatex()
+ end
+ elseif expression.operation == BinaryOperation.ADD or expression.operation == BinaryOperation.SUB then
+ out = out .. '\\left(' .. expression:tolatex() .. '\\right)'
+ else
+ out = out .. expression:tolatex()
+ end
+ else
+ if expression == Integer(-1) then
+ out = out .. '-'
+ elseif expression:type() == Rational and expression.numerator == Integer.one() then
+ denom = denom .. expression.denominator:tolatex()
+ elseif expression:type() == Rational and expression.numerator == Integer(-1) then
+ out = out .. '-'
+ denom = denom .. expression.denominator:tolatex()
+ elseif expression:type() == Rational then
+ out = out .. expression.numerator:tolatex()
+ denom = denom .. expression.denominator:tolatex()
+ else
+ out = out .. expression:tolatex()
+ end
+ end
+ end
+ if string.sub(out,1,1) == '-' then
+ sign = '-'
+ out = string.sub(out,2,-1)
+ end
+ if denom ~= '' and out == '' then
+ return sign .. '\\frac{' .. '1' .. '}{' .. denom .. '}'
+ end
+ if denom ~= '' then
+ return sign .. '\\frac{' .. out .. '}{' .. denom .. '}'
+ end
+ return sign..out
+ end
+ if self.operation == BinaryOperation.ADD then
+ local out = ''
+ for index, expression in ipairs(self.expressions) do
+ out = out .. expression:tolatex()
+ if self.expressions[index + 1] and string.sub(self.expressions[index + 1]:tolatex(), 1, 1) ~= "-" then
+ out = out .. '+'
+ end
+ end
+ return out
+ end
+ if self.operation == BinaryOperation.DIV then
+ return '\\frac{' .. self.expressions[1]:tolatex() .. '}{' .. self.expressions[2]:tolatex() .. '}'
+ end
+ if self.operation == BinaryOperation.SUB then
+ local out = ''
+ if not self.expressions[2] then
+ if not self.expressions[1]:isatomic() then
+ out = '-\\left(' .. self.expressions[1]:tolatex() .. '\\right)'
+ else
+ out = '-' .. self.expressions[1]:tolatex()
+ end
+ else
+ for index, expression in ipairs(self.expressions) do
+ if expression.operation and (expression.operation == BinaryOperation.ADD or expression.operation == BinaryOperation.SUB) and index >1 then
+ out = out .. "\\left(" .. expression:tolatex() .. "\\right)"
+ else
+ out = out .. expression:tolatex()
+ end
+ if self.expressions[index + 1] then
+ out = out .. '-'
+ end
+ end
+ end
+ return out
+ end
+ return self
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__BinaryOperation.__index = CompoundExpression
+__BinaryOperation.__call = BinaryOperation.new
+BinaryOperation = setmetatable(BinaryOperation, __BinaryOperation)
+
+----------------------
+-- Static constants --
+----------------------
+
+BinaryOperation.ADD = function(a, b)
+ return a + b
+end
+
+BinaryOperation.SUB = function(a, b)
+ return a - b
+end
+
+BinaryOperation.MUL = function(a, b)
+ return a * b
+end
+
+BinaryOperation.DIV = function(a, b)
+ return a / b
+end
+
+BinaryOperation.IDIV = function(a, b)
+ return a // b
+end
+
+BinaryOperation.MOD = function(a, b)
+ return a % b
+end
+
+BinaryOperation.POW = function(a, b)
+ return a ^ b
+end
+
+BinaryOperation.DEFAULT_NAMES = {
+ [BinaryOperation.ADD] = "+",
+ [BinaryOperation.SUB] = "-",
+ [BinaryOperation.MUL] = "*",
+ [BinaryOperation.DIV] = "/",
+ [BinaryOperation.IDIV] = "//",
+ [BinaryOperation.MOD] = "%",
+ [BinaryOperation.POW] = "^"
+}
+
+BinaryOperation.COMMUTATIVITY = {
+ [BinaryOperation.ADD] = true,
+ [BinaryOperation.SUB] = false,
+ [BinaryOperation.MUL] = true,
+ [BinaryOperation.DIV] = false,
+ [BinaryOperation.IDIV] = false,
+ [BinaryOperation.MOD] = false,
+ [BinaryOperation.POW] = false
+}
+
+BinaryOperation.ADDEXP = function(expressions, name)
+ return BinaryOperation(BinaryOperation.ADD, expressions, name)
+end
+
+BinaryOperation.SUBEXP = function(expressions, name)
+ return BinaryOperation(BinaryOperation.SUB, expressions, name)
+end
+
+BinaryOperation.MULEXP = function(expressions, name)
+ return BinaryOperation(BinaryOperation.MUL, expressions, name)
+end
+
+BinaryOperation.DIVEXP = function(expressions, name)
+ return BinaryOperation(BinaryOperation.DIV, expressions, name)
+end
+
+BinaryOperation.IDIVEXP = function(expressions, name)
+ return BinaryOperation(BinaryOperation.IDIV, expressions, name)
+end
+
+BinaryOperation.MODEXP = function(expressions, name)
+ return BinaryOperation(BinaryOperation.MOD, expressions, name)
+end
+
+BinaryOperation.POWEXP = function(expressions, name)
+ return BinaryOperation(BinaryOperation.POW, expressions, name)
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/binaryoperation/difference.lua b/macros/luatex/latex/luacas/tex/core/binaryoperation/difference.lua
new file mode 100644
index 0000000000..2cc56b6600
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/binaryoperation/difference.lua
@@ -0,0 +1,14 @@
+-- Seperates the various binary operations into their own files for readability
+
+--- Automatic simplification of difference expressions.
+--- @return BinaryOperation
+function BinaryOperation:simplifydifference()
+ local term1 = self.expressions[1]
+ local term2 = self.expressions[2]
+
+ if not term2 then
+ return BinaryOperation(BinaryOperation.MUL, {Integer(-1), term1}):autosimplify()
+ end
+
+ return BinaryOperation(BinaryOperation.ADD, {term1, BinaryOperation(BinaryOperation.MUL, {Integer(-1), term2}):autosimplify()}):autosimplify()
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/binaryoperation/power.lua b/macros/luatex/latex/luacas/tex/core/binaryoperation/power.lua
new file mode 100644
index 0000000000..35df72c440
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/binaryoperation/power.lua
@@ -0,0 +1,169 @@
+-- Seperates the various binary operations into their own files for readability
+
+--- Automatic simplification of power expressions.
+--- @return BinaryOperation
+function BinaryOperation:simplifypower()
+ local base = self.expressions[1]
+ local exponent = self.expressions[2]
+
+ if base:isconstant() and exponent:isconstant() and exponent:getring() ~= Rational:getring() then
+ return self:evaluate()
+ end
+
+ -- Simplifies i^x for x integer.
+ if base == I and exponent:isconstant() and exponent:getring() == Integer:getring() then
+ if exponent % Integer(4) == Integer(0) then
+ return Integer(1)
+ end
+ if exponent % Integer(4) == Integer(1) then
+ return I
+ end
+ if exponent % Integer(4) == Integer(2) then
+ return Integer(-1)
+ end
+ if exponent % Integer(4) == Integer(3) then
+ return -I
+ end
+ end
+
+ -- Simplifies complex numbers raised to negative integer powers
+ if not base:isrealconstant() and base:iscomplexconstant() and exponent:isconstant() and exponent:getring() == Integer:getring() and exponent < Integer.zero() then
+ local a
+ local b
+ if base.operation == BinaryOperation.MUL then
+ a = Integer.zero()
+ b = base.expressions[1]
+ elseif base.operation == BinaryOperation.ADD and base.expressions[2] == I then
+ a = base.expressions[1]
+ b = Integer.one()
+ else
+ a = base.expressions[1]
+ b = base.expressions[2].expressions[1]
+ end
+ return (((a-b*I)/(a^Integer(2)+b^Integer(2)))^(-exponent)):expand():autosimplify()
+ end
+
+ -- Uses the property that 0^x = 0 if x does not equal 0
+ if base:isconstant() and base == base:zero() then
+ return Integer.zero()
+ end
+
+ -- Uses the property that 1^x = 1
+ if base:isconstant() and base == base:one() then
+ return base:one()
+ end
+
+ -- Uses the property that x^0 = 1
+ if exponent:isconstant() and exponent == exponent:zero() then
+ return exponent:one()
+ end
+
+ -- Uses the property that x^1 = x
+ if exponent:isconstant() and exponent == exponent:one() then
+ return base
+ end
+
+ -- Uses the property that b ^ (log(b, x)) == x
+ if exponent:type() == Logarithm and exponent.base == base then
+ return exponent.expression
+ end
+
+ -- Uses the property that b ^ (a * log(b, x)) == x ^ a
+ if exponent.operation == BinaryOperation.MUL then
+ local x
+ local rest = Integer.one()
+ for _, expression in ipairs(exponent.expressions) do
+ if expression:type() == Logarithm and expression.base == base and not log then
+ x = expression.expression
+ else
+ rest = rest * expression
+ end
+ end
+ if x then
+ return (x ^ rest):autosimplify()
+ end
+ end
+
+ -- Uses the property that (x^a)^b = x^(a*b)
+ if not base:isatomic() and base.operation == BinaryOperation.POW and exponent:isconstant() then
+ base, exponent = base.expressions[1], BinaryOperation(BinaryOperation.MUL, {exponent, base.expressions[2]}):autosimplify()
+ return BinaryOperation(BinaryOperation.POW, {base, exponent}):autosimplify()
+ end
+
+ -- Uses the property that (x_1*x_2*...*x_n)^a = x_1^a*x_2^a*..x_n^a if a is an integer
+ if base.operation == BinaryOperation.MUL and exponent:type() == Integer then
+ local results = {}
+ for index, expression in ipairs(base.expressions) do
+ results[index] = BinaryOperation(BinaryOperation.POW, {expression, exponent}):autosimplify()
+ end
+ return BinaryOperation(BinaryOperation.MUL, results):autosimplify()
+ end
+
+ -- Uses the property that sqrt(x,r)^d == sqrt(x,r/d)
+ if base:type() == SqrtExpression and exponent:type() == Integer and exponent > Integer.zero() then
+ local root = base.root
+ local expr = base.expression
+ local comm = Integer.gcd(root,exponent)
+ root = root / comm
+ local expo = exponent / comm
+ expr = expr ^ expo
+ return SqrtExpression(expr,root):autosimplify()
+ end
+
+ -- Rationalizing SqrtExpressions
+ if base:type() == SqrtExpression and exponent:type() == Integer and base.expression:type() == Integer and exponent < Integer.zero() then
+ local root = base.root
+ local expr = base.expression
+ local result = (SqrtExpression(expr ^ (root - Integer.one()),root) / expr) ^ exponent:neg()
+ return result:autosimplify()
+ end
+
+ if base:isconstant() and exponent:isconstant() and exponent:getring() == Rational.getring() then
+ return self --:simplifyrationalpower()
+ end
+
+ -- Our expression cannot be simplified
+ return self
+end
+
+-- Automatic simplification of rational power expressions
+function BinaryOperation:simplifyrationalpower()
+ local base = self.expressions[1]
+ local exponent = self.expressions[2]
+
+ if base:getring() == Rational.getring() then
+ return (BinaryOperation(BinaryOperation.POW, {base.numerator, exponent}):simplifyrationalpower()) /
+ (BinaryOperation(BinaryOperation.POW, {base.denominator, exponent}):simplifyrationalpower())
+ end
+
+ if base == Integer(-1) then
+ if exponent == Integer(1) / Integer(2) then
+ return I
+ end
+
+ return self
+ end
+
+ local primes = base:primefactorization()
+
+ if primes.expressions[1] and not primes.expressions[2] then
+ local primeexponent = primes.expressions[1].expressions[2]
+ local primebase = primes.expressions[1].expressions[1]
+ local newexponent = primeexponent * exponent
+ local integerpart
+ if newexponent.getring() == Rational.getring() then
+ integerpart = newexponent.numerator // newexponent.denominator
+ else
+ integerpart = newexponent
+ end
+
+ if integerpart == Integer.zero() then
+ return BinaryOperation(BinaryOperation.POW, {primebase, newexponent})
+ end
+ return BinaryOperation(BinaryOperation.MUL,
+ {BinaryOperation(BinaryOperation.POW, {primebase, integerpart}),
+ BinaryOperation(BinaryOperation.POW, {primebase, newexponent - integerpart})}):autosimplify()
+ end
+
+ return BinaryOperation(BinaryOperation.POW, {primes:autosimplify(), exponent})
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/binaryoperation/product.lua b/macros/luatex/latex/luacas/tex/core/binaryoperation/product.lua
new file mode 100644
index 0000000000..b104cb5e8a
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/binaryoperation/product.lua
@@ -0,0 +1,231 @@
+-- Seperates the various binary operations into their own files for readability
+
+--- Automatic simplification of multiplication expressions.
+--- @return BinaryOperation
+function BinaryOperation:simplifyproduct()
+ if not self.expressions[1] then
+ error("Execution error: attempted to simplify empty product")
+ end
+
+ if not self.expressions[2] then
+ return self.expressions[1]
+ end
+
+ -- Uses the property that x*0=0
+ for _, expression in ipairs(self.expressions) do
+ if expression:isconstant() and expression == expression:zero() then
+ return expression:zero()
+ end
+ end
+
+ local result = self:simplifyproductrec()
+
+ -- We don't really know what ring we are working in here, so just assume the integer ring
+ if not result.expressions[1] then
+ return Integer.one()
+ end
+
+ if not result.expressions[2] then
+ return result.expressions[1]
+ end
+
+ return result
+end
+
+function BinaryOperation:simplifyproductrec()
+ local term1 = self.expressions[1]
+ local term2 = self.expressions[2]
+
+ if not self.expressions[3] then
+ if (term1:isconstant() or not (term1.operation == BinaryOperation.MUL)) and
+ (term2:isconstant() or not (term2.operation == BinaryOperation.MUL)) then
+
+ if term1:isconstant() and term2:isconstant() then
+ local result = self:evaluate()
+ if result == result:one() then
+ return BinaryOperation(BinaryOperation.MUL, {})
+ end
+ return BinaryOperation(BinaryOperation.MUL, {result})
+ end
+
+ -- Uses the property that x*1 = x
+ if term1:isconstant() and term1 == term1:one() then
+ return BinaryOperation(BinaryOperation.MUL, {term2})
+ end
+
+ if term2:isconstant() and term2 == term2:one() then
+ return BinaryOperation(BinaryOperation.MUL, {term1})
+ end
+
+ -- Distributes constants if the other term is a sum expression.
+ if term1:isconstant() and term2.operation == BinaryOperation.ADD then
+ local distributed = BinaryOperation(BinaryOperation.ADD, {})
+ for i, exp in ipairs(term2:subexpressions()) do
+ distributed.expressions[i] = term1 * exp
+ end
+ return BinaryOperation(BinaryOperation.MUL, {distributed:autosimplify()})
+ end
+
+ if term2:isconstant() and term1.operation == BinaryOperation.ADD then
+ local distributed = BinaryOperation(BinaryOperation.ADD, {})
+ for i, exp in ipairs(term1:subexpressions()) do
+ distributed.expressions[i] = term2 * exp
+ end
+ return BinaryOperation(BinaryOperation.MUL, {distributed:autosimplify()})
+ end
+
+ -- Uses the property that sqrt(a,r)*sqrt(b,r) = sqrt(a*b,r) if a,r are positive integers
+ if term1:type() == SqrtExpression and term2:type() == SqrtExpression and term1.expression:isconstant() and term2.expression:isconstant() and term1.root:type() == Integer then
+ if term1.root == term2.root and term1.expression > Integer.zero() then
+ local expression = term1.expression*term2.expression
+ local result = SqrtExpression(expression,term1.root):autosimplify()
+ if result == Integer.one() then
+ return BinaryOperation(BinaryOperation.MUL,{})
+ else
+ return BinaryOperation(BinaryOperation.MUL,{result})
+ end
+ end
+ end
+
+ --if term1.operation == BinaryOperation.POW and term2.operation == BinaryOperation.POW and term1.expressions[1]:type() == SqrtExpression and term2.expressions[1]:type() == SqrtExpression and term1.expressions[2]:type() == Integer and term2.expressions[2]:type() == Integer and term1.expressions[2] < Integer.zero() and term2.expressions[2] < Integer.zero() and term1.expressions[1].root == term2.expressions[1].root then
+ -- local expo1 = term1.expressions[2]:neg()
+ -- local expo2 = term2.expressions[2]:neg()
+ -- local root = term1.expressions[1].root
+ -- local expr1 = term1.expressions[1].expression
+ -- local expr2 = term2.expressions[1].expression
+ -- local result1 = BinaryOperation(BinaryOperation.POW,{SqrtExpression(expr1,root),expo1}):simplifypower()
+ -- local result2 = BinaryOperation(BinaryOperation.POW,{SqrtExpression(expr2,root),expo2}):simplifypower()
+ -- local result = BinaryOperation(BinaryOperation.MUL,{result1,result2}):autosimplify()
+ -- if result == Integer.one() then
+ -- return BinaryOperation(BinaryOperation.MUL,{})
+ -- end
+ -- if result:type() == Integer then
+ -- return BinaryOperation(BinaryOperation.MUL,{Rational(Integer.one(),result)})
+ -- end
+ -- return BinaryOperation(BinaryOperation.MUL,{BinaryOperation(BinaryOperation.POW, {result,Integer(-1)})}):autosimplify()
+ --end
+
+ -- Uses the property that x^a*x^b=x^(a+b)
+ local revertterm1 = false
+ local revertterm2 = false
+ if term1.operation ~= BinaryOperation.POW then
+ term1 = BinaryOperation(BinaryOperation.POW, {term1, Integer.one()})
+ revertterm1 = true
+ end
+ if term2.operation ~= BinaryOperation.POW then
+ term2 = BinaryOperation(BinaryOperation.POW, {term2, Integer.one()})
+ revertterm2 = true
+ end
+ if term1.expressions[1] == term2.expressions[1]
+ --and not
+ -- (term1.expressions[1]:type() == Integer and
+ -- term1.expressions[2]:type() ~= term2.--expressions[2]:type())
+ then
+ local result = BinaryOperation(BinaryOperation.POW,
+ {term1.expressions[1],
+ BinaryOperation(BinaryOperation.ADD,
+ {term1.expressions[2], term2.expressions[2]}):autosimplify()}):autosimplify()
+ if result:isconstant() and result == result:one() then
+ return BinaryOperation(BinaryOperation.MUL, {})
+ end
+ return BinaryOperation(BinaryOperation.MUL, {result})
+ end
+
+ if revertterm1 then
+ term1 = term1.expressions[1]
+ end
+ if revertterm2 then
+ term2 = term2.expressions[1]
+ end
+
+ if term2:order(term1) then
+ return BinaryOperation(BinaryOperation.MUL, {term2, term1})
+ end
+
+ return self
+ end
+
+ if term1.operation == BinaryOperation.MUL and not (term2.operation == BinaryOperation.MUL) then
+ return term1:mergeproducts(BinaryOperation(BinaryOperation.MUL, {term2}))
+ end
+
+ if not (term1.operation == BinaryOperation.MUL) and term2.operation == BinaryOperation.MUL then
+ return BinaryOperation(BinaryOperation.MUL, {term1}):mergeproducts(term2)
+ end
+
+ return term1:mergeproducts(term2)
+ end
+
+ local rest = {}
+ for index, expression in ipairs(self.expressions) do
+ if index > 1 then
+ rest[index - 1] = expression
+ end
+ end
+
+ local result = BinaryOperation(BinaryOperation.MUL, rest):simplifyproductrec()
+
+ if term1.operation ~= BinaryOperation.MUL then
+ term1 = BinaryOperation(BinaryOperation.MUL, {term1})
+ end
+ if result.operation ~= BinaryOperation.MUL then
+ result = BinaryOperation(BinaryOperation.MUL, {result})
+ end
+ return term1:mergeproducts(result)
+end
+
+-- Merges two lists of products
+function BinaryOperation:mergeproducts(other)
+ if not self.expressions[1] then
+ return other
+ end
+
+ if not other.expressions[1] then
+ return self
+ end
+
+ local first = BinaryOperation(BinaryOperation.MUL, {self.expressions[1], other.expressions[1]}):simplifyproductrec()
+
+ local selfrest = {}
+ for index, expression in ipairs(self.expressions) do
+ if index > 1 then
+ selfrest[index - 1] = expression
+ end
+ end
+
+ local otherrest = {}
+ for index, expression in ipairs(other.expressions) do
+ if index > 1 then
+ otherrest[index - 1] = expression
+ end
+ end
+
+ if first.operation ~= BinaryOperation.MUL or not first.expressions[2] then
+ local result = BinaryOperation(self.operation, selfrest):mergeproducts(BinaryOperation(other.operation, otherrest))
+ if not first.expressions[1] then
+ return result
+ end
+ table.insert(result.expressions, 1, first.expressions[1])
+
+ if result.operation == BinaryOperation.MUL and not result.expressions[3] and result.expressions[1] and result.expressions[2] then
+ if result.expressions[1]:isconstant() and result.expressions[2]:isconstant() then
+ return result:simplifyproductrec()
+ end
+ end
+ return result
+ end
+
+ local result
+ if first.expressions[1] == self.expressions[1] then
+ result = BinaryOperation(self.operation, selfrest):mergeproducts(other)
+ else
+ result = self:mergeproducts(BinaryOperation(other.operation, otherrest))
+ end
+ table.insert(result.expressions, 1, first.expressions[1])
+ if result.operation == BinaryOperation.MUL and not result.expressions[3] and result.expressions[1] and result.expressions[2] then
+ if result.expressions[1]:isconstant() and result.expressions[2]:isconstant() then
+ return result:simplifyproductrec()
+ end
+ end
+ return result
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/binaryoperation/quotient.lua b/macros/luatex/latex/luacas/tex/core/binaryoperation/quotient.lua
new file mode 100644
index 0000000000..4dac489bf5
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/binaryoperation/quotient.lua
@@ -0,0 +1,14 @@
+-- Seperates the various binary operations into their own files for readability
+
+-- Automatic simplification of quotient expressions.
+--- @return BinaryOperation
+function BinaryOperation:simplifyquotient()
+ local numerator = self.expressions[1]
+ local denominator = self.expressions[2]
+
+ if numerator:isconstant() and denominator:isconstant() then
+ return self:evaluate()
+ end
+
+ return BinaryOperation(BinaryOperation.MUL, {numerator, BinaryOperation(BinaryOperation.POW, {denominator, Integer(-1)}):autosimplify()}):autosimplify()
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/binaryoperation/sum.lua b/macros/luatex/latex/luacas/tex/core/binaryoperation/sum.lua
new file mode 100644
index 0000000000..5bb204066f
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/binaryoperation/sum.lua
@@ -0,0 +1,226 @@
+-- Seperates the various binary operations into their own files for readability
+
+-- Automatic simplification of addition expressions.
+--- @return BinaryOperation
+function BinaryOperation:simplifysum()
+ if not self.expressions[1] then
+ error("Execution error: Attempted to simplify empty sum")
+ end
+
+ if not self.expressions[2] then
+ return self.expressions[1]
+ end
+
+ local result = self:simplifysumrec()
+
+ -- We don't really know what ring we are working in here, so just assume the integer ring
+ if not result.expressions[1] then
+ return Integer.zero()
+ end
+
+ -- Simplifies single sums to their operands
+ if not result.expressions[2] then
+ return result.expressions[1]
+ end
+
+ return result
+end
+
+function BinaryOperation:simplifysumrec()
+ local term1 = self.expressions[1]
+ local term2 = self.expressions[2]
+
+ if self.expressions[1] and self.expressions[2] and not self.expressions[3] then
+ if (term1:isconstant() or not (term1.operation == BinaryOperation.ADD)) and
+ (term2:isconstant() or not (term2.operation == BinaryOperation.ADD)) then
+
+ if term1:isconstant() and term2:isconstant() then
+ return BinaryOperation(BinaryOperation.ADD, {self:evaluate()})
+ end
+
+ -- Uses the property that x + 0 = x
+ if term1:isconstant() and term1 == term1:zero() then
+ return BinaryOperation(BinaryOperation.ADD, {term2})
+ end
+
+ if term2:isconstant() and term2 == term2:zero() then
+ return BinaryOperation(BinaryOperation.ADD, {term1})
+ end
+
+ local revertterm1 = false
+ local revertterm2 = false
+ -- Uses the property that a*x+b*x= (a+b)*x
+ -- This is only done if a and b are constant, since otherwise this could be counterproductive
+ -- We SHOULD be okay to only check left distributivity, since constants always come first when ordered
+ if term1.operation == BinaryOperation.MUL and term2.operation == BinaryOperation.MUL then
+ local findex = 2
+ local sindex = 2
+ if not term1.expressions[1]:isconstant() then
+ revertterm1 = true
+ findex = 1
+ end
+ if not term2.expressions[1]:isconstant() then
+ revertterm2 = true
+ sindex = 1
+ end
+ if FancyArrayEqual(term1.expressions,term2.expressions,findex,sindex) then
+ local result
+ if not revertterm1 and not revertterm2 then
+ result = BinaryOperation(
+ BinaryOperation.ADD,
+ {term1.expressions[1],term2.expressions[1]}
+ )
+ end
+ if revertterm1 and not revertterm2 then
+ result = BinaryOperation(
+ BinaryOperation.ADD,
+ {Integer.one(),term2.expressions[1]}
+ )
+ end
+ if not revertterm1 and revertterm2 then
+ result = BinaryOperation(
+ BinaryOperation.ADD,
+ {term1.expressions[1],Integer.one()}
+ )
+ end
+ if revertterm1 and revertterm2 then
+ result = Integer(2)
+ end
+ result = result:autosimplify()
+ for i=findex,#term1.expressions do
+ result = BinaryOperation(
+ BinaryOperation.MUL,
+ {result,term1.expressions[i]}
+ )
+ end
+ result = result:autosimplify()
+ if result:isconstant() and result == result:zero() then
+ return BinaryOperation(BinaryOperation.ADD, {})
+ end
+ return BinaryOperation(BinaryOperation.ADD, {result})
+ end
+ end
+
+ if term1.operation ~= BinaryOperation.MUL or not term1.expressions[1]:isconstant() then
+ term1 = BinaryOperation(BinaryOperation.MUL,{Integer.one(), term1})
+ revertterm1 = true
+ end
+ if term2.operation ~= BinaryOperation.MUL or not term2.expressions[1]:isconstant() then
+ term2 = BinaryOperation(BinaryOperation.MUL, {Integer.one(), term2})
+ revertterm2 = true
+ end
+ if ArrayEqual(term1.expressions, term2.expressions, 2) then
+ local result = BinaryOperation(
+ BinaryOperation.ADD,
+ {term1.expressions[1],term2.expressions[1]}
+ )
+ result = result:autosimplify()
+ for i=2,#term1.expressions do
+ result = BinaryOperation(
+ BinaryOperation.MUL,
+ {result,term1.expressions[i]}
+ )
+ end
+ result = result:autosimplify()
+ --local result = BinaryOperation(BinaryOperation.MUL,
+ -- {BinaryOperation(BinaryOperation.ADD,
+ -- {term1.expressions[1],
+ -- term2.expressions[1]}):autosimplify(),
+ -- term1.expressions[2]}):autosimplify()
+ if result:isconstant() and result == result:zero() then
+ return BinaryOperation(BinaryOperation.ADD, {})
+ end
+ return BinaryOperation(BinaryOperation.ADD, {result})
+ end
+
+ if revertterm1 then
+ term1 = term1.expressions[2]
+ end
+ if revertterm2 then
+ term2 = term2.expressions[2]
+ end
+
+ if term2:order(term1) then
+ return BinaryOperation(BinaryOperation.ADD, {term2, term1})
+ end
+
+ return self
+ end
+
+ if term1.operation == BinaryOperation.ADD and not (term2.operation == BinaryOperation.ADD) then
+ return term1:mergesums(BinaryOperation(BinaryOperation.ADD, {term2}))
+ end
+
+ if not (term1.operation == BinaryOperation.ADD) and term2.operation == BinaryOperation.ADD then
+ return BinaryOperation(BinaryOperation.ADD, {term1}):mergesums(term2)
+ end
+
+ return term1:mergesums(term2)
+ end
+
+ local rest = {}
+ for index, expression in ipairs(self.expressions) do
+ if index > 1 then
+ rest[index - 1] = expression
+ end
+ end
+
+ local result = BinaryOperation(BinaryOperation.ADD, rest):simplifysumrec()
+
+ if term1.operation ~= BinaryOperation.ADD then
+ term1 = BinaryOperation(BinaryOperation.ADD, {term1})
+ end
+ if result.operation ~= BinaryOperation.ADD then
+ result = BinaryOperation(BinaryOperation.ADD, {result})
+ end
+ return term1:mergesums(result)
+end
+
+-- Merges two lists of sums
+function BinaryOperation:mergesums(other)
+ if not self.expressions[1] then
+ return other
+ end
+
+ if not other.expressions[1] then
+ return self
+ end
+
+ local first = BinaryOperation(BinaryOperation.ADD, {self.expressions[1], other.expressions[1]}):simplifysumrec()
+
+ local selfrest = {}
+ for index, expression in ipairs(self.expressions) do
+ if index > 1 then
+ selfrest[index - 1] = expression
+ end
+ end
+
+ local otherrest = {}
+ for index, expression in ipairs(other.expressions) do
+ if index > 1 then
+ otherrest[index - 1] = expression
+ end
+ end
+
+ if first.operation ~= BinaryOperation.ADD or not first.expressions[2] then
+ local result = BinaryOperation(self.operation, selfrest):mergesums(BinaryOperation(other.operation, otherrest))
+ if not first.expressions[1] then
+ return result
+ end
+ if first.expressions[1] ~= Integer.zero(0) then
+ table.insert(result.expressions, 1, first.expressions[1])
+ end
+ return result
+ end
+
+ local result
+ if first.expressions[1] == self.expressions[1] then
+ result = BinaryOperation(self.operation, selfrest):mergesums(other)
+ else
+ result = self:mergesums(BinaryOperation(other.operation, otherrest))
+ end
+
+ table.insert(result.expressions, 1, first.expressions[1])
+
+ return result
+end
diff --git a/macros/luatex/latex/luacas/tex/core/compoundexpression.lua b/macros/luatex/latex/luacas/tex/core/compoundexpression.lua
new file mode 100644
index 0000000000..792c36cfc2
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/compoundexpression.lua
@@ -0,0 +1,52 @@
+--- @class CompoundExpression
+--- Interface for an expression consisting of one or more subexpressions.
+CompoundExpression = {}
+__CompoundExpression = {}
+
+----------------------
+-- Instance methods --
+----------------------
+
+--- @param symbol SymbolExpression
+--- @return boolean
+function CompoundExpression:freeof(symbol)
+ for _, expression in ipairs(self:subexpressions()) do
+ if not expression:freeof(symbol) then
+ return false
+ end
+ end
+ return true
+ end
+
+--- @param map table<Expression, Expression>
+--- @return Expression
+function CompoundExpression:substitute(map)
+ for expression, replacement in pairs(map) do
+ if self == expression then
+ return replacement
+ end
+ end
+
+ local results = {}
+ for index, expression in ipairs(self:subexpressions()) do
+ results[index] = expression:substitute(map)
+ end
+ return self:setsubexpressions(results)
+end
+
+--- @return boolean
+function CompoundExpression:isatomic()
+ return false
+end
+
+--- @return boolean
+function CompoundExpression:isconstant()
+ return false
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__CompoundExpression.__index = Expression
+CompoundExpression = setmetatable(CompoundExpression, __CompoundExpression) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/constantexpression.lua b/macros/luatex/latex/luacas/tex/core/constantexpression.lua
new file mode 100644
index 0000000000..0f91a037a9
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/constantexpression.lua
@@ -0,0 +1,62 @@
+--- @class ConstantExpression
+--- @alias Constant ConstantExpression
+--- Interface for a mathematical expression without any symbols.
+--- ConstantExpressions are AtomicExpressions by default, but individual classes may overwrite that inheritance.
+ConstantExpression = {}
+__ConstantExpression = {}
+
+----------------------
+-- Instance methods --
+----------------------
+
+
+--- @param symbol SymbolExpression
+--- @return boolean
+function ConstantExpression:freeof(symbol)
+ return true
+end
+
+--- @return boolean
+function ConstantExpression:isconstant()
+ return true
+end
+
+--- @param other Expression
+--- @return boolean
+function ConstantExpression:order(other)
+
+ -- Constants come before non-constants.
+ if not other:isconstant() then
+ return true
+ end
+
+ if self ~= E and self ~= PI and self ~= I then
+ if other ~= E and other ~= PI and other ~= I then
+ -- If both self and other are ring elements, we use the total order on the ring to sort.
+ return self < other
+ end
+ -- Special constants come after ring elements.
+ return true
+ end
+
+ -- Special constants come after ring elements.
+ if other ~= E and other ~= PI and other ~= I then
+ return false
+ end
+
+ -- Ensures E < PI < I.
+
+ if self == E then return true end
+
+ if self == I then return false end
+
+ return other == I
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__ConstantExpression.__index = AtomicExpression
+ConstantExpression = setmetatable(ConstantExpression, __ConstantExpression)
+
diff --git a/macros/luatex/latex/luacas/tex/core/expression.lua b/macros/luatex/latex/luacas/tex/core/expression.lua
new file mode 100644
index 0000000000..8ff4659f79
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/expression.lua
@@ -0,0 +1,280 @@
+--- @class Expression
+--- Interface for an arbitrary mathematical expression.
+Expression = {}
+
+----------------------
+-- Required methods --
+----------------------
+
+--- Evaluates the current expression recursively by evaluating each sub-expression.
+--- @return Expression
+function Expression:evaluate()
+ error("Called unimplemented method : evaluate()")
+end
+
+--- Performs automatic simplification of an expression. Called on every expression before being output from the CAS.
+--- @return Expression
+function Expression:autosimplify()
+ error("Called unimplemented method : autosimplify()")
+end
+
+--- Performs more rigorous simplification of an expression. Checks different equivalent forms and determines the 'smallest' expresion.
+--- @return Expression
+function Expression:simplify()
+ local me = self:unlock():autosimplify()
+ local results = {}
+ for index, expression in ipairs(me:subexpressions()) do
+ results[index] = expression:simplify()
+ end
+ me = me:setsubexpressions(results)
+
+ local out = me
+ local minsize = self:size()
+
+ local test = me:expand()
+ if test:size() < minsize then
+ out = test
+ minsize = test:size()
+ end
+
+ test = me:factor()
+ if test:size() < minsize then
+ out = test
+ minsize = test:size()
+ end
+
+ return out
+end
+
+--- Changes the autosimplify behavior of an expression depending on its parameters.
+--- THIS METHOD MUTATES THE OBJECT IT ACTS ON.
+--- @param mode number
+--- @param permanent boolean
+--- @param recursive boolean
+--- @return Expression
+function Expression:lock(mode, permanent, recursive)
+ function self:autosimplify()
+ if not permanent then
+ self.autosimplify = nil
+ end
+
+ if mode == Expression.NIL then
+ return self
+ elseif mode == Expression.SUBS then
+ local results = {}
+ for index, expression in ipairs(self:subexpressions()) do
+ results[index] = expression:autosimplify()
+ end
+ self = self:setsubexpressions(results, true) -- TODO: Add mutate setsubexpressions
+ return self
+ end
+ end
+ if recursive then
+ for _, expression in ipairs(self:subexpressions()) do
+ expression:lock(mode, permanent, recursive)
+ end
+ end
+ return self
+end
+
+--- Frees any locks on expressions.
+--- THIS METHOD MUTATES THE OBJECT IT ACTS ON.
+--- @param recursive boolean
+--- @return Expression
+function Expression:unlock(recursive)
+ self.autosimplify = nil
+ if recursive then
+ for _, expression in ipairs(self:subexpressions()) do
+ expression:unlock(recursive)
+ end
+ end
+ return self
+end
+
+--- Returns a list of all subexpressions of an expression.
+--- @return table<number, Expression>
+function Expression:subexpressions()
+ error("Called unimplemented method : subexpressions()")
+end
+
+--- Returns the total number of atomic and compound expressions that make up an expression, or the number of nodes in the expression tree.
+--- @return Integer
+function Expression:size()
+ local out = Integer.one()
+ for _, expression in ipairs(self:subexpressions()) do
+ out = out + expression:size()
+ end
+ return out
+end
+
+--- Returns a copy of the original expression with each subexpression substituted with a new one, or a mutated version if mutate is true.
+--- @param subexpressions table<number, Expression>
+--- @param mutate boolean
+--- @return Expression
+function Expression:setsubexpressions(subexpressions, mutate)
+ error("Called unimplemented method : setsubexpressions()")
+end
+
+--- Determines whether or not an expression contains a particular symbol.
+--- @param symbol SymbolExpression
+--- @return boolean
+function Expression:freeof(symbol)
+ error("Called unimplemented method : freeof()")
+end
+
+--- Substitutes occurances of specified sub-expressions with other sub-expressions.
+--- @param map table<Expression, Expression>
+--- @return Expression
+function Expression:substitute(map)
+ error("Called unimplemented method : substitute()")
+end
+
+--- Algebraically expands an expression by turning products of sums into sums of products and expanding powers.
+--- @return Expression
+function Expression:expand()
+ return self
+end
+
+--- Attempts to factor an expression by turning sums of products into products of sums, and identical terms multiplied together into factors.
+--- @return Expression
+function Expression:factor()
+ return self
+end
+
+--- Attempts to combine an expression by collapsing sums of expressions together into a single factor, e.g. common denominator
+--- @return Expression
+function Expression:combine()
+ return self
+end
+
+--- Attempts to collect all occurances of an expression in this expression.
+--- @param collect Expression
+--- @return Expression
+function Expression:collect(collect)
+ return self
+end
+
+--- Returns all non-constant subexpressions of this expression - helper method for factor.
+--- @return table<number, Expression>
+function Expression:getsubexpressionsrec()
+ local result = {}
+
+ for _, expression in ipairs(self:subexpressions()) do
+ if not expression:isconstant() then
+ result[#result+1] = expression
+ end
+ result = JoinArrays(result, expression:getsubexpressionsrec())
+ end
+
+ return result
+end
+
+--- Determines whether an expression is atomic.
+--- Atomic expressions are not necessarily constant, since polynomial rings, for instance, are atomic parts that contain symbols.
+--- @return boolean
+function Expression:isatomic()
+ error("Called unimplemented method: isatomic()")
+end
+
+--- Determines whether an expression is a constant, i.e., an atomic expression that is not a varaible and cannot be converted into an equivalent compound expression.
+--- @return boolean
+function Expression:isconstant()
+ error("Called unimplemented method: isconstant()")
+end
+
+--- Determines whether an expression is a 'proper' real constant, i.e., is free of every varaible.
+function Expression:isrealconstant()
+ if self:isconstant() or self == PI or self == E then
+ return true
+ end
+
+ for _, expression in ipairs(self:subexpressions()) do
+ if not expression:isrealconstant() then
+ return false
+ end
+ end
+
+ return self:type() ~= SymbolExpression
+end
+
+--- Determines whether an expression is a 'proper' complex constant, i.e., is free of every varaible and is of the form a + bI for nonzero a and b.
+--- @return boolean
+function Expression:iscomplexconstant()
+ return self:isrealconstant() or (self.operation == BinaryOperation.ADD and #self.expressions == 2 and self.expressions[1]:isrealconstant()
+ and ((self.expressions[2].operation == BinaryOperation.MUL and #self.expressions[2].expressions == 2 and self.expressions[2].expressions[1]:isrealconstant() and self.expressions[2].expressions[2] == I)
+ or self.expressions[2] == I)) or (self.operation == BinaryOperation.MUL and #self.expressions == 2 and self.expressions[1]:isrealconstant() and self.expressions[2] == I)
+end
+
+--- A total order on autosimplified expressions. Returns true if self < other.
+--- @param other Expression
+--- @return boolean
+function Expression:order(other)
+ error("Called unimplemented method: order()")
+end
+
+--- Returns an autosimplified expression as a single-variable polynomial in a ring, if it can be converted. Returns itself otherwise.
+--- @return PolynomialRing, boolean
+function Expression:topolynomial()
+ return self, false
+end
+
+--- Converts this expression to LaTeX code.
+--- @return string
+function Expression:tolatex()
+ error("Called Unimplemented method: tolatex()")
+end
+
+----------------------
+-- Instance methods --
+----------------------
+
+--- Returns the type of the expression, i.e., the table used to create objects of that type.
+--- @return table
+function Expression:type()
+ return getmetatable(self).__index
+end
+
+--------------------------
+-- Instance metamethods --
+--------------------------
+
+__ExpressionOperations = {}
+
+__ExpressionOperations.__unm = function(a)
+ return BinaryOperation.SUBEXP({a})
+end
+
+__ExpressionOperations.__add = function(a, b)
+ return BinaryOperation.ADDEXP({a, b})
+end
+
+__ExpressionOperations.__sub = function(a, b)
+ return BinaryOperation.SUBEXP({a, b})
+end
+
+__ExpressionOperations.__mul = function(a, b)
+ return BinaryOperation.MULEXP({a, b})
+end
+
+__ExpressionOperations.__div = function(a, b)
+ return BinaryOperation.DIVEXP({a, b})
+end
+
+__ExpressionOperations.__pow = function(a, b)
+ return BinaryOperation.POWEXP({a, b})
+end
+
+-- For iterating over the subexpressions of a easily.
+__ExpressionOperations.__call = function(a, ...)
+ if a:type() == SymbolExpression then
+ return FunctionExpression(a, table.pack(...))
+ end
+ return BinaryOperation.MULEXP({a, table.pack(...)[1]})
+end
+
+----------------------
+-- Static constants --
+----------------------
+
+Expression.NIL = 0
+Expression.SUBS = 1 \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/functionexpression.lua b/macros/luatex/latex/luacas/tex/core/functionexpression.lua
new file mode 100644
index 0000000000..dc94790b81
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/functionexpression.lua
@@ -0,0 +1,295 @@
+--- @class FunctionExpression
+--- Represents a generic function that takes zero or more expressions as inputs.
+--- @field name SymbolExpression
+--- @field expressions table<number, Expression>
+--- @field orders table<number, Integer>
+--- @field variables table<number,SymbolExpression>
+--- @alias Function FunctionExpression
+FunctionExpression = {}
+__FunctionExpression = {}
+
+----------------------------
+-- Instance functionality --
+----------------------------
+
+--- Creates a new function expression with the given operation.
+--- @param name string|SymbolExpression
+--- @param expressions table<number, Expression>
+--- @param derivatives table<number,Integer>
+--- @return FunctionExpression
+function FunctionExpression:new(name, expressions, derivatives)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+
+ if type(name) == "table" and name:type() == SymbolExpression then
+ name = name.symbol
+ end
+
+ if TrigExpression.NAMES[name] and #expressions == 1 then
+ return TrigExpression(name, expressions[1])
+ end
+
+ -- TODO: Symbol Checking For Constructing derivatives like this
+ --if string.sub(name, #name, #name) == "'" and #expressions == 1 then
+ -- return DerivativeExpression(FunctionExpression(string.sub(name, 1, #name - 1), expressions), SymbolExpression("x"), true)
+ --end
+
+ o.name = name
+ o.expressions = Copy(expressions)
+ o.variables = Copy(expressions)
+ for _,expression in ipairs(o.variables) do
+ if not expression:isatomic() then
+ o.variables = {}
+ if #o.expressions < 4 then
+ local defaultvars = {SymbolExpression('x'),SymbolExpression('y'),SymbolExpression('z')}
+ for i=1,#o.expressions do
+ o.variables[i] = defaultvars[i]
+ end
+ else
+ for i=1,#o.expressions do
+ o.variables[i] = SymbolExpression('x_'..tostring(i))
+ end
+ end
+ end
+ end
+ if derivatives then
+ o.derivatives = Copy(derivatives)
+ else
+ o.derivatives = {}
+ for i=1,#o.variables do
+ o.derivatives[i] = Integer.zero()
+ end
+ end
+
+ __o.__index = FunctionExpression
+ __o.__tostring = function(a)
+ local total = Integer.zero()
+ for _,integer in ipairs(a.derivatives) do
+ total = total + integer
+ end
+ if total == Integer.zero() then
+ local out = a.name .. '('
+ for index, expression in ipairs(a.expressions) do
+ out = out .. tostring(expression)
+ if a.expressions[index + 1] then
+ out = out .. ', '
+ end
+ end
+ return out .. ')'
+ else
+ local out = 'd'
+ if total > Integer.one() then
+ out = out ..'^' .. tostring(total)
+ end
+ out = out .. a.name .. '/'
+ for index,integer in ipairs(a.derivatives) do
+ if integer > Integer.zero() then
+ out = out .. 'd' .. tostring(a.variables[index])
+ if integer > Integer.one() then
+ out = out .. '^' .. tostring(integer)
+ end
+ end
+ end
+ out = out .. '('
+ for index, expression in ipairs(a.expressions) do
+ out = out .. tostring(expression)
+ if a.expressions[index + 1] then
+ out = out .. ', '
+ end
+ end
+ return out .. ')'
+ end
+ end
+ __o.__eq = function(a, b)
+ -- if b:type() == TrigExpression then
+ -- return a == b:tofunction()
+ -- end
+ if b:type() ~= FunctionExpression then
+ return false
+ end
+ if #a.expressions ~= #b.expressions then
+ return false
+ end
+ for index, _ in ipairs(a.expressions) do
+ if a.expressions[index] ~= b.expressions[index] then
+ return false
+ end
+ end
+ for index,_ in ipairs(a.derivatives) do
+ if a.derivatives[index] ~= b.derivatives[index] then
+ return false
+ end
+ end
+ return a.name == b.name
+ end
+
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- @return FunctionExpression
+function FunctionExpression:evaluate()
+ local results = {}
+ for index, expression in ipairs(self:subexpressions()) do
+ results[index] = expression:evaluate()
+ end
+ local result = FunctionExpression(self.name, results, self.derivatives)
+ result.variables = self.variables
+ return result
+end
+
+--- @return FunctionExpression
+function FunctionExpression:autosimplify()
+ -- Since the function is completely generic, we can't really do anything execpt autosimplify subexpressions.
+ local results = {}
+ for index, expression in ipairs(self:subexpressions()) do
+ results[index] = expression:autosimplify()
+ end
+ local result = FunctionExpression(self.name, results, self.derivatives)
+ result.variables = self.variables
+ return result
+end
+
+--- @return table<number, Expression>
+function FunctionExpression:subexpressions()
+ return self.expressions
+end
+
+--- @param subexpressions table<number, Expression>
+--- @return FunctionExpression
+function FunctionExpression:setsubexpressions(subexpressions)
+ local result = FunctionExpression(self.name, subexpressions, self.derivatives)
+ result.variables = self.variables
+ return result
+end
+
+--- @param other Expression
+--- @return boolean
+function FunctionExpression:order(other)
+ if other:isatomic() then
+ return false
+ end
+
+ -- CASC Autosimplfication has some symbols appearing before functions, but that looks bad to me, so all symbols appear before products now.
+ -- if other:type() == SymbolExpression then
+ -- return SymbolExpression(self.name):order(other)
+ -- end
+
+ if other:type() == BinaryOperation then
+ if other.operation == BinaryOperation.ADD or other.operation == BinaryOperation.MUL then
+ return BinaryOperation(other.operation, {self}):order(other)
+ end
+
+ if other.operation == BinaryOperation.POW then
+ return (self^Integer.one()):order(other)
+ end
+ end
+
+ if other:type() == SqrtExpression then
+ return self:order(other:topower())
+ end
+
+ -- TODO: Make Logarithm and AbsExpression inherit from function expression to reduce code duplication
+ if other:type() == Logarithm then
+ return self:order(FunctionExpression("log", {other.base, other.expression}))
+ end
+
+ if other:type() ~= FunctionExpression and other:type() ~= TrigExpression then
+ return true
+ end
+
+ if self.name ~= other.name then
+ return SymbolExpression(self.name):order(SymbolExpression(other.name))
+ end
+
+ local k = 1
+ while self:subexpressions()[k] and other:subexpressions()[k] do
+ if self:subexpressions()[k] ~= other:subexpressions()[k] then
+ return self:subexpressions()[k]:order(other:subexpressions()[k])
+ end
+ k = k + 1
+ end
+ return #self.expressions < #other.expressions
+end
+
+--- @return string
+function FunctionExpression:tolatex()
+ local out = tostring(self.name)
+ if self:type() == TrigExpression then
+ out = "\\" .. out
+ end
+ if self:type() ~= TrigExpression and #self.name>1 then
+ --if out:sub(2,2) ~= "'" then
+ --local fp = out:find("'")
+ --if fp then
+ -- out = '\\operatorname{' .. out:sub(1,fp-1) .. '}' .. out:sub(fp,-1)
+ --else
+ out = '\\operatorname{' .. out .. '}'
+ --end
+ --end
+ end
+ local total = Integer.zero()
+ for _,integer in ipairs(self.derivatives) do
+ total = total + integer
+ end
+ if #self.expressions == 1 then
+ if total == Integer.zero() then
+ goto continue
+ else
+ if total < Integer(5) then
+ while total > Integer.zero() do
+ out = out .. "'"
+ total = total - Integer.one()
+ end
+ else
+ out = out .. '^{(' .. total:tolatex() .. ')}'
+ end
+ end
+ end
+ if #self.expressions > 1 then
+ if total == Integer.zero() then
+ goto continue
+ else
+ if total < Integer(4) then
+ out = out .. '_{'
+ for index,integer in ipairs(self.derivatives) do
+ local i = integer:asnumber()
+ while i > 0 do
+ out = out .. self.variables[index]:tolatex()
+ i = i - 1
+ end
+ end
+ out = out .. '}'
+ else
+ out = '\\frac{\\partial^{' .. total:tolatex() .. '}' .. out .. '}{'
+ for index, integer in ipairs(self.derivatives) do
+ if integer > Integer.zero() then
+ out = out .. '\\partial ' .. self.variables[index]:tolatex()
+ if integer ~= Integer.one() then
+ out = out .. '^{' .. integer:tolatex() .. '}'
+ end
+ end
+ end
+ out = out .. '}'
+ end
+ end
+ end
+ ::continue::
+ out = out ..'\\mathopen{}' .. '\\left('
+ for index, expression in ipairs(self:subexpressions()) do
+ out = out .. expression:tolatex()
+ if self:subexpressions()[index + 1] then
+ out = out .. ', '
+ end
+ end
+ return out .. '\\right)'
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__FunctionExpression.__index = CompoundExpression
+__FunctionExpression.__call = FunctionExpression.new
+FunctionExpression = setmetatable(FunctionExpression, __FunctionExpression) \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/core/symbolexpression.lua b/macros/luatex/latex/luacas/tex/core/symbolexpression.lua
new file mode 100644
index 0000000000..0d9192c084
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/core/symbolexpression.lua
@@ -0,0 +1,132 @@
+--- @class SymbolExpression
+--- An atomic expression corresponding to a symbol representing an arbitary value.
+--- @field symbol string
+--- @alias Symbol SymbolExpression
+SymbolExpression = {}
+__SymbolExpression = {}
+
+----------------------
+-- Instance methods --
+----------------------
+
+--- Given the name of the symbol as a string, creates a new symbol.
+--- @param symbol string
+--- @return SymbolExpression
+function SymbolExpression:new(symbol)
+ local o = {}
+ local __o = Copy(__ExpressionOperations)
+ __o.__index = SymbolExpression
+ __o.__tostring = function(a)
+ return a.symbol
+ end
+ __o.__eq = function(a, b)
+ return a.symbol == b.symbol
+ end
+
+ if type(symbol) ~= "string" then
+ error("Sent parameter of wrong type: symbol must be a string")
+ end
+
+ o.symbol = symbol
+ o = setmetatable(o, __o)
+
+ return o
+end
+
+--- @return boolean
+function SymbolExpression:freeof(symbol)
+ return symbol~=self
+end
+
+--- @return boolean
+function SymbolExpression:isconstant()
+ return false
+end
+
+--- @param other Expression
+--- @return boolean
+function SymbolExpression:order(other)
+
+ -- Symbol Expressions come after constant expressions.
+ if other:isconstant() then
+ return false
+ end
+
+ -- Lexographic order on symbols.
+ if other:type() == SymbolExpression then
+ for i = 1, math.min(#self.symbol, #other.symbol) do
+ if string.byte(self.symbol, i) ~= string.byte(other.symbol, i) then
+ return string.byte(self.symbol, i) < string.byte(other.symbol, i)
+ end
+ end
+
+ return #self.symbol < #other.symbol
+ end
+
+ if other.operation == BinaryOperation.POW then
+ return BinaryOperation(BinaryOperation.POW, {self, Integer.one()}):order(other)
+ end
+
+ if other.operation == BinaryOperation.MUL then
+ return BinaryOperation(BinaryOperation.MUL, {self}):order(other)
+ end
+
+ if other.operation == BinaryOperation.ADD then
+ return BinaryOperation(BinaryOperation.ADD, {self}):order(other)
+ end
+
+ -- CASC Autosimplfication has some symbols appearing before functions, but that looks bad to me, so all symbols appear before products now.
+ if other:type() == FunctionExpression or other:type() == TrigExpression or other:type() == Logarithm then
+ return true
+ end
+
+ return false
+end
+
+--- Converts this symbol to an element of a polynomial ring.
+--- @return PolynomialRing, boolean
+function SymbolExpression:topolynomial()
+ return PolynomialRing({Integer.zero(), Integer.one()}, self.symbol), true
+end
+
+-----------------
+-- Inheritance --
+-----------------
+
+__SymbolExpression.__index = AtomicExpression
+__SymbolExpression.__call = SymbolExpression.new
+SymbolExpression = setmetatable(SymbolExpression, __SymbolExpression)
+
+
+----------------------
+-- Static Constants --
+----------------------
+
+-- The constant pi.
+PI = SymbolExpression("pi")
+function PI:tolatex()
+ return "\\pi "
+end
+
+-- Approximates pi as a rational number. Uses continued fraction expansion.
+function PI:approximate()
+ return Integer(313383936) / Integer(99753205)
+end
+
+--function PI:isconstant()
+-- return true
+--end
+
+-- The constant e.
+E = SymbolExpression("e")
+
+-- Approximates pi as a rational number. Uses continued fraction expansion.
+function E:approximate()
+ return Integer(517656) / Integer(190435)
+end
+
+-- The imaginary constant i.
+I = SymbolExpression("i")
+function I:tolatex()
+ return "i"
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/luacas.sty b/macros/luatex/latex/luacas/tex/luacas.sty
new file mode 100644
index 0000000000..461fae6efa
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/luacas.sty
@@ -0,0 +1,312 @@
+% Permission is granted to copy, distribute and/or modify this
+% software under the terms of the LaTeX Project Public License
+% (LPPL), version 1.3c or any later version.
+%
+% This software is provided 'as is', without warranty of any kind,
+% either expressed or implied, including, but not limited to, the
+% implied warranties of merchantability and fitness for a
+% particular purpose.
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{luacas}
+ [2022/05/07 v1.0 CAS written in Lua for LaTeX]
+
+\RequirePackage{iftex}
+\ifluatex
+ \RequirePackage{luacode}
+\else
+ {\PackageError{luacas}
+ {Not running under LuaLaTeX}
+ {This package requires LuaLaTeX. Try compiling this document with\MessageBreak 'lualatex' instead of 'latex'. This is a fatal error; I'm aborting now.}%
+ }\stop
+\fi
+
+%Required Packages
+\RequirePackage{xparse}
+\RequirePackage{pgfkeys}
+\RequirePackage{verbatim}
+\RequirePackage{tikz}
+\RequirePackage{xcolor}
+\RequirePackage{mathtools}
+
+%These files contain Lua code for parsing luacas output; they also initialize the CAS itself
+\directlua{require('test.parser')
+ require('test.helper')
+}
+
+\NewDocumentEnvironment{CAS}%
+ {+b}%
+ {\luaexec{CASparse([[#1]])}}%
+ {}
+
+\newcommand{\get}%
+ [2][true]%
+ {\directlua{disp(#2, #1)}}
+
+\newcommand{\fetch}[1]{
+ \directlua{tex.print(tostring(#1))}
+}
+
+\NewDocumentCommand{\store}{m O{#1}}{
+ \expandafter\def\csname #2\endcsname{%
+ \directlua{
+ input = #1
+ if not input[1] then
+ tex.sprint{tostring(input)}
+ else
+ tex.sprint("{")
+ for _,entry in ipairs(input) do
+ tex.sprint(tostring(entry),",")
+ end
+ tex.sprint("}")
+ end
+ }%
+ }%
+}%
+
+\NewDocumentCommand{\yoink}{m O{#1}}{%
+\expandafter\def\csname #2\endcsname{%
+ \directlua{
+ tex.print(tostring(#1))
+ }%
+ }%
+}
+
+%\newcommand{\eval}[1]{\luaexec{tex.print(parse('#1'):tolatex())}}
+
+%%%%%%%%%%
+%% Core %%
+%%%%%%%%%%
+
+%pretty print
+
+\NewDocumentCommand{\print}{s m}{%
+ \IfBooleanTF{#1}{%
+ \directlua{
+ local sym = #2
+ if sym then
+ tex.print(sym:autosimplify():tolatex())
+ else
+ tex.print('nil')
+ end
+ }%
+ }{%
+ \directlua{
+ local sym = #2
+ if sym then
+ tex.print(sym:tolatex())
+ else
+ tex.print('nil')
+ end
+ }%
+ }%
+}
+
+%verbatim print
+
+\NewDocumentCommand{\vprint}{s m}{%
+ \IfBooleanTF{#1}{%
+ \directlua{
+ local sym = #2
+ tex.sprint([[\unexpanded{\begin{verbatim}]]..tostring(sym)..[[\end{verbatim}}]])
+ }%
+ }{%
+ \directlua{
+ local sym = #2
+ tex.sprint([[\unexpanded{\begin{verbatim}]]..tostring(sym:autosimplify())..[[\end{verbatim}}]])
+ }%
+ }%
+}
+
+\NewDocumentCommand{\lprint}{m O{nil,nil}}{%
+ \luaexec{
+ local tbl = #1
+ local low,upp = #2
+ local tmp =0
+ if tbl[0] == nil then
+ tmp = 1
+ end
+ upp = upp or \#tbl
+ low = low or tmp
+ for i=low,upp do
+ tex.print(tbl[i]:tolatex())
+ if tbl[i+1] then
+ tex.print(",")
+ end
+ end
+ }
+}
+
+%prints the first level of an expression tree; for use within a tikzpicture environment
+
+\NewDocumentCommand{\printshrub}{s m}{%
+ \IfBooleanTF{#1}{%
+ \directlua{
+ local sym = #2
+ sym = sym:autosimplify()
+ tex.print("\\node [label=90:",whatis(sym),"] {",nameof(sym),"}")
+ tex.print(sym:gettheshrub())
+ tex.print(";")
+ }%
+ }{%
+ \directlua{
+ local sym = #2
+ tex.print("\\node [label=90:",whatis(sym),"] {",nameof(sym),"}")
+ tex.print(sym:gettheshrub())
+ tex.print(";")
+ }%
+ }
+}
+
+%prints the full expression tree; for use within a tikzpicture environment
+
+\NewDocumentCommand{\printtree}{s m}{%
+ \IfBooleanTF{#1}{%
+ \luaexec{
+ local sym = #2
+ sym = sym:autosimplify()
+ tex.print("\\node {",nameof(sym),"}")
+ tex.print(sym:getthetree())
+ tex.print(";")
+ }%
+ }{%
+ \luaexec{
+ local sym = #2
+ tex.print("\\node {",nameof(sym),"}")
+ tex.print(sym:getthetree())
+ tex.print(";")
+ }%
+ }
+}
+
+%parses an expression tree for use within the forest environment; result is stored in \forestresult
+
+\NewDocumentCommand{\parseforest}{s m}{%
+ \IfBooleanTF{#1}{%
+ \luaexec{
+ local sym = #2
+ sym = sym:autosimplify()
+ tex.print("\\def\\forestresult{")
+ tex.print("[")
+ tex.print(nameof(sym))
+ tex.print(sym:gettheforest())
+ tex.print("]")
+ tex.print("}")
+ }%
+ }{%
+ \luaexec {
+ local sym = #2
+ tex.print("\\def\\forestresult{")
+ tex.print("[")
+ tex.print(nameof(sym))
+ tex.print(sym:gettheforest())
+ tex.print("]")
+ tex.print("}")
+ }%
+ }
+}
+
+\NewDocumentCommand{\parseshrub}{s m}{%
+ \IfBooleanTF{#1}{%
+ \luaexec{
+ local sym = #2
+ sym = sym:autosimplify()
+ tex.print("\\def\\shrubresult{")
+ tex.print("[")
+ tex.print(nameof(sym))
+ tex.print(", tikz+={\\node[anchor=south] at (.north) {test};}")
+ tex.print(sym:getthefancyshrub())
+ tex.print("]")
+ tex.print("}")
+ }%
+ }{%
+ \luaexec{
+ local sym = #2
+ tex.print("\\def\\shrubresult{")
+ tex.print("[")
+ tex.print(nameof(sym))
+ tex.print(", tikz+={\\node[anchor=south,font=\\ttfamily\\footnotesize,gray] at (.north) {",longwhatis(sym),"};}")
+ tex.print(sym:getthefancyshrub())
+ tex.print("]")
+ tex.print("}")
+ }%
+ }
+}
+
+\NewDocumentCommand{\whatis}{m}{%
+ \luaexec{
+ tex.sprint("{\\ttfamily",longwhatis(#1),"}")
+ }%
+}
+
+\NewDocumentCommand{\freeof}{s m m}{%
+ \IfBooleanTF{#1}{%
+ \luaexec{
+ local sym1 = #2
+ local sym2 = #3
+ if sym1:freeof(sym2) then
+ tex.print(1)
+ else
+ tex.print(0)
+ end
+ }
+ }{%
+ \luaexec{
+ local sym1 = #2
+ local sym2 = #3
+ sym1 = sym1:autosimplify()
+ sym2 = sym2:autosimplify()
+ if sym1:freeof(sym2) then
+ tex.print(1)
+ else
+ tex.print(0)
+ end
+ }%
+ }%
+}
+
+\NewDocumentCommand{\isatomic}{s m}{%
+ \IfBooleanTF{#1}{
+ \luaexec{
+ local sym = #2
+ if sym:isatomic() then
+ tex.print(1)
+ else
+ tex.print(0)
+ end
+ }
+ }{%
+ \luaexec{
+ local sym = #2
+ sym = sym:autosimplify()
+ if sym:isatomic() then
+ tex.print(1)
+ else
+ tex.print(0)
+ end
+ }%
+ }%
+}
+
+\NewDocumentCommand{\isconstant}{s m}{%
+ \IfBooleanTF{#1}{%
+ \luaexec{
+ local sym = #2
+ if sym:isconstant() then
+ tex.print(1)
+ else
+ tex.print(0)
+ end
+ }%
+ }{%
+ \luaexec{%
+ local sym = #2
+ sym = sym:autosimplify()
+ if sym:isconstant() then
+ tex.print(1)
+ else
+ tex.print(0)
+ end
+ }%
+ }%
+}
diff --git a/macros/luatex/latex/luacas/tex/test/calculus/derivatives.lua b/macros/luatex/latex/luacas/tex/test/calculus/derivatives.lua
new file mode 100644
index 0000000000..5d5429f59b
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/calculus/derivatives.lua
@@ -0,0 +1,30 @@
+local a = DD(SymbolExpression("x") * SymbolExpression("y"), SymbolExpression("x"))
+local b = DD(Integer(3) * SymbolExpression("x") ^ Integer(2) + Integer(2) * SymbolExpression("x") + Integer(6), SymbolExpression("x"))
+local c = DD(E ^ SymbolExpression("x"), SymbolExpression("x"))
+local d = DD(FunctionExpression("f", {SymbolExpression("x") ^ Integer(2)}))
+local e = DD(SymbolExpression("x") ^ SymbolExpression("x"))
+local f = DD(PolynomialRing({Integer(3), Integer(4), Integer(5)}, "x"))
+local g = DD(LN(SymbolExpression("y")), SymbolExpression("y"))
+local h = DD(SymbolExpression("x") ^ SymbolExpression("n"))
+local i = DD(SIN((SymbolExpression("x"))))
+local j = DD(SIN(Integer(2) * COS(SymbolExpression("x"))))
+local k = DD(ARCTAN(SymbolExpression("x") ^ (Integer(1) / Integer(2))))
+local l = DD(ARCSEC(SymbolExpression("x")))
+
+starttest("derivatives")
+
+testeq(a, dparse("DD(x*y, x)"))
+testeq(a:autosimplify(), parse("y"), a)
+testeq(b:autosimplify(), parse("6 * x + 2"), b)
+testeq(c:autosimplify(), parse("e^x"), c)
+testeq(d:autosimplify(), (Integer(2) * SymbolExpression("x") * FunctionExpression("f", {SymbolExpression("x")^Integer(2)}, {Integer(1)})):autosimplify(), d)
+testeq(e:autosimplify(), parse("x^x * (1 + ln(x))"), e)
+testeq(f:autosimplify(), parse("4 + 10 * x"), f)
+testeq(g:autosimplify(), parse("y ^ -1"), g)
+testeq(h:autosimplify(), parse("n * x ^ (-1 + n)"), h)
+testeq(i:autosimplify(), parse("cos(x)"), i)
+testeq(j:autosimplify(), parse("-2 * cos(2 * cos(x)) * sin(x)"), j)
+testeq(k:autosimplify(), parse("1/2 * x^(-1/2) * (1+x) ^ -1"), k)
+testeq(l:autosimplify(), parse("abs(x)^-1 * (1 + -(x^2))^ (-1/2)"), l)
+
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/calculus/integrals.lua b/macros/luatex/latex/luacas/tex/test/calculus/integrals.lua
new file mode 100644
index 0000000000..0c12f7a787
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/calculus/integrals.lua
@@ -0,0 +1,54 @@
+local a = dparse("int(x^2, x)")
+local b = dparse("int(x^-1, x, 1, e)")
+local c = dparse("int(3*x^2+2*x+6, x)")
+local d = dparse("int(sin(x)*cos(x), x)")
+local e = dparse("int(2*x*cos(x^2), x)")
+local f = dparse("int(sin(2*x), x)")
+local g = dparse("int(e^sin(x), x)")
+local h = dparse("int((1 / (1 + (1 / x))), x)")
+local i = dparse("int(e^(x^(1/2)), x)")
+local j = dparse("int((x^3+1)/(x-2), x)")
+local k = dparse("int((x^2-x+1)/(x^3+3*x^2+3*x+1), x)")
+local l = dparse("int(1 / (x^3+6*x), x)")
+local m = dparse("int(1/(x^2+x+1), x)")
+local n = dparse("int(1/(x^3+2*x+2), x, 0, 1)")
+
+local o = dparse("int(x^2*e^x, x)")
+local p = dparse("int((x^2+6*x+3)*sin(x), x)")
+local q = dparse("int(x*e^x*sin(x),x)")
+local r = dparse("int(cos(x)^3, x)")
+local s = dparse("int(1/(e^x+1), x)")
+local t = dparse("int(e^(2*x)*cos(3*x), x)")
+local u = dparse("int((x^2-1)^2, x, -1, 1)")
+
+starttest("integration")
+testeq(a, dparse("int(x ^ 2, x)"))
+testeq(a:autosimplify(), parse("x^3/3"), a)
+testeq(b:autosimplify(), parse("1"), b)
+testeq(c:autosimplify(), parse("x^3+x^2+6*x"), c)
+testeq(d:autosimplify(), parse("(-1/2 * (cos(x) ^ 2))"), d)
+testeq(e:autosimplify(), parse("sin((x ^ 2))"), e)
+testeq(f:autosimplify(), parse("(-1/2 * cos((2 * x)))"), f)
+-- testeq(g:autosimplify(), dparse("int(e ^ (sin(x)), x)"), g)
+testeq(h:autosimplify(), parse("x + (-1 * (log(e, 1 + (x ^ -1)))) + (-1 * (log(e, x)))"), h)
+testeq(i:autosimplify(), parse("-2 * (e ^ (x ^ (1/2))) + 2 * (e ^ (x ^ (1/2))) * (x ^ (1/2))"), i)
+testeq(j:autosimplify(), parse("((4 * x) + (x ^ 2) + (1/3 * (x ^ 3)) + (9 * log(e, (-2 + x))))"), j)
+testeq(k:autosimplify(), parse("((-3/2 * ((1 + x) ^ -2)) + (3 * ((1 + x) ^ -1)) + log(e, (1 + x)))"), k)
+testeq(l:autosimplify(), parse("((1/6 * log(e, x)) + (-1/12 * log(e, (6 + (x ^ 2)))))"), l)
+testeq(m:autosimplify(), parse("2/3 * (3 ^ (1/2)) * (arctan((3 ^ (1/2)) * (1/3 + (2/3 * x))))"), m)
+-- test(n:autosimplify(), [[((((6 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (1/420 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3)))
+-- * log(e, (18 * (((-6 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2) * (((-18 * ((-264600 + (1/2
+-- * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/140 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3)) + (12 * (((-6 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2))) ^ -1)))) + (((6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -1) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + ((-1/840 + (1/840 * (-3 ^ 1/2))) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) * log(e, (18 * (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -1) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + ((1/840 + (-1/840 * (-3 ^ 1/2))) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2) * (((-18 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -1) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + ((1/280 + (-1/280 * (-3 ^ 1/2))) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3)) + (12 * (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -1) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + ((1/840 + (-1/840 * (-3 ^ 1/2))) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2))) ^ -1)))) + (((6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (1/420 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ 2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) * log(e, (18 * (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ 2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2) * (((-18 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/140 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ 2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3)) + (12 * (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ 2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2))) ^ -1)))) + (((-6 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) * log(e, (1 + (18 * (((-6 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2) * (((-18 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/140 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3)) + (12 * (((-6 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2))) ^ -1))))) + (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -1) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + ((1/840 + (-1/840 * (-3 ^ 1/2))) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) * log(e, (1 + (18 * (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -1) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + ((1/840 + (-1/840 * (-3 ^ 1/2))) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2) * (((-18 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -1) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + ((1/280 + (-1/280 * (-3 ^ 1/2))) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3)) + (12 * (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -1) * ((-264600 + (1/2 * (216040608000
+-- ^ 1/2))) ^ -1/3)) + ((1/840 + (-1/840 * (-3 ^ 1/2))) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2))) ^ -1))))) + (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -2) *
+-- ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ 2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) * log(e, (1 + (18 *
+-- (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ 2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2) * (((-18 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/140 * ((-1/2 + (1/2 * (-3 ^ 1/2)))
+-- ^ 2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3)) + (12 * (((-6 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ -2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ -1/3)) + (-1/420 * ((-1/2 + (1/2 * (-3 ^ 1/2))) ^ 2) * ((-264600 + (1/2 * (216040608000 ^ 1/2))) ^ 1/3))) ^ 2))) ^ -1))))))]], n)
+
+testeq(o:autosimplify(), parse("((2 * (e ^ x)) + (-2 * (e ^ x) * x) + ((e ^ x) * (x ^ 2)))"), o)
+testeq(p:autosimplify(), parse("((2 * cos(x)) + ((-3 + (-6 * x) + (-1 * (x ^ 2))) * cos(x)) + ((6 + (2 * x)) * sin(x)))"), p)
+testeq(q:autosimplify(), parse("(1/2 * (e ^ x) * (cos(x))) + (-1/2 * (e ^ x) * x * (cos(x))) + (1/2 * (e ^ x) * x * (sin(x)))"), q)
+testeq(r:autosimplify(), parse("((3/4 * sin(x)) + (1/12 * sin((3 * x))))"), r)
+testeq(s:autosimplify(), parse("log(e, 1 + (-1 * ((1 + (e ^ x)) ^ -1)))"), s)
+testeq(t:autosimplify(), parse("(2/13 * (e ^ (2 * x)) * (cos(3 * x))) + (3/13 * (e ^ (2 * x)) * (sin(3 * x)))"), t)
+testeq(u:autosimplify(), parse("16/15"), u)
+endtest()
diff --git a/macros/luatex/latex/luacas/tex/test/expressions/autosimplify.lua b/macros/luatex/latex/luacas/tex/test/expressions/autosimplify.lua
new file mode 100644
index 0000000000..09ad9874cd
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/expressions/autosimplify.lua
@@ -0,0 +1,199 @@
+local a = BinaryOperation.ADDEXP
+ ({Integer(3),
+ Integer(5)})
+
+local b = BinaryOperation.MULEXP
+ ({BinaryOperation.ADDEXP
+ ({Integer(13),
+ Integer(12)}),
+ Integer(-4)})
+
+local c = BinaryOperation.DIVEXP
+ ({SymbolExpression("x"),
+ SymbolExpression("y")})
+
+local d = BinaryOperation.DIVEXP
+ ({BinaryOperation.ADDEXP
+ ({Integer(4),
+ Integer(-3)}),
+ SymbolExpression("y")})
+
+local e = BinaryOperation.ADDEXP
+ ({Integer(3),
+ Integer(4),
+ Integer(5),
+ Integer(6)})
+
+starttest("expression construction")
+testeq(a, "3 + 5")
+testeq(b, "(13 + 12) * -4")
+testeq(c, "x / y")
+testeq(d, "(4 + -3) / y")
+testeq(e, "3 + 4 + 5 + 6")
+endtest()
+
+starttest("expression evaluation...")
+testeq(a:evaluate(), dparse("8"))
+testeq(b:evaluate(), dparse("-100"))
+testeq(c:evaluate(), dparse("(x / y)"))
+testeq(d:evaluate(), dparse("(1 / y)"))
+testeq(e:evaluate(), dparse("18"))
+endtest()
+
+local g = BinaryOperation.POWEXP
+ ({Integer(0),
+ SymbolExpression("x")})
+
+local h = BinaryOperation.POWEXP
+ ({Integer(1),
+ SymbolExpression("x")})
+
+local i = BinaryOperation.POWEXP
+ ({SymbolExpression("x"),
+ Integer(0)})
+
+local j = BinaryOperation.POWEXP
+ ({SymbolExpression("x"),
+ Integer(1)})
+
+local k = BinaryOperation.POWEXP
+ ({SymbolExpression("x"),
+ SymbolExpression("y")})
+
+local l = BinaryOperation.POWEXP
+ ({BinaryOperation.POWEXP
+ ({BinaryOperation.POWEXP
+ ({SymbolExpression("x"),
+ Integer(3)}),
+ Integer(4)}),
+ Integer(5)})
+
+local m = BinaryOperation.POWEXP
+ ({BinaryOperation.MULEXP
+ ({SymbolExpression("x"),
+ SymbolExpression("y")}),
+ SymbolExpression("a")})
+
+ local n = BinaryOperation.MULEXP
+ ({SymbolExpression("x"),
+ SymbolExpression("y"),
+ Integer(0),
+ Integer(-2)
+ })
+
+local o = BinaryOperation.MULEXP
+ ({SymbolExpression("x"),
+ BinaryOperation.MULEXP
+ ({SymbolExpression("y"),
+ SymbolExpression("z")})})
+
+local p = BinaryOperation.MULEXP
+ ({SymbolExpression("x")})
+
+local q = BinaryOperation.MULEXP
+ ({SymbolExpression("x"), SymbolExpression("x"), SymbolExpression("x"), SymbolExpression("x")})
+
+local r = BinaryOperation.MULEXP
+ ({SymbolExpression("x"), Integer(3), SymbolExpression("a")})
+
+ local s = BinaryOperation.ADDEXP
+ ({SymbolExpression("x")})
+
+local t = BinaryOperation.ADDEXP
+ ({SymbolExpression("x"),
+ BinaryOperation.ADDEXP
+ ({Integer(3),
+ SymbolExpression("y")})})
+
+local u = BinaryOperation.ADDEXP
+ ({BinaryOperation.MULEXP
+ ({SymbolExpression("x"),
+ SymbolExpression("y")}),
+ BinaryOperation.MULEXP
+ ({SymbolExpression("y"),
+ SymbolExpression("x")})})
+
+local v = BinaryOperation.ADDEXP
+ ({Integer(3),
+ BinaryOperation.ADDEXP
+ ({BinaryOperation.MULEXP
+ ({Integer(2),
+ BinaryOperation.POWEXP
+ ({SymbolExpression("x"),
+ Integer(2)})}),
+ BinaryOperation.ADDEXP
+ ({BinaryOperation.MULEXP
+ ({Integer(1),
+ SymbolExpression("y")}),
+ BinaryOperation.MULEXP
+ ({Integer(0),
+ SymbolExpression("x")})})}),
+ Integer(6)})
+
+ local w = BinaryOperation.MULEXP
+ ({BinaryOperation.DIVEXP
+ ({Integer(1),
+ SymbolExpression("x")}),
+ SymbolExpression("x")})
+
+local x = BinaryOperation.MULEXP
+ ({BinaryOperation.DIVEXP
+ ({SymbolExpression("y"),
+ SymbolExpression("x")}),
+ BinaryOperation.DIVEXP
+ ({SymbolExpression("x"),
+ SymbolExpression("y")})})
+
+local y = BinaryOperation.MULEXP
+ ({BinaryOperation.DIVEXP
+ ({Integer(1),
+ Integer(3)}),
+ SymbolExpression("x")})
+
+local z = BinaryOperation.ADDEXP
+ ({SymbolExpression("x"),
+ SymbolExpression("y"),
+ BinaryOperation.SUBEXP
+ ({SymbolExpression("x"),
+ SymbolExpression("y")})})
+
+local A = dparse("(-aa-x)+(x+aa)")
+
+starttest("expression autosimplification")
+testeq(g:autosimplify(), parse("0"), g)
+testeq(h:autosimplify(), parse("1"), h)
+testeq(i:autosimplify(), parse("1"), i)
+testeq(j:autosimplify(), parse("x"), j)
+testeq(k:autosimplify(), parse("x ^ y"), k)
+testeq(l:autosimplify(), parse("(x ^ 60)"), l)
+testeq(m:autosimplify(), parse("((x * y) ^ a)"), m)
+testeq(n:autosimplify(), parse("0"), n)
+testeq(o:autosimplify(), parse("(x * y * z)"), o)
+testeq(p:autosimplify(), parse("x"), p)
+testeq(q:autosimplify(), parse("(x ^ 4)"), q)
+testeq(r:autosimplify(), parse("(3 * a * x)"), r)
+testeq(s:autosimplify(), parse("x"), s)
+testeq(t:autosimplify(), parse("(3 + x + y)"), t)
+testeq(u:autosimplify(), parse("(2 * x * y)"), u)
+testeq(v:autosimplify(), parse("(9 + (2 * (x ^ 2)) + y)"), v)
+testeq(w:autosimplify(), parse("1"), w)
+testeq(x:autosimplify(), parse("1"), x)
+testeq(y:autosimplify(), parse("(1/3 * x)"), y)
+testeq(z:autosimplify(), parse("(2 * x)"), z)
+testeq(A:autosimplify(), parse("0"), A)
+endtest()
+
+
+local aa = SymbolExpression("x") + SymbolExpression("y") + SymbolExpression("z")
+local ab = -(SymbolExpression("x") / SymbolExpression("y"))
+local ac = Integer(2)*SymbolExpression("x")*SymbolExpression("y") - Integer(3)*SymbolExpression("x")*SymbolExpression("z")
+
+starttest("metamethod expressions")
+
+testeq(aa, dparse("(x + y) + z"))
+testeq(aa:autosimplify(), parse("(x + y + z)"), aa)
+testeq(ab, dparse("- (x / y)"))
+testeq(ab:autosimplify(), parse("(-1 * x * (y ^ -1))"), ab)
+testeq(ac:autosimplify(), parse("((2 * x * y) + (-3 * x * z))"), ac)
+
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/expressions/collect.lua b/macros/luatex/latex/luacas/tex/test/expressions/collect.lua
new file mode 100644
index 0000000000..b40e8e3d09
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/expressions/collect.lua
@@ -0,0 +1,26 @@
+local x = SymbolExpression("x")
+local ex = parse("e^x")
+local lnx = parse("ln(x)")
+
+local a = parse("y^2")
+local b = parse("x + y + 1")
+local c = parse("x*(y+1)+x+3*x*y^2")
+local d = parse("x^2+2*x*y+y^2+x")
+local e = parse("(x*y+x)^2+x^2")
+local f = parse("-x^2/e^x-2*x/e^x-2/e^x+x^2*e^x-2*x*e^x+2*e^x")
+local g = parse("x^(-2)+y*x^(-2)+z*x^2+2*x^2")
+local h = parse("a*ln(x)-ln(x)*x-x")
+
+
+starttest("collect method")
+
+testeq(a:collect(x), parse("y^2"), a)
+testeq(b:collect(x), parse("x + y + 1"), b)
+testeq(c:collect(x), parse("(3*y^2+y+2)*x"), c)
+testeq(d:collect(x), parse("x^2+(2*y+1)*x+y^2"), d)
+testeq(e:collect(x), parse("((y+1)^2+1)*x^2"), e)
+testeq(f:collect(ex), parse("(x^2-2*x+2)*e^x+(-x^2-2*x-2)/e^x"), f)
+testeq(g:collect(x), parse("(y+1)*x^(-2)+(z+2)*x^2"), g)
+testeq(h:collect(lnx), parse("(a-x)*ln(x)-x"), h)
+
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/expressions/equations.lua b/macros/luatex/latex/luacas/tex/test/expressions/equations.lua
new file mode 100644
index 0000000000..3d6aa131fc
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/expressions/equations.lua
@@ -0,0 +1,17 @@
+local a = Equation(parse("2^x"), parse("1"))
+local b = Equation(parse("x^2+2*x+1"), parse("0"))
+local c = Equation(parse("2*x^x"), parse("3*y"))
+local d = Equation(parse("e^x+1"), parse("y"))
+local e = Equation(parse("z*sin(x/2)"), parse("4"))
+local f = Equation(parse("4"), parse("0"))
+
+
+starttest("equation solving")
+testeq(a:solvefor(parse("x")), Equation(parse("x"), parse("0")), a)
+testeq(b:solvefor(parse("x")), Equation(parse("x"), parse("-1")), b) -- This will need to be fixed once set expressions are woring
+testeq(c:solvefor(parse("x")), Equation(parse("x^x"), parse("3/2*y")), c)
+testeq(c:solvefor(parse("y")), Equation(parse("y"), parse("2/3*x^x")), c)
+testeq(d:solvefor(parse("x")), Equation(parse("x"), parse("ln(y - 1)")), d)
+testeq(e:solvefor(parse("x")), Equation(parse("x"), parse("2*arcsin(4/z)")), e)
+testeq(f:autosimplify(), "false", f) -- Same, with boolean expressions
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/expressions/functions.lua b/macros/luatex/latex/luacas/tex/test/expressions/functions.lua
new file mode 100644
index 0000000000..e2c15096d6
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/expressions/functions.lua
@@ -0,0 +1,17 @@
+local a = FunctionExpression("f",
+ {SymbolExpression("x"),
+ BinaryOperation.MULEXP
+ ({SymbolExpression("x"),
+ Integer(2)})})
+
+local b = BinaryOperation.ADDEXP
+ ({FunctionExpression("g",
+ {SymbolExpression("x")}),
+ FunctionExpression("f",
+ {SymbolExpression("x")}),
+ Integer(4)})
+
+starttest("function expressions")
+testeq(a:autosimplify(), parse("f(x, (2 * x))"), a)
+testeq(b:autosimplify(), parse("(4 + f(x) + g(x))"), b)
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/expressions/logarithms.lua b/macros/luatex/latex/luacas/tex/test/expressions/logarithms.lua
new file mode 100644
index 0000000000..2813a6b8f5
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/expressions/logarithms.lua
@@ -0,0 +1,26 @@
+local a = LN(SymbolExpression("x"))
+local b = LN(BinaryOperation.POWEXP({E, SymbolExpression("x")}))
+local c = BinaryOperation.POWEXP({Integer(2), LOG(Integer(2), SymbolExpression("y"))})
+local d = dparse("e^(-x*ln(x))")
+
+local e = Logarithm(Integer(2), Integer(256))
+local f = Logarithm(Integer(4), Integer(8))
+local g = Logarithm(Integer(1)/Integer(5), Integer((125)))
+local h = Logarithm(Integer(1)/Integer(9), Integer(1)/Integer(243))
+local i = Logarithm(Integer(1)/Integer(25), Integer(3125))
+
+local k = Logarithm(E, Integer(1)/Integer(9))
+
+starttest("logarithms")
+testeq(a, "log(e, x)")
+testeq(a:autosimplify(), "log(e, x)", a)
+testeq(b:autosimplify(), "x", b)
+testeq(c:autosimplify(), "y", c)
+testeq(d:autosimplify(), parse("x^(-x)"), d)
+testeq(e:autosimplify(), parse("8"), e)
+testeq(f:autosimplify(), parse("3/2"), f)
+testeq(g:autosimplify(), parse("-3"), g)
+testeq(h:autosimplify(), parse("5/2"), h)
+testeq(i:autosimplify(), parse("-5/2"), i)
+testeq(k:autosimplify(), parse("-ln(9)"), k)
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/expressions/rationalexponent.lua b/macros/luatex/latex/luacas/tex/test/expressions/rationalexponent.lua
new file mode 100644
index 0000000000..85690f8c66
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/expressions/rationalexponent.lua
@@ -0,0 +1,15 @@
+local a = BinaryOperation.POWEXP({Integer(8), Integer(1) / Integer(2)})
+local b = BinaryOperation.POWEXP({Integer(27), Integer(1) / Integer(3)})
+local c = BinaryOperation.POWEXP({Integer(36), Integer(1) / Integer(2)})
+local d = BinaryOperation.POWEXP({Integer(36264691), Integer(1) / Integer(2)})
+local e = BinaryOperation.POWEXP({Integer(357911), Integer(1) / Integer(2)})
+local f = BinaryOperation.ADDEXP({BinaryOperation.POWEXP({Integer(8), Integer(1) / Integer(2)}), BinaryOperation.POWEXP({Integer(32), Integer(1) / Integer(2)})})
+
+starttest("rational powers")
+testeq(a:autosimplify(), "(2 * (2 ^ 1/2))", a)
+testeq(b:autosimplify(), "3", b)
+testeq(c:autosimplify(), "6", c)
+testeq(d:autosimplify(), "(331 * (331 ^ 1/2))", d)
+testeq(e:autosimplify(), "(71 * (71 ^ 1/2))", e)
+testeq(f:autosimplify(), "(6 * (2 ^ 1/2))", f)
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/expressions/simplify.lua b/macros/luatex/latex/luacas/tex/test/expressions/simplify.lua
new file mode 100644
index 0000000000..ac5582901d
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/expressions/simplify.lua
@@ -0,0 +1,22 @@
+local a = SymbolExpression("x")*(SymbolExpression("y") + SymbolExpression("z"))
+local b = SymbolExpression("x")*(Integer(1)+ SymbolExpression("z"))
+local c = parse("((2*x+1)*(3*x-1)+6)*(6*y-z)")
+local d = parse("(x+1)*(x+2)*(x+3)")
+
+local e = parse("x*y*z+x^2")
+local f = parse("x + 1/x^2")
+local g = parse("e^x - e^x*x^2")
+
+starttest("expression expansion")
+testeq(a:expand(), parse("((x * y) + (x * z))"), a)
+testeq(b:expand(), parse("(x + (x * z))"), b)
+testeq(c:expand(), parse("((30 * y) + (6 * x * y) + (36 * (x ^ 2) * y) + (-5 * z) + (-1 * x * z) + (-6 * (x ^ 2) * z))"), c)
+testeq(d:expand(), parse("(6 + (11 * x) + (6 * (x ^ 2)) + (x ^ 3))"))
+endtest()
+
+starttest("expression factoring beyond monovariate polynomials")
+testeq(e:factor(), parse("(x * (x + (y * z)))"))
+testeq(f:factor(), parse("((x ^ -2) * (1 + x) * (1 + (-1 * x) + (x ^ 2)))"))
+testeq(g:factor(), parse("((e ^ x) * (1 + x) * (1 + (-1 * x)))"))
+
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/expressions/substitute.lua b/macros/luatex/latex/luacas/tex/test/expressions/substitute.lua
new file mode 100644
index 0000000000..4d1f3496bf
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/expressions/substitute.lua
@@ -0,0 +1,10 @@
+local a = parse("3*(x+1)^1/2-6*y+3*z^2")
+local b = parse("sin(e^x - 1) + e^x")
+
+starttest("substitution")
+testeq(a:substitute({[parse("x")] = Integer(3),
+ [parse("y")] = Integer(-1),
+ [parse("z")] = Integer(4)/Integer(3)}):autosimplify(), parse("52/3"))
+
+testeq(b:substitute({[parse("e^x")] = parse("x^e")}), parse("((x ^ e) + sin((-1 + (x ^ e))))"))
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/helper.lua b/macros/luatex/latex/luacas/tex/test/helper.lua
new file mode 100644
index 0000000000..b550967da9
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/helper.lua
@@ -0,0 +1,322 @@
+-- helper functions
+
+
+ function whatis(a)
+ if a == nil then
+ return nil
+ end
+ if a:type() == SymbolExpression then
+ return "Sym"
+ end
+ if a:type() == BinaryOperation then
+ return "BinOp"
+ end
+ if a:type() == FunctionExpression then
+ return "FncExp"
+ end
+ if a:type() == TrigExpression then
+ return "TrigExp"
+ end
+ if a:type() == Integer then
+ return "Int"
+ end
+ if a:type() == Rational then
+ return "Ratl"
+ end
+ if a:type() == DerivativeExpression then
+ return "DervExp"
+ end
+ if a:type() == DiffExpression then
+ return "DiffExp"
+ end
+ if a:type() == IntegralExpression then
+ return "Intgrl"
+ end
+ if a:type() == SqrtExpression then
+ return "Sqrt"
+ end
+ if a:type() == PolynomialRing then
+ return "Poly"
+ end
+ if a:type() == AbsExpression then
+ return "ABS"
+ end
+ if a:type() == Logarithm then
+ return "LOG"
+ end
+ if a:type() == RootExpression then
+ return "RootOf"
+ end
+ if a:type() == Equation then
+ return "="
+ end
+ return "No Clue"
+end
+
+function longwhatis(a)
+ if a == nil then
+ return nil
+ end
+ if a:type() == SymbolExpression then
+ return "SymbolExpression"
+ end
+ if a:type() == BinaryOperation then
+ return "BinaryOperation"
+ end
+ if a:type() == FunctionExpression then
+ return "FunctionExpression"
+ end
+ if a:type() == TrigExpression then
+ return "TrigExpression"
+ end
+ if a:type() == Integer then
+ return "Integer"
+ end
+ if a:type() == Rational then
+ return "Rational"
+ end
+ if a:type() == DerivativeExpression then
+ return "DerivativeExpression"
+ end
+ if a:type() == DiffExpression then
+ return "DiffExpression"
+ end
+ if a:type() == IntegralExpression then
+ return "IntegralExpression"
+ end
+ if a:type() == SqrtExpression then
+ return "SqrtExpression"
+ end
+ if a:type() == PolynomialRing then
+ return "PolynomialRing"
+ end
+ if a:type() == AbsExpression then
+ return "AbsExpression"
+ end
+ if a:type() == Logarithm then
+ return "Logarithm"
+ end
+ if a:type() == RootExpression then
+ return "RootExpression"
+ end
+ if a:type() == Equation then
+ return "Equation"
+ end
+ return "No Clue"
+end
+
+function whatring(a)
+ if a:getring() == Rational.makering() then
+ return "Rational"
+ end
+ if a:getring() == PolynomialRing.makering() then
+ return "PolynomialRing"
+ end
+ if a:getring() == Integer.makering() then
+ return "Integer"
+ end
+ if a:getring() == IntegerModN.makering() then
+ return "IntegerModN"
+ end
+ return "No Clue"
+end
+
+function nameof(sym)
+ if sym == nil then
+ return nil
+ end
+ if sym:type() == BinaryOperation then
+ local binops = {BinaryOperation.ADD,
+ BinaryOperation.MUL,
+ BinaryOperation.SUB,
+ BinaryOperation.DIV,
+ BinaryOperation.POW,
+ BinaryOperation.IDIV,
+ BinaryOperation.MOD}
+ local obslab = {"ADD",
+ "MUL",
+ "SUB",
+ "DIV",
+ "POW",
+ "IDIV",
+ "MOD"}
+ for i,j in pairs(binops) do
+ if sym.operation == j then
+ return obslab[i]
+ end
+ end
+ end
+ if sym:type() == FunctionExpression or sym:type() == TrigExpression then
+ return tostring(sym.name)
+ end
+ if sym:type() == SymbolExpression or sym:type() == Integer then
+ return tostring(sym)
+ end
+ if sym:type() == Rational then
+ return tostring(sym.numerator).."/"..tostring(sym.denominator)
+ end
+ if sym:type() == DerivativeExpression then
+ return "DD"
+ end
+ if sym:type() == DiffExpression then
+ return "diff"
+ end
+ if sym:type() == IntegralExpression then
+ return "$\\mathtt{\\int}$"
+ end
+ if sym:type() == SqrtExpression then
+ return "$\\mathtt{\\sqrt{\\phantom{x}}}$"
+ end
+ if sym:type() == PolynomialRing then
+ return "Poly"
+ end
+ if sym:type() == AbsExpression then
+ return "abs"
+ end
+ if sym:type() == Logarithm then
+ return "log"
+ end
+ if sym:type() == RootExpression then
+ return "RootOf"
+ end
+ if sym:type() == Equation then
+ return "$\\mathtt{=}$"
+ end
+ return "No Clue"
+end
+
+function Expression:getfullsubexpressionsrec()
+ local result = {}
+ for _, expression in ipairs(self:subexpressions()) do
+ result[#result+1] = expression
+ result = JoinArrays(result, expression:getfullsubexpressionsrec())
+ end
+ return result
+end
+
+function Expression:gettheshrub()
+ local string = ""
+ for index, expression in ipairs(self:subexpressions()) do
+ string = string.."child {node [label=-90:{expr["..tostring(index).."]}] {$\\mathtt{"..expression:tolatex().."}$}}"
+ end
+ return string
+end
+
+function Expression:getthetree()
+ local string = ""
+ for _, expression in ipairs(self:subexpressions()) do
+ if expression:isatomic() then
+ string = string.."child {node{"..nameof(expression).."}}"
+ else
+ string = string.."child {node{"..nameof(expression).."}"..expression:getthetree().."}"
+ end
+ end
+ return string
+end
+
+function Expression:gettheforest()
+ local string = ""
+ for _, expression in ipairs(self:subexpressions()) do
+ if expression:isatomic() then
+ string = string.." [ "..nameof(expression).." ] "
+ else
+ string = string.." [ "..nameof(expression)..expression:gettheforest().." ] "
+ end
+ end
+ return string
+end
+
+function Expression:getthefancyshrub()
+ local string = ""
+ if self:type() == DiffExpression then
+ for _, expression in ipairs(self:subexpressions()) do
+ string = string.." [ $\\mathtt{"..expression:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.expression};} ] "
+ end
+ string = string.." [ $\\mathtt{\\{"
+ for _,symbol in ipairs(self.symbols) do
+ if next(self.symbols,_) == nil then
+ string = string .. symbol:tolatex().."\\}}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.symbols};} ] "
+ else
+ string = string .. symbol:tolatex() .. ","
+ end
+ end
+ return string
+ end
+ if self:type() == IntegralExpression then
+ string = string .. " [ $\\mathtt{"..self.expression:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.expression};} ] "
+ string = string .. "[ $\\mathtt{"..self.symbol:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.symbol};} ]"
+ if self:isdefinite() then
+ string = string .. "[ $\\mathtt{"..self.lower:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.lower};} ] "
+ string = string .. "[ $\\mathtt{"..self.upper:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.upper};} ] "
+ return string
+ end
+ return string
+ end
+ if self:type() == PolynomialRing then
+ string = string .. " [ $\\mathtt{\\{"
+ for index=0, self.degree:asnumber() do
+ string = string .. tostring(self.coefficients[index])
+ if index < self.degree:asnumber() then
+ string = string .. ","
+ end
+ end
+ string = string .. "\\} }$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.coefficients}; \\node[anchor=south west, font=\\ttfamily\\footnotesize,gray] at (.north west) {.ring "..whatring(self).."};} ]"
+ string = string .. " [ $\\mathtt{"..self.symbol.. "}$, tikz+={\\node[anchor=north, font=\\ttfamily\\footnotesize,gray] at (.south) {.symbol};} ]"
+ return string
+ end
+ if self:type() == SqrtExpression then
+ string = string .. " [ $\\mathtt{"..self.expression:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.expression};} ]"
+ string = string .. "[ $\\mathtt{"..self.root:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.root};} ]"
+ return string
+ end
+ if self:type() == TrigExpression then
+ string = string .. " [ $\\mathtt{"..self.expression:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.expression};} ]"
+ return string
+ end
+ if self:type() == AbsExpression then
+ string = string .. " [ $\\mathtt{"..self.expression:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.expression};} ] "
+ return string
+ end
+ if self:type() == Logarithm then
+ string = string .. " [$\\mathtt{" ..self.expression:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.expression};} ]"
+ string = string .. " [$\\mathtt{" ..self.base:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.base};} ]"
+ return string
+ end
+ if self:type() == RootExpression then
+ string = string .. "[$\\mathtt{" ..self.expression:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.expression};} ]"
+ return string
+ end
+ if self:type() == FunctionExpression then
+ local string1 = ''
+ local string2 = ''
+ local string3 = ''
+ for index=1, #self.variables do
+ string1 = string1 .. tostring(self.expressions[index])
+ if index < #self.variables then
+ string1 = string1 .. ","
+ end
+ string2 = string2 .. tostring(self.variables[index])
+ if index < #self.variables then
+ string2 = string2 .. ","
+ end
+ string3 = string3 .. tostring(self.derivatives[index])
+ if index < #self.variables then
+ string3 = string3 .. ","
+ end
+ end
+ string = string .. "[$\\mathtt{ \\{" .. string1 .. "\\}}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.expressions};} ]"
+ string = string .. "[$\\mathtt{ \\{" .. string2 .. "\\}}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.variables};} ]"
+ string = string .. "[$\\mathtt{ \\{" .. string3 .. "\\}}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.derivatives};} ]"
+ return string
+ end
+ if self:type() == Equation then
+ string = string .. " [$\\mathtt{" ..self.lhs:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.lhs};} ]"
+ string = string .. " [$\\mathtt{" ..self.rhs:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.rhs};} ]"
+ return string
+ end
+ for index, expression in ipairs(self:subexpressions()) do
+ string = string.." [ $\\mathtt{"..expression:tolatex().."}$, tikz+={\\node[anchor=north,font=\\ttfamily\\footnotesize,gray] at (.south) {.expression["..index.."]};} ] "
+ end
+ return string
+end
+
diff --git a/macros/luatex/latex/luacas/tex/test/main.lua b/macros/luatex/latex/luacas/tex/test/main.lua
new file mode 100644
index 0000000000..638f7734b5
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/main.lua
@@ -0,0 +1,154 @@
+---@diagnostic disable: lowercase-global
+-- Runs test code from test files.
+
+require("calculus._init")
+require("_lib.pepperfish")
+
+-- 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"}
+
+local function parser(s)
+ if string.find(s, "[0-9]+") then
+ return "Integer(\"" .. s .. "\")"
+ end
+
+ if s.find(s, "[%^%\\%[%]]") then
+ return string.gsub(s, "[^%^%\\%[%]]+", parser)
+ end
+
+ for string, replace in pairs(constants) do
+ if s == string then
+ return replace
+ end
+ end
+
+ return "SymbolExpression(\"" .. s .. "\")"
+end
+
+function parse(input)
+ local parsed = string.gsub(input, "[0-9]+", parser)
+ parsed = string.gsub(parsed, "[A-z']+", parser)
+ local exe, err = load("return " .. parsed)
+ if exe then
+ return exe():autosimplify()
+ else
+ print(err)
+ end
+end
+
+function dparse(input)
+ local parsed = string.gsub(input, "[0-9]+", parser)
+ parsed = string.gsub(parsed, "[A-z']+", parser)
+ local exe, err = load("return " .. parsed)
+ if exe then
+ return exe()
+ else
+ print(err)
+ end
+end
+
+-- Stuff required for test code.
+local tests
+local failures
+local totaltests = 0
+local totalfailures = 0
+function starttest(name)
+ print("Testing " .. name .. "...")
+ print()
+ tests = 0
+ failures = 0
+end
+
+-- Tests two objects for equality, irrespective of order. If the object is a table or expression, the objects may be sorted to ensure the correct order.
+function testeq(actual, expected, initial, sort)
+ if sort and type(actual) == "table" and not actual.type then
+ table.sort(actual, function (a, b)
+ return a:order(b)
+ end)
+ elseif sort and type(actual) == "table" and actual.type and actual:type() == BinaryOperation and actual:iscommutative() then
+ table.sort(actual.expressions, function (a, b)
+ return a:order(b)
+ end)
+ end
+
+ if initial then
+ if ToStringArray(expected) == ToStringArray(actual) then
+ print(ToStringArray(initial) .. " -> " .. ToStringArray(actual))
+ else
+ print(ToStringArray(initial) .. " -> " .. ToStringArray(actual) .. " (Expected: " .. ToStringArray(expected) .. ")")
+ failures = failures + 1
+ end
+ else
+ if ToStringArray(expected) == ToStringArray(actual) then
+ print("Result: " .. ToStringArray(actual))
+ else
+ print("Result: ".. ToStringArray(actual) .. " (Expected: " .. ToStringArray(expected) .. ")")
+ failures = failures + 1
+ end
+ end
+ tests = tests + 1
+end
+
+-- Tests whether converting an element to a different ring produces the expected object in the expected ring
+function testringconvert(expression, toring, expected, expectedring)
+ testeq(expression:inring(toring), expected, expression)
+ testeq(expression:inring(toring):getring(), expectedring)
+end
+
+function endtest()
+ print()
+ print("Finished test without errors.")
+ print()
+ totaltests = totaltests + tests
+ totalfailures = totalfailures + failures
+ if failures == 0 then
+ print("Performed " .. tests .. " tests, all of which passed!")
+ else
+ print("Performed tests, " .. failures .. "/" .. tests .. " failed.")
+ end
+ print("=====================================================================================================================")
+end
+
+function endall()
+ if totalfailures == 0 then
+ print("Performed " .. totaltests .. " tests in total, all of which passed!")
+ else
+ print("Performed tests, " .. totalfailures .. "/" .. totaltests .. " failed.")
+ end
+end
+
+
+-- TODO: Add profiling and error catching options.
+-- Comment out these lines to only run certain test code.
+
+-- profiler = newProfiler()
+-- profiler:start()
+
+require("test.calculus.derivatives")
+require("test.calculus.integrals")
+
+require("test.expressions.autosimplify")
+require("test.expressions.collect")
+require("test.expressions.equations")
+require("test.expressions.simplify")
+require("test.expressions.functions")
+require("test.expressions.logarithms")
+-- require("test.expressions.rationalexponent")
+require("test.expressions.substitute")
+
+require("test.polynomials.polynomial")
+require("test.polynomials.partialfractions")
+require("test.polynomials.polynomialmod")
+require("test.polynomials.roots")
+
+require("test.rings.conversion")
+require("test.rings.modulararithmetic")
+require("test.rings.number")
+
+endall()
+
+-- profiler:stop()
+
+-- local outfile = io.open( "profile.txt", "w+" )
+-- profiler:report( outfile )
+-- outfile:close() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/parser.lua b/macros/luatex/latex/luacas/tex/test/parser.lua
new file mode 100644
index 0000000000..6cb22a1bdb
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/parser.lua
@@ -0,0 +1,323 @@
+-- Rudimentary parser for making the CAS easier to use. Essentially just wraps SymbolExpression() around symbols and Integer() around integers.
+
+
+
+require("calculus._init")
+
+-- Splits a string on a seperator.
+function split(str, sep)
+ local t={}
+ for match in string.gmatch(str, "([^".. sep .."]+)") do
+ t[#t+1] = match
+ end
+ return t
+end
+
+-- Displays an expression. For use in the parser.
+function disp(expression, inline, simple)
+ if type(expression) ~= "table" then
+ tex.print(tostring(expression))
+ elseif expression.autosimplify then
+ if inline then
+ if simple then
+ tex.print('$' .. expression:autosimplify():tolatex() .. '$')
+ else
+ tex.print('$' .. expression:tolatex() .. '$')
+ end
+ else
+ if simple then
+ tex.print('\\[' .. expression:autosimplify():tolatex() .. '\\]')
+ else
+ tex.print('\\[' .. expression:tolatex() .. '\\]')
+ end
+ end
+ else
+ tex.print(tostring(expression))
+ end
+end
+
+-- Displays an expression. For use in the parser.
+function displua(expression)
+ if type(expression) ~= "table" then
+ print(tostring(expression))
+ elseif expression.autosimplify then
+ print(expression:autosimplify():tolatex())
+ else
+ print(tostring(expression))
+ end
+end
+
+function vars(...)
+ for _, string in ipairs(table.pack(...)) do
+ if string ~= "_" then
+ _G[string] = SymbolExpression(string)
+ end
+ end
+end
+
+function clearvars()
+ for index, value in pairs(_G) do
+ if type(value) == "table" and value.type and value:type() == SymbolExpression then
+ _G[index] = nil
+ end
+ end
+end
+
+function range(a, b, step)
+ if not b then
+ b = a
+ a = Integer.one()
+ end
+ step = step or Integer.one()
+ local f =
+ step > Integer.zero() and
+ function(_, lastvalue)
+ local nextvalue = lastvalue + step
+ if nextvalue <= b then return nextvalue end
+ end or
+ step < Integer.zero() and
+ function(_, lastvalue)
+ local nextvalue = lastvalue + step
+ if nextvalue >= b then return nextvalue end
+ end or
+ function(_, lastvalue) return lastvalue end
+ return f, nil, a - step
+ end
+
+function factor(exp,squarefrei)
+ if exp:type() == Integer then
+ return exp:primefactorization()
+ end
+ if exp:type() == PolynomialRing then
+ if not squarefrei then
+ return exp:factor()
+ else
+ if exp.ring == Integer.getring() or Rational.getring() then
+ return exp:squarefreefactorization()
+ end
+ if exp.ring == IntegerModN.getring() then
+ return exp:modularsquarefreefactorization()
+ end
+ return exp:factor()
+ end
+ end
+ return exp:autosimplify():factor()
+end
+
+function expand(exp)
+ return exp:autosimplify():expand()
+end
+
+function simplify(exp)
+ return exp:simplify()
+end
+
+function exp(x)
+ return e^x
+end
+
+function substitute(tbl,expr)
+ return expr:substitute(tbl)
+end
+
+function roots(expression)
+ poly,ispoly = topoly(expression)
+ if ispoly then
+ return poly:roots()
+ end
+ return RootExpression(expression)
+end
+
+function combine(expr)
+ return expr:combine()
+end
+
+function Mod(f,n)
+ if f:type() == Integer then
+ return IntegerModN(f,n)
+ end
+ if f:type() == PolynomialRing and f.ring == Integer.getring() then
+ local coeffs = {}
+ for i=0,f.degree:asnumber() do
+ coeffs[i] = IntegerModN(f.coefficients[i],n)
+ end
+ return PolynomialRing(coeffs,f.symbol,f.degree)
+ end
+end
+
+function Poly(coefficients,symbol,degree)
+ local variable = symbol or 'x'
+ return PolynomialRing:new(coefficients,variable,degree)
+end
+
+function topoly(a)
+ a = a:expand():autosimplify()
+ return a:topolynomial()
+end
+
+function gcd(a,b)
+ if a:type() == Integer and b:type() == Integer then
+ return Integer.gcd(a,b)
+ end
+ if a:type() == PolynomialRing and b:type() == PolynomialRing then
+ return PolynomialRing.gcd(a,b)
+ end
+end
+
+function gcdext(a,b)
+ if a:type() == Integer and b:type() == Integer then
+ return Integer.extendedgcd(a,b)
+ end
+ A, ATF = topoly(a)
+ B, BTF = topoly(b)
+ if ATF and BTF then
+ return PolynomialRing.extendedgcd(A,B)
+ end
+ return nil,nil,nil
+end
+
+function parfrac(f,g,ffactor)
+ local f,check1 = topoly(f)
+ local g,check2 = topoly(g)
+ if check1 and check2 then
+ if f.degree >= g.degree then
+ local q,r
+ q,r = f:divremainder(g)
+ return q + PolynomialRing.partialfractions(r,g,ffactor)
+ else
+ return PolynomialRing.partialfractions(f,g,ffactor)
+ end
+ else
+ return f/g
+ end
+end
+
+function factorial(a)
+ return FactorialExpression(a)
+end
+
+-- Constants for the CAS. We may not want these in Lua itself, but in the latex end the user probably expects them.
+e = E
+pi = PI
+-- sqrt = SQRT
+ln = LN
+log = LOG
+int = INT
+sin = SIN
+cos = COS
+tan = TAN
+csc = CSC
+sec = SEC
+cot = COT
+arcsin = ARCSIN
+arccos = ARCCOS
+arctan = ARCTAN
+arccsc = ARCCSC
+arcsec = ARCSEC
+arccot = ARCCOT
+abs = ABS
+
+function ZTable(t)
+ t = t or {}
+ return setmetatable(t, JoinTables(getmetatable(t),
+ {__index = function (t, k)
+ if type(k) == "table" and k.type and k:type() == Integer then
+ return rawget(t, k:asnumber())
+ else
+ return rawget(t, k)
+ end
+ end,
+ __newindex = function (t, k, v)
+ if type(k) == "table" and k.type and k:type() == Integer then
+ rawset(t, k:asnumber(), v)
+ else
+ rawset(t, k, v)
+ end
+ end}))
+end
+
+function RR(n)
+ if type(n) == "number" then
+ return n
+ end
+
+ if type(n) == "string" then
+ return tonumber(n)
+ end
+
+ if type(n) == "table" and n.asnumber then
+ return n:asnumber()
+ end
+
+ error("Could not convert to a real number.")
+end
+
+function ZZ(n)
+ if type(n) == "table" and n.type and n:type() == Rational then
+ return n.numerator // n.denominator
+ end
+ return Integer(n)
+end
+
+function QQ(n)
+ if type(n) == "table" then
+ return n
+ end
+
+ if type(n) == "number" then
+ n = tostring(n)
+ end
+
+ if type(n) == "string" then
+ local parts = split(n, "%.")
+ if #parts == 1 then
+ return Integer(parts[1])
+ else
+ return Integer(parts[1])..Integer(parts[2])
+ end
+ end
+
+ error("Could not convert to a rational number.")
+end
+
+--- Parses raw input into Lua code and executes it.
+--- @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)
+ -- 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
+ end
+
+ if string.match(string.sub(s, 1, 1), "[0-9]") then
+ return "Integer('" .. s .. "')"
+ end
+
+ return string.sub(s, 1, 1) .. "Integer('" .. string.sub(s, 2, #s) .. "')"
+ end)
+
+ --------------------------
+ -- HERE COMES THE JANK. --
+ --------------------------
+
+ -- 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]
+ end)
+ str = string.gsub(str, ".?%.Integer%('[0-9]+'%)", function (s)
+ if string.sub(s, 1, 2) == ".." then
+ return
+ end
+ return string.sub(s, 1, 1) .. "Integer('0')." .. string.sub(s, 2, #s)
+ end)
+
+ local exe, err = load(str .. "\n return true")
+ if exe then
+ exe()
+ else
+ print(err)
+ end
+end \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/polynomials/partialfractions.lua b/macros/luatex/latex/luacas/tex/test/polynomials/partialfractions.lua
new file mode 100644
index 0000000000..280d4d56f3
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/polynomials/partialfractions.lua
@@ -0,0 +1,12 @@
+local g1 = parse("x^3+4*x^2-x-2"):topolynomial()
+local f1 = parse("x^4-x^2"):topolynomial()
+
+local g2 = parse("2*x^6-4*x^5+5*x^4-3*x^3+x^2+3*x"):topolynomial()
+local f2 = parse("x^7-3*x^6+5*x^5-7*x^4+7*x^3-5*x^2+3*x-1"):topolynomial()
+
+starttest("partial fraction decomposition")
+
+testeq(PolynomialRing.partialfractions(g1, f1):autosimplify(), parse("((2 * (x ^ -2)) + (x ^ -1) + ((-1 + x) ^ -1) + (-1 * ((1 + x) ^ -1)))"))
+testeq(PolynomialRing.partialfractions(g2, f2):autosimplify(), parse("(((-1 + x) ^ -3) + ((-1 + x) ^ -1) + ((1 + (x ^ 2)) ^ -2) + ((1 + x) * ((1 + (x ^ 2)) ^ -1)))"))
+
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/polynomials/polynomial.lua b/macros/luatex/latex/luacas/tex/test/polynomials/polynomial.lua
new file mode 100644
index 0000000000..545538c764
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/polynomials/polynomial.lua
@@ -0,0 +1,153 @@
+
+local a = PolynomialRing({
+ Integer(1),
+ Integer(2),
+ Integer(3),
+ Integer(4),
+ Integer(5)
+}, "x")
+
+local b = PolynomialRing({
+ Integer(1) / Integer(3),
+ Integer(1) / Integer(12),
+ Integer(6) / Integer(3),
+}, "x")
+
+local c = PolynomialRing({
+ Integer(12),
+ Integer(4)
+}, "x")
+
+local h = PolynomialRing({Integer(2), Integer(3), Integer(1)}, "x")
+local i = PolynomialRing({Integer(8), Integer(20), Integer(18), Integer(7), Integer(1)}, "x")
+local j = PolynomialRing({Integer(108), Integer(324), Integer(387), Integer(238), Integer(80), Integer(14), Integer(1)}, "x")
+local k = PolynomialRing({Integer(30), Integer(11), Integer(1)}, "x")
+local l = PolynomialRing({Integer(6), Integer(11), Integer(6), Integer(1)}, "z")
+local m = PolynomialRing({Integer(1), Integer(10), Integer(45), Integer(120), Integer(210), Integer(252), Integer(210), Integer(120), Integer(45), Integer(10), Integer(1)}, "x")
+local n = PolynomialRing({Integer(24), Integer(50), Integer(35), Integer(10), Integer(1), Integer(0), Integer(24), Integer(50), Integer(35), Integer(10), Integer(1)}, "x")
+local o = PolynomialRing({Integer(24), Integer(50), Integer(59), Integer(60), Integer(36), Integer(10), Integer(1)}, "x")
+local p = PolynomialRing({Integer(-110592), Integer(59904), Integer(-5760), Integer(720), Integer(-48), Integer(1)}, "x")
+
+local d = PolynomialRing({Integer(21), Integer(10), Integer(1)}, "x")
+local e = PolynomialRing({Integer(-6), Integer(1), Integer(1)}, "x")
+
+local f = PolynomialRing({Integer(-1), Integer(-2), Integer(15), Integer(36)}, "x")
+local g = PolynomialRing({Integer(1), Integer(7), Integer(15), Integer(9)}, "x")
+
+local q = PolynomialRing({Integer(3), Integer(-9), Integer(27), Integer(-36), Integer(36)}, "z")
+local r = PolynomialRing({Integer(0), Integer(0), Integer(0), Integer(0), Integer(0), Integer(0), Integer(4)}, "x");
+local s = PolynomialRing({Integer(1), Integer(0), Integer(-4), Integer(0), Integer(1)}, "x")
+
+local x = Integer(3)
+local y = Integer(-1) / Integer(6)
+
+local multia = PolynomialRing({Integer(4),
+ Integer(0),
+ PolynomialRing({Integer(0), Integer(0), Integer(-6)}, "y"),
+ PolynomialRing({Integer(1), Integer(3)}, "y")}, "x")
+local multib = PolynomialRing({PolynomialRing({Integer(0), Integer(6)}, "y"),
+ Integer(0),
+ PolynomialRing({Integer(-4), Integer(12)}, "y")}, "x")
+
+starttest("polynomial construction")
+testeq(a, "5x^4+4x^3+3x^2+2x^1+1x^0")
+testeq(a.degree, 4)
+testeq(b, "2x^2+1/12x^1+1/3x^0")
+testeq(b.degree, 2)
+testeq(multia, "(3y^1+1y^0)x^3+(-6y^2+0y^1+0y^0)x^2+(0)x^1+(4)x^0")
+testeq(multia.degree, 3)
+endtest()
+
+starttest("polynomial-expression conversion")
+testeq(a:tocompoundexpression():autosimplify():topolynomial(), a)
+testeq(b:tocompoundexpression():autosimplify():topolynomial(), b)
+testeq(c:tocompoundexpression():autosimplify():topolynomial(), c)
+endtest()
+
+starttest("polynomial arithmetic")
+testeq(a + a, "10x^4+8x^3+6x^2+4x^1+2x^0")
+testeq(a + b, "5x^4+4x^3+5x^2+25/12x^1+4/3x^0")
+testeq(b + a, "5x^4+4x^3+5x^2+25/12x^1+4/3x^0")
+testeq(a - a, "0x^0")
+testeq(a - b, "5x^4+4x^3+1x^2+23/12x^1+2/3x^0")
+testeq(b:multiplyDegree(4), "2x^6+1/12x^5+1/3x^4+0x^3+0x^2+0x^1+0x^0")
+testeq(a:multiplyDegree(12), "5x^16+4x^15+3x^14+2x^13+1x^12+0x^11+0x^10+0x^9+0x^8+0x^7+0x^6+0x^5+0x^4+0x^3+0x^2+0x^1+0x^0")
+testeq(c * c, "16x^2+96x^1+144x^0")
+testeq(a * c, "20x^5+76x^4+60x^3+44x^2+28x^1+12x^0")
+testeq(c * a, "20x^5+76x^4+60x^3+44x^2+28x^1+12x^0")
+testeq(b * c, "8x^3+73/3x^2+7/3x^1+4x^0")
+local qq, rr = a:divremainder(c)
+testeq(qq, "5/4x^3+-11/4x^2+9x^1+-53/2x^0")
+testeq(rr, "319x^0")
+qq, rr = a:divremainder(b)
+testeq(qq, "5/2x^2+91/48x^1+1157/1152x^0")
+testeq(rr, "17755/13824x^1+2299/3456x^0")
+endtest()
+
+starttest("polynomial pseudodivision")
+local pq, pr = a:pseudodivide(c)
+testeq(pq, "320x^3+-704x^2+2304x^1+-6784x^0")
+testeq(pr, "81664x^0")
+
+pq, pr = multia:pseudodivide(multib)
+testeq(pq, "(36y^2+0y^1+-4y^0)x^1+(-72y^3+24y^2+0y^1+0y^0)x^0")
+testeq(pr, "(-216y^3+0y^2+24y^1+0y^0)x^1+(432y^4+-144y^3+576y^2+-384y^1+64y^0)x^0")
+endtest()
+
+
+starttest("combined polynomial/coefficient operations")
+testeq(a + x, "5x^4+4x^3+3x^2+2x^1+4x^0")
+testeq(x + a, "5x^4+4x^3+3x^2+2x^1+4x^0")
+testeq(b - y, "2x^2+1/12x^1+1/2x^0")
+testeq(a * x, "15x^4+12x^3+9x^2+6x^1+3x^0")
+testeq(x * a, "15x^4+12x^3+9x^2+6x^1+3x^0")
+endtest()
+
+starttest("polynomial formal derivatives")
+testeq(a:derivative(), "20x^3+12x^2+6x^1+2x^0")
+testeq(b:derivative(), "4x^1+1/12x^0")
+testeq(c:derivative():derivative(), "0x^0")
+endtest()
+
+
+starttest("polynomial gcd...")
+testeq(PolynomialRing.gcd(d, e), "1x^1+3x^0")
+testeq(PolynomialRing.gcd(b, c), "1x^0")
+testeq(PolynomialRing.gcd(f, g), "1x^2+2/3x^1+1/9x^0")
+endtest()
+
+starttest("square-free factorization")
+testeq(h:squarefreefactorization():autosimplify(), parse("(2 + (3 * x) + (x ^ 2))"), h)
+testeq(i:squarefreefactorization():autosimplify(), parse("((1 + x) * ((2 + x) ^ 3))"), i)
+testeq((Integer(2)*i):squarefreefactorization():autosimplify(), parse("(((2 + x) ^ 3) * (2 + (2 * x)))"), (Integer(2)*i), true)
+testeq(j:squarefreefactorization():autosimplify(), parse("((1 + x) * ((2 + x) ^ 2) * ((3 + x) ^ 3))"), j)
+testeq(o:squarefreefactorization():autosimplify(), parse("(24 + (50 * x) + (59 * (x ^ 2)) + (60 * (x ^ 3)) + (36 * (x ^ 4)) + (10 * (x ^ 5)) + (x ^ 6))"), o)
+endtest()
+
+starttest("polynomial factorization")
+testeq(c:factor(), BinaryOperation.MULEXP({Integer(4), BinaryOperation.POWEXP({PolynomialRing({Integer(3), Integer(1)}, "x"), Integer(1)})}), c)
+testeq(h:factor():autosimplify(), parse("((1 + x) * (2 + x))"), h)
+testeq(k:factor():autosimplify(), parse("((5 + x) * (6 + x))"), k)
+testeq(j:factor():autosimplify(), parse("((1 + x) * ((2 + x) ^ 2) * ((3 + x) ^ 3))"), j)
+testeq(p:factor():autosimplify(), parse("((-24 + x) * (96 + (x ^ 2)) * (48 + (-24 * x) + (x ^ 2)))"), p)
+testeq(l:factor():autosimplify(), parse("((1 + z) * (2 + z) * (3 + z))"), l)
+testeq(m:factor():autosimplify(), parse("((1 + x) ^ 10)"), m)
+testeq(b:factor(), BinaryOperation.MULEXP({Integer(1)/Integer(12), BinaryOperation.POWEXP({PolynomialRing({Integer(4), Integer(1), Integer(24)}, "x"), Integer(1)})}), b)
+testeq(o:factor():autosimplify(), parse("((1 + x) * (2 + x) * (3 + x) * (4 + x) * (1 + (x ^ 2)))"), o)
+testeq(n:factor():autosimplify(), parse("((1 + x) * (2 + x) * (3 + x) * (4 + x) * (1 + (x ^ 2)) * (1 + (-1 * (x ^ 2)) + (x ^ 4)))"), n)
+endtest()
+
+starttest("polynomial decomposition")
+testeq(c:decompose(), "{4x^1+12x^0}", c, true)
+testeq(h:decompose(), "{1x^2+3x^1+2x^0}", h, true)
+testeq(k:decompose(), "{1x^2+11x^1+30x^0}", k, true)
+testeq(j:decompose(), "{1x^6+14x^5+80x^4+238x^3+387x^2+324x^1+108x^0}", j, true)
+testeq(l:decompose(), "{1z^3+6z^2+11z^1+6z^0}", l, true)
+testeq(m:decompose(), "{1x^5+5x^4+10x^3+10x^2+5x^1+1x^0, 1x^2+2x^1+0x^0}", m, true)
+testeq(b:decompose(), "{2x^2+1/12x^1+1/3x^0}", b, true)
+testeq(o:decompose(), "{1x^6+10x^5+36x^4+60x^3+59x^2+50x^1+24x^0}", o, true)
+testeq(n:decompose(), "{1x^10+10x^9+35x^8+50x^7+24x^6+0x^5+1x^4+10x^3+35x^2+50x^1+24x^0}", n, true)
+testeq(q:decompose(), "{36z^2+18z^1+3z^0, 1z^2+-1/2z^1+0z^0}", q, true)
+testeq(r:decompose(),"{4x^3+0x^2+0x^1+0x^0, 1x^2+0x^1+0x^0}", r, true)
+testeq(s:decompose(), "{1x^2+4x^1+1x^0, 1x^2+0x^1+-4x^0}", s, true)
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/polynomials/polynomialmod.lua b/macros/luatex/latex/luacas/tex/test/polynomials/polynomialmod.lua
new file mode 100644
index 0000000000..1906ad2d72
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/polynomials/polynomialmod.lua
@@ -0,0 +1,76 @@
+local a = PolynomialRing({IntegerModN(Integer(1), Integer(11)),
+ IntegerModN(Integer(6), Integer(11)),
+ IntegerModN(Integer(1), Integer(11)),
+ IntegerModN(Integer(9), Integer(11)),
+ IntegerModN(Integer(1), Integer(11))}, "y")
+
+local b = PolynomialRing({IntegerModN(Integer(7), Integer(11)),
+ IntegerModN(Integer(7), Integer(11)),
+ IntegerModN(Integer(6), Integer(11)),
+ IntegerModN(Integer(2), Integer(11)),
+ IntegerModN(Integer(1), Integer(11))}, "y")
+
+local q = PolynomialRing({IntegerModN(Integer(2), Integer(13)),
+ IntegerModN(Integer(6), Integer(13)),
+ IntegerModN(Integer(4), Integer(13))}, "z")
+
+local p = PolynomialRing({IntegerModN(Integer(4), Integer(13)),
+ IntegerModN(Integer(11), Integer(13)),
+ IntegerModN(Integer(1), Integer(13)),
+ IntegerModN(Integer(12), Integer(13)),
+ IntegerModN(Integer(1), Integer(13))}, "x")
+
+local r = PolynomialRing({IntegerModN(Integer(1), Integer(3)),
+ IntegerModN(Integer(0), Integer(3)),
+ IntegerModN(Integer(0), Integer(3)),
+ IntegerModN(Integer(2), Integer(3)),
+ IntegerModN(Integer(0), Integer(3)),
+ IntegerModN(Integer(0), Integer(3)),
+ IntegerModN(Integer(1), Integer(3))}, "x")
+
+local s = PolynomialRing({IntegerModN(Integer(1), Integer(5)),
+ IntegerModN(Integer(1), Integer(5)),
+ IntegerModN(Integer(1), Integer(5)),
+ IntegerModN(Integer(1), Integer(5)),
+ IntegerModN(Integer(1), Integer(5))}, "x")
+
+local t = PolynomialRing({IntegerModN(Integer(1), Integer(7))}, "x"):multiplyDegree(7) - PolynomialRing({IntegerModN(Integer(1), Integer(7))}, "x"):multiplyDegree(1)
+
+local u = PolynomialRing({IntegerModN(Integer(1), Integer(13)),
+ IntegerModN(Integer(5), Integer(13)),
+ IntegerModN(Integer(6), Integer(13)),
+ IntegerModN(Integer(5), Integer(13)),
+ IntegerModN(Integer(1), Integer(13))}, "x")
+
+local v = PolynomialRing({IntegerModN(Integer(24), Integer(7)),
+ IntegerModN(Integer(50), Integer(7)),
+ IntegerModN(Integer(59), Integer(7)),
+ IntegerModN(Integer(60), Integer(7)),
+ IntegerModN(Integer(36), Integer(7)),
+ IntegerModN(Integer(10), Integer(7)),
+ IntegerModN(Integer(1), Integer(7))}, "z")
+
+starttest("modular polynomial operations")
+testeq(q*q, "3z^4+9z^3+0z^2+11z^1+4z^0")
+testeq(PolynomialRing.gcd(a, b), "1y^0")
+local Q, R, S = PolynomialRing.extendedgcd(a, b)
+testeq(Q, "1y^0")
+testeq(R, "4y^3+5y^2+1y^1+3y^0")
+testeq(S, "7y^3+0y^2+7y^1+6y^0")
+endtest()
+
+starttest("modular square free factoring")
+testeq(p:squarefreefactorization(), Integer(1) * BinaryOperation.POWEXP({(PolynomialRing({Integer(2), Integer(6), Integer(1)}, SymbolExpression("x"))), Integer(2)}))
+testeq(r:squarefreefactorization(), Integer(1) * BinaryOperation.POWEXP({(PolynomialRing({Integer(1), Integer(0), Integer(0), Integer(1)}, SymbolExpression("x"))), Integer(2)}))
+testeq(q:squarefreefactorization(), Integer(4) * BinaryOperation.POWEXP({(PolynomialRing({Integer(7), Integer(8), Integer(1)}, SymbolExpression("z"))), Integer(1)}))
+testeq(s:squarefreefactorization(), Integer(1) * BinaryOperation.POWEXP({(PolynomialRing({Integer(4), Integer(1)}, SymbolExpression("x"))), Integer(4)}))
+endtest()
+
+starttest("modular polynomial factoring")
+testeq(q:factor(), BinaryOperation.MULEXP({Integer(4), BinaryOperation.POWEXP({PolynomialRing({Integer(7), Integer(1)}, SymbolExpression("z")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(1), Integer(1)}, SymbolExpression("z")), Integer(1)})}), q)
+testeq(p:factor(), Integer(1) * BinaryOperation.POWEXP({PolynomialRing({Integer(2), Integer(6), Integer(1)}, SymbolExpression("x")), Integer(2)}), p)
+testeq(r:factor(), Integer(1) * BinaryOperation.POWEXP({(PolynomialRing({Integer(1), Integer(0), Integer(0), Integer(1)}, SymbolExpression("x"))), Integer(2)}), r)
+testeq(t:factor(), BinaryOperation.MULEXP({Integer(1), BinaryOperation.POWEXP({PolynomialRing({Integer(0), Integer(1)}, SymbolExpression("x")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(6), Integer(1)}, SymbolExpression("x")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(5), Integer(1)}, SymbolExpression("x")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(4), Integer(1)}, SymbolExpression("x")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(3), Integer(1)}, SymbolExpression("x")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(2), Integer(1)}, SymbolExpression("x")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(1), Integer(1)}, SymbolExpression("x")), Integer(1)})}), t)
+testeq(u:factor(), BinaryOperation.MULEXP({Integer(1), BinaryOperation.POWEXP({PolynomialRing({Integer(11), Integer(1)}, SymbolExpression("x")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(10), Integer(1)}, SymbolExpression("x")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(6), Integer(1)}, SymbolExpression("x")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(4), Integer(1)}, SymbolExpression("x")), Integer(1)})}), u)
+testeq(v:factor(), BinaryOperation.MULEXP({Integer(1), BinaryOperation.POWEXP({PolynomialRing({Integer(1), Integer(1)}, SymbolExpression("z")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(2), Integer(1)}, SymbolExpression("z")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(1), Integer(0), Integer(1)}, SymbolExpression("z")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(4), Integer(1)}, SymbolExpression("z")), Integer(1)}), BinaryOperation.POWEXP({PolynomialRing({Integer(3), Integer(1)}, SymbolExpression("z")), Integer(1)})}), v)
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/polynomials/roots.lua b/macros/luatex/latex/luacas/tex/test/polynomials/roots.lua
new file mode 100644
index 0000000000..01975ab1ec
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/polynomials/roots.lua
@@ -0,0 +1,43 @@
+local a = PolynomialRing({Integer(1), Integer(2), Integer(3), Integer(4), Integer(5)}, "x")
+local b = PolynomialRing({Integer(1) / Integer(3), Integer(1) / Integer(12), Integer(6) / Integer(3)}, "x")
+local c = PolynomialRing({Integer(12), Integer(4)}, "x")
+local d = PolynomialRing({Integer(21), Integer(10), Integer(1)}, "x")
+local e = PolynomialRing({Integer(-6), Integer(1), Integer(1)}, "x")
+local f = PolynomialRing({Integer(-1), Integer(-2), Integer(15), Integer(36)}, "x")
+local g = PolynomialRing({Integer(1), Integer(7), Integer(15), Integer(9)}, "x")
+local h = PolynomialRing({Integer(2), Integer(3), Integer(1)}, "x")
+local i = PolynomialRing({Integer(8), Integer(20), Integer(18), Integer(7), Integer(1)}, "x")
+local j = PolynomialRing({Integer(108), Integer(324), Integer(387), Integer(238), Integer(80), Integer(14), Integer(1)}, "x")
+local k = PolynomialRing({Integer(30), Integer(11), Integer(1)}, "x")
+local l = PolynomialRing({Integer(6), Integer(11), Integer(6), Integer(1)}, "z")
+local m = PolynomialRing({Integer(1), Integer(10), Integer(45), Integer(120), Integer(210), Integer(252), Integer(210), Integer(120), Integer(45), Integer(10), Integer(1)}, "x")
+local n = PolynomialRing({Integer(24), Integer(50), Integer(35), Integer(10), Integer(1), Integer(0), Integer(24), Integer(50), Integer(35), Integer(10), Integer(1)}, "x")
+local o = PolynomialRing({Integer(24), Integer(50), Integer(59), Integer(60), Integer(36), Integer(10), Integer(1)}, "x")
+local p = PolynomialRing({Integer(-110592), Integer(59904), Integer(-5760), Integer(720), Integer(-48), Integer(1)}, "x")
+local q = PolynomialRing({Integer(3), Integer(-9), Integer(27), Integer(-36), Integer(36)}, "z")
+local r = PolynomialRing({Integer(0), Integer(0), Integer(0), Integer(0), Integer(0), Integer(0), Integer(4)}, "x")
+local s = PolynomialRing({Integer(1), Integer(0), Integer(-4), Integer(0), Integer(1)}, "x")
+local t = PolynomialRing({Integer(1), Integer(-1), Integer(1), Integer(1)}, "t")
+
+starttest("polynomial root-finding")
+testeq(a:roots(), "{Root Of: (5x^4+4x^3+3x^2+2x^1+1x^0)}", a, true)
+testeq(b:roots(), "{-1/48 + (-1/48 * (383 ^ (1/2)) * i), -1/48 + (1/48 * (383 ^ (1/2)) * i)}", b, true)
+testeq(c:roots(), "{-3}", c, true)
+testeq(d:roots(), "{-7, -3}", d, true)
+testeq(e:roots(), "{-3, 2}", e, true)
+testeq(f:roots(), "{-1/3, 1/4}", f, true)
+testeq(g:roots(), "{-1, -1/3}", g, true)
+testeq(h:roots(), "{-2, -1}", h, true)
+testeq(i:roots(), "{-2, -1}", i, true)
+testeq(j:roots(), "{-3, -2, -1}", j, true)
+testeq(k:roots(), "{-6, -5}", k, true)
+testeq(l:roots(), "{-3, -2, -1}", l, true)
+testeq(m:roots(), "{-1}", m, true)
+testeq(n:roots(), "{-4, -3, -2, -1, i, -1 * i, -1/2 * ((2 + (-2 * (3 ^ (1/2)) * i)) ^ (1/2)), 1/2 * ((2 + (-2 * (3 ^ (1/2)) * i)) ^ (1/2)), -1/2 * ((2 + (2 * (3 ^ (1/2)) * i)) ^ (1/2)), 1/2 * ((2 + (2 * (3 ^ (1/2)) * i)) ^ (1/2))}", n, true)
+testeq(o:roots(), "{-4, -3, -2, -1, i, -1 * i}", o, true)
+testeq(p:roots(), "{24, 12 + (-4 * (6 ^ (1/2))), 12 + (4 * (6 ^ (1/2))), -4 * (6 ^ (1/2)) * i, 4 * (6 ^ (1/2)) * i}", p, true)
+testeq(q:roots(), "{1/4 + (-1/2 * ((-3/4 + (-1/3 * (3 ^ (1/2)) * i)) ^ (1/2))), 1/4 + (1/2 * ((-3/4 + (-1/3 * (3 ^ (1/2)) * i)) ^ (1/2))), 1/4 + (-1/2 * ((-3/4 + (1/3 * (3 ^ (1/2)) * i)) ^ (1/2))), 1/4 + (1/2 * ((-3/4 + (1/3 * (3 ^ (1/2)) * i)) ^ (1/2)))}", q, true)
+testeq(r:roots(), "{0}", r, true)
+testeq(s:roots(), "{-1/2 * ((8 + (-4 * (3 ^ (1/2)))) ^ (1/2)), 1/2 * ((8 + (-4 * (3 ^ (1/2)))) ^ (1/2)), -1/2 * ((8 + (4 * (3 ^ (1/2)))) ^ (1/2)), 1/2 * ((8 + (4 * (3 ^ (1/2)))) ^ (1/2))}", s, true)
+testeq(t:roots(), "{-1/3 + (-4/3 * ((19 + (3 * (33 ^ (1/2)))) ^ (-1/3))) + (-1/3 * ((19 + (3 * (33 ^ (1/2)))) ^ (1/3))), -1/3 + (-4/3 * ((19 + (3 * (33 ^ (1/2)))) ^ (-1/3)) * ((-1/2 + (1/2 * (3 ^ (1/2)) * i)) ^ -1)) + (-1/3 * ((19 + (3 * (33 ^ (1/2)))) ^ (1/3)) * (-1/2 + (1/2 * (3 ^ (1/2)) * i))), -1/3 + (-4/3 * ((19 + (3 * (33 ^ (1/2)))) ^ (-1/3)) * ((-1/2 + (1/2 * (3 ^ (1/2)) * i)) ^ -2)) + (-1/3 * ((19 + (3 * (33 ^ (1/2)))) ^ (1/3)) * ((-1/2 + (1/2 * (3 ^ (1/2)) * i)) ^ 2))}", t, true)
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/rings/conversion.lua b/macros/luatex/latex/luacas/tex/test/rings/conversion.lua
new file mode 100644
index 0000000000..b9eb523c7a
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/rings/conversion.lua
@@ -0,0 +1,273 @@
+local a = Integer(12)
+local b = Integer(3) / Integer(2)
+local c = IntegerModN(Integer(4), Integer(7))
+local d = IntegerModN(Integer(8), Integer(14))
+local e = PolynomialRing({Integer(6), Integer(0), Integer(3)}, SymbolExpression("x"))
+local f = PolynomialRing({Integer(4)/Integer(5), Integer(12)}, SymbolExpression("x"))
+local g = PolynomialRing({
+ PolynomialRing({
+ PolynomialRing({Integer(-4), Integer(12), Integer(1)}, SymbolExpression("x")),
+ PolynomialRing({Integer(0)}, SymbolExpression("x")),
+ PolynomialRing({Integer(8)}, SymbolExpression("x"))
+ },
+ SymbolExpression("y")),
+ PolynomialRing({
+ PolynomialRing({Integer(8), Integer(7), Integer(6), Integer(5)}, SymbolExpression("x")),
+ PolynomialRing({Integer(-1)}, SymbolExpression("x")),
+ },
+ SymbolExpression("y")),
+ PolynomialRing({
+ PolynomialRing({Integer(0)}, SymbolExpression("x")),
+ PolynomialRing({Integer(2), Integer(8), Integer(1)}, SymbolExpression("x")),
+ PolynomialRing({Integer(-16), Integer(4)}, SymbolExpression("x")),
+ PolynomialRing({Integer(1)}, SymbolExpression("x"))
+ },
+ SymbolExpression("y")),
+ PolynomialRing({
+ PolynomialRing({Integer(1)}, SymbolExpression("x"))
+ },
+ SymbolExpression("y"))
+ }, SymbolExpression("z"))
+local h = PolynomialRing({
+ PolynomialRing({
+ PolynomialRing({Integer(-4), Integer(4)/Integer(5), Integer(1)}, SymbolExpression("z")),
+ PolynomialRing({Integer(0)}, SymbolExpression("z")),
+ PolynomialRing({Integer(8)}, SymbolExpression("z"))
+ },
+ SymbolExpression("y")),
+ PolynomialRing({
+ PolynomialRing({Integer(8), Integer(7), Integer(6), Integer(5)}, SymbolExpression("z")),
+ PolynomialRing({Integer(-1)}, SymbolExpression("z")),
+ },
+ SymbolExpression("y")),
+ PolynomialRing({
+ PolynomialRing({Integer(0)}, SymbolExpression("z")),
+ PolynomialRing({Integer(2), Integer(8), Integer(1)}, SymbolExpression("z")),
+ PolynomialRing({Integer(-16), Integer(1)/Integer(9)}, SymbolExpression("z")),
+ PolynomialRing({Integer(1)}, SymbolExpression("z"))
+ },
+ SymbolExpression("y")),
+ PolynomialRing({
+ PolynomialRing({Integer(1)}, SymbolExpression("z"))
+ },
+ SymbolExpression("y"))
+ }, SymbolExpression("x"))
+local i = Rational(PolynomialRing({-Integer(2), Integer(1)}, SymbolExpression("x")),
+ PolynomialRing({Integer(3), Integer(3), Integer(1)}, SymbolExpression("x")))
+local j = Rational(PolynomialRing({-Integer(2), Integer(1)}, SymbolExpression("x")),
+ PolynomialRing({Integer(3)/Integer(4), Integer(3)/Integer(8), Integer(1)}, SymbolExpression("x")))
+local k = PolynomialRing({IntegerModN(Integer(0), Integer(5)),
+ IntegerModN(Integer(1), Integer(5)),
+ IntegerModN(Integer(3), Integer(5)),
+ IntegerModN(Integer(1), Integer(5))}, SymbolExpression("x"))
+
+local l = PolynomialRing({Rational(
+ PolynomialRing({Integer(4), Integer(1)}, SymbolExpression("x")),
+ PolynomialRing({Integer(0), Integer(4)}, SymbolExpression("x"))
+ ),
+ Rational(
+ PolynomialRing({Integer(3)/Integer(2)}, SymbolExpression("x")),
+ PolynomialRing({Integer(6), Integer(1)/Integer(2), Integer(8), Integer(1)}, SymbolExpression("x"))
+ ),
+ Rational(
+ PolynomialRing({Integer(6), Integer(6), Integer(4)}, SymbolExpression("x")),
+ PolynomialRing({Integer(3), Integer(1)}, SymbolExpression("x"))
+ ),
+ Rational(
+ PolynomialRing({Integer(7)/Integer(6)}, SymbolExpression("x")),
+ PolynomialRing({Integer(2), Integer(1)}, SymbolExpression("x"))
+ )
+ }, SymbolExpression("y"))
+
+local aring = a:getring() -- ZZ
+local bring = b:getring() -- QQ
+local cring = c:getring() -- ZZ_7
+local dring = d:getring() -- ZZ_14
+local ering = e:getring() -- ZZ[x]
+local fring = f:getring() -- QQ[x]
+local gring = g:getring() -- ZZ[x][y][z]
+local hring = h:getring() -- QQ[z][y][x]
+local iring = i:getring() -- ZZ(x)
+local jring = j:getring() -- QQ(x)
+local kring = k:getring() -- ZZ_5[x]
+local lring = l:getring() -- QQ(x)[y]
+
+starttest("ring construction")
+testeq(aring, "ZZ")
+testeq(bring, "QQ")
+testeq(cring, "Z/Z7")
+testeq(dring, "Z/Z14")
+testeq(ering, "ZZ[x]")
+testeq(fring, "QQ[x]")
+testeq(gring, "ZZ[x][y][z]")
+testeq(hring, "QQ[z][y][x]")
+testeq(iring, "ZZ(x)")
+testeq(jring, "QQ(x)")
+testeq(kring, "Z/Z5[x]")
+testeq(lring, "QQ(x)[y]")
+endtest()
+
+starttest("ring conversion")
+
+-- Commented-out tests denote elements whos rings are not subrings of the ring that is being converted to
+
+testringconvert(a, aring, "12", "ZZ")
+testringconvert(a, bring, "12/1", "QQ")
+testringconvert(a, cring, "5", "Z/Z7")
+testringconvert(a, dring, "12", "Z/Z14")
+testringconvert(a, ering, "12x^0", "ZZ[x]")
+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, kring, "2x^0", "Z/Z5[x]")
+testringconvert(a, lring, "((12x^0)/(1x^0))y^0", "ZZ(x)[y]")
+
+-- testringconvert(b, aring, "3/2", "ZZ")
+testringconvert(b, bring, "3/2", "QQ")
+-- testringconvert(b, cring, "3/2", "Z/Z7")
+-- testringconvert(b, dring, "3/2", "Z/Z14")
+-- testringconvert(b, ering, "3/2", "ZZ[x]")
+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, kring, "3/2", "Z/Z5[x]")
+testringconvert(b, lring, "((3/2x^0)/(1x^0))y^0", "QQ(x)[y]")
+
+testringconvert(c, aring, "4", "ZZ")
+-- testringconvert(c, bring, "4", "QQ")
+testringconvert(c, cring, "4", "Z/Z7")
+testringconvert(c, dring, "4", "Z/Z14")
+testringconvert(c, ering, "4x^0", "ZZ[x]")
+-- testringconvert(c, fring, "4x", "QQ[x]")
+testringconvert(c, gring, "((4x^0)y^0)z^0", "ZZ[x][y][z]")
+-- testringconvert(c, hring, "4", "QQ[z][y][x]")
+testringconvert(c, iring, "(4x^0)/(1x^0)", "ZZ(x)")
+-- testringconvert(c, jring, "12/1x^0/1/1x^0", "QQ(x)")
+testringconvert(c, kring, "4x^0", "Z/Z5[x]")
+-- testringconvert(c, lring, "4", "QQ(x)[y]")
+
+testringconvert(d, aring, "8", "ZZ")
+-- testringconvert(d, bring, "8", "QQ")
+testringconvert(d, cring, "1", "Z/Z7")
+testringconvert(d, dring, "8", "Z/Z14")
+testringconvert(d, ering, "8x^0", "ZZ[x]")
+-- testringconvert(d, fring, "8", "QQ[x]")
+testringconvert(d, gring, "((8x^0)y^0)z^0", "ZZ[x][y][z]")
+-- testringconvert(d, hring, "8", "QQ[z][y][x]")
+testringconvert(d, iring, "(8x^0)/(1x^0)", "ZZ(x)")
+-- testringconvert(d, jring, "8", "QQ(x)")
+testringconvert(d, kring, "3x^0", "Z/Z5[x]")
+-- testringconvert(d, lring, "8", "QQ(x)[y]")
+
+-- testringconvert(e, aring, "3x^2+0x^1+6x^0", "ZZ")
+-- testringconvert(e, bring, "3x^2+0x^1+6x^0", "QQ")
+-- testringconvert(e, cring, "3x^2+0x^1+6x^0", "Z/Z7")
+-- testringconvert(e, dring, "3x^2+0x^1+6x^0", "Z/Z14")
+testringconvert(e, ering, "3x^2+0x^1+6x^0", "ZZ[x]")
+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, 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(f, aring, "12x^1+4/5x^0", "ZZ")
+-- testringconvert(f, bring, "12x^1+4/5x^0", "QQ")
+-- testringconvert(f, cring, "12x^1+4/5x^0", "Z/Z7")
+-- testringconvert(f, dring, "12x^1+4/5x^0", "Z/Z14")
+-- testringconvert(f, ering, "12x^1+4/5x^0", "ZZ[x]")
+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, 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(g, aring, "", "ZZ")
+-- testringconvert(g, bring, "", "QQ")
+-- testringconvert(g, cring, "", "Z/Z7")
+-- testringconvert(g, dring, "", "Z/Z14")
+-- testringconvert(g, ering, "", "ZZ[x]")
+-- testringconvert(g, fring, "", "QQ[x]")
+testringconvert(g, gring, "((1x^0)y^0)z^3+((1x^0)y^3+(4x^1+-16x^0)y^2+(1x^2+8x^1+2x^0)y^1+(0x^0)y^0)z^2+((-1x^0)y^1+(5x^3+6x^2+7x^1+8x^0)y^0)z^1+((8x^0)y^2+(0x^0)y^1+(1x^2+12x^1+-4x^0)y^0)z^0", "ZZ[x][y][z]")
+-- testringconvert(g, hring, "", "QQ[z][y][x]")
+-- testringconvert(g, iring, "", "ZZ(x)")
+-- testringconvert(g, jring, "", "QQ(x)")
+-- testringconvert(g, kring, "", "Z/Z5[x]")
+-- testringconvert(g, lring, "", "QQ(x)[y]")
+
+-- testringconvert(h, aring, "", "ZZ")
+-- testringconvert(h, bring, "", "QQ")
+-- testringconvert(h, cring, "", "Z/Z7")
+-- testringconvert(h, dring, "", "Z/Z14")
+-- testringconvert(h, ering, "", "ZZ[x]")
+-- testringconvert(h, fring, "", "QQ[x]")
+-- testringconvert(h, gring, "", "ZZ[x][y][z]")
+testringconvert(h, hring, "((1z^0)y^0)x^3+((1z^0)y^3+(1/9z^1+-16z^0)y^2+(1z^2+8z^1+2z^0)y^1+(0z^0)y^0)x^2+((-1z^0)y^1+(5z^3+6z^2+7z^1+8z^0)y^0)x^1+((8z^0)y^2+(0z^0)y^1+(1z^2+4/5z^1+-4z^0)y^0)x^0", "QQ[z][y][x]")
+-- testringconvert(h, iring, "", "ZZ(x)")
+-- testringconvert(h, jring, "", "QQ(x)")
+-- testringconvert(h, kring, "", "Z/Z5[x]")
+-- testringconvert(h, lring, "", "QQ(x)[y]")
+
+-- testringconvert(i, aring, "", "ZZ")
+-- testringconvert(i, bring, "", "QQ")
+-- testringconvert(i, cring, "", "Z/Z7")
+-- testringconvert(i, dring, "", "Z/Z14")
+-- testringconvert(i, ering, "", "ZZ[x]")
+-- testringconvert(i, fring, "", "QQ[x]")
+-- 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, kring, "", "Z/Z5[x]")
+testringconvert(i, lring, "((1x^1+-2x^0)/(1x^2+3x^1+3x^0))y^0", "ZZ(x)[y]")
+
+-- testringconvert(j, aring, "", "ZZ")
+-- testringconvert(j, bring, "", "QQ")
+-- testringconvert(j, cring, "", "Z/Z7")
+-- testringconvert(j, dring, "", "Z/Z14")
+-- testringconvert(j, ering, "", "ZZ[x]")
+-- testringconvert(j, fring, "", "QQ[x]")
+-- testringconvert(j, gring, "", "ZZ[x][y][z]")
+-- testringconvert(j, hring, "", "QQ[z][y][x]")
+-- testringconvert(j, iring, "", "ZZ(x)")
+testringconvert(j, jring, "(1x^1+-2x^0)/(1x^2+3/8x^1+3/4x^0)", "QQ(x)")
+-- testringconvert(j, kring, "", "Z/Z5[x]")
+testringconvert(j, lring, "((1x^1+-2x^0)/(1x^2+3/8x^1+3/4x^0))y^0", "QQ(x)[y]")
+
+-- testringconvert(k, aring, "", "ZZ")
+-- testringconvert(k, bring, "", "QQ")
+-- testringconvert(k, cring, "", "Z/Z7")
+-- testringconvert(k, dring, "", "Z/Z14")
+testringconvert(k, ering, "1x^3+3x^2+1x^1+0x^0", "ZZ[x]")
+-- testringconvert(k, fring, "", "QQ[x]")
+testringconvert(k, gring, "((1x^3+3x^2+1x^1+0x^0)y^0)z^0", "ZZ[x][y][z]")
+-- testringconvert(k, hring, "", "QQ[z][y][x]")
+testringconvert(k, iring, "(1x^3+3x^2+1x^1+0x^0)/(1x^0)", "ZZ(x)")
+-- testringconvert(k, jring, "", "QQ(x)")
+testringconvert(k, kring, "1x^3+3x^2+1x^1+0x^0", "Z/Z5[x]")
+-- testringconvert(k, lring, "", "QQ(x)[y]")
+
+-- testringconvert(l, aring, "", "ZZ")
+-- testringconvert(l, bring, "", "QQ")
+-- testringconvert(l, cring, "", "Z/Z7")
+-- testringconvert(l, dring, "", "Z/Z14")
+-- testringconvert(l, ering, "", "ZZ[x]")
+-- testringconvert(l, fring, "", "QQ[x]")
+-- testringconvert(l, gring, "", "ZZ[x][y][z]")
+-- testringconvert(l, hring, "", "QQ[z][y][x]")
+-- testringconvert(l, iring, "", "ZZ(x)")
+-- testringconvert(l, jring, "", "QQ(x)")
+-- testringconvert(j, kring, "", "Z/Z5[x]")
+testringconvert(l, lring, "((7/6x^0)/(1x^1+2x^0))y^3+((4x^2+6x^1+6x^0)/(1x^1+3x^0))y^2+((3/2x^0)/(1x^3+8x^2+1/2x^1+6x^0))y^1+((1/4x^1+1x^0)/(1x^1+0x^0))y^0", "QQ(x)[y]")
+
+
+
+
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/rings/modulararithmetic.lua b/macros/luatex/latex/luacas/tex/test/rings/modulararithmetic.lua
new file mode 100644
index 0000000000..1bd315595a
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/rings/modulararithmetic.lua
@@ -0,0 +1,20 @@
+local a = IntegerModN(Integer(5), Integer(3))
+local b = IntegerModN(Integer(1), Integer(3))
+local c = IntegerModN(Integer(-12), Integer(3))
+local f = IntegerModN(Integer(100), Integer(62501))
+local d = IntegerModN(Integer(16), Integer(36))
+local e = IntegerModN(Integer(27), Integer(36))
+
+starttest("modular arithmetic")
+testeq(a, "2")
+testeq(b, "1")
+testeq(c, "0")
+testeq(a + b, "0")
+testeq(a - b, "1")
+testeq(a * b, "2")
+testeq(a:inv(), "2")
+testeq(b:inv(), "1")
+testeq(f:inv(), "61876")
+testeq(d * e, "0")
+testeq(a * d, "2")
+endtest() \ No newline at end of file
diff --git a/macros/luatex/latex/luacas/tex/test/rings/number.lua b/macros/luatex/latex/luacas/tex/test/rings/number.lua
new file mode 100644
index 0000000000..9afea0d364
--- /dev/null
+++ b/macros/luatex/latex/luacas/tex/test/rings/number.lua
@@ -0,0 +1,118 @@
+local a = Integer(5)
+local b = Integer(3)
+local c = Integer(-12)
+local d = Integer("-54321")
+local e = Integer("99989999999999999989999999999999999999999999999999999999989999999999999999999999999998999999999999999999999999989999999998")
+local f = Integer("-1267650600228229401496703205376")
+local g = Integer(16)
+local h = Integer(8)
+local x = Integer(8) / Integer(5)
+local y = Integer(1) / Integer(12)
+local z = Integer(-7) / Integer(10)
+
+
+starttest("integer construction")
+testeq(a, 5)
+testeq(b, 3)
+testeq(c, -12)
+testeq(d, "-54321")
+testeq(e, "99989999999999999989999999999999999999999999999999999999989999999999999999999999999998999999999999999999999999989999999998")
+testeq(f, "-1267650600228229401496703205376")
+endtest()
+
+starttest("integer operations")
+testeq(-c, 12)
+testeq(a + b, 8)
+testeq(b - c, 15)
+testeq(d - d, 0)
+testeq(e + f, "99989999999999999989999999999999999999999999999999999999989999999999999999999999999998999998732349399771770598493296794622")
+testeq(a * c, -60)
+testeq(f * f, "1606938044258990275541962092341162602522202993782792835301376")
+testeq(e * f, "-126752383516820657842978847503263945985032967946239999999987323493997717705985032967944972349399771770598503296781947493995182404784576509143246593589248")
+testeq(a // b, 1)
+testeq(a % b, 2)
+testeq(f // d, "23336289836862896513258283")
+testeq(f % d, "-14533")
+testeq(e // -f, "78878201913048970415230130190415677050906625793723347950240237316957169209243093407705758276")
+testeq(e % -f, "1011644662020502370048160308222")
+testeq(c ^ a, -248832)
+testeq(d ^ a, "-472975648731213834575601")
+testeq(a == b, false)
+testeq(b < a, true)
+testeq(a <= a, true)
+testeq(f < d, true)
+testeq(e <= f, false)
+endtest()
+
+
+
+starttest("integer conversions")
+testeq(a / b, "5/3")
+testeq(g / c, "-4/3")
+testeq(c / b, -4)
+endtest()
+
+
+starttest("rational operations")
+testeq(-x, "-8/5")
+testeq(x + y, "101/60")
+testeq(z - y, "-47/60")
+testeq(x * z, "-28/25")
+testeq(x / y, "96/5")
+testeq(y<x, true)
+testeq(z<z, false)
+testeq(z<=z, true)
+endtest()
+
+starttest("combined integer/rational operations")
+testeq(a + x, "33/5")
+testeq(x + a, "33/5")
+testeq(b - y, "35/12")
+testeq(y - b, "-35/12")
+testeq(c * y, -1)
+testeq(y * c, -1)
+testeq(a / x, "25/8")
+testeq(x / a, "8/25")
+testeq(a/h == x, false)
+testeq(h/a == x, true)
+testeq(y < b , true)
+testeq(b < y, false)
+endtest()
+
+local f = Integer(3)
+local g = Integer(216)
+local h = Integer(945)
+local i = Integer("7766999")
+local j = Integer(4)
+local k = Integer(8)
+local m = Integer(16)
+local n = Integer(100000000003)
+local o = Integer(200250077)
+
+starttest("Miller-Rabin Primes")
+testeq(f:isprime(), true, f)
+testeq(g:isprime(), false, g)
+testeq(h:isprime(), false, h)
+testeq(i:isprime(), false, i)
+testeq(n:isprime(), true, n)
+testeq(o:isprime(), false, o)
+endtest()
+
+
+starttest("Pollard Rho algorithm")
+testeq(f:findafactor(), 3, f)
+testeq(g:findafactor(), 2, g)
+testeq(h:findafactor(), 3, h)
+testeq(i:findafactor(), 41, i)
+testeq(j:findafactor(), 2, j)
+testeq(k:findafactor(), 2, k)
+testeq(m:findafactor(), 2, m)
+endtest()
+
+starttest("prime factorization")
+testeq(f:primefactorization(), "* (3 ^ 1)", f, true)
+testeq(g:primefactorization(), "(2 ^ 3) * (3 ^ 3)", g, true)
+testeq(h:primefactorization(), "(3 ^ 3) * (5 ^ 1) * (7 ^ 1)", h, true)
+testeq(i:primefactorization(), "(41 ^ 1) * (189439 ^ 1)", i, true)
+testeq(o:primefactorization(), "(10007 ^ 1) * (20011 ^ 1)", o, true)
+endtest() \ No newline at end of file
diff --git a/support/TeX4ht/source/ChangeLog b/support/TeX4ht/source/ChangeLog
index f65184fe92..ed15608655 100644
--- a/support/TeX4ht/source/ChangeLog
+++ b/support/TeX4ht/source/ChangeLog
@@ -1,3 +1,8 @@
+2022-11-04 Michal Hoftich <michal.h21@gmail.com>
+
+ * tex4ht-4ht.tex (xskak.4ht),
+ * tex4ht-html4.tex (html4.4ht): added support for the Xskak package.
+
2022-11-02 Michal Hoftich <michal.h21@gmail.com>
* tex4ht-mathml.tex (mathml.4ht): added extra group for \mathcal, to
diff --git a/support/TeX4ht/source/mktex4ht-cnf.tex b/support/TeX4ht/source/mktex4ht-cnf.tex
index 3d85a09637..bb78f1e571 100644
--- a/support/TeX4ht/source/mktex4ht-cnf.tex
+++ b/support/TeX4ht/source/mktex4ht-cnf.tex
@@ -1,4 +1,4 @@
-% $Id: mktex4ht-cnf.tex 1219 2022-10-20 14:40:14Z michal_h21 $
+% $Id: mktex4ht-cnf.tex 1235 2022-11-04 15:46:19Z michal_h21 $
% Manually-maintained file, listing *.4ht files created by tex4ht-4ht.tex.
% Read by tex4ht-cond4ht.
%
@@ -373,6 +373,7 @@
\AddFile{9}{parallel}
\AddFile{9}{skak}
\AddFile{9}{chessboard}
+\AddFile{9}{xskak}
\AddFile{9}{texmate}
\AddFile{9}{menukeys}
\AddFile{9}{animate}
diff --git a/support/TeX4ht/source/tex4ht-4ht.tex b/support/TeX4ht/source/tex4ht-4ht.tex
index 88324fc0db..29bfdaf2db 100644
--- a/support/TeX4ht/source/tex4ht-4ht.tex
+++ b/support/TeX4ht/source/tex4ht-4ht.tex
@@ -1,4 +1,4 @@
-% $Id: tex4ht-4ht.tex 1229 2022-10-31 11:23:43Z michal_h21 $
+% $Id: tex4ht-4ht.tex 1235 2022-11-04 15:46:19Z michal_h21 $
% tex tex4ht-4ht or ht tex tex4ht-4ht
%
% Copyright 2009-2022 TeX Users Group
@@ -33442,6 +33442,74 @@ of \''\:temp' below.
%%%%%%%%%%%%%%%%%%%%%%%%
+\Section{xskak.sty}
+%%%%%%%%%%%%%%%%%%%%%%%%
+
+\<xskak.4ht\><<<
+% xskak.4ht (|version), generated from |jobname.tex
+% Copyright 2022 TeX Users Group
+|<TeX4ht license text|>
+|<xskak depth|>
+|<xskak inline elements|>
+|<xskak.sty|>
+\Hinput{xskak}
+\endinput
+>>>\AddFile{9}{xskak}
+
+This command calculates vertical align for Xskak commands that can be used inline,
+so the image is aligned correctly to the baseline of the surrounding text.
+
+\<xskak depth\><<<
+\ExplSyntaxOn
+\def\:xskakdepth{vertical-align:-\fp_eval:n{
+ \dim_to_fp:n{\dp0}/(\dim_to_fp:n{\ht0}+\dim_to_fp:n{\dp0}) * 100
+}\@percentchar;}
+\ExplSyntaxOff
+>>>
+
+The following commands are used in text, so we save the result in a box, which can
+be used for calculation of vertical alignment.
+
+\<xskak inline elements\><<<
+\NewConfigure{mainline}{2}
+\def\:tempa#1{%
+ \setbox0=\hbox{\o:mainline:{#1}}%
+ \edef\:xskakalt{\detokenize{#1}}%
+ %\a:mainline\Picture*[\detokenize{#1}]{ style="\:xskakdepth"}\box0\EndPicture\b:mainline%
+ \a:mainline\box0\b:mainline%
+}
+\HLet\mainline\:tempa
+
+\NewConfigure{xskakget}{2}
+\def\:tempa#1{%
+ \edef\:xskakalt{\detokenize{#1}}%
+ \setbox0=\hbox{\o:xskakget:{#1}}%
+ \a:xskakget\box0\b:xskakget%
+}
+\HLet\xskakget\:tempa
+>>>
+
+The chessboard configuration is defined by chessboard.4ht too. We declare it here
+because
+
+\<xskak.sty\><<<
+\NewConfigure{chessboard}{2}
+>>>
+
+\<xskak.sty\><<<
+\def\:tempa{%
+ % reset to the original version of \chessboard
+ \let\:currchesboard\chessboard
+ \let\chessboard\o:chessboard:%
+ \o:newchessgame:%
+ % set the TeX4ht version of \chessboard back
+ \let\chessboard\:currchesboard
+}
+\HLet\newchessgame\:tempa
+>>>
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%
\Section{texmate.sty}
%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/support/TeX4ht/source/tex4ht-html4.tex b/support/TeX4ht/source/tex4ht-html4.tex
index b9119693d3..00502d272b 100644
--- a/support/TeX4ht/source/tex4ht-html4.tex
+++ b/support/TeX4ht/source/tex4ht-html4.tex
@@ -1,4 +1,4 @@
-% $Id: tex4ht-html4.tex 1226 2022-10-27 14:19:24Z michal_h21 $
+% $Id: tex4ht-html4.tex 1235 2022-11-04 15:46:19Z michal_h21 $
% Compile 4 times: latex tex4ht-html4
% Copy html4.4ht into the work directory before all but the last compilation.
%
@@ -19426,6 +19426,27 @@ latex.ltx, fontmath.ltx, plain.tex
>>>
%%%%%%%%%%%%%%%%%
+\section{xskak}
+%%%%%%%%%%%%%%%%%
+
+\<configure html4 xskak\><<<
+\Configure{mainline}{\Picture*[\:xskakalt]{ style="\:xskakdepth"}}{\EndPicture}
+\Configure{xskakget}{\Picture*[\:xskakalt]{ style="\:xskakdepth"}}{\EndPicture}
+>>>
+
+%%%%%%%%%%%%%%%%%
+\section{chessboard}
+%%%%%%%%%%%%%%%%%
+
+If called from Xskak, we can use the xskakget command to get
+chess notation as an alt text.
+
+\<configure html4 chessboard\><<<
+\def\chessboard:alt{\ifdefined\o:xskakget:\o:xskakget:{nextfen}\fi}
+\Configure{chessboard}{\Picture*[\chessboard:alt]{}}{\EndPicture}
+>>>
+
+%%%%%%%%%%%%%%%%%
\section{texmate}
\<configure html4 texmate\><<<
diff --git a/support/bib2gls/CHANGES b/support/bib2gls/CHANGES
index a00bda1966..a3e2fbcaac 100644
--- a/support/bib2gls/CHANGES
+++ b/support/bib2gls/CHANGES
@@ -1,3 +1,22 @@
+v3.1 (2022-11-05):
+
+ * If new versions of glossaries.sty, mfirstuc.sty and glossaries-extra.sty
+ are detected, \bibglsuppercase and \bibglslowercase will now
+ have \glsuppercase and \glslowercase in their definition,
+ instead of \MakeTextUppercase and \MakeTextLowercase
+
+ * skip empty elements in src
+
+ * added resource option merge-ranges
+
+ * added support for \ifglsfieldvoid
+
+ * bug fix: secondary sort type not included in group label
+
+ * TeX parser library (texparserlib.jar) now has limited support
+ for \NewDocumentCommand, \RenewDocumentCommand,
+ \ProvideDocumentCommand and \DeclareDocumentCommand.
+
v3.0 (2022-10-14):
* support for \glsxtr@select@entry and \glsxtr@select@entry@nameref
diff --git a/support/bib2gls/bib2gls-begin.pdf b/support/bib2gls/bib2gls-begin.pdf
index 4e3e53dcdc..c825f2b8c0 100644
--- a/support/bib2gls/bib2gls-begin.pdf
+++ b/support/bib2gls/bib2gls-begin.pdf
Binary files differ
diff --git a/support/bib2gls/bib2gls-en.xml b/support/bib2gls/bib2gls-en.xml
index 29947b6bb6..1deb254d84 100644
--- a/support/bib2gls/bib2gls-en.xml
+++ b/support/bib2gls/bib2gls-en.xml
@@ -364,6 +364,7 @@ Aliased entries must be in the same resource set as their target entries with op
<entry key="warning.exclusions.unknown.token">Unknown token in exclusion list: {0}</entry>
<entry key="warning.blockers.unknown.token">Unknown token in blocker list: {0}</entry>
<entry key="warning.mappings.cant.parse">Can''t parse mapping list: {0}</entry>
+<entry key="warning.mismatched.sty">Mismatching versions of packages {0} ({1}) and {2} ({3}). Please upgrade {2}</entry>
<entry key="error.title">Error: {0}</entry>
<entry key="error.alias.map.forbidden">The 'alias' field can't be mapped.</entry>
@@ -597,6 +598,7 @@ converted to @dualabbreviationentry.)</entry>
<entry key="latex.package.not.loaded">Package ''{0}'' has not been loaded</entry>
<entry key="latex.error.counter.too.large">Counter too large</entry>
<entry key="latex.error.counter.out.of.range">Counter value {0} out of valid range ({1})</entry>
+<entry key="latex.unsupported.xparse_type">Unsupported xparse type {0} in command {1}</entry>
<entry key="bibtex.error.missing_value">Missing value</entry>
<entry key="bibtex.error.expecting">Expecting ''{0}''</entry>
diff --git a/support/bib2gls/bib2gls.pdf b/support/bib2gls/bib2gls.pdf
index 77ed097987..e23282b73c 100644
--- a/support/bib2gls/bib2gls.pdf
+++ b/support/bib2gls/bib2gls.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-authors.pdf b/support/bib2gls/examples/sample-authors.pdf
index 9a4103cbf4..5094e02ec7 100644
--- a/support/bib2gls/examples/sample-authors.pdf
+++ b/support/bib2gls/examples/sample-authors.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-bacteria.pdf b/support/bib2gls/examples/sample-bacteria.pdf
index 291ebb781e..f8bc312fef 100644
--- a/support/bib2gls/examples/sample-bacteria.pdf
+++ b/support/bib2gls/examples/sample-bacteria.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-chemical.pdf b/support/bib2gls/examples/sample-chemical.pdf
index a2f40c87e0..0d303e4783 100644
--- a/support/bib2gls/examples/sample-chemical.pdf
+++ b/support/bib2gls/examples/sample-chemical.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-citations.pdf b/support/bib2gls/examples/sample-citations.pdf
index 5bc3bf66ee..46c9b2e8a9 100644
--- a/support/bib2gls/examples/sample-citations.pdf
+++ b/support/bib2gls/examples/sample-citations.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-constants.pdf b/support/bib2gls/examples/sample-constants.pdf
index 52152df16b..07c5a9b596 100644
--- a/support/bib2gls/examples/sample-constants.pdf
+++ b/support/bib2gls/examples/sample-constants.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-hierarchical.pdf b/support/bib2gls/examples/sample-hierarchical.pdf
index eec25021db..9f83fe64da 100644
--- a/support/bib2gls/examples/sample-hierarchical.pdf
+++ b/support/bib2gls/examples/sample-hierarchical.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-markuplanguages.pdf b/support/bib2gls/examples/sample-markuplanguages.pdf
index 49b5ea397f..959a97cfbf 100644
--- a/support/bib2gls/examples/sample-markuplanguages.pdf
+++ b/support/bib2gls/examples/sample-markuplanguages.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-markuplanguages.tex b/support/bib2gls/examples/sample-markuplanguages.tex
index aba4cbf219..925f8e8c11 100644
--- a/support/bib2gls/examples/sample-markuplanguages.tex
+++ b/support/bib2gls/examples/sample-markuplanguages.tex
@@ -38,7 +38,7 @@
\glssetcategoryattribute{markuplanguage}{glossname}{firstuc}
-\renewcommand*{\glsxtrtagfont}[1]{\underline{\MakeTextUppercase{#1}}}
+\renewcommand*{\glsxtrtagfont}[1]{\underline{\glsuppercase{#1}}}
\begin{document}
diff --git a/support/bib2gls/examples/sample-maths.pdf b/support/bib2gls/examples/sample-maths.pdf
index 7489b36c4c..327dea9a3e 100644
--- a/support/bib2gls/examples/sample-maths.pdf
+++ b/support/bib2gls/examples/sample-maths.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-media.pdf b/support/bib2gls/examples/sample-media.pdf
index 7838716497..085fba316a 100644
--- a/support/bib2gls/examples/sample-media.pdf
+++ b/support/bib2gls/examples/sample-media.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-msymbols.pdf b/support/bib2gls/examples/sample-msymbols.pdf
index 5b83de642e..1a9fef2971 100644
--- a/support/bib2gls/examples/sample-msymbols.pdf
+++ b/support/bib2gls/examples/sample-msymbols.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-multi1.pdf b/support/bib2gls/examples/sample-multi1.pdf
index feb6e36e18..0376963383 100644
--- a/support/bib2gls/examples/sample-multi1.pdf
+++ b/support/bib2gls/examples/sample-multi1.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-multi2.pdf b/support/bib2gls/examples/sample-multi2.pdf
index 65a6867d42..c356c6c2f9 100644
--- a/support/bib2gls/examples/sample-multi2.pdf
+++ b/support/bib2gls/examples/sample-multi2.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-nested.pdf b/support/bib2gls/examples/sample-nested.pdf
index da75246646..9b59f762b1 100644
--- a/support/bib2gls/examples/sample-nested.pdf
+++ b/support/bib2gls/examples/sample-nested.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-people.pdf b/support/bib2gls/examples/sample-people.pdf
index bc03fd1e1c..8824610553 100644
--- a/support/bib2gls/examples/sample-people.pdf
+++ b/support/bib2gls/examples/sample-people.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-textsymbols.pdf b/support/bib2gls/examples/sample-textsymbols.pdf
index ce6b25d425..64aab9e3ac 100644
--- a/support/bib2gls/examples/sample-textsymbols.pdf
+++ b/support/bib2gls/examples/sample-textsymbols.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-textsymbols2.pdf b/support/bib2gls/examples/sample-textsymbols2.pdf
index 4f6fd3116b..f50f6f721d 100644
--- a/support/bib2gls/examples/sample-textsymbols2.pdf
+++ b/support/bib2gls/examples/sample-textsymbols2.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-units1.pdf b/support/bib2gls/examples/sample-units1.pdf
index fdf5d60878..f5c7275af8 100644
--- a/support/bib2gls/examples/sample-units1.pdf
+++ b/support/bib2gls/examples/sample-units1.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-units2.pdf b/support/bib2gls/examples/sample-units2.pdf
index e9641f4918..7445d761e6 100644
--- a/support/bib2gls/examples/sample-units2.pdf
+++ b/support/bib2gls/examples/sample-units2.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-units3.pdf b/support/bib2gls/examples/sample-units3.pdf
index 897e09a989..2d1e53be63 100644
--- a/support/bib2gls/examples/sample-units3.pdf
+++ b/support/bib2gls/examples/sample-units3.pdf
Binary files differ
diff --git a/support/bib2gls/examples/sample-usergroups.pdf b/support/bib2gls/examples/sample-usergroups.pdf
index 74cbc7cbe5..27bcbecd59 100644
--- a/support/bib2gls/examples/sample-usergroups.pdf
+++ b/support/bib2gls/examples/sample-usergroups.pdf
Binary files differ
diff --git a/support/bib2gls/src/bib2gls-begin.tex b/support/bib2gls/src/bib2gls-begin.tex
index 95993aae36..b0c002641e 100644
--- a/support/bib2gls/src/bib2gls-begin.tex
+++ b/support/bib2gls/src/bib2gls-begin.tex
@@ -655,7 +655,29 @@ as \gls{commentchar} (percent), \gls{ampchar} (ampersand), \gls{param} (hash),
\gls{mshiftchar} (dollar), or \gls{nbspchar} (tilde). Be careful of packages
that make other characters active (such as \isty{babel} with its shortcuts). If
you are using \isty{inputenc}, this also includes extended Latin characters and
-characters from other scripts. If you want to include UTF-8 characters in the
+characters from other scripts.
+
+Recent updates to the \LaTeX\ kernel mean that there's much better
+support for UTF-8 characters. Ensure that you have at least
+\sty{glossaries} v4.50, \sty{glossaries-extra} v1.49 and
+\sty{mfirstuc} 2.08. It's now possible to have:
+\begin{codeenv}
+\gls{newglossaryentry}\marg{élite}\comment{label}
+\marg{
+ \field{name} = \marg{élite},
+ \field{description} = \marg{group of people regarded as
+ the best of a particular society or organisation}
+}
+\end{codeenv}
+Regardless of whether or not you use a native UTF-8 engine, provided
+you have a new \TeX\ distribution.
+
+\begin{important}
+The UTF-8 information below refers to older
+versions and older kernels.
+\end{important}
+
+If you want to include UTF-8 characters in the
label then you must use a \TeX\ engine with native Unicode support (that is,
\XeLaTeX\ or \LuaLaTeX).
@@ -4665,36 +4687,14 @@ Additionally, depending on the page style, the section argument may
also be reproduced in the page header. This repeated use of the same
material can cause complications, in particular it can prematurely
triggering the \idx{firstuseflag} switch and cause unwanted
-indexing. If the content appears in the page header and the page
-styles converts headers to upper case, this can also cause a
-problem.
+indexing.
-For example:
-\begin{codeenv}
-\cmd{documentclass}\marg{book}
-\strut
-\cmd{usepackage}\marg{lipsum}\comment{provides \cmd{lipsum}}
-\cmd{usepackage}\marg{glossaries}
-\strut
-\gls{newglossaryentry}\marg{sample}\marg{\field{name}=\marg{sample},\field{description}=\marg{an example}}
-\strut
-\cmd{begin}{document}
-\gls{chapter}\marg{A \gls{gls}\marg{sample} chapter}
-\cmd{lipsum} \comment{dummy text}
-\cmd{end}\marg{document}
-\end{codeenv}
-I've used \csfmt{lipsum} here to create some dummy text that ensures
-a multi-paged document because the problem doesn't occur until the second
-page. The error message is:
-\begin{verbatim}
-Glossary entry `SAMPLE' has not been defined.
-\end{verbatim}
-The problem here is that the chapter title is copied to the header
-(which doesn't appear on the chapter's first page) but the header
-uses \gls{MakeUppercase}, which can't expand \gls{gls} but it does
-change the label, so \code{\gls{gls}\marg{sample}} is converted to
-\code{\gls{gls}\marg{SAMPLE}}, but there's no entry with that label.
-(Labels are case-sensitive.)
+If the content appears in the page header and the page
+styles converts headers to upper case, this can also cause a
+problem. However, if you make sure you have \sty{glossaries} v4.50+,
+\sty{glossaries-extra} v1.49+ and \sty{mfirstuc} v2.08+ with a
+recent \TeX\ distribution, most of the case-changing problems should
+now no longer occur.
Here's an example that doesn't cause an error (because there's not
enough text to trigger a page break) but does cause unexpected output:
@@ -4769,8 +4769,8 @@ case-change hasn't been applied, so the page heading appears as:
\begin{result}
4\hfill \emph{CHAPTER 1. A CHAPTER ABOUT hypertext markup language}
\end{result}
-This can be corrected by setting the \catattr{headuc} attribute to
-\code{true}:
+This shouldn't occur any more, but if it does, it can be corrected by
+setting the \catattr{headuc} attribute to \code{true}:
\begin{codeenv}
\gls{glssetcategoryattribute}\marg{abbreviation}\marg{headuc}\marg{true}
\end{codeenv}
@@ -4802,14 +4802,14 @@ the \sty{slantsc} package. Another possibility is to redefine:
which is used by the \qt{sc} abbreviation styles:
\begin{codeenv}
\cmd{renewcommand}\marg{\gls{glsabbrvscfont}}[1]\marg{\comment{}
- \gls{glsxtrifinmark}\marg{\gls{MakeTextUppercase}\marg{\gls{param}1}}\marg{\gls{textsc}\marg{\gls{param}1}}\comment{}
+ \gls{glsxtrifinmark}\marg{\gls{glsuppercase}\marg{\gls{param}1}}\marg{\gls{textsc}\marg{\gls{param}1}}\comment{}
}
\end{codeenv}
This uses:
\nosecformatdef{glsxtrifinmark}
which expands to \meta{true} in headings and the \gls{toc},
otherwise it expands to \meta{false}. This use of
-\gls{MakeTextUppercase} replaces the need for the \catattr{headuc}
+\gls{glsuppercase} replaces the need for the \catattr{headuc}
attribute. Both \catattr{headuc} and the above redefinition of
\gls{glsabbrvscfont} will cause the abbreviation to appear in
\idx{uppercase} in the \gls{toc}. If you don't want this,
@@ -4826,7 +4826,7 @@ which makes \gls{glsxtrifinmark} expand to \meta{false} in the
\cmd{usepackage}\marg{glossaries-extra}
\strut
\cmd{renewcommand}\marg{\gls{glsabbrvscfont}}[1]\marg{\comment{}
- \gls{glsxtrifinmark}\marg{\gls{MakeTextUppercase}\marg{\gls{param}1}}\marg{\gls{textsc}{\gls{param}1}}\comment{}
+ \gls{glsxtrifinmark}\marg{\gls{glsuppercase}\marg{\gls{param}1}}\marg{\gls{textsc}{\gls{param}1}}\comment{}
}
\strut
\gls{setabbreviationstyle}\marg{\abbrstyle{long-short-sc}}
@@ -4867,125 +4867,6 @@ Instead, use \gls{glsentrysymbol}:
Better still, use commands like
\gls{glsxtrpostlinkAddSymbolOnFirstUse} (see \sectionref{sec:postlinkhooks}).
-The next most problematic nesting is the use of \gls{gls} in
-abbreviations. It's far more of a problem if you only use the base
-\sty{glossaries} package. For example:
-\begin{codeenv}
-\cmd{documentclass}\marg{book}
-\strut
-\gls{usepackage}\marg{glossaries}
-\strut
-\gls{newacronym}\marg{ssi}\marg{SSI}\marg{server-side includes}
-\gls{newacronym}\marg{html}\marg{HTML}\marg{hypertext markup language}
-\gls{newacronym}\marg{shtml}\marg{SHTML}\marg{\gls{gls}\marg{ssi} enabled \gls{gls}\marg{html}}\incorrect
-\strut
-\cmd{begin}\marg{document}
-\gls{gls}\marg{shtml}.
-\cmd{end}\marg{document}
-\end{codeenv}
-This results in:
-\begin{result}
-server-side includes (SSI) enabled hypertext markup language (HTML) (SHTML).
-\end{result}
-The \sty{glossaries-extra} package temporarily changes \gls{gls}
-within the \idx{link-text} to avoid this type of problem.
-\begin{codeenv}
-\cmd{documentclass}\marg{book}
-\strut
-\cmd{usepackage}\marg{glossaries-extra}
-\strut
-\gls{newabbreviation}\marg{ssi}\marg{SSI}\marg{server-side includes}
-\gls{newabbreviation}\marg{html}\marg{HTML}\marg{hypertext markup language}
-\gls{newabbreviation}\marg{shtml}\marg{SHTML}\marg{\gls{gls}\marg{ssi} enabled \gls{gls}\marg{html}}
-\strut
-\cmd{begin}\marg{document}
-\gls{gls}\marg{shtml}.
-\cmd{end}\marg{document}
-\end{codeenv}
-This now produces:
-\begin{result}
-SSI enabled HTML (SHTML).
-\end{result}
-This doesn't seem so bad, but now let's see what happens if the
-\catattr{glossdesc} is set to \code{firstuc}:
-\begin{codeenv}
-\cmd{documentclass}\marg{book}
-\strut
-\cmd{usepackage}\marg{glossaries-extra}
-\strut
-\gls{glssetcategoryattribute}\marg{abbreviation}\marg{\catattr{glossdesc}}\marg{firstuc}
-\gls{newabbreviation}\marg{ssi}\marg{SSI}\marg{server-side includes}
-\gls{newabbreviation}\marg{html}\marg{HTML}\marg{hypertext markup language}
-\gls{newabbreviation}\marg{shtml}\marg{SHTML}\marg{\gls{gls}\marg{ssi} enabled \gls{gls}\marg{html}}\incorrect
-\strut
-\cmd{begin}\marg{document}
-\gls{gls}\marg{shtml}.
-\gls{printunsrtglossary}
-\cmd{end}\marg{document}
-\end{codeenv}
-This now causes an error:
-\begin{verbatim}
-! Missing \endcsname inserted.
-<to be read again>
- \protect
-\end{verbatim}
-The problem here is that the first-letter upper-casing command is
-being applied to \code{\gls{gls}\marg{ssi}}. This results in
-\code{\gls{gls}\marg{\gls{MakeTextUppercase} ssi}} which has an
-invalid label. A workaround is to insert an empty group before
-the initial \gls{gls}:
-\begin{codeenv}
-\gls{newabbreviation}\marg{shtml}\marg{SHTML}\marg{\marg{}\gls{gls}\marg{ssi} enabled \gls{gls}\marg{html}}
-\end{codeenv}
-However, \gls{gls} functions normally within the glossary, so the
-result is:
-\begin{result}
-\textbf{SSI} Server-side includes
-
-\textbf{HTML} Hypertext markup language
-
-\textbf{SHTML} server-side includes (SSI) enabled hypertext markup language (HTML)
-\end{result}
-Since neither \code{ssi} nor \code{html} have been referenced in the
-document, the reference in the \code{shtml} description in the
-glossary is the \idx{firstuse} for both of them, so they show the
-full form, but the upper-casing can't be applied. This means that
-the description for the \code{shtml} entry doesn't start with an
-\idx{uppercase} letter. The best solution is to avoid using
-\gls{gls} in the \field{long} field:
-\begin{codeenv}
-\cmd{documentclass}\marg{book}
-\strut
-\cmd{usepackage}[\styopt{record}]\marg{glossaries-extra}
-\strut
-\gls{glssetcategoryattribute}\marg{abbreviation}\marg{glossdesc}\marg{firstuc}
-\strut
-\gls[noindex]{glsdefpostdesc}\marg{abbreviation}\marg{\comment{}
- \gls{glsxtrifhasfield}\marg{\field{seealso}}\marg{\gls{glscurrententrylabel}}\comment{}
- \marg{ (\gls{emph}\marg{see also} \gls[noindex=false]{glsxtrseelist}\marg{\gls{glscurrentfieldvalue}})}\comment{}
- \marg{}\comment{}
-}
-\strut
-\gls{newabbreviation}\marg{ssi}\marg{SSI}\marg{server-side includes}
-\gls{newabbreviation}\marg{html}\marg{HTML}\marg{hypertext markup language}
-\gls{newabbreviation}\oarg{\field{seealso}=\marg{ssi,html}}\marg{shtml}\marg{SHTML}
-\marg{server-side includes enabled hypertext markup language}
-\strut
-\cmd{begin}\marg{document}
-\gls{Gls}\marg{shtml}.
-\gls{printunsrtglossary}
-\cmd{end}\marg{document}
-\end{codeenv}
-I've used the \field{seealso} key here. (The value should be a
-comma-separated list of labels.) It doesn't show
-in the glossary by default as it needs an indexing application to add it
-automatically to the \idx{locationlist}, so I've used the
-\idx{postdescriptionhook} to append the cross-reference. The
-\styopt{record} option is needed, otherwise the default
-\gls{makeindex} setting will be assumed and an error will occur as
-the associated \gls{makeindex} file hasn't been opened. If \bibgls\
-support is added then the hook won't be needed.
-
\section{Shortcut Commands or Active Characters}
\label{sec:activeshortcuts}
diff --git a/support/bib2gls/src/bib2gls-terms.bib b/support/bib2gls/src/bib2gls-terms.bib
index 10cb3b3eec..8ca871f605 100644
--- a/support/bib2gls/src/bib2gls-terms.bib
+++ b/support/bib2gls/src/bib2gls-terms.bib
@@ -409,7 +409,7 @@
user1={idx.ignoredglossary},
description={A glossary defined with commands like
\cs{newignoredglossary}. An ignored glossary doesn't have an
-associated title (so it one is required it needs to be explicitly
+associated title (so if one is required it needs to be explicitly
set), and isn't picked up by iterative commands such as
\cs{printunsrtglossaries}. See \sectionref{sec:newglossary}.}
}
diff --git a/support/bib2gls/src/bib2gls.bib b/support/bib2gls/src/bib2gls.bib
index b55acf3f20..9f2d002928 100644
--- a/support/bib2gls/src/bib2gls.bib
+++ b/support/bib2gls/src/bib2gls.bib
@@ -1078,6 +1078,16 @@ given label},
category={command}
}
+@mainglscommand{glssubgroupheading,
+ name={\csfmt{gls\-sub\-group\-heading}},
+ user1={\margm{previous level}\margm{level}\margm{parent-label}\margm{group-label}},
+ description={formats the heading for the \idx{sub-group} identified by the
+given label},
+ topics={glossarystylecommands,formattingcommands},
+ note={\styfmt{glossaries-extra} v1.49+},
+ category={command}
+}
+
@bibglscommand{bibglssetlastgrouptitle,
name={\csfmt{bib\-gls\-set\-last\-group\-title}},
user1={\margm{cs}\margm{specs}},
@@ -1108,7 +1118,7 @@ given label},
@bibglscommand{bibglsgrouplevel,
name={\csfmt{bib\-gls\-group\-level}},
user1={\margm{label}{n}},
- description={expands to the group label for hierarchical level \meta{n}
+ description={expands to the \idx{sub-group} label for hierarchical level \meta{n}
where \meta{label} is the label that would normally be applied to
level~0},
topics={glossarystylecommands,groupcommands},
@@ -1119,7 +1129,7 @@ given label},
@bibglscommand{bibglshiersubgrouptitle,
name={\csfmt{bib\-gls\-hier\-sub\-group\-title}},
user1={\marg{level}\marg{parent}\marg{title}},
- description={formats the title for a hierarchical group where the
+ description={formats the title for a \idx{hiergroup} where the
normal non-hierarchical title would be \meta{title}},
topics={glossarystylecommands,groupcommands},
note={\bibgls},
@@ -3168,6 +3178,13 @@ field\dhyphen locale}},
parent={resourceoptions}
}
+@resourceoption{opt.merge-ranges,
+ name={\csoptfmt{merge\dhyphen ranges}},
+ user1={\meta{boolean}},
+ category={resourceoption},
+ parent={resourceoptions}
+}
+
@resourceoption{opt.min-loc-range,
name={\csoptfmt{min\dhyphen loc\dhyphen range}},
user1={\meta{value}},
@@ -10570,6 +10587,73 @@ primary formats, if enabled.},
name={custom group}
}
+@index{sub-group}
+@index{hiergroup,
+ name={hierarchical group},
+ alias={sub-group}
+}
+@index{smallgroup,
+ name={small group}
+}
+
+@indexplural{group}
+@index{groups.letter,
+ parent={group},
+ name={letter},
+ text={letter group},
+ alias={lettergroup}}
+@index{groups.nonletter,
+ parent={group},
+ name={non-letter},
+ text={non-letter group},
+ alias={nonlettergroup}
+}
+@index{groups.symbol,
+ parent={group},
+ name={symbol},
+ text={symbol group},
+ alias={symbolgroup}}
+@index{groups.empty,
+ parent={group},
+ name={empty},
+ text={empty group},
+ alias={emptygroup}}
+@index{groups.number,
+ parent={group},
+ name={number},
+ text={number group},
+ alias={numbergroup}}
+@index{groups.datetime,
+ parent={group},
+ name={datetime},
+ text={datetime group},
+ alias={datetimegroup}}
+@index{groups.date,
+ parent={group},
+ name={date},
+ text={date group},
+ alias={dategroup}}
+@index{groups.time,
+ parent={group},
+ name={time},
+ text={time group},
+ alias={timegroup}}
+@index{groups.custom,
+ parent={group},
+ name={custom},
+ text={custom group},
+ alias={customgroup}}
+@index{groups.sub,
+ parent={group},
+ name={hierarchical},
+ text={hierarchical group},
+ alias={sub-group}}
+@index{groups.small,
+ parent={group},
+ name={small},
+ text={small group},
+ alias={smallgroup}}
+
@index{localeprovider,
name={locale provider}
}
@@ -10591,6 +10675,24 @@ primary formats, if enabled.},
name = {location list}
}
+@index{range}
+@index{explicit-range,
+ parent={range},
+ name={explicit},
+ text={explicit range}
+}
+@index{implicit-range,
+ parent={range},
+ name={implicit},
+ text={implicit range}
+}
+@index{interloper,parent={range}}
+@index{locationrange,
+ parent={locationlist},
+ name={ranges},
+ alias={range}
+}
+
@index{postlinkhook,
name={post-link hook}
}
@@ -12703,14 +12805,31 @@ field value},
category={command}
}
+@glscommand{ifglsfieldvoid,
+ name={\csfmt{if\-gls\-field\-void}},
+ user1={\margm{field label}\margm{entry label}\margm{true}\margm{false}},
+ description={expands to \meta{true} if the given entry doesn't
+exist, or exists but doesn't have the field (identified by its
+internal field label) defined or does have the field defined but the
+field is empty. Otherwise expands to \meta{false}. This is
+essentially like \gls{GlsXtrIfFieldUndef} but also
+tests for an empty value},
+ topics={conditional,fieldrefcommands},
+ note={\styfmt{glossaries} v4.50+\texparserdefnote},
+ seealso={GlsXtrIfFieldUndef},
+ category={command}
+}
+
@glscommand{GlsXtrIfFieldUndef,
name={\csfmt{Gls\-Xtr\-If\-Field\-Undef}},
user1={\margm{field label}\margm{entry label}\margm{true}\margm{false}},
- description={tests if the given field isn't defined for the given
+ description={tests if the given field (identified by its
+internal field label) isn't defined for the given
entry, which may also not exist},
topics={conditional,fieldrefcommands},
note={\styfmt{glossaries-extra} v1.23+},
- category={command}
+ category={command},
+ seealso={ifglsfieldvoid},
}
@glscommand{GlsXtrIfFieldEqStr,
@@ -13566,6 +13685,30 @@ letter to \idx{uppercase}},
category={command}
}
+@glscommand{glsstartrange,
+ name={\csfmt{gls\-start\-range}},
+ user1={\oargm{options}\margm{label list}},
+ description={essentially like
+ \code{\cs{glsaddeach}\oarg{\glsopt[\idx{openrange}]{format},\meta{options}}\margm{label-list}}.
+ If \glsopt{format} is used in \meta{options}, the open marker
+ \idx{openrange} will be inserted in front of the value},
+ topics={indexing},
+ note={\styfmt{glossaries-extra} v1.50+},
+ category={command}
+}
+
+@glscommand{glsendrange,
+ name={\csfmt{gls\-end\-range}},
+ user1={\oargm{options}\margm{label list}},
+ description={essentially like
+ \code{\cs{glsaddeach}\oarg{\glsopt[\idx{closerange}]{format},\meta{options}}\margm{label-list}}.
+ If \glsopt{format} is used in \meta{options}, the close marker
+ \idx{closerange} will be inserted in front of the value},
+ topics={indexing},
+ note={\styfmt{glossaries-extra} v1.50+},
+ category={command}
+}
+
@glsaddoption{glsadd.thevalue,
name={\csoptfmt{thevalue}},
user1={\margm{value}},
@@ -14093,6 +14236,43 @@ usual delay},
category={command}
}
+@command{NewDocumentCommand,
+ name={\csfmt{New\-Document\-Com\-mand}},
+ user1={\margm{cs}\margm{arg specs}\margm{code}},
+ description={defines a new robust command (only partially
+implemented by \TeX\ Parser Library)},
+ note={\LaTeX3 command\texparserdefnote},
+ category={command}
+}
+
+@command{RenewDocumentCommand,
+ name={\csfmt{Renew\-Document\-Com\-mand}},
+ user1={\margm{cs}\margm{arg specs}\margm{code}},
+ description={redefines a new robust command (only partially
+implemented by \TeX\ Parser Library)},
+ note={\LaTeX3 command\texparserdefnote},
+ category={command}
+}
+
+@command{ProvideDocumentCommand,
+ name={\csfmt{Provide\-Document\-Com\-mand}},
+ user1={\margm{cs}\margm{arg specs}\margm{code}},
+ description={provides a robust command, if not already defined (only partially
+implemented by \TeX\ Parser Library)},
+ note={\LaTeX3 command\texparserdefnote},
+ category={command}
+}
+
+@command{DeclareDocumentCommand,
+ name={\csfmt{Declare\-Document\-Com\-mand}},
+ user1={\margm{cs}\margm{arg specs}\margm{code}},
+ description={defines a robust command, regardless of whether or not
+ it has already been defined (only partially
+implemented by \TeX\ Parser Library)},
+ note={\LaTeX3 command\texparserdefnote},
+ category={command}
+}
+
@command{section,
name={\csfmt{section}},
user1={\oargm{\idx{toc} title}\margm{title}},
@@ -16646,6 +16826,22 @@ has three or more items},
category={command}
}
+@command{glslowercase,
+ name={\csfmt{gls\-lower\-case}},
+ user1={\margm{text}},
+ description={converts \meta{text} to lower case},
+ note={\styfmt{glossaries} v4.50+\texparserdefnote},
+ category={command}
+}
+
+@command{glsuppercase,
+ name={\csfmt{gls\-upper\-case}},
+ user1={\margm{text}},
+ description={converts \meta{text} to \idx{uppercase}},
+ note={\styfmt{glossaries} v4.50+\texparserdefnote},
+ category={command}
+}
+
@command{MakeTextLowercase,
name={\csfmt{Make\-Text\-Lower\-case}},
user1={\margm{text}},
diff --git a/support/bib2gls/src/bib2gls.tex b/support/bib2gls/src/bib2gls.tex
index 0bee0fd314..dd56fedc31 100644
--- a/support/bib2gls/src/bib2gls.tex
+++ b/support/bib2gls/src/bib2gls.tex
@@ -1663,14 +1663,14 @@ the order in which the glossaries were defined. You may use
preferred order.
\end{important}
-\item[\field{group}] The entries within a glossary can form groups
+\item[\field{group}] The entries within a glossary can form \idxpl{group}
as a by-product of the sorting method. This must be enabled with the
\longarg{group} switch and isn't available for the sort methods
listed in \tableref{tab:sortoptionsnosort}. The group label is
stored in the \field{group} field. This is an internal field that
typically shouldn't be set in the \ext{bib} file.
-You can specify your own custom groups but if you do so you must
+You can specify your own \idxpl{customgroup} but if you do so you must
ensure that the terms are ordered in such a way that they are
gathered according to group. This is typically done by splitting the
glossary into blocks using a separate \gls{GlsXtrLoadResources} with
@@ -1719,7 +1719,7 @@ commands within the document. For example:
Suppose that your list of terms spans many pages and you feel it
would be helpful to the reader to split it up into letter groups
then you would need to run \bibgls\ with the \longarg{group} switch
-and use a glossary style that supports letter groups for that
+and use a glossary style that supports \idxpl{lettergroup} for that
glossary. For example:
\begin{codeenv}
\cs{printunsrtglossary}\oarg{\printglossopt[indexgroup]{style}}
@@ -3042,7 +3042,7 @@ The \styfmt{glossaries-extra} \styopt{record} package option
automatically creates a new internal field called \field{group}. If the
\longarg{group} switch is used with the default \csopt[auto]{group}
option then, when sorting, \bibgls\ will try
-to determine the letter group for each entry and assign it to the
+to determine the \idx{group} for each entry and assign it to the
\field{group} field. (Some \csopt{sort} options ignore this
setting.) This value will be picked up by \ics{printunsrtglossary}
if group headings are required (for example with the
@@ -3052,6 +3052,11 @@ if group headings are required (for example with the
the glossary, there's no need to use this switch. Note that this
switch doesn't automatically select an appropriate glossary style.
+If you want \idxpl{sub-group}, you will need to use the \csopt{group-level}
+resource option and ensure you have \sty{glossaries-extra} v1.49+.
+\Idxpl{smallgroup} can be merged with the \csopt{merge-small-groups}
+resource option.
+
\begin{important}
The \field{group} field should typically not be set in the \ext{bib}
file and will trigger a warning if found. The explicit use of the
@@ -3514,13 +3519,18 @@ Don't check for instances of \ics{citation} in the \ext{aux} file
\argsection{collapse-same-location-range}
-Collapse any explicit \gls{location} range into a normal \gls{record} if
-the \glspl{location} are the same (default).
+Collapse any \idx{explicit-range} into a normal \gls{record} if
+the start and end \glspl{location} are the same (default).
+This record will be treated as a normal \gls{location} that can be
+merged with neighbouring \glspl{location}, regardless of
+\csopt{merge-ranges}.
\argsection{no-collapse-same-location-range}
-Don't collapse any explicit \gls{location} range into a normal
-\gls{record} if the \glspl{location} are the same.
+Don't collapse any \idx{explicit-range} into a normal
+\gls{record} if the start and end \glspl{location} are the same.
+The \idx{explicit-range} will only be able to merge with
+neighbouring \glspl{location} if \csopt[true]{merge-ranges}.
\argsection{map-format}
@@ -3539,8 +3549,8 @@ This essentially means that if there's a \gls{record} conflict
involving \encap{emph}, try replacing \encap{emph} with
\encap{hyperbf} and see if that resolves the conflict.
-Note that if the conflict includes a range formation, the
-range takes precedence.
+Note that if the conflict includes a \idx{range} formation, the
+\idx{range} takes precedence.
The mapping tests are applied as the records are
read. For example, suppose the records are listed in the \ext{aux}
file as:
@@ -3824,11 +3834,12 @@ switch with a comma-separated list of formats that should be
retained. Note that exact duplicates will still be merged.
This switch has a cumulative effect.
-Take care if you use this switch and you have an explicit range with
+Take care if you use this switch and you have an \idx{explicit-range} with
coincident start and end locations. If the
\glsdisp{principallocation}{principal record} is between
-the start and end format markers then the range can't collapse
-to an ordinary \gls{record}.
+the start and end format markers then the \idx{range} can't collapse
+to an ordinary \gls{record}. (You may need to use
+\csopt[true]{merge-ranges}.)
\argsection{no-retain-formats}
@@ -8648,10 +8659,11 @@ The default setting is \csopt[no value]{missing-parent-category}.
\optsection{group-level}
-If letter group formation is enabled (see \csopt{group},
+If letter \idx{group} formation is enabled (see \csopt{group},
\csopt{group-formation} and \longarg{group}) then the default
behaviour is to only assign the group label for \glspl{top-levelentry}.
-This option allows the group label to be assigned to \glspl{sub-entry}.
+This option allows the group label to be assigned to \glspl{sub-entry}
+if \idxpl{sub-group} are required.
The value may be one of the following:
\begin{itemize}
\item \meta{n}: only assign the group for level~\meta{n} entries;
@@ -8671,10 +8683,10 @@ hierarchical levels start at 0 (\gls{top-levelentry}). For any value
other than \csopt[0]{group-level}, the parent entry label will be
included in the group label.
-The hierarchical group titles are formatted according to
+The \idx{hiergroup} titles are formatted according to
\gls{bibglshiersubgrouptitle}. If the group title would
usually be set with the command \csfmt{bibglsset\ldots group}
-for \glspl{top-levelentry} then the hierarchical group title would
+for \glspl{top-levelentry} then the \idx{hiergroup} title would
be set with the analogous \csfmt{bibglsset\ldots group} command.
For example, letter groups are normally set with
\gls{bibglssetlettergrouptitle} but hierarchical letter groups are set
@@ -8684,21 +8696,30 @@ If the \longarg{no-group} setting is on then this option has no effect.
\begin{important}
Any value other than the default \csopt[0]{group-level} requires
-\sty{glossaries-extra} v1.49+.
+\sty{glossaries-extra} v1.49+, which provides \cs{glssubgroupheading}.
\end{important}
+\Idxpl{sub-group} are implemented by the glossary style command:
+\nosecformatdef{glssubgroupheading}
+The \sty{glossaries-extra} package automatically implements:
+\begin{codeenv}
+\cmd{renewcommand}*\marg{\cs{glssubgroupheading}}[4]\marg{\cs{glsgroupheading}\marg{\idx{param}4}}
+\end{codeenv}
+whenever a style is set, so that if the style doesn't provide a
+definition for this command, it will behave like \cs{glsgroupheading}.
+
\optsection{merge-small-groups}
-Merges consecutive groups that have less than \meta{n} entries. The
+Merges consecutive \idxpl{smallgroup} that have less than \meta{n} entries. The
default is \csopt[0]{merge-small-groups}, which switches off this
action. If \meta{n} is omitted, \csopt[1]{merge-small-groups} is
assumed.
This setting only has an effect if group formation is enabled.
-If hierarchical sub-groups are enabled (\csopt{group-level}) then
+If hierarchical \idxpl{sub-group} are enabled (\csopt{group-level}) then
merging is only performed on consecutive small groups within the
same hierarchical level. Any \glspl{childentry} that aren't in their
-own sub-group are included in the higher level group count.
+own \idx{sub-group} are included in the higher level group count.
For example, suppose you have a large number of entries in most of
the letter groups:
@@ -12338,7 +12359,7 @@ the locations, use \csopt[see]{save-locations} (or similar).
The \field{loclist} field has the syntax of an \isty{etoolbox}
internal list and includes every \gls{location} (except for the
\glsdisp{discardedrecord}{discarded duplicates} and
-\iglspl{ignoredrecord}) with no range formations. Any explicit range
+\iglspl{ignoredrecord}) with no \idx{range} formations. Any \idx{explicit-range}
markup is stripped from the \glsopt{format} information to leave
just the \idx{encap} name, so you just get the start and end
\glspl{location} added as individual elements but they are still
@@ -12396,34 +12417,54 @@ The \csfmt{hyper}\meta{xx} set of commands all internally use
\gls{location}. See Table~6.1 in the \styfmt{glossaries}~\cite{glossaries}
user manual for a list of all the \csfmt{hyper}\meta{xx} commands.
-Ranges can be explicitly formed using the parenthetical
+\Idxpl{range} can be explicitly formed using the parenthetical
syntax \glsopt[\idx{openrange}]{format} and
\glsopt[\idx{openrange}]{format} or \glsopt[\idx{openrange}\meta{csname}]{format} and
\glsopt[\idx{closerange}\meta{csname}]{format} (where \meta{csname} is again the name of
a text-block command without the initial backslash) in the optional
-argument of commands like \ics{gls} or \ics{glsadd}. These will always
-form a range, regardless of \csopt{min-loc-range}, and will be
-encapsulated by \gls!{bibglsrange}. (This command is not used with
-ranges that are formed by collating consecutive \glspl{location}.) The
-initial marker is stripped from the \meta{format} argument of the
-\gls{location} formatting commands, such as \gls{glsnoidxdisplayloc}, to
-allow for easy conversion to the corresponding text-block command.
-
-Explicit ranges don't merge with neighbouring \glspl{location}, but
-will absorb any single \glspl{location} within the range that don't
-conflict. (Conflicts will be moved to the start of the explicit
-range.) For example, if \code{\cs{gls}\marg{sample}} is used on
-page~1,
+argument of commands like \ics{gls} or \ics{glsadd}. With
+\sty{glossaries-extra} v1.50+, you can also use \ics{glsstartrange}
+and \ics{glsendrange} (which is useful if the unbalanced parentheses
+upset syntax highlighting).
+
+These \idxpl{explicit-range} will always form a \idx{range},
+regardless of \csopt{min-loc-range}, unless the start and end
+coincide and \longarg{collapse-same-location-range} is in effect.
+The \idx{explicit-range} will be encapsulated with \gls!{bibglsrange}
+(unless \csopt[true]{merge-ranges}). (This command is not used with
+\idxpl{implicit-range} that are formed by collating consecutive \glspl{location}.)
+The initial marker is stripped from the \meta{format} argument of
+the \gls{location} formatting commands, such as
+\gls{glsnoidxdisplayloc}, to allow for easy conversion to the
+corresponding text-block command.
+
+\Idxpl{explicit-range} don't merge with neighbouring
+\glspl{location} (unless \csopt[true]{merge-ranges}), but will
+absorb any individual \glspl{location} within the \idx{range} that doesn't
+conflict. (Conflicts, denoted \idxpl{interloper}, will be moved to the start of the
+\idx{explicit-range}, regardless of \csopt{merge-ranges}.)
+For example, if \code{\cs{gls}\marg{sample}} is used on page~1,
\code{\cs{gls}\oarg{\glsopt[\idx{openrange}]{format}}\marg{sample}}
is used on page~2, \code{\cs{gls}\marg{sample}} is used on page~3, and
\code{\cs{gls}\oarg{\glsopt[\idx{closerange}]{format}}\marg{sample}}
is used on page~4, then the \gls{locationlist} will be 1, 2--4. The
-entry on page~3 is absorbed into the explicit range, but the range
-can't be expanded to include page~1. If the entry on page~3 had a
-different format to the explicit range, for example
+entry on page~3 is absorbed into the \idx{explicit-range}, but, with
+the default \csopt[false]{merge-ranges}, the
+\idx{range} can't be expanded to include page~1. If the entry on page~3 had a
+different format to the \idx{explicit-range}, for example
\code{\cs{gls}\oarg{\glsopt[\encap{textbf}]{format}}\marg{sample}}
-then it would cause a warning and be moved before the start of the
-range so that the \gls{locationlist} would then be 1, \textbf{3}, 2--4.
+then this will cause a warning and the \idx{interloper} will be
+moved before the start of the \idx{range} so that the
+\gls{locationlist} would then be 1, \textbf{3}, 2--4.
+
+The \csopt[true]{merge-ranges} option will make \idxpl{explicit-range}
+behave like \idxpl{implicit-range}, which allows them to merge with
+neighbouring \idxpl{range}. The \gls{bibglsrange} command won't be used in
+this case (regardless of whether or not the \idx{range} was merged with
+neighbouring \glspl{location}). Options such as
+\csopt{min-loc-range} won't have an effect on the merged \idx{range}, but
+will still effect \idxpl{implicit-range} that haven't been merged with an
+\idx{explicit-range}.
An \igls{ignoredrecord} identifies a term that needs to be treated as
though it has a \gls{record} for selection purposes, but the
@@ -12464,14 +12505,14 @@ If you also want to drop front matter \glspl{location} as well:
\cs{GlsXtrSetDefaultNumberFormat}\marg{\encap{glsignore}}
\end{codeenv}
-Note that explicit range formations aren't discarded, so if
-\encap{glsignore} is used in a range, such as:
+Note that \idx{explicit-range} formations aren't discarded, so if
+\encap{glsignore} is used in a \idx{range}, such as:
\begin{codeenv}
\ics{glsadd}\oarg{\glsopt[\idx{openrange}\encap{glsignore}]{format}}\marg{sample}
\ldots
\cs{glsadd}\oarg{\glsopt[\idx{closerange}\encap{glsignore}]{format}}\marg{sample}
\end{codeenv}
-then the range will be included in the \gls{locationlist} (encapsulated
+then the \idx{range} will be included in the \gls{locationlist} (encapsulated
with \ics{glsignore}), but this case would be a rather odd use of
this special format and is not recommended.
@@ -12531,8 +12572,8 @@ then the \ext{aux} file will contain:
This is a partial \gls{record} match. In this case, \bibgls\
makes the following tests:
\begin{itemize}
-\item If one of the formats includes a range formation, the
-range takes precedence.
+\item If one of the formats includes an \idx{explicit-range} formation marker, the
+\idx{range} takes precedence.
\item If one of the formats is \encap{glsnumberformat} (as in the
above example) or an \igls{ignoredrecord} format such as
@@ -12661,7 +12702,8 @@ usual \igls{locationlist};
\igls{locationlist}. You may want to consider using the
\longswitch{retain-formats} switch with this setting if you don't
want to lose a partial \gls{location} match (for example, if the
-\gls{principallocation} coincides with the start of an explicit range).
+\gls{principallocation} coincides with the start of an
+\idx{explicit-range}).
\end{itemize}
@@ -12812,7 +12854,7 @@ the \field{location} field due to the
\csopt[remove]{save-principal-locations} setting. This can lead to a
ragged \gls{locationlist}. The option \csopt[default
format]{save-principal-locations} can allow the
-\gls{principallocation} to be absorbed into a range.
+\gls{principallocation} to be absorbed into a \idx{range}.
The \gls{mainglossary} records are added through the category-independent post-name
hook with \cs{glsadd}. This won't be implemented until the entries are actually
@@ -12935,7 +12977,7 @@ define the custom commands as follows:
This is more useful if you want to simply omit the
\code{\glsopt[primaryfmt]{format}} option (just remove it from the above four
definitions), which makes it easier to merge the \glspl{location} into
-ranges in the index.
+\idxpl{range} in the index.
\optsection{primary-loc-counters}
@@ -13073,18 +13115,39 @@ first, followed by the \counter{chapter} group. Whereas with:
then the \field{primarylocations} field will have the
\counter{chapter} group first, followed by the \counter{page} group.
+\optsection{merge-ranges}
+
+This boolean option determines whether or not \idxpl{explicit-range} should
+merge with neighbouring \glspl{location} on either side of the
+\idx{range}. The default setting is \csopt[false]{merge-ranges}.
+
+Note that \gls{bibglsrange} won't be used with
+\csopt[true]{merge-ranges}, regardless of whether or not the \idx{range}
+was merged with neighbouring \glspl{location}. Options such as
+\csopt{min-loc-range}, \csopt{suffixF} and \csopt{suffixFF}
+won't have an effect on the merged range, but
+will still effect \idxpl{implicit-range} that haven't been merged with an
+\idx{explicit-range}.
+
+Regardless of the value of this option, \idxpl{interloper} will still be
+moved to the start of the \idx{range} and encapsulated with
+\gls{bibglsinterloper}.
+
\optsection{min-loc-range}
By default, three or more consecutive \glspl{location} \meta{loc-1},
\meta{loc-2}, \ldots, \meta{loc-n} are compressed into
-the range \code{\meta{loc-1}\ics{delimR} \meta{loc-n}}. Otherwise
+the \idx{range} \code{\meta{loc-1}\ics{delimR} \meta{loc-n}}
+(an \idx{implicit-range}). Otherwise
the \glspl{location} are separated by \gls!{bibglsdelimN} or \gls!{bibglslastDelimN}.
-As mentioned above, these aren't merged with explicit range formations.
+As mentioned above, these aren't merged with \idx{explicit-range}
+formations unless \csopt[true]{merge-ranges}.
-You can change this with the \csopt{min-loc-range} setting where
+You can change how many consecutive \glspl{location} are need to
+form an \idx{implicit-range} with the \csopt{min-loc-range} setting where
\meta{value} is either \optfmt{none} (don't form ranges) or an
integer greater than one indicating how many consecutive
-\glspl{location} should be converted into a range.
+\glspl{location} should be converted into a \idx{range}.
\bibgls\ determines if one \gls{location}
\code{\margm{prefix-2}\margm{counter-2}\margm{format-2}\margm{location-2}}
@@ -13278,7 +13341,8 @@ For example, if \meta{location-1} is \qt{B} and \meta{location-2}
is \qt{C}, then $n_1 = 66$ and $n_2 = 67$. Since $n_2 = 67 = 66+1=
n_1+1$ then \meta{location-2} immediately follows \meta{location-1}.
-This is used in the range formations within the \glspl{locationlist} (as described
+This is used in the \idx{implicit-range} formations within
+the \glspl{locationlist} (as described
in the above section).
So, for example, the list \qt{1, 2, 3, 5, 7, 8, 10, 11, 12, 58, 59,
61} becomes
@@ -13288,9 +13352,9 @@ The automatically indexing of commands like \ics{gls} means that
the \glspl{locationlist} can become long and ragged. You could
deal with this by switching off the automatic indexing and
only explicitly index pertinent use or you can adjust
-the value of \optfmt{max-loc-diff} so that a range can be formed even
+the value of \optfmt{max-loc-diff} so that a \idx{range} can be formed even
if there are one or two gaps in it.
-By default, any \gls{location} ranges that have skipped gaps in this
+By default, any \idxpl{range} that have skipped gaps in this
manner will be followed by \gls!{bibglspassim}. The default
definition of this command is obtained from the resource file.
For English, this is \verb*| passim| (space followed by \qt{passim}).
@@ -13302,9 +13366,11 @@ term.
\optsection{suffixF}
-If set, a range consisting of two consecutive \glspl{location}
-\meta{loc-1} and \meta{loc-2} will be
-displayed in the \gls{locationlist} as \meta{loc-1}\meta{value}.
+If set, an \idx{implicit-range} consisting of two consecutive
+\glspl{location} \meta{loc-1} and \meta{loc-2} will be displayed in
+the \gls{locationlist} as \meta{loc-1}\meta{value}. This option
+doesn't affect \idxpl{explicit-range}, even with
+\csopt[true]{merge-ranges}.
Note that \csopt[\empty]{suffixF} sets the suffix to the
empty string. To remove the suffix formation use
@@ -13314,9 +13380,11 @@ The default is \csopt[none]{suffixF}.
\optsection{suffixFF}
-If set, a range consisting of three or more consecutive \glspl{location}
-\meta{loc-1} and \meta{loc-2} will be
+If set, an \idx{implicit-range} consisting of three or more
+consecutive \glspl{location} \meta{loc-1} and \meta{loc-2} will be
displayed in the \gls{locationlist} as \meta{loc-1}\meta{value}.
+This option doesn't affect \idxpl{explicit-range}, even with
+\csopt[true]{merge-ranges}.
Note that \csopt[\empty]{suffixFF} sets the suffix to the
empty string. To remove the suffix formation use
@@ -13331,9 +13399,9 @@ The \meta{value} may be an integer \meta{n} or \optfmt{false} (equivalent to
\csopt[3]{compact-ranges}). If no \meta{value} is specified,
\optfmt{true} is assumed.
-This setting allows \gls{location} ranges such as 184--189 to appear
+This setting allows location \idxpl{range} such as 184--189 to appear
more compactly as 184--9. The end \gls{location} is encapsulated
-in the command \gls{bibglscompact}, so the range would actually
+in the command \gls{bibglscompact}, so the \idx{range} would actually
become:
\begin{codeenv}
184\ics{delimR}\gls{bibglscompact}\marg{digit}\marg{18}\marg{9}
@@ -13341,7 +13409,7 @@ become:
If the \gls{location} is in the form \code{\meta{cs}\margm{loc}}
(where \meta{cs} is a command)
then \gls{bibglscompact} will be inside the argument.
-For example, if the range would normally be:
+For example, if the \idx{range} would normally be:
\begin{codeenv}
\cmd{custom}\marg{184}\ics{delimR}\cmd{custom}\marg{189}
\end{codeenv}
@@ -13630,9 +13698,9 @@ consider the following document:
\cmd{end}\marg{document}
\end{codeenv}
This results in the \gls{locationlist} \qt{1, 1--3, 3--5}. This
-looks a little odd and it may seem as though the range formation
+looks a little odd and it may seem as though the \idx{implicit-range} formation
hasn't worked, but the \glspl{location} are actually: page~1, equation~1,
-equation~2, equation~3, page~3, page~4 and page~5. Ranges can't
+equation~2, equation~3, page~3, page~4 and page~5. \Idxpl{range} can't
be formed across different counters.
The \csopt[\meta{list}]{loc-counters} option instructs \bibgls\
@@ -13727,7 +13795,7 @@ the place where the corresponding \cs{label} occurred.
This method works partially with \idx!{makeindex} and \idx!{xindy}
but from their point of view the \gls{location} is the value of the
\counter{wrglossary} counter, which interferes with their ability to
-merge duplicate page numbers and form ranges. Since \bibgls\ is
+merge duplicate page numbers and form \idxpl{range}. Since \bibgls\ is
designed specifically to work with \sty{glossaries-extra}, it's
aware of this special counter and will merge and collate the
\glspl{location} according to the corresponding page number instead.
@@ -16962,9 +17030,9 @@ will be adjusted to locally change the field to
in the \gls{secondaryglossary} will be the same as for the
\gls{primaryglossary}.
-(If the glossary \meta{type} doesn't exist, it will be
+If the glossary \meta{type} doesn't exist, it will be
defined with \ics{provideignoredglossary*}\margm{type}
-even if \longarg{no-provide-glossaries} is set.)
+even if \longarg{no-provide-glossaries} is set.
Note that if the glossary already exists and contains entries,
the existing entries aren't re-ordered. The new entries are
simply appended to the list.
@@ -19006,6 +19074,8 @@ This defaults to \code{,\idx{nbspchar}} to discourage lonely \glspl{location}.
\cssection{bibglscompact}
\formatdef{bibglscompact}
+This command is used with \csopt{compact-ranges} when the end
+\gls{location} in a \idx{range} is compacted.
The first argument \meta{pattern} indicates the \gls{location} pattern:
\code{digit} for digits, \code{roman} for \idx!{lowercase}
Roman numerals, \code{ROMAN} for \idx!{uppercase} Roman
@@ -19019,9 +19089,9 @@ This just does \meta{part2} by default.
\cssection{bibglspassim}
\formatdef{bibglspassim}
-If \csopt{max-loc-diff} is greater than~1, then any ranges that have
-skipped over gaps will be followed by \gls{bibglspassim}, which is
-defined as:
+If \csopt{max-loc-diff} is greater than~1, then any
+\idxpl{implicit-range} that have skipped over gaps will be followed
+by \gls{bibglspassim}, which is defined as:
\begin{codeenv}
\cs{providecommand}\marg{\gls{bibglspassim}}\marg{ \gls{bibglspassimname}}
\end{codeenv}
@@ -19048,25 +19118,26 @@ The default definition is obtained from the \langxml. For example, with
\cssection{bibglsrange}
\formatdef{bibglsrange}
-Explicit ranges formed using \glsopt[\idx{openrange}]{format} and
+\Idxpl{explicit-range} formed using \glsopt[\idx{openrange}]{format} and
\glsopt[\idx{closerange}]{format} or \glsopt[\idx{openrange}\meta{csname}]{format} and
\glsopt[\idx{closerange}\meta{csname}]{format} (where \meta{csname} matches and is a
text-block command without the initial backslash) in the optional
argument of commands like \ics{gls} or \ics{glsadd} are encapsulated within
-the argument of \gls{bibglsrange}. By default this simply does its
-argument. This command is not used with ranges that are formed by collating
-consecutive \glspl{location}.
+the argument of \gls{bibglsrange}. By default, this simply does its
+argument. This command is not used with \idxpl{implicit-range} that
+are formed by collating consecutive \glspl{location} or when
+\csopt[true]{merge-ranges} is used.
\cssection{bibglsinterloper}
\formatdef{bibglsinterloper}
-If an explicit range conflicts with a record, a warning will be
-issued and the conflicting record will be shifted to the front
-of the range inside the argument of \gls{bibglsinterloper}.
+If an \idxpl{explicit-range} conflicts with a record, a warning will be
+issued and the conflicting record (the \idx{interloper}) will be shifted to the front
+of the \idx{range} inside the argument of \gls{bibglsinterloper}.
The default definition just does \meta{location}\gls!{bibglsdelimN}
so that it fits neatly into the list.
-For example, suppose on page~4 of my document I start a range with:
+For example, suppose on page~4 of my document I start a \idx{range} with:
\begin{codeenv*}
\cs{glsadd}\oarg{\glsaddopt[\idx{openrange}]{format}}\marg{sample}
\end{codeenv*}
@@ -19074,21 +19145,22 @@ and end it on page~9 with:
\begin{codeenv*}
\cs{glsadd}\oarg{\glsaddopt[\idx{closerange}]{format}}\marg{sample}
\end{codeenv*}
-This forms an explicit range, but let's suppose on page~6 I
+This forms an \idx{explicit-range}, but let's suppose on page~6 I
have:
\begin{codeenv}
\cs{gls}\oarg{\glsopt[\encap{hyperbf}]{format}}\marg{sample}
\end{codeenv}
-This record conflicts with the explicit range (which doesn't include
+This record conflicts with the \idx{explicit-range} (which doesn't include
\encap{hyperbf} in the format). This causes a warning and
the conflicting entry will be moved before the start of the
-explicit range resulting in \textbf{6}, 4--9.
+\idx{explicit-range} resulting in \textbf{6}, 4--9.
-Note that implicit ranges can't be formed from interlopers (nor can
-implicit ranges be merged with explicit ones),
+Note that \idxpl{implicit-range} can't be formed from \idxpl{interloper} (nor can
+\idxpl{implicit-range} be merged with explicit ones with the default
+\csopt[false]{merge-ranges}),
so if \code{\cs{gls}\oarg{\glsopt[\encap{hyperbf}]{format}}\marg{sample}}
also occurs on pages~7 and~8 then the result will be \textbf{6}, \textbf{7},
-\textbf{8}, 4--9. Either remove the explicit range or
+\textbf{8}, 4--9. Either remove the \idx{explicit-range} or
remove the conflicting entries. (Alternatively, redefine
\gls{bibglsinterloper} to ignore its argument, which will
discard the conflicting entries.)
@@ -20708,12 +20780,16 @@ This command is only provided if \longarg{replace-quotes} is used.
\formatdef{bibglsuppercase}
Converts \meta{text} to \idx{uppercase}. This just uses
-\ics{MakeTextUppercase} by default.
+\ics{glsuppercase} (if \sty{glossaries} v4.50+,
+\sty{glossaries-extra} v1.49+ and \sty{mfirstuc} v2.08+)
+or \ics{MakeTextUppercase} by default.
\cssection{bibglslowercase}
\formatdef{bibglslowercase}
Converts \meta{text} to \idx{lowercase}. This just uses
+\ics{glslowercase} (if \sty{glossaries} v4.50+,
+\sty{glossaries-extra} v1.49+ and \sty{mfirstuc} v2.08+)
\ics{MakeTextLowercase} by default.
\cssection{bibglstitlecase}
@@ -24532,14 +24608,14 @@ The tagging format is governed by \ics{glsxtrtagfont} which
underlines its argument by default. I've redefined it to also
convert the letter to \idx{uppercase}:
\begin{codeenv}
-\cs{renewcommand}*\marg{\cs{glsxtrtagfont}}[1]\marg{\ics{underline}\marg{\cs{MakeTextUppercase}\marg{\idx{param}1}}}
+\cs{renewcommand}*\marg{\cs{glsxtrtagfont}}[1]\marg{\ics{underline}\marg{\cs{glsuppercase}\marg{\idx{param}1}}}
\end{codeenv}
Note that in the \code{mathml} case, the first tag consists of more
than one letter:
\begin{codeenv}
\field{long}=\marg{\cmd{abbrvtag}\marg{m\cs{NoCaseChange}\marg{ath}}ematical }\idx{stringconcat}markuplang
\end{codeenv}
-Here \ics{NoCaseChange} prevents \ics{MakeTextUppercase}
+Here \ics{NoCaseChange} prevents \ics{glsuppercase}
from applying the case change.
The default \csopt{selection} criteria includes entries that have
diff --git a/systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl b/systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl
index a3087f84ca..3dae9e5774 100644
--- a/systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl
+++ b/systems/texlive/tlnet/tlpkg/installer/ctan-mirrors.pl
@@ -51,9 +51,6 @@ $mirrors = {
'https://mirror.kakao.com/CTAN/' => 1,
'https://mirror.navercorp.com/CTAN/' => 1,
},
- 'Philippines' => {
- 'http://mirror.rise.ph/ctan/' => 1,
- },
'Singapore' => {
'https://download.nus.edu.sg/mirror/ctan/' => 1,
},
@@ -130,7 +127,6 @@ $mirrors = {
'https://ftp.snt.utwente.nl/pub/software/tex/' => 1,
'https://mirror.koddos.net/CTAN/' => 1,
'https://mirror.lyrahosting.com/CTAN/' => 1,
- 'https://mirrors.evoluso.com/CTAN/' => 1,
},
'Norway' => {
'https://ctan.uib.no/' => 1,
@@ -152,6 +148,7 @@ $mirrors = {
'https://ctan.altspu.ru/' => 1,
'https://mirror.macomnet.net/pub/CTAN/' => 1,
'https://mirror.truenetwork.ru/CTAN/' => 1,
+ 'https://mirrors.mi-ras.ru/CTAN/' => 1,
},
'Slovenia' => {
'https://ctan.ijs.si/tex-archive/' => 1,
diff --git a/systems/texlive/tlnet/tlpkg/texlive.tlpdb b/systems/texlive/tlnet/tlpkg/texlive.tlpdb
index 93e4c6d064..bfb7fae318 100644
--- a/systems/texlive/tlnet/tlpkg/texlive.tlpdb
+++ b/systems/texlive/tlnet/tlpkg/texlive.tlpdb
@@ -26,7 +26,7 @@ depend container_split_src_files/1
depend frozen/0
depend minrelease/2016
depend release/2022
-depend revision/64920
+depend revision/64937
name 00texlive.image
category TLCore
@@ -4664,7 +4664,7 @@ depend setting_available_architectures:aarch64-linux amd64-freebsd amd64-netbsd
name 00texlive.installer
category TLCore
-revision 64913
+revision 64922
shortdesc TeX Live standalone installer package
longdesc This package defines the files to go into the installer
longdesc archives (install-tl-unx.tar.gz, install-tl.zip) built by the
@@ -74109,18 +74109,18 @@ catalogue-topics font font-mf font-maths
name concmath-otf
category Package
-revision 64635
+revision 64924
shortdesc Concrete based OpenType Math font
relocated 1
longdesc This package provides an OpenType version of the Concrete Math
longdesc font created by Ulrik Vieth in Metafont. "concmath-otf.sty" is
longdesc a replacement for the original "concmath.sty" package to be
longdesc used with LuaTeX or XeTeX engines.
-containersize 133876
-containerchecksum 386ab377b16da8e10ebf1c775b955598a181f507e2d89e492ba92fec69676d59ff2823db5519a893c852eed982f3e673e2d6c71aa6fb4b5d2a0ce7428b3d5d75
-doccontainersize 1856412
-doccontainerchecksum a139b5560f478c4b843c3be77bcfb6e7ba7097e3755a68b2394b93c290d66bc91952236934937ab6fe3163f91cee8c2cdcc2bbd22db233944ab28ca948291efe
-docfiles size=482
+containersize 133888
+containerchecksum 0d6f22025552df9c9af0221912d55c85d1a477b33572e50be1be0d6c4044bf1976f85e595a31fb86358cfa7bee2c891aed1a9cd97a198889bcc1005077eecfb5
+doccontainersize 1856924
+doccontainerchecksum a95fe63c92fdeb65adbcb530298900dcc87e10e0d7aea53eaebffa17e153548253eec786ebd390ac41e1afee0e6575c2e72684220a01b9ee9a8d2b4309114b1e
+docfiles size=483
RELOC/doc/fonts/concmath-otf/README.md details="Readme"
RELOC/doc/fonts/concmath-otf/concmath-otf.ltx
RELOC/doc/fonts/concmath-otf/concmath-otf.pdf details="Package documentation"
@@ -74132,7 +74132,7 @@ runfiles size=57
catalogue-ctan /fonts/concmath-otf
catalogue-license ofl lppl1.3
catalogue-topics font font-otf font-maths font-supp
-catalogue-version 0.23
+catalogue-version 0.24
name concprog
category Package
@@ -102350,7 +102350,7 @@ catalogue-topics notes editorial
name easybook
category Package
-revision 64821
+revision 64929
shortdesc Easily typesetting Chinese theses or books
relocated 1
longdesc easybook is a pure academic template created based on the
@@ -102361,16 +102361,16 @@ longdesc providing multiple commands and interfaces allows users to
longdesc easily customize the thesis template. Its basic macro package
longdesc easybase can also be used with CTeX and standard document
longdesc classes.
-containersize 16568
-containerchecksum cbecb02ebb6aa7dfb9c743083617971859fe5c814979ff97330e1d8b7e7bf12e98d5f63c5a16f2e8676f9d841cea93a87239e7e46d91d87dc90672ef079067f0
-doccontainersize 610744
-doccontainerchecksum 6d1b7803c9537a94cb991c467e9ee2bc928c413343e75c0432b46080ba4c55808f589b7094fd77ceadeb0ab762712d6d3dd76aa7d0fbe386524f3f6775a1b051
+containersize 16564
+containerchecksum ed429726429719dfe736010c226f0d09630d0d48bf5af780423044d6ed91ff5c81821c24dc836fca613f348fefc313b5a2bee04a030a4e7e38152c711c25c948
+doccontainersize 611556
+doccontainerchecksum 64fe4e6bcde1676d958ba6bb4fc0768e571d661d2809eab471bffadbd0aa60fd4d3ea9d9e22da973d56605cb27dc3776c0a4786206ab4d8d115ead49bbdb0b65
docfiles size=166
RELOC/doc/latex/easybook/README.md details="Readme"
RELOC/doc/latex/easybook/easybook.pdf details="Package documentation" language="zh"
RELOC/doc/latex/easybook/easybook.tex
-srccontainersize 16468
-srccontainerchecksum f3de12a6331afa1ca93ae9edabe2ca16db61ab65b699c456f9d0bbd0b0812bae798238c3fbaa34a79450fb570faeb8295470478d9d0bb2cc5019df6ac1bd1d8e
+srccontainersize 16480
+srccontainerchecksum 2f62ef96030387cc4bfd6d9354bf89beec38e18ae445c7ab39cc5952b0b574c30ba795ebd9b4e4e8a80f4a4723dba1d364244449dda94cca086d6df5796fcb6d
srcfiles size=20
RELOC/source/latex/easybook/easybook.dtx
RELOC/source/latex/easybook/easybook.ins
@@ -102383,7 +102383,7 @@ catalogue-contact-repository https://gitee.com/texno3/easybook
catalogue-ctan /macros/latex/contrib/easybook
catalogue-license lppl1.3c
catalogue-topics book-pub dissertation class doc-templ chinese expl3
-catalogue-version 1.71A
+catalogue-version 1.71C
name easyfig
category Package
@@ -111794,15 +111794,15 @@ catalogue-version 1.12
name erewhon-math
category Package
-revision 64751
+revision 64925
shortdesc Utopia based OpenType Math font
relocated 1
longdesc OpenType version of the fourier Type1 fonts designed by Michel
longdesc Bovani.
-containersize 267704
-containerchecksum 81904cdd7e384eb7f8baa8b23b717552823ab1e7671d54f33aa0686bcf591be83c3fa57b976cd12abb479e93c1ff8a63933b56b4cc09ccedcc8f3c83d2b5aecf
-doccontainersize 1987076
-doccontainerchecksum ae63d6475f2688ccc5086d4b151fcdd237f9d22e303d58a7d6546b12f103baa5b9d6e22d286c2349903dbe829eb8832a187442649f38b8c20c386179fd91944e
+containersize 267620
+containerchecksum fb14c63ef33ad5497639b2d47611bdc1ae7cd5d54fa7456dbccb29436a7bd5b52587ab2f5bcba72f4eb77e7d8a02314f36af4a26d70e707c9bf9fd5597e5e5a5
+doccontainersize 1987428
+doccontainerchecksum 908a186ac753ff68519cd0ef1e381a7b313a81e780540313ce2eba372076b446135139513f2bc99f295d4f05af1e2d87f336866308aa82453357761c57b3144b
docfiles size=520
RELOC/doc/fonts/erewhon-math/Erewhon-Math.ltx
RELOC/doc/fonts/erewhon-math/Erewhon-Math.pdf details="Package documentation"
@@ -111816,7 +111816,7 @@ runfiles size=124
catalogue-ctan /fonts/erewhon-math
catalogue-license ofl lppl1.3
catalogue-topics font font-proportional font-otf font-supp font-maths
-catalogue-version 0.54
+catalogue-version 0.55
name errata
category Package
@@ -165734,7 +165734,7 @@ catalogue-version 0.9.2
name kotex-oblivoir
category Package
-revision 63005
+revision 64928
shortdesc A LaTeX document class for typesetting Korean documents
relocated 1
longdesc The class is based on memoir, and is adapted to typesetting
@@ -165743,10 +165743,10 @@ longdesc belongs to the ko.TeX bundle. It depends on memoir and
longdesc kotex-utf to function.
depend kotex-utf
depend memoir
-containersize 43476
-containerchecksum 2ae500f385671d0950274962eb1816f59f41a8c7d5a1fb8494071d1efa9e1af539cbafbce54890b2c9ea4ca6fd183957e551204bdd43ecd43b862dac17295597
-doccontainersize 539232
-doccontainerchecksum 092c248bb2c00006f2ab0ebf9b026c5b9b3621b3844c2bd24e946701f6dc2efbf059ad65d2da080c9b1b6a1062967c5c365085dd57dd775c13217973740fa9f0
+containersize 43484
+containerchecksum 17a5f52990cfdf57fdc8ffdc3fc417a15ab52e21296a95b6b1698166ac215937bc26db8aeac7b38459cdf3411480045af85aec491884cd89f00896916a7702e0
+doccontainersize 539216
+doccontainerchecksum 5ef9480a78f216dcf11fe06d8766ba66817a86faa7d0003af77a1fe1079f5e8a8c527bcd333e5fe02153f6ca0b0a82eaa206eb1c5bb7b9517b9ef2865faadc4b
docfiles size=186
RELOC/doc/latex/kotex-oblivoir/ChangeLog
RELOC/doc/latex/kotex-oblivoir/README details="Readme"
@@ -165794,7 +165794,7 @@ runfiles size=71
catalogue-ctan /language/korean/kotex-oblivoir
catalogue-license lppl1.3c
catalogue-topics korean class
-catalogue-version 3.1.5
+catalogue-version 3.1.5.1
name kotex-plain
category Package
@@ -192617,19 +192617,25 @@ catalogue-version 1.17
name luafindfont
category Package
-revision 64270
+revision 64936
shortdesc Search fonts in the LuaTeX font database
longdesc This Lua script searches for fonts in the font database.
depend luafindfont.ARCH
-containersize 5372
-containerchecksum 586d8fff43665138697ab94d8b58f344674283c01cec6595c8d2dc83fe39c1df0a36969455390d7f3955a411815696945c7ffc19535fde3d0a9dc5a245d371d0
-doccontainersize 116040
-doccontainerchecksum 3927285d9a56e5aae54add1bd8254530254d8b321a03ef44e4fa6b6ea2e8b4aa437c4bd352630bf99cfaf68284de6f5fec7bceb4c41fd7e92d3520c981aedbae
-docfiles size=36
+containersize 5536
+containerchecksum a73bfe0aa1b6a907224cc98f2d5f6344249f79010ad5552c66286eb7c103d5c69851a452cb6eebf39ebd5b6e8e64062efb125bea1c6586ef5117f994a97244bd
+doccontainersize 124356
+doccontainerchecksum e7196d9a2e69b5a6d5582d7ddc00ac480b16228b424cb9d568ef1ff6fbef48e5926776d5f22fa0eb5c4b09b6b29a283416206f64cf324356b35d66228bbbd3ea
+docfiles size=44
texmf-dist/doc/man/man1/luafindfont.1
texmf-dist/doc/man/man1/luafindfont.man1.pdf
texmf-dist/doc/support/luafindfont/CHANGELOG
texmf-dist/doc/support/luafindfont/README.md details="Readme"
+ texmf-dist/doc/support/luafindfont/exa01.txt
+ texmf-dist/doc/support/luafindfont/exa02.txt
+ texmf-dist/doc/support/luafindfont/exa03.txt
+ texmf-dist/doc/support/luafindfont/exa04.txt
+ texmf-dist/doc/support/luafindfont/exa05.txt
+ texmf-dist/doc/support/luafindfont/exa06.txt
texmf-dist/doc/support/luafindfont/luafindfont-doc.pdf details="Package documentation"
texmf-dist/doc/support/luafindfont/luafindfont-doc.tex
runfiles size=5
@@ -192638,7 +192644,7 @@ catalogue-alias findfont
catalogue-ctan /support/luafindfont
catalogue-license lppl1.3c
catalogue-topics font-sel
-catalogue-version 0.10
+catalogue-version 0.11
name luafindfont.aarch64-linux
category Package
@@ -194239,15 +194245,15 @@ catalogue-version 0.6
name luaquotes
category Package
-revision 64812
+revision 64934
shortdesc Smart setting of quotation marks
relocated 1
longdesc This package automatically generates quotation marks and
longdesc punctuation depending on the selected language.
containersize 2628
-containerchecksum dd941d27963117b3630c7b047f11b661b70a85b143906be7d3e33d090f2cb5ee947f777ce14ccd8914d3f81684382f747d18758a1f42e791aa76fa125225ace9
-doccontainersize 113916
-doccontainerchecksum 06ef0dd46d16896c968a4e7378b1b8c15d316e7677207ce0ef4fba450d3952d61628a4b4c4568f5fc584d7535ed5432f765ca3ff33e8172ef9fac801299086ee
+containerchecksum 592b10797200066f8e7640c9a9f51ee7ed5b718c5c6c7ab6cf9ef959bb013399da8c1dc4bcc960db3703fd7de2e7fd4418140722583d3a853090ba51f42e183d
+doccontainersize 114004
+doccontainerchecksum ce1b1d79c1c29046d62a1a660b208c6a6888357f89be7ef9fb3af342b10a8a2f71ee698a6706d1c6ab9e79a7f6e870a143344a2e17998cbeb057f9d5e896eec6
docfiles size=37
RELOC/doc/lualatex/luaquotes/README details="Readme"
RELOC/doc/lualatex/luaquotes/luaquotes-documentation.pdf details="Package documentation"
@@ -194259,7 +194265,7 @@ catalogue-contact-repository https://github.com/ezgranet/luaquotes
catalogue-ctan /macros/luatex/latex/luaquotes
catalogue-license lppl1.3 cc-by-sa-3
catalogue-topics quote-marks
-catalogue-version 1.1.0
+catalogue-version 1.2.0
name luarandom
category Package
@@ -198817,7 +198823,7 @@ catalogue-version 1.143
name mathastext
category Package
-revision 52840
+revision 64930
shortdesc Use the text font in maths mode
relocated 1
longdesc The package uses a text font (usually the document's text font)
@@ -198829,35 +198835,31 @@ longdesc little worry that no specially designed accompanying maths
longdesc fonts are available. The package also offers a simple mechanism
longdesc for using many different choices of (text hence, now, maths)
longdesc font in the same document. Of course, using one font for two
-longdesc purposes helps produce smaller PDF files. The package, if
-longdesc running under LuaTeX, requires the TeX live 2013 distribution
-longdesc (or later).
-containersize 15984
-containerchecksum a8c0ac4c041b52032e1bfba14eeb6ad42206428a8cc576597d7b509d652c818b2568f043a6edef0bd491057c661e30d9e61575d1fe0c8414e45f08d9629b4e19
-doccontainersize 530004
-doccontainerchecksum b2b1deeb2cb3a966d64a1e367de2ae6f932b5cca714a15ed113def4b00d3046fadfa4724dfe21028fe82bcd5d76a1c73196a2c599a0f6a56ec113fd4421c3456
-docfiles size=148
+longdesc purposes helps produce smaller PDF files.
+containersize 17188
+containerchecksum 28288f54a92726e0b5d741fbc78c1e6f87d9892b28bef5c2aa7285762bd265048c801f55e8590b18de43be82fd353f23d18e55a8d9491ed446c058373f736bda
+doccontainersize 584908
+doccontainerchecksum 758c42dfbc5950d37804e9c5cf1f6b6cadf1c838c1687a7cc4f9a0e0193e75f6630e0568767953a45f73bc04b22332ab62adff9f4262f106e9a341cba6207dd6
+docfiles size=159
RELOC/doc/latex/mathastext/ChangeLog.md
- RELOC/doc/latex/mathastext/INSTALL.txt
RELOC/doc/latex/mathastext/README.md details="Readme"
RELOC/doc/latex/mathastext/mathastext.pdf details="Package documentation"
RELOC/doc/latex/mathastext/mathastext.tex
- RELOC/doc/latex/mathastext/mathastexttestalphabets.pdf details="Output of test script"
RELOC/doc/latex/mathastext/mathastexttestalphabets.tex
RELOC/doc/latex/mathastext/mathastexttestmathversions.tex
RELOC/doc/latex/mathastext/mathastexttestunicodelinux.tex
RELOC/doc/latex/mathastext/mathastexttestunicodemacos.tex
-srccontainersize 94384
-srccontainerchecksum f22a88f5ba02bb077fa0e3128a27e78b736e07702d611a059867646d5c6b4cbb6548c1f0f0e968735c3f887038f51c351a98aa6d60ec557c246ec523afdc735d
-srcfiles size=95
+srccontainersize 105248
+srccontainerchecksum b184975082833620ce3af3aed8fbfbfd616648753abb0a82dbc602a1077455a024ab916daface8eddf54f9dc1d1ce7727a8959ec7990305ca48f7864915fca94
+srcfiles size=106
RELOC/source/latex/mathastext/mathastext.dtx
-runfiles size=24
+runfiles size=27
RELOC/tex/latex/mathastext/mathastext.sty
catalogue-contact-home http://jf.burnol.free.fr/mathastext.html
catalogue-ctan /macros/latex/contrib/mathastext
catalogue-license lppl1.3
catalogue-topics maths font-supp-maths font-sel
-catalogue-version 1.3w
+catalogue-version 1.3y
name mathcommand
category Package
@@ -241752,7 +241754,7 @@ catalogue-version 0.13
name pdfmanagement-testphase
category Package
-revision 64521
+revision 64931
shortdesc LaTeX PDF management testphase bundle
relocated 1
longdesc This is a temporary package, which is used during a test phase
@@ -241765,11 +241767,11 @@ longdesc allow users and package authors to safely test the code. At a
longdesc later stage it will be integrated into the LaTeX kernel (or in
longdesc parts into permanent support packages), and the current
longdesc testphase bundle will be removed.
-containersize 76696
-containerchecksum f520e397271b6139f54b8308b926c4f4897a8917114e115736a4ea6c321b2f4f34399659494d42242b3d67ef103f1647f6a113dddc667bd10edbf378a057ae50
-doccontainersize 8723252
-doccontainerchecksum 8de7714cca7aee6726f9bc611e0a0906f339a4145dd783d45c6c337d892017f208a6023fe077dd5b8a9f9b3ee96c76cbd21d46e5cf50e8128b55ecf1fe55bf4e
-docfiles size=2862
+containersize 75348
+containerchecksum 100e0975abefea1ccb9ff4eff625d943a1076d22f61117bfdf6f3e0d496b37f8ad719d1db8f7ff117230d25745090636293e5f1bbf394ec2dbe379c4107b8664
+doccontainersize 8727504
+doccontainerchecksum 38867f8ef9ce941ac59557e51fd9338d9c5113d0b0c9bb4e591085e7d40b84d45b66c5cad0236059cf07e40b56e527892cdcd2f3680cab27f2f3f7d68cdc49d0
+docfiles size=2865
RELOC/doc/latex/pdfmanagement-testphase/CHANGELOG.md
RELOC/doc/latex/pdfmanagement-testphase/README.md details="Readme"
RELOC/doc/latex/pdfmanagement-testphase/hyperref-generic.pdf
@@ -241792,8 +241794,8 @@ docfiles size=2862
RELOC/doc/latex/pdfmanagement-testphase/output-patches-tmp-ltx.pdf
RELOC/doc/latex/pdfmanagement-testphase/pdfmanagement-firstaid.pdf
RELOC/doc/latex/pdfmanagement-testphase/pdfmanagement-testphase.pdf details="Package documentation"
-srccontainersize 137740
-srccontainerchecksum 9ad5720f0080a87f24ce572f07c20cef8e7eb7b0fde2bffd9d777a733badbe334c2cf45940f69e014695235030d9864126e485d2293bdd95c036bff044469e8f
+srccontainersize 136668
+srccontainerchecksum 26c2b7269babdedd4019da3336f52cdac8c047420b560e9b17c8d88ac270e19f8d6a94d0aa587a7fc8b4f86f75d611744fce989ba86861c360b281de6b7f30ca
srcfiles size=197
RELOC/source/latex/pdfmanagement-testphase/hyperref-generic.dtx
RELOC/source/latex/pdfmanagement-testphase/l3backend-testphase.dtx
@@ -241816,7 +241818,7 @@ srcfiles size=197
RELOC/source/latex/pdfmanagement-testphase/pdfmanagement-firstaid.dtx
RELOC/source/latex/pdfmanagement-testphase/pdfmanagement-testphase.dtx
RELOC/source/latex/pdfmanagement-testphase/pdfmanagement-testphase.ins
-runfiles size=145
+runfiles size=142
RELOC/tex/latex/pdfmanagement-testphase/color-ltx.sty
RELOC/tex/latex/pdfmanagement-testphase/colorspace-patches-tmp-ltx.sty
RELOC/tex/latex/pdfmanagement-testphase/fontspec-luatex-tmp-ltx.sty
@@ -241833,18 +241835,16 @@ runfiles size=145
RELOC/tex/latex/pdfmanagement-testphase/l3ref-tmp.sty
RELOC/tex/latex/pdfmanagement-testphase/luaotfload-l3color-tmp.lua
RELOC/tex/latex/pdfmanagement-testphase/output-patches-tmp-ltx.sty
- RELOC/tex/latex/pdfmanagement-testphase/pdflscape-ltx.sty
RELOC/tex/latex/pdfmanagement-testphase/pdfmanagement-firstaid.sty
RELOC/tex/latex/pdfmanagement-testphase/pdfmanagement-testphase.ltx
RELOC/tex/latex/pdfmanagement-testphase/pdfmanagement-testphase.sty
- RELOC/tex/latex/pdfmanagement-testphase/transparent-ltx.sty
RELOC/tex/latex/pdfmanagement-testphase/xcolor-patches-tmp-ltx.sty
catalogue-contact-bugs https://github.com/latex3/pdfresources/issues
catalogue-contact-repository https://github.com/latex3/pdfresources
catalogue-ctan /macros/latex/contrib/pdfmanagement-testphase
catalogue-license lppl1.3c
catalogue-topics latex-devel pdf-feat
-catalogue-version 0.95s
+catalogue-version 0.95t
name pdfmarginpar
category Package
@@ -254849,7 +254849,7 @@ catalogue-version 1.0
name proflycee
category Package
-revision 64765
+revision 64932
shortdesc A LaTeX package for French maths teachers in high school
relocated 1
longdesc This package provides some commands to help French mathematics
@@ -254858,11 +254858,11 @@ longdesc create splines with "derivative control"; \paramCF and \ligneCF
longdesc in order to create an xcas-windows-like; \envconsolepythontex
longdesc and \envcodepythontex to create code presentation and code
longdesc execution with pythontex.
-containersize 20544
-containerchecksum 07c1b7bc5cd2e27966024fd0ce314298337e4afd49dcc53cfe70824f15dd5fa7d08064eb43b2ce05249736dc1e3bf09b4c1a0a9d27177e769f011801c38c345f
-doccontainersize 1069480
-doccontainerchecksum be134b49b7a0dfb6eb4d48e84e42beabbefe9a07674bfcecf543cb2ff382ba11bd4e5b613616d79ba9367e4e45be0ad4ddb1f4e830df878a9b89f4f3d6b98ebb
-docfiles size=316
+containersize 20704
+containerchecksum 5a138008e08bd4f9b03e3fbb920a70a223c7b6c40a98c0ef4049639bf58fe1387c3f3fd899282da6f4a2100888c9faa499215726f2fbed40db19502ba345070b
+doccontainersize 835036
+doccontainerchecksum 1aa8e811f2835634e11d07dceb1175962399d240a2cf4e6ba7cb266d3d8b613c11a776e9f25ec822731839177386ed28a312c7737d64fff58bb696976a684770
+docfiles size=260
RELOC/doc/latex/proflycee/ProfLycee-doc.pdf details="Package documentation" language="fr"
RELOC/doc/latex/proflycee/ProfLycee-doc.tex
RELOC/doc/latex/proflycee/README.md details="Readme"
@@ -254890,7 +254890,7 @@ catalogue-contact-repository https://github.com/cpierquet/ProfLycee
catalogue-ctan /macros/latex/contrib/proflycee
catalogue-license lppl1.3c
catalogue-topics maths french teaching expl3 pgf-tikz
-catalogue-version 1.3.6
+catalogue-version 1.3.7
name program
category Package
@@ -281488,7 +281488,7 @@ catalogue-version 1.6a
name simpleicons
category Package
-revision 64855
+revision 64926
shortdesc Simple Icons for LaTeX
relocated 1
longdesc Similar to FontAwesome icons being provided on LaTeX by the
@@ -281496,10 +281496,10 @@ longdesc fontawesome package, this package aims to do the same with
longdesc Simple Icons. For reference, visit their website:
longdesc https://simpleicons.org/.
execute addMap simpleicons.map
-containersize 2700300
-containerchecksum 3858ed784073fc2179a2c704f85185bee6a2feab267f7e6e807211756ec8154ae962059a61a3f00d091cdc959955941a0ff5b6f5c6786ef39ff07c5e2dec629a
-doccontainersize 1982104
-doccontainerchecksum 0c320ba57476b239b8224837e4b8885e27794f8bea4a95616917a2b7b03191513f8a11b3395376482b7c37bb81f94724c805d43c07348d5af207e6ec171b6e9d
+containersize 2701752
+containerchecksum edcc96c4f32809ef444e9f86926e0e7a6a34aa2f380ce5e5476dc1a9b5cd4761806e9d521d143626e25cf04efd8c3e5af413999d424c9a9db88d5087f0803f2e
+doccontainersize 1983352
+doccontainerchecksum 26882d62cdc4bb96e02e5580d63f030f44faf0310bd31961c8e9ec31d702745134aa6448b33695f83cfc1c010875de6a2d3a2ef1a9a4afdead36039cb419640a
docfiles size=516
RELOC/doc/fonts/simpleicons/README.md details="Readme"
RELOC/doc/fonts/simpleicons/bindings.tex
@@ -281547,7 +281547,7 @@ catalogue-contact-repository https://github.com/ineshbose/simple-icons-latex
catalogue-ctan /fonts/simpleicons
catalogue-license cc-by-1
catalogue-topics font font-symbol font-supp-symbol font-otf font-type1
-catalogue-version 7.17.0
+catalogue-version 7.18.0
name simpleinvoice
category Package
@@ -298839,7 +298839,7 @@ binfiles arch=x86_64-solaris size=1
name tex4ht
category Package
-revision 64911
+revision 64937
shortdesc Convert (La)TeX to HTML/XML
longdesc A converter from TeX and LaTeX to SGML-based formats such as
longdesc (X)HTML, MathML, OpenDocument, and Docbook, providing a
@@ -298860,10 +298860,10 @@ longdesc files from them is nontrivial, and generally done with the
longdesc Makefile in development, from which the TeX4ht package in TeX
longdesc Live is updated.
depend tex4ht.ARCH
-containersize 945148
-containerchecksum c24f6fc600e50f3e93d33e675d9c0f973b316fe665447fee3f2101737977bceab82de1c11cc14b617d39208b642a5a56e3073fa93e7fe5dc633b5aff059dadd9
+containersize 945544
+containerchecksum fc31d4ce2eab11bdfb03ac59ef19c260a908398cf3147f163b61c5bdaf1ad3b93b2fa2d07e64a3a6b15a412190c03fe25efce07948b90c5457f4f3857de651de
doccontainersize 136384
-doccontainerchecksum 01f6161e55c0cfccb8724b4d991c7d17ca44749b65df33240e2260721a1bafeb0caed1cd0d82569ec557d449f6cac5651cb6cc03da6fdee1fe830a82473a7634
+doccontainerchecksum 5a1d90bce26deb874c820f5de1f91d10ab73ddc0938ac8cbd1753e7012a6cebe2c8d897a52edddf642914f19c9608ad769a4bde89607f04c5a0c7325b17255c6
docfiles size=260
texmf-dist/doc/generic/tex4ht/Makefile
texmf-dist/doc/generic/tex4ht/README details="Readme"
@@ -298935,9 +298935,9 @@ docfiles size=260
texmf-dist/doc/generic/tex4ht/mn56.html
texmf-dist/doc/generic/tex4ht/mn5x.png
texmf-dist/doc/generic/tex4ht/mn6x.png
-srccontainersize 1278688
-srccontainerchecksum b08589be57a8ef923ba8befba38414d5aacb5b8e29b12bea1176e6c5c6d871bc37489491bb627c45e7941b7c69ba8e8598320289ef0ea3f92e472b1011f82b57
-srcfiles size=3872
+srccontainersize 1273088
+srccontainerchecksum a0173447edf9606dcea32f6015ebf69891956bd3e7c3e2c165422c5f83d71bb4567b4ad48d17e744f04a29337324bc1197c317cd20c6d849a15a52909151b5cd
+srcfiles size=3873
texmf-dist/source/generic/tex4ht/ChangeLog
texmf-dist/source/generic/tex4ht/Makefile
texmf-dist/source/generic/tex4ht/README
@@ -299013,7 +299013,7 @@ srcfiles size=3872
texmf-dist/source/generic/tex4ht/update-htfonts
texmf-dist/source/generic/tex4ht/wripro.tex
texmf-dist/source/generic/tex4ht/xtpipes.tex
-runfiles size=11194
+runfiles size=11196
texmf-dist/scripts/tex4ht/ht.sh
texmf-dist/scripts/tex4ht/htcontext.sh
texmf-dist/scripts/tex4ht/htcopy.pl
@@ -299491,6 +299491,7 @@ runfiles size=11194
texmf-dist/tex/generic/tex4ht/xr-hyper.4ht
texmf-dist/tex/generic/tex4ht/xr.4ht
texmf-dist/tex/generic/tex4ht/xrhyper-hooks.4ht
+ texmf-dist/tex/generic/tex4ht/xskak.4ht
texmf-dist/tex/generic/tex4ht/xy.4ht
texmf-dist/tex4ht/base/unix/tex4ht.env
texmf-dist/tex4ht/base/win32/tex4ht.env
@@ -309584,7 +309585,7 @@ docfiles size=376
name texlive-scripts
category TLCore
-revision 64913
+revision 64922
shortdesc TeX Live infrastructure programs
longdesc Includes install-tl, tl-portable, rungs, etc.; not needed for
longdesc tlmgr to run but still ours. Not included in tlcritical.
@@ -309592,10 +309593,10 @@ depend texlive-scripts.ARCH
depend texlive.infra
execute addMap mathpple.map
postaction shortcut type=menu name="TeX Live command-line" cmd=TEXDIR/tlpkg/installer/tl-cmd.bat
-containersize 113708
-containerchecksum 94a8ace5173a872b342d812aebc48cfbda216bf352d0812b65f2f95e5d34dc85f9e9311d662883ed17018398492ef5e0a68dcf6c84533b4f854b33e872726758
+containersize 113712
+containerchecksum cea7c5ca8a54774649a802d786a4f5ce8610f9b89dd110f82d46b762a61f504e9dae37b485f6ef626a66651751c10e38cc033d3b630239ff25d039ad144471bb
doccontainersize 423096
-doccontainerchecksum ffabcc4ac1756f56cd8b50c7657642e1e4496a819974e7612869cfc733bb9e553b7a03b1026c064d51c822bbac31ed5e745bcd9048148320edaab903eab4a13f
+doccontainerchecksum 78c588755f0d967ba2a458f06fd3f86f68e31ed9a3445b216f088f2025cd1bc63ed70e6dbb7c70fb40dcbbd3af8b812b5f735ca0b19e1f8dccb2ede7da30d6f0
docfiles size=531
doc.html
texmf-dist/doc/man/man1/fmtutil-sys.1
@@ -312668,17 +312669,17 @@ catalogue-version 1.03
name textcsc
category Package
-revision 64365
+revision 64935
shortdesc Simple commands for caps-to-small-caps text
relocated 1
-longdesc This package provides a simple command (\textcsc and \cscshape}
+longdesc This package provides a simple command (\textcsc and \cscshape)
longdesc for caps-to-small-caps text, to allow for small caps acronyms
longdesc to be presented as uppercase in text (useful for things like
longdesc copying and pasting from a PDF).
containersize 1120
-containerchecksum e7ba38af0785d6ec4b326c84f122b6a22afca819c8fb815205c7aed8c11138987e3483fb4340951effdf5d2c38184f1bfa13ea36a484fb50ed7fb8468efe24d3
-doccontainersize 51980
-doccontainerchecksum 15ea5f3add1348a378fbe5fbb2e52abfc36c572f5d7777cc280cec7fc3ae207b2550db4d3286d7d8858896d9b4ca070a229e079ef3e65178ff82907312f6f0ef
+containerchecksum 461d0e22efb4a80e1e76b6d209cb0247724d9cf9471875f612d4a664875a2055da58151ab6a0a763b7d73dbdf47c63bcb9f7c3e648e1bf074dc4758a7b01b071
+doccontainersize 51964
+doccontainerchecksum d10191e310550a7d4f83bbbaa9413be0448d38349839b8cff46f4fc3cd1c76c0b20ef002d01a5e78919fc019f33a8069fb6fd1c53ef32c81d12d65806c55439f
docfiles size=16
RELOC/doc/latex/textcsc/README details="Readme"
RELOC/doc/latex/textcsc/textcsc-documentation.pdf details="Package documentation"
@@ -312690,7 +312691,7 @@ catalogue-contact-home https://github.com/ezgranet/textcsc
catalogue-ctan /macros/unicodetex/latex/textcsc
catalogue-license lppl1.3 cc-by-sa-3
catalogue-topics typesetting unicode
-catalogue-version 1.0.0
+catalogue-version 1.0.1
name textfit
category Package
@@ -325514,7 +325515,7 @@ catalogue-version 1.0
name tuda-ci
category Package
-revision 64039
+revision 64933
shortdesc LaTeX templates of Technische Universitat Darmstadt
relocated 1
longdesc The TUDa-CI-Bundle provides a possibility to use the Corporate
@@ -325522,10 +325523,10 @@ longdesc Design of TU Darmstadt in LaTeX. It contains documentclasses as
longdesc well as some helper packages and config files together with
longdesc some templates for user documentation, which currently are only
longdesc available in German.
-containersize 43676
-containerchecksum a0232b2b1bdbba95a670f9b200e93b851f8f17497ac61e37650a3711edc4e131bf5d65223251a9e329317791b433e1dc8338c1dd2f849875be84ab65af61e7d5
-doccontainersize 1132348
-doccontainerchecksum 3007dffdbbb5638d2cd2ed7bf698735cdb7f3d821f8dd5d0e8c0e14e64a8fe3522303202c9a9af09a71c0274a2c7d8406b6df5185187111806655c2b0c5bb180
+containersize 43792
+containerchecksum 57e2b8a58ed7e14220571e5b5fbd97cfe43ea987fc2c2679dfc4612c0e879a0c0b94d33891f352eac3840af4bb8ed38e8405c098468205bac5c66c3b23f67497
+doccontainersize 1131412
+doccontainerchecksum b500f889806717f65a4a9278cc1277d1c6237c1ef10daf732834d1757e58716240586fd32e7aadc40ff9342568a8cc9ee32c6fad38865475c75f2b7046ccad6b
docfiles size=1615
RELOC/doc/latex/tuda-ci/DEMO-TUDaAnnouncement.pdf
RELOC/doc/latex/tuda-ci/DEMO-TUDaAnnouncement.tex
@@ -325588,7 +325589,7 @@ catalogue-contact-repository https://github.com/tudace/tuda_latex_templates
catalogue-ctan /macros/latex/contrib/tuda-ci
catalogue-license lppl1.3c
catalogue-topics class doc-templ letter dissertation presentation poster std-conform
-catalogue-version 3.27
+catalogue-version 3.28
name tudscr
category Package
@@ -346083,30 +346084,31 @@ catalogue-version 1.24
name xcharter-math
category Package
-revision 64653
+revision 64927
shortdesc XCharter-based OpenType Math font for LuaTeX and XeTeX
relocated 1
longdesc This package provides an Unicode Math font XCharter-Math.otf
longdesc meant to be used together with XCharter Opentype Text fonts
longdesc (extension of Bitstream Charter) in LuaLaTeX or XeLaTeX
longdesc documents.
-containersize 201872
-containerchecksum a713c3cc5f7fce3ed5d9182afe1383ba439c7b4966b8ba429802667b4bb248dcd576f26100ea7ce4c6514fd02722a04bcdf7a7ec928b58f1a940bf363aeb0a84
-doccontainersize 1947500
-doccontainerchecksum cfb6e88407d2f802d43247abad4e32ba1ef54148a8a87b43bcd3a02ccf82ad639391dc1db99518f3d9172c295661a21d7b758c140ae00e11cab5d8aa14631466
+containersize 217836
+containerchecksum 552c473140f49ec64e6bb6e42259fe9a292e966b827337b8e3c9c0fde21e9b51af16abde3e2449c32d4d83e1439ce2258e38cce9c2f616c51c4079ffa65bf999
+doccontainersize 1948324
+doccontainerchecksum bbd3ad62b4cc737dc753f7537ec0a60a11ee0c442c1cfc37a36e08b270b317c239e20a11d527865ec92ebca175d7eeb6ffb717377655bd9eeb8022922a9006a6
docfiles size=509
RELOC/doc/fonts/xcharter-math/README.md details="Readme"
RELOC/doc/fonts/xcharter-math/XCharter-Math.ltx
RELOC/doc/fonts/xcharter-math/XCharter-Math.pdf details="Package documentation"
RELOC/doc/fonts/xcharter-math/unimath-xcharter.ltx
RELOC/doc/fonts/xcharter-math/unimath-xcharter.pdf details="List of glyphs"
-runfiles size=82
+runfiles size=102
+ RELOC/fonts/opentype/public/xcharter-math/XCharter-Math-Bold.otf
RELOC/fonts/opentype/public/xcharter-math/XCharter-Math.otf
RELOC/tex/latex/xcharter-math/xcharter-otf.sty
catalogue-ctan /fonts/xcharter-math
catalogue-license ofl lppl1.3
catalogue-topics font font-otf font-maths font-supp-maths
-catalogue-version 0.34
+catalogue-version 0.35
name xcite
category Package
diff --git a/systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5 b/systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5
index e721dbb643..6b61e00b76 100644
--- a/systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5
+++ b/systems/texlive/tlnet/tlpkg/texlive.tlpdb.md5
@@ -1 +1 @@
-318fa06db7dec21b895767b15257880e texlive.tlpdb
+2f61f18d1b033905daad588a720df725 texlive.tlpdb
diff --git a/systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512 b/systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512
index 3fa5ab32a2..2630f0e8fc 100644
--- a/systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512
+++ b/systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512
@@ -1 +1 @@
-e339577dc4a791e304b03b2709cedfe022e83f24e5991e3ac3bed2c8ae9f4e189948297f528c1caa1944ab15989c7eaf5680b23506e057fdd4ac20e4ca4a206e texlive.tlpdb
+fc126fd763127c539cbef9639d95b22fb141b26d16d5fd71770a3939cdbce4f134737c3abfd567c3d3be95db8bdf4d2e554bb333ba370fb769995efd26909acd texlive.tlpdb
diff --git a/systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc b/systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc
index c359538b02..eca4cb1726 100644
--- a/systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc
+++ b/systems/texlive/tlnet/tlpkg/texlive.tlpdb.sha512.asc
@@ -1,10 +1,10 @@
-----BEGIN PGP SIGNATURE-----
-iQEcBAEBCgAGBQJjZGKCAAoJEEzhh34ZQ4xw+SQH/RaeOdeJgU/CgZJWrDVMmS+m
-ZotYjnqq01XWR1tpw1PWKKC6L+BCPyTGUQK+g8uUNx4uuXr1Yknmxar+mOHGls7E
-Mmluwz06yy3obYXKzyrQyP4g8iBZoYSeyctdPrgRo1sp1hj9hgLbEAD2wMx4RIzK
-JfwXdhpdkKKqHbX4+HgOTd9QnXTzsSV/w0jfVc3mnb//uKjlrxmh+sVLUsb1wCuK
-vOiyQuG4GoGDcv+i5ZUQ08M7fEycGDEgU/mKPhfS7yxpKg1s4o0f4Y8uK2UvT/in
-0obwG8TOcMxDy52dnGI+hMfz/+CpT/ot8Cq3MH80oMmfVC/Tb9RQz4biXvYPvaU=
-=BjJP
+iQEcBAEBCgAGBQJjZbQ4AAoJEEzhh34ZQ4xw+KoIALvLk/CGoga4HielfXeO1KmZ
+8NhLse1NofDeHS0rA0S8wyzhF04EG/pywumD48YX1FMnJ6W30USiX5O01PgcEpUp
+qoNwj63hcczwqzZfxIhl83C4jDPWZdAnIu2t4CLK1pDs0pSh1b48ObFBiuYxTETf
+fevUAbYb9wk9JwZ96be1p1WZDZmZ5fStzagHnpQM7/2pU9EaX96BEdn8SWIGnD41
+HzNkNVjmWkzfKlEPsqwnGCFZ51Gzz9OWLhXOT4W5xmZ7wz+s866R563MSgx/DtbO
+tewRHDugiBjcGop5MYnthgS7zVejHNDkloTLQyVow5bNKfKH9HZaAPscNFiMq1U=
+=i6HJ
-----END PGP SIGNATURE-----
diff --git a/systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz b/systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz
index 5ce7ffc314..5f97a3725c 100644
--- a/systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz
+++ b/systems/texlive/tlnet/tlpkg/texlive.tlpdb.xz
Binary files differ
diff --git a/systems/win32/miktex/tm/packages/files.csv.lzma b/systems/win32/miktex/tm/packages/files.csv.lzma
index 56af282917..0615e3dde7 100644
--- a/systems/win32/miktex/tm/packages/files.csv.lzma
+++ b/systems/win32/miktex/tm/packages/files.csv.lzma
Binary files differ
diff --git a/systems/win32/miktex/tm/packages/next/pr.ini b/systems/win32/miktex/tm/packages/next/pr.ini
index adbb325537..ed869f70fa 100644
--- a/systems/win32/miktex/tm/packages/next/pr.ini
+++ b/systems/win32/miktex/tm/packages/next/pr.ini
@@ -1,13 +1,13 @@
[repository]
-date=1667596615
+date=1667683015
lastupd=miktex-arctrl-bin-x64-2.9 miktex-asymptote-bin-x64-2.9 miktex-autosp-bin-x64-2.9 miktex-axohelp-bin-x64-2.9 miktex-bibarts-bin-x64-2.9 miktex-bibtex-bin-x64-2.9 miktex-bibtex8bit-bin-x64-2.9 miktex-bzip2-bin-x64-2.9 miktex-cairo-bin-x64-2.9 miktex-chktex-bin-x64-2.9 miktex-cjkutils-bin-x64-2.9 miktex-console-bin-x64-2.9 miktex-curl-bin-x64-2.9 miktex-cweb-bin-x64-2.9 miktex-devnag-bin-x64-2.9 miktex-doc miktex-dvicopy-bin-x64-2.9 miktex-dvipdfmx-bin-x64-2.9 miktex-dvipng-bin-x64-2.9 miktex-dvips-bin-x64-2.9
-lstdigest=65afc29186860a6fd9cbd2b83c3f9892
+lstdigest=0f25a431c5f0c6daaca013a4542eef3d
numpkg=6168
relstate=next
-version=8343
+version=8344
;;;;This configuration file is signed by a MiKTeX maintainer. The signature follows.
;;;;-----BEGIN MIKTEX SIGNATURE-----
-;;;; signature/miktex: SiQbwDrrO++rCKWeyj0bNv0k4uEm7wpz47dKLzLVW4NNQ2NLcusX4KsaDtmOVdWFF2WmYqYusHBNmBmnNN4f2ff52596b/VonzgKO6X6o/8B20bmzlxdHNQaYfXQrN8LMYEOFYzIo1L1N5TlVvcEIxJSsgrYjlrpBN26kYZLgS617RQQvD4a2MfGSWFY5CUIMA+K/kHFOJx6pl0pGgB8zaLKU1vxn8b0luzmTE+/79oBhQEkMOr9Cppe4CyQHqF988ZD4079mT8SRE0rF2ZycECYbxf4nzop9D4JGI5BIU6VMd71gv5D/RZqJ+QLyXuar/eaob9qOcV+PqPX12cebg==
+;;;; signature/miktex: NP64l5aRXFz829wfxyCBpIrXYaee53wh3r6jHgYueGI3etumbsofHnPRpvVRj3rQtmjbUYUY6AdqAUIJTMLCb+jK49fhiSNUD7LL1ABb8D4yHsQIjyxifjqVZQ21YAqyoblHB12f64T5imm4Sv6/yHAjQr8ZaoxEnhGtTLVhsDBtFCr50zuQos2HyJuj6gaOddsOgKW1igVysTA5I/vfVMku6/3yX//sjjDlWG7DLKmyT1eHqfki/XL2UgQaAoniJWu5vBQF7YmlTq/6gddPbaRVwYWIFXK4qzsfgb5Ue6m6hidqgqaylPqE6lKR1qTMhzd9Y2B0X7HQFEgKZO8Pjg==
;;;;-----END MIKTEX SIGNATURE-----
diff --git a/systems/win32/miktex/tm/packages/pr.ini b/systems/win32/miktex/tm/packages/pr.ini
index 29426843bd..d66be32e33 100644
--- a/systems/win32/miktex/tm/packages/pr.ini
+++ b/systems/win32/miktex/tm/packages/pr.ini
@@ -1,13 +1,13 @@
[repository]
-date=1667596284
+date=1667682682
lastupd=huaz huaz__doc huaz__source accanthis accanthis__doc achemso achemso__doc achemso__source acmart acmart__doc acmart__source adjustbox adjustbox__doc adjustbox__source association-matrix association-matrix__doc association-matrix__source babel babel__doc babel__source
-lstdigest=1f4e32142de03ccb6cb192061c6663b3
+lstdigest=9fda8f4432c3d1178a7d82c5914df967
numpkg=6168
relstate=stable
-version=8343
+version=8344
;;;;This configuration file is signed by a MiKTeX maintainer. The signature follows.
;;;;-----BEGIN MIKTEX SIGNATURE-----
-;;;; signature/miktex: iu+X7qdzNiw4y80GqinUzgc4scaJw+Hxc0Wk5Gj8QhWZmQlKPoRUHUX3vowf+wzxCuXlTonukr9+glUIpkbjiYABvGksaebGtkudbQNTM4D7qe8XS3i6o7ZLhRWnDr0bCNv+ARfhHvOGJHsr6EPhbAcJW48YiVUUdbJ17JDUfoRpZDQMgt00A7b3r+v3vLJlUdsTOnkOnlmlXV8GiiF2AdgBWuMDCVBJkK+pIYg5rkN5Z+pc8MSGnLXvdkmI8/oY8EjdQbYgul+y+qkNwP4M+PkbK2byU/ivkT9/wYEshNSNCystAyjs3X98MyHesDFy6CkVrwf5LYJvMjWPMhLf2Q==
+;;;; signature/miktex: qGnb0903iCRFy1OiQn0lsvPpHqrgGWde9rNMW63+nKnbGGSv5ejtiwzysyMVYfgRUKii9LDaXULzr3OIu/PeGUsiAbie69c8g7BCXMn8jiQUYkRe03ajs7n+cOqpcCmSal9gA3n7m9k16Wlfl2evFAdkW+2gJd4DQBQq1Qk1l8w8olebe/3mkvkqC45eQLUxs/8ft9x/9eOe64cAFHPCFh8w4T3tNUqq1ZlF/uR0/4xQqHI6UqfoSg8Q80wyXCaqixTpOpFAvBhxZCj7QD3t5ZILh2NqoizgSqbkNSqbpfqy+X/ZokSBf4K6ooEE4NDCF1uxscrHx3nI++cM5ulUpQ==
;;;;-----END MIKTEX SIGNATURE-----
diff --git a/timestamp b/timestamp
index 435a59874c..182846b051 100644
--- a/timestamp
+++ b/timestamp
@@ -2,4 +2,4 @@
# The source CTAN of this site's material:
dante.ctan.org
# The year-month-day-hour-minute of this site's material:
-2022-11-05-02-02
+2022-11-06-02-02