631 lines
14 KiB
CoffeeScript
631 lines
14 KiB
CoffeeScript
bigInt = require('big-integer')
|
|
|
|
# also change the version in the package.json file
|
|
version = "1.4.0"
|
|
|
|
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_COMPUTER = "PRINTMODE_COMPUTER"
|
|
PRINTMODE_HUMAN = "PRINTMODE_HUMAN"
|
|
PRINTMODE_LIST = "PRINTMODE_LIST"
|
|
|
|
# when the user uses the generic "print" statement
|
|
# this setting kicks-in.
|
|
printMode = PRINTMODE_COMPUTER
|
|
|
|
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++
|
|
FORCE_FIXED_PRINTOUT = counter++
|
|
MAX_FIXED_PRINTOUT_DIGITS = 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
|
|
|
|
#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)
|
|
isNumericAtom = (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
|
|
isNumericAtomOrTensor = (p) ->
|
|
if isNumericAtom(p) or p == symbol(SYMBOL_IDENTITY_MATRIX)
|
|
return 1
|
|
|
|
if !istensor(p) and !isNumericAtom(p)
|
|
#console.log "p not an atom nor a tensor: " + p
|
|
return 0
|
|
|
|
n = p.tensor.nelem
|
|
a = p.tensor.elem
|
|
|
|
for i in [0...n]
|
|
if !isNumericAtomOrTensor(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
|
|
$.isNumericAtom = isNumericAtom
|
|
$.isstr = isstr
|
|
$.istensor = istensor
|
|
$.issymbol = issymbol
|
|
$.iskeyword = iskeyword
|
|
|
|
|
|
|
|
|
|
$.CONS = CONS
|
|
$.NUM = NUM
|
|
$.DOUBLE = DOUBLE
|
|
$.STR = STR
|
|
$.TENSOR = TENSOR
|
|
$.SYM = SYM
|