algebrite
Version:
Computer Algebra System in Coffeescript
631 lines (532 loc) • 13.7 kB
text/coffeescript
bigInt = require('big-integer')
# also change the version in the package.json file
version = "1.1.1"
SELFTEST = 1
# size of the symbol table
NSYM = 1000
DEBUG = false
PRINTOUTRESULT = false
# printing-related constants
PRINTMODE_LATEX = "PRINTMODE_LATEX"
PRINTMODE_2DASCII = "PRINTMODE_2DASCII"
PRINTMODE_FULL = "PRINTMODE_FULL"
PRINTMODE_PLAIN = "PRINTMODE_PLAIN"
PRINTMODE_LIST = "PRINTMODE_LIST"
# when the user uses the generic "print" statement
# this setting kicks-in.
environment_printmode = PRINTMODE_PLAIN
printMode = PRINTMODE_PLAIN
dontCreateNewRadicalsInDenominatorWhenEvalingMultiplication = true
recursionLevelNestedRadicalsRemoval = 0
do_simplify_nested_radicals = true
avoidCalculatingPowersIntoArctans = true
# Symbolic expressions are built by connecting U structs.
#
# For example, (a b + c) is built like this:
#
# _______ _______ _______
# |CONS |--->|CONS |----------------------------->|CONS |
# | | | | | |
# |_______| |_______| |_______|
# | | |
# ___v___ ___v___ _______ _______ ___v___
# |ADD | |CONS |--->|CONS |--->|CONS | |SYM c |
# | | | | | | | | | |
# |_______| |_______| |_______| |_______| |_______|
# | | |
# ___v___ ___v___ ___v___
# |MUL | |SYM a | |SYM b |
# | | | | | |
# |_______| |_______| |_______|
class rational
a: null # a bigInteger
b: null # a bigInteger
class U
cons: null # will have a car and cdr
printname: ""
str: ""
tensor: null
# rational number a over b
q: null # will point to a rational
d: 0.0 # a double
k: 0
tag: 0
toString: -> print_expr(this)
toLatexString: -> collectLatexStringFromReturnValue(this)
constructor: ->
@cons = {}
@cons.car = null
@cons.cdr = null
@q = new rational()
errorMessage = ""
# the following enum is for struct U, member k
CONS = 0
NUM = 1
DOUBLE = 2
STR = 3
TENSOR = 4
SYM = 5
# the following enum is for indexing the symbol table
# standard functions first, then nil, then everything else
counter = 0
ABS = counter++
ADD = counter++
ADJ = counter++
AND = counter++
APPROXRATIO = counter++
ARCCOS = counter++
ARCCOSH = counter++
ARCSIN = counter++
ARCSINH = counter++
ARCTAN = counter++
ARCTANH = counter++
ARG = counter++
ATOMIZE = counter++
BESSELJ = counter++
BESSELY = counter++
BINDING = counter++
BINOMIAL = counter++
CEILING = counter++
CHECK = counter++
CHOOSE = counter++
CIRCEXP = counter++
CLEAR = counter++
CLEARALL = counter++
CLEARPATTERNS = counter++
CLOCK = counter++
COEFF = counter++
COFACTOR = counter++
CONDENSE = counter++
CONJ = counter++
CONTRACT = counter++
COS = counter++
COSH = counter++
DECOMP = counter++
DEFINT = counter++
DEGREE = counter++
DENOMINATOR = counter++
DERIVATIVE = counter++
DET = counter++
DIM = counter++
DIRAC = counter++
DIVISORS = counter++
DO = counter++
DOT = counter++
DRAW = counter++
DSOLVE = counter++
EIGEN = counter++
EIGENVAL = counter++
EIGENVEC = counter++
ERF = counter++
ERFC = counter++
EVAL = counter++
EXP = counter++
EXPAND = counter++
EXPCOS = counter++
EXPSIN = counter++
FACTOR = counter++
FACTORIAL = counter++
FACTORPOLY = counter++
FILTER = counter++
FLOATF = counter++
FLOOR = counter++
FOR = counter++
FUNCTION = counter++
GAMMA = counter++
GCD = counter++
HERMITE = counter++
HILBERT = counter++
IMAG = counter++
INDEX = counter++
INNER = counter++
INTEGRAL = counter++
INV = counter++
INVG = counter++
ISINTEGER = counter++
ISPRIME = counter++
LAGUERRE = counter++
# LAPLACE = counter++
LCM = counter++
LEADING = counter++
LEGENDRE = counter++
LOG = counter++
LOOKUP = counter++
MOD = counter++
MULTIPLY = counter++
NOT = counter++
NROOTS = counter++
NUMBER = counter++
NUMERATOR = counter++
OPERATOR = counter++
OR = counter++
OUTER = counter++
PATTERN = counter++
PATTERNSINFO = counter++
POLAR = counter++
POWER = counter++
PRIME = counter++
PRINT_LEAVE_E_ALONE = counter++
PRINT_LEAVE_X_ALONE = counter++
PRINT = counter++
PRINT2DASCII = counter++
PRINTFULL = counter++
PRINTLATEX = counter++
PRINTLIST = counter++
PRINTPLAIN = counter++
PRODUCT = counter++
QUOTE = counter++
QUOTIENT = counter++
RANK = counter++
RATIONALIZE = counter++
REAL = counter++
ROUND = counter++
YYRECT = counter++
ROOTS = counter++
SETQ = counter++
SGN = counter++
SILENTPATTERN = counter++
SIMPLIFY = counter++
SIN = counter++
SINH = counter++
SHAPE = counter++
SQRT = counter++
STOP = counter++
SUBST = counter++
SUM = counter++
SYMBOLSINFO = counter++
TAN = counter++
TANH = counter++
TAYLOR = counter++
TEST = counter++
TESTEQ = counter++
TESTGE = counter++
TESTGT = counter++
TESTLE = counter++
TESTLT = counter++
TRANSPOSE = counter++
UNIT = counter++
ZERO = counter++
# ALL THE SYMBOLS ABOVE NIL ARE KEYWORDS,
# WHICH MEANS THAT USER CANNOT REDEFINE THEM
NIL = counter++ # nil goes here, after standard functions
LAST = counter++
LAST_PRINT = counter++
LAST_2DASCII_PRINT = counter++
LAST_FULL_PRINT = counter++
LAST_LATEX_PRINT = counter++
LAST_LIST_PRINT = counter++
LAST_PLAIN_PRINT = counter++
AUTOEXPAND = counter++
BAKE = counter++
ASSUME_REAL_VARIABLES = counter++
TRACE = counter++
YYE = counter++
DRAWX = counter++ # special purpose internal symbols
METAA = counter++
METAB = counter++
METAX = counter++
SECRETX = counter++
VERSION = counter++
PI = counter++
SYMBOL_A = counter++
SYMBOL_B = counter++
SYMBOL_C = counter++
SYMBOL_D = counter++
SYMBOL_I = counter++
SYMBOL_J = counter++
SYMBOL_N = counter++
SYMBOL_R = counter++
SYMBOL_S = counter++
SYMBOL_T = counter++
SYMBOL_X = counter++
SYMBOL_Y = counter++
SYMBOL_Z = counter++
SYMBOL_IDENTITY_MATRIX = counter++
SYMBOL_A_UNDERSCORE = counter++
SYMBOL_B_UNDERSCORE = counter++
SYMBOL_X_UNDERSCORE = counter++
C1 = counter++
C2 = counter++
C3 = counter++
C4 = counter++
C5 = counter++
C6 = counter++
USR_SYMBOLS = counter++ # this must be last
E = YYE
# TOS cannot be arbitrarily large because the OS seg faults on deep recursion.
# For example, a circular evaluation like x=x+1 can cause a seg fault.
# At this setting (100,000) the evaluation stack overruns before seg fault.
TOS = 100000
BUF = 10000
MAX_PROGRAM_SIZE = 100001
MAXPRIMETAB = 10000
MAX_CONSECUTIVE_APPLICATIONS_OF_ALL_RULES = 5
MAX_CONSECUTIVE_APPLICATIONS_OF_SINGLE_RULE = 10
ENABLE_CACHING = true
cached_runs = null # the LRU cache will go here
cached_findDependenciesInScript = null # the LRU cache will go here
#define _USE_MATH_DEFINES // for MS C++
MAXDIM = 24
# needed for the mechanism to
# find all dependencies between variables
# in a script
symbolsDependencies = {}
symbolsHavingReassignments = []
symbolsInExpressionsWithoutAssignments = []
patternHasBeenFound = false
predefinedSymbolsInGlobalScope_doNotTrackInDependencies = [
"rationalize"
"abs"
"e"
"i"
"pi"
"sin"
"ceiling"
"cos"
"roots"
"integral"
"derivative"
"defint"
"sqrt"
"eig"
"cov"
"deig"
"dcov"
"float"
"floor"
"product"
"root"
"round"
"sum"
"test"
"unit"
]
# you can do some little simplifications
# at parse time, such as calculating away
# immediately simple operations on
# constants, removing 1s from products
# etc.
parse_time_simplifications = true
chainOfUserSymbolsNotFunctionsBeingEvaluated = []
stringsEmittedByUserPrintouts = ""
# flag use to potentially switch on/off some quirks "deep"
# in the code due to call from Algebra block.
# Currently not used.
called_from_Algebra_block = false
class tensor
ndim: 0 # number of dimensions
dim: null # dimension length, for each dimension
nelem: 0 # total number of elements
elem: null # an array containing all the data
constructor: ->
@dim = (0 for [0..MAXDIM])
@elem = []
class display
h: 0
w: 0
n: 0
a: [] # will contain an array of c,x,y (color,x,y)
class text_metric
ascent: 0
descent: 0
width: 0
tos = 0 # top of stack
expanding = 0
evaluatingAsFloats = 0
evaluatingPolar = 0
fmt_x = 0
fmt_index = 0
fmt_level = 0
verbosing = 0
primetab = do ->
primes = [2]
i = 3
while primes.length < MAXPRIMETAB
j = 0
ceil = Math.sqrt(i)
while j < primes.length and primes[j] <= ceil
if i % primes[j] == 0
j = -1
break
j++
if j != -1
primes.push(i)
i += 2
primes[MAXPRIMETAB] = 0
return primes
esc_flag = 0
draw_flag = 0
mtotal = 0
trigmode = 0
logbuf = ""
program_buf = ""
# will contain the variable names
symtab = []
# will contain the contents of the variable
# in the corresponding position in symtab array
binding = []
isSymbolReclaimable = []
arglist = [] # will contain U
stack = [] # will contain *U
frame = 0
p0 = null # will contain U
p1 = null # will contain U
p2 = null # will contain U
p3 = null # will contain U
p4 = null # will contain U
p5 = null # will contain U
p6 = null # will contain U
p7 = null # will contain U
p8 = null # will contain U
p9 = null # will contain U
zero = null # will contain U
one = null # will contain U
one_as_double = null
imaginaryunit = null # will contain U
out_buf = ""
out_count = 0
test_flag = 0
codeGen = false
draw_stop_return = null # extern jmp_buf ?????
userSimplificationsInListForm = []
userSimplificationsInStringForm = []
transpose_unicode = 7488
dotprod_unicode = 183
symbol = (x) -> (symtab[x])
iscons = (p) -> (p.k == CONS)
isrational = (p) -> (p.k == NUM)
isdouble = (p) -> (p.k == DOUBLE)
isnum = (p) -> (isrational(p) || isdouble(p))
isstr = (p) -> (p.k == STR)
istensor = (p) -> (if !p? then debugger else p.k == TENSOR)
# because of recursion, we consider a scalar to be
# a tensor, so a numeric scalar will return true
isnumerictensor = (p) ->
if isnum(p) or p == symbol(SYMBOL_IDENTITY_MATRIX)
return 1
if !istensor(p) and !isnum(p)
#console.log "p not even a tensor: " + p
return 0
n = p.tensor.nelem
a = p.tensor.elem
for i in [0...n]
if !isnumerictensor(a[i])
#console.log "non-numeric element: " + a[i]
return 0
return 1
issymbol = (p) -> (p.k == SYM)
iskeyword = (p) -> (issymbol(p) && symnum(p) < NIL)
car = (p) -> if iscons(p) then p.cons.car else symbol(NIL)
cdr = (p) -> if iscons(p) then p.cons.cdr else symbol(NIL)
caar = (p) -> car(car(p))
cadr = (p) -> car(cdr(p))
cdar = (p) -> cdr(car(p))
cddr = (p) -> cdr(cdr(p))
caadr = (p) -> car(car(cdr(p)))
caddr = (p) -> car(cdr(cdr(p)))
cadar = (p) -> car(cdr(car(p)))
cdadr = (p) -> cdr(car(cdr(p)))
cddar = (p) -> cdr(cdr(car(p)))
cdddr = (p) -> cdr(cdr(cdr(p)))
caaddr = (p) -> car(car(cdr(cdr(p))))
cadadr = (p) -> car(cdr(car(cdr(p))))
caddar = (p) -> car(cdr(cdr(car(p))))
cdaddr = (p) -> cdr(car(cdr(cdr(p))))
cadddr = (p) -> car(cdr(cdr(cdr(p))))
cddddr = (p) -> cdr(cdr(cdr(cdr(p))))
caddddr = (p) -> car(cdr(cdr(cdr(cdr(p)))))
cadaddr = (p) -> car(cdr(car(cdr(cdr(p)))))
cddaddr = (p) -> cdr(cdr(car(cdr(cdr(p)))))
caddadr = (p) -> car(cdr(cdr(car(cdr(p)))))
cdddaddr = (p) -> cdr(cdr(cdr(car(cdr(cdr(p))))))
caddaddr = (p) -> car(cdr(cdr(car(cdr(cdr(p))))))
# not used yet
listLength = (p) ->
startCount = -1
while iscons(p)
p = cdr(p)
startCount++
return startCount
# not used yet
nthCadr = (p,n) ->
startCount = 0
while startCount <= n
p = cdr(p)
startCount++
return car(p)
isadd = (p) -> (car(p) == symbol(ADD))
ismultiply = (p) -> (car(p) == symbol(MULTIPLY))
ispower = (p) -> (car(p) == symbol(POWER))
isfactorial = (p) -> (car(p) == symbol(FACTORIAL))
isinnerordot = (p) -> ((car(p) == symbol(INNER)) or (car(p) == symbol(DOT)))
istranspose = (p) -> (car(p) == symbol(TRANSPOSE))
isinv = (p) -> (car(p) == symbol(INV))
# TODO this is a bit of a shallow check, we should
# check when we are passed an actual tensor and possibly
# cache the test result.
isidentitymatrix = (p) -> (p == symbol(SYMBOL_IDENTITY_MATRIX))
MSIGN = (p) ->
if p.isPositive()
return 1
else if p.isZero()
return 0
else
return -1
MLENGTH = (p) -> p.toString().length
MZERO = (p) -> p.isZero()
MEQUAL = (p, n) ->
if !p?
debugger
p.equals(n)
reset_after_error = ->
moveTos 0
esc_flag = 0
draw_flag = 0
frame = TOS
evaluatingAsFloats = 0
evaluatingPolar = 0
$ = (exports ? this)
$.version = version
$.isadd = isadd
$.ismultiply = ismultiply
$.ispower = ispower
$.isfactorial = isfactorial
$.car = car
$.cdr = cdr
$.caar = caar
$.cadr = cadr
$.cdar = cdar
$.cddr = cddr
$.caadr = caadr
$.caddr = caddr
$.cadar = cadar
$.cdadr = cdadr
$.cddar = cddar
$.cdddr = cdddr
$.caaddr = caaddr
$.cadadr = cadadr
$.caddar = caddar
$.cdaddr = cdaddr
$.cadddr = cadddr
$.cddddr = cddddr
$.caddddr = caddddr
$.cadaddr = cadaddr
$.cddaddr = cddaddr
$.caddadr = caddadr
$.cdddaddr = cdddaddr
$.caddaddr = caddaddr
$.symbol = symbol
$.iscons = iscons
$.isrational = isrational
$.isdouble = isdouble
$.isnum = isnum
$.isstr = isstr
$.istensor = istensor
$.issymbol = issymbol
$.iskeyword = iskeyword
$.CONS = CONS
$.NUM = NUM
$.DOUBLE = DOUBLE
$.STR = STR
$.TENSOR = TENSOR
$.SYM = SYM