summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/luacas/tex/test
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/latex/luacas/tex/test')
-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
20 files changed, 1910 insertions, 0 deletions
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