1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
|
-- Rudimentary parser for making the CAS easier to use. Essentially just wraps SymbolExpression() around symbols and Integer() around integers.
require("calculus.luacas-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
|