algebrite
Version:
Computer Algebra System in Coffeescript
1,189 lines (959 loc) • 57.1 kB
text/coffeescript
#jmp_buf stop_return, draw_stop_return
# s is a string here
stop = (s) ->
#if (draw_flag == 2)
# longjmp(draw_stop_return, 1)
#else
errorMessage += "Stop: "
errorMessage += s
#debugger
message = errorMessage
errorMessage = ''
tos = 0
throw new Error(message)
#longjmp(stop_return, 1)
# Figuring out dependencies is key to automatically
# generating a method signature when generating JS code
# from algebrite scripts.
# This is important because the user can keep using normal Algebrite
# scripting without special notations.
# Basically the process consists of figuring out
# the "ground variables" that are needed to compute each variable.
# Now there are two ways of doing this:
# * at parse time
# * after running the scripts
# Doing it at parse time means that we can't track simplifications
# canceling-out some variables for example. But on the other side
# it's very quick and the user can somehow see what the signature is
# going to look like (assuming tha code is rather simple), or anyways
# is going to easily make sense of the generated signature.
# Doing it after execution on the other hand would allow us to see
# if some variable cancel-out. But if variables cancel out then
# they might do so according to some run-time behaviour that the user
# might struggle to keep track of.
# So the effort for the user to make sense of the signature in the first case
# is similar to the effort of leeping tab of types in a typed language.
# While in the second case the effort is similar to running the
# code and simplifications in her head.
test_dependencies = ->
do_clearall()
testResult = findDependenciesInScript('1')
if testResult[0] == "All local dependencies: . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: " and
testResult[1] == "1" and
testResult[2] == ""
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('f = x+1\n g = f\n h = g\n f = g')
if testResult[0] == "All local dependencies: variable f depends on: x, g, ; variable g depends on: f, ; variable h depends on: g, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: x, ; f --> g --> ... then f again, variable g depends on: x, ; g --> f --> ... then g again, variable h depends on: x, ; h --> g --> f --> ... then g again, " and
testResult[1] == "" and
testResult[2] == "// f is part of a cyclic dependency, no code generated.\n// g is part of a cyclic dependency, no code generated.\n// h is part of a cyclic dependency, no code generated."
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
if findDependenciesInScript('f = x+1\n g = f + y\n h = g')[0] == "All local dependencies: variable f depends on: x, ; variable g depends on: f, y, ; variable h depends on: g, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: x, ; variable g depends on: x, y, ; variable h depends on: x, y, ; "
console.log "ok dependency test"
else
console.log "fail dependency test"
do_clearall()
if findDependenciesInScript('g = h(x,y)')[0] == "All local dependencies: variable g depends on: h, x, y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable g depends on: h, x, y, ; "
console.log "ok dependency test"
else
console.log "fail dependency test"
do_clearall()
if findDependenciesInScript('f(x,y) = k')[0] == "All local dependencies: variable f depends on: 'x, 'y, k, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: 'x, 'y, k, ; "
console.log "ok dependency test"
else
console.log "fail dependency test"
do_clearall()
if findDependenciesInScript('x = z\n f(x,y) = k')[0] == "All local dependencies: variable x depends on: z, ; variable f depends on: 'x, 'y, k, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: z, ; variable f depends on: 'x, 'y, k, ; "
console.log "ok dependency test"
else
console.log "fail dependency test"
do_clearall()
if findDependenciesInScript('x = z\n g = f(x,y)')[0] == "All local dependencies: variable x depends on: z, ; variable g depends on: f, x, y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: z, ; variable g depends on: f, z, y, ; "
console.log "ok dependency test"
else
console.log "fail dependency test"
do_clearall()
if findDependenciesInScript('x = 1\n x = y\n x = z')[0] == "All local dependencies: variable x depends on: y, z, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: y, z, ; "
console.log "ok dependency test"
else
console.log "fail dependency test"
do_clearall()
testResult = findDependenciesInScript('x = y*y')
if testResult[0] == "All local dependencies: variable x depends on: y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: y, ; " and
testResult[1] == "" and
testResult[2] == "x = function (y) { return ( Math.pow(y, 2) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('x = -sqrt(2)/2')
if testResult[0] == "All local dependencies: variable x depends on: ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: ; " and
testResult[1] == "" and
testResult[2] == "x = -1/2*Math.pow(2, (1/2));"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('x = 2^(1/2-a)*2^a/10')
if testResult[0] == "All local dependencies: variable x depends on: a, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: a, ; " and
testResult[1] == "" and
testResult[2] == "x = 1/10*Math.pow(2, (1/2));"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('x = rationalize(t*y/(t+y)+2*t^2*y*(2*t+y)^(-2))')
if testResult[0] == "All local dependencies: variable x depends on: t, y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: t, y, ; " and
testResult[1] == "" and
testResult[2] == "x = function (t, y) { return ( t*y*(6*Math.pow(t, 2) + Math.pow(y, 2) + 6*t*y) / ((t + y)*Math.pow((2*t + y), 2)) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('x = abs((a+i*b)/(c+i*d))')
if testResult[0] == "All local dependencies: variable x depends on: a, b, c, d, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: a, b, c, d, ; " and
testResult[1] == "" and
testResult[2] == "x = function (a, b, c, d) { return ( Math.pow((Math.pow(a, 2) + Math.pow(b, 2)), (1/2)) / (Math.pow((Math.pow(c, 2) + Math.pow(d, 2)), (1/2))) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('x = sin(1/10)^2 + cos(1/10)^2 + y')
if testResult[0] == "All local dependencies: variable x depends on: y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: y, ; " and
testResult[1] == "" and
testResult[2] == "x = function (y) { return ( 1 + y ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('x = sin(1/10)^2 + cos(1/10)^2')
if testResult[0] == "All local dependencies: variable x depends on: ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable x depends on: ; " and
testResult[1] == "" and
testResult[2] == "x = 1;"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('f(x) = x * x')
if testResult[0] == "All local dependencies: variable f depends on: 'x, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: 'x, ; " and
testResult[1] == "" and
testResult[2] == "f = function (x) { return ( x*x ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('f(x) = x * x + g(y)')
if testResult[0] == "All local dependencies: variable f depends on: 'x, g, y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: 'x, g, y, ; " and
testResult[1] == "" and
testResult[2] == "f = function (g, y, x) { return ( g(y) + Math.pow(x, 2) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('y = 2\nf(x) = x * x + g(y)')
if testResult[0] == "All local dependencies: variable y depends on: ; variable f depends on: 'x, g, y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable y depends on: ; variable f depends on: 'x, g, ; " and
testResult[1] == "" and
testResult[2] == "y = 2;\nf = function (g, x) { return ( g(2) + Math.pow(x, 2) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('g(x) = x + 2\ny = 2\nf(x) = x * x + g(y)')
if testResult[0] == "All local dependencies: variable g depends on: 'x, ; variable y depends on: ; variable f depends on: 'x, g, y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable g depends on: 'x, ; variable y depends on: ; variable f depends on: 'x, ; " and
testResult[1] == "" and
testResult[2] == "g = function (x) { return ( 2 + x ); }\ny = 2;\nf = function (x) { return ( 4 + Math.pow(x, 2) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('g(x) = x + 2\nf(x) = x * x + g(y)')
if testResult[0] == "All local dependencies: variable g depends on: 'x, ; variable f depends on: 'x, g, y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable g depends on: 'x, ; variable f depends on: 'x, y, ; " and
testResult[1] == "" and
testResult[2] == "g = function (x) { return ( 2 + x ); }\nf = function (y, x) { return ( 2 + y + Math.pow(x, 2) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
###
testResult = findDependenciesInScript('g(x) = f(x)\nf(x)=g(x)')
if testResult[0] == "All local dependencies: variable g depends on: 'x, f, x, ; variable f depends on: 'x, g, x, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable g depends on: 'x, ; g --> f --> ... then g again, variable f depends on: 'x, x, ; f --> g --> ... then f again, " and
testResult[1] == "" and
testResult[2] == "// g is part of a cyclic dependency, no code generated.\n// f is part of a cyclic dependency, no code generated."
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
###
testResult = findDependenciesInScript('f = roots(a*x^2 + b*x + c, x)')
if testResult[0] == "All local dependencies: variable f depends on: a, b, c, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: a, b, c, ; " and
testResult[1] == "" and
testResult[2] == "f = function (a, b, c) { return ( [-1/2*(Math.pow((Math.pow(b, 2) / (Math.pow(a, 2)) - 4*c / a), (1/2)) + b / a),1/2*(Math.pow((Math.pow(b, 2) / (Math.pow(a, 2)) - 4*c / a), (1/2)) - b / a)] ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('f = roots(a*x^2 + b*x + c)')
if testResult[0] == "All local dependencies: variable f depends on: a, b, c, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: a, b, c, ; " and
testResult[1] == "" and
testResult[2] == "f = function (a, b, c) { return ( [-1/2*(Math.pow((Math.pow(b, 2) / (Math.pow(a, 2)) - 4*c / a), (1/2)) + b / a),1/2*(Math.pow((Math.pow(b, 2) / (Math.pow(a, 2)) - 4*c / a), (1/2)) - b / a)] ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('f = roots(integral(a*x + b))')
if testResult[0] == "All local dependencies: variable f depends on: a, b, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: a, b, ; " and
testResult[1] == "" and
testResult[2] == "f = function (a, b) { return ( [0,-2*b / a] ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('f = roots(defint(a*x + y,y,0,1))')
if testResult[0] == "All local dependencies: variable f depends on: a, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: a, ; " and
testResult[1] == "" and
testResult[2] == "f = function (a) { return ( -1 / (2*a) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('f = roots(defint(a*x + y + z,y,0,1, z, 0, 1))')
if testResult[0] == "All local dependencies: variable f depends on: a, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: a, ; " and
testResult[1] == "" and
testResult[2] == "f = function (a) { return ( -1 / a ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('f = defint(2*x - 3*y,x,0,2*y)')
if testResult[0] == "All local dependencies: variable f depends on: y, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: y, ; " and
testResult[1] == "" and
testResult[2] == "f = function (y) { return ( -2*Math.pow(y, 2) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('f = defint(12 - x^2 - (y^2)/2,x,0,2,y,0,3)')
if testResult[0] == "All local dependencies: variable f depends on: ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable f depends on: ; " and
testResult[1] == "" and
testResult[2] == "f = 55;"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
# this example checks that functions are not meddled with,
# in particular that in the function body, the variables
# bound by the parameters remain "separate" from previous
# variables with the same name.
testResult = findDependenciesInScript('a = 2\nf(a) = a+1+b')
if testResult[0] == "All local dependencies: variable a depends on: ; variable f depends on: 'a, b, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable a depends on: ; variable f depends on: 'a, b, ; " and
testResult[1] == "" and
testResult[2] == "a = 2;\nf = function (a, b) { return ( 1 + a + b ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
# similar as test above but this time we are not
# defining a function, so things are a bit different.
testResult = findDependenciesInScript('a = 2\nf = a+1')
if testResult[0] == "All local dependencies: variable a depends on: ; variable f depends on: a, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable a depends on: ; variable f depends on: ; " and
testResult[1] == "" and
testResult[2] == "a = 2;\nf = 3;"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
# similar as test above but this time we do a
# trick with the quote to see whether we
# get confused with the indirection
testResult = findDependenciesInScript('a := b\nf = a+1')
if testResult[0] == "All local dependencies: variable a depends on: b, ; variable f depends on: a, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable a depends on: b, ; variable f depends on: b, ; " and
testResult[1] == "" and
testResult[2] == "a = function (b) { return ( b ); }\nf = function (b) { return ( 1 + b ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
# another tricky case of indirection through quote
testResult = findDependenciesInScript('a := b\nf(a) = a+1')
if testResult[0] == "All local dependencies: variable a depends on: b, ; variable f depends on: 'a, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable a depends on: b, ; variable f depends on: 'a, ; " and
testResult[1] == "" and
testResult[2] == "a = function (b) { return ( b ); }\nf = function (a) { return ( 1 + a ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
# reassignment
testResult = findDependenciesInScript('b = 1\nb=a+b+c')
if testResult[0] == "All local dependencies: variable b depends on: a, c, ; . Symbols with reassignments: b, . Symbols in expressions without assignments: . All dependencies recursively: variable b depends on: a, c, ; " and
testResult[1] == "" and
testResult[2] == "b = function (a, c) { return ( 1 + a + c ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
# reassignment
testResult = findDependenciesInScript('a = a+1')
if testResult[0] == "All local dependencies: variable a depends on: ; . Symbols with reassignments: a, . Symbols in expressions without assignments: . All dependencies recursively: variable a depends on: ; " and
testResult[1] == "" and
testResult[2] == "" and
testResult[5] == "Error: Stop: recursive evaluation of symbols: a -> a"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
# reassignment
testResult = computeDependenciesFromAlgebra('pattern(a,b)\nc= d\na=a+1')
if testResult.affectsVariables.length is 3 and
testResult.affectsVariables.indexOf("c") != -1 and
testResult.affectsVariables.indexOf("a") != -1 and
testResult.affectsVariables.indexOf("PATTERN_DEPENDENCY") != -1 and
testResult.affectedBy.length is 3 and
testResult.affectedBy.indexOf("d") != -1 and
testResult.affectedBy.indexOf("a") != -1 and
testResult.affectedBy.indexOf("PATTERN_DEPENDENCY") != -1
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = computeDependenciesFromAlgebra('PCA(M) = eig(Mᵀ⋅M)')
if testResult.affectsVariables.length is 1 and
testResult.affectsVariables.indexOf("PCA") != -1 and
testResult.affectsVariables.indexOf("PATTERN_DEPENDENCY") == -1 and
testResult.affectedBy.length is 1 and
testResult.affectedBy.indexOf("PATTERN_DEPENDENCY") != -1
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = computeDependenciesFromAlgebra('pattern(a_ᵀ⋅a_, cov(a_))')
if testResult.affectsVariables.length is 1 and
testResult.affectsVariables.indexOf("PATTERN_DEPENDENCY") != -1 and
testResult.affectedBy.length is 1 and
testResult.affectedBy.indexOf("PATTERN_DEPENDENCY") != -1
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('a = b\nf = a+1')
if testResult[0] == "All local dependencies: variable a depends on: b, ; variable f depends on: a, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable a depends on: b, ; variable f depends on: b, ; " and
testResult[1] == "" and
testResult[2] == "a = function (b) { return ( b ); }\nf = function (b) { return ( 1 + b ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
testResult = findDependenciesInScript('PCA(M) = eig(cov(M))')
if testResult[0] == "All local dependencies: variable PCA depends on: 'M, ; . Symbols with reassignments: . Symbols in expressions without assignments: . All dependencies recursively: variable PCA depends on: 'M, ; " and
testResult[1] == "" and
testResult[2] == "PCA = function (M) { return ( eig(cov(M)) ); }"
console.log "ok dependency test"
else
console.log "fail dependency test. expected: " + testResult
do_clearall()
computeResultsAndJavaScriptFromAlgebra('PCA(M) = eig(Mᵀ⋅M)')
testResult = run('symbolsinfo')
if testResult.indexOf('AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE') != -1
console.log "fail dependency tests. found AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE"
else
console.log "ok dependency test"
do_clearall()
# this checks error handling in case of pattern and syntax error
# picked up during scanning.
computeResultsAndJavaScriptFromAlgebra('pattern(a_ᵀ⋅a_, cov(a_))')
computeResultsAndJavaScriptFromAlgebra('simplify(')
testResult = computeResultsAndJavaScriptFromAlgebra('PCA = Mᵀ·M')
if testResult.code == "PCA = function (M) { return ( cov(M) ); }" and
testResult.latexResult == "$$PCA(M) = cov(M)$$" and
testResult.result == "$$PCA(M) = cov(M)$$" and
testResult.dependencyInfo.affectedBy[0] == "M" and
testResult.dependencyInfo.affectedBy[1] == "PATTERN_DEPENDENCY" and
testResult.dependencyInfo.affectsVariables.length == 1 and
testResult.dependencyInfo.affectsVariables[0] == "PCA"
console.log "ok dependency test"
else
console.log "fail dependency tests. Error handling 1"
console.log testResult
return
do_clearall()
console.log "checking hit/miss patterns ======================="
resetCache()
original_CACHE_HITSMISS_DEBUGS = CACHE_HITSMISS_DEBUGS
CACHE_HITSMISS_DEBUGS = true
# first two should miss because caches are completely empty
# note that each call produces two misses because one is from
# "findDependenciesInScript" itself and one is from "run"
# after these two, all the others should hit a cache
computeResultsAndJavaScriptFromAlgebra('pattern(a_ᵀ⋅a_, cov(a_))')
computeResultsAndJavaScriptFromAlgebra('PCA = Mᵀ·M')
if totalAllCachesHits() != 0
console.log "test hecking hit/miss patterns fail, got: " + totalAllCachesHits() + " instead of 0"
clearAlgebraEnvironment()
console.log "\nclearAlgebraEnvironment()"
currentStateHash = getStateHash()
console.log "state hash after nclearAlgebraEnvironment: " + currentStateHash
console.log "\n"
computeResultsAndJavaScriptFromAlgebra('pattern(a_ᵀ⋅a_, cov(a_))')
computeResultsAndJavaScriptFromAlgebra('PCA = Mᵀ·M')
if totalAllCachesHits() != 2
console.log "test hecking hit/miss patterns fail, got: " + totalAllCachesHits() + " instead of 2"
clearAlgebraEnvironment()
console.log "\nclearAlgebraEnvironment()"
currentStateHash = getStateHash()
console.log "state hash after nclearAlgebraEnvironment: " + currentStateHash
console.log "\n"
computeResultsAndJavaScriptFromAlgebra('pattern(a_ᵀ⋅a_, cov(a_))')
computeResultsAndJavaScriptFromAlgebra('PCA = Mᵀ·M')
CACHE_HITSMISS_DEBUGS = original_CACHE_HITSMISS_DEBUGS
if totalAllCachesHits() != 4
console.log "test hecking hit/miss patterns fail, got: " + totalAllCachesHits() + " instead of 4"
clearAlgebraEnvironment()
console.log "\nclearAlgebraEnvironment()"
currentStateHash = getStateHash()
console.log "state hash after nclearAlgebraEnvironment: " + currentStateHash
console.log "\n"
computeResultsAndJavaScriptFromAlgebra('pattern(a_ᵀ⋅a_, cov(a_))')
# note that in case of syntax error, "last" is not updated.
# so if new symbols have been scanned yet, then they are not created
# and the cache should hit
# TODO ideally a scan resulting in an error should produce no new
# symbols in the symbol table at all
computeResultsAndJavaScriptFromAlgebra('simplify(')
currentStateHash = getStateHash()
console.log "state hash after syntax error: " + currentStateHash
computeResultsAndJavaScriptFromAlgebra('PCA = Mᵀ·M')
CACHE_HITSMISS_DEBUGS = original_CACHE_HITSMISS_DEBUGS
if totalAllCachesHits() != 6
console.log "test hecking hit/miss patterns fail, got: " + totalAllCachesHits() + " instead of 6"
if totalAllCachesMisses() != 5
console.log "test hecking hit/miss patterns fail, got: " + totalAllCachesMisses() + " instead of 5"
resetCache()
console.log "end of checking hit/miss patterns ======================="
do_clearall()
testResult = computeResultsAndJavaScriptFromAlgebra('x + x + x')
if testResult.code == "" and
testResult.latexResult == "$$3x$$" and
testResult.result == "$$3x$$" and
testResult.dependencyInfo.affectedBy.length == 2 and
testResult.dependencyInfo.affectedBy[0] == "x" and
testResult.dependencyInfo.affectedBy[1] == "PATTERN_DEPENDENCY" and
testResult.dependencyInfo.affectsVariables.length == 0
console.log "ok dependency test"
else
console.log "fail dependency tests"
do_clearall()
computeResultsAndJavaScriptFromAlgebra('x = y + 2')
testResult = computeResultsAndJavaScriptFromAlgebra('x + x + x')
if testResult.code == "" and
testResult.latexResult == "$$3y+6$$" and
testResult.result == "$$3y+6$$" and
testResult.dependencyInfo.affectedBy.length == 2 and
testResult.dependencyInfo.affectedBy[0] == "x" and
testResult.dependencyInfo.affectedBy[1] == "PATTERN_DEPENDENCY" and
testResult.dependencyInfo.affectsVariables.length == 0
console.log "ok dependency test"
else
console.log "fail dependency tests"
do_clearall()
console.log "-- done dependency tests"
do_clearall()
# if we just want to compute the dependencies, we don't need to do
# anything costly, we don't "run" the code and we don't simplify
# the code. Just finding the plain dependencies
# TODO change the name of this function, as it doesn't just find the
# dependencies. It also runs it and generates the JS code.
findDependenciesInScript = (stringToBeParsed, dontGenerateCode) ->
if DEBUG then console.log "stringToBeParsed: " + stringToBeParsed
timeStartFromAlgebra = new Date().getTime()
if CACHE_DEBUGS or CACHE_HITSMISS_DEBUGS or TIMING_DEBUGS
console.log " --------- findDependenciesInScript input: " + stringToBeParsed + " at: " + (new Date())
currentStateHash = getStateHash()
console.log "state hash: " + currentStateHash
inited = true
codeGen = true
symbolsDependencies = {}
symbolsHavingReassignments = []
symbolsInExpressionsWithoutAssignments = []
patternHasBeenFound = false
indexOfPartRemainingToBeParsed = 0
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
n = 0
# we are going to store the dependencies _of the block as a whole_
# so all affected variables in the whole block are lumped
# together, and same for the variable that affect those, we
# lump them all together.
dependencyInfo =
affectsVariables: []
affectedBy: []
stringToBeRun = stringToBeParsed
if ENABLE_CACHING and stringToBeRun != "clearall"
currentStateHash = getStateHash()
cacheKey = currentStateHash + " stringToBeRun: " + stringToBeRun + " - " + dontGenerateCode
if CACHE_DEBUGS then console.log "cached_findDependenciesInScript key: " + cacheKey
possiblyCached = cached_findDependenciesInScript.get(cacheKey)
if possiblyCached?
if CACHE_HITSMISS_DEBUGS then console.log "cached_findDependenciesInScript hit on " + stringToBeRun
unfreeze(possiblyCached)
# return the output string
if TIMING_DEBUGS
totalTime = new Date().getTime() - timeStartFromAlgebra
console.log "findDependenciesInScript input: " + stringToBeRun + " time: " + totalTime + "ms, saved " + (possiblyCached[possiblyCached.length-5] - totalTime) + "ms due to cache hit"
return [
possiblyCached[possiblyCached.length - 7],
possiblyCached[possiblyCached.length - 6],
possiblyCached[possiblyCached.length - 5],
possiblyCached[possiblyCached.length - 4],
possiblyCached[possiblyCached.length - 3],
possiblyCached[possiblyCached.length - 2],
possiblyCached[possiblyCached.length - 1]
]
else
if CACHE_HITSMISS_DEBUGS then console.log "cached_findDependenciesInScript miss on: " + stringToBeRun
if TIMING_DEBUGS
cacheMissPenalty = (new Date().getTime() - timeStartFromAlgebra)
# parse the input. This collects the
# dependency information
while (1)
try
errorMessage = ""
check_stack()
if DEBUG then console.log "findDependenciesInScript: scanning"
n = scan(stringToBeParsed.substring(indexOfPartRemainingToBeParsed))
if DEBUG then console.log "scanned"
pop()
check_stack()
catch error
if PRINTOUTRESULT then console.log error
errorMessage = error + ""
#debugger
reset_after_error()
break
if (n == 0)
break
indexOfPartRemainingToBeParsed += n
testableString = ""
# print out all local dependencies as collected by this
# parsing pass
if DEBUG then console.log "all local dependencies ----------------"
testableString += "All local dependencies: "
for key, value of symbolsDependencies
if DEBUG then console.log "variable " + key + " depends on: "
dependencyInfo.affectsVariables.push key
testableString += " variable " + key + " depends on: "
for i in value
if DEBUG then console.log " " + i
if i[0] != "'"
dependencyInfo.affectedBy.push i
testableString += i + ", "
testableString += "; "
testableString += ". "
# print out the symbols with re-assignments:
if DEBUG then console.log "Symbols with reassignments ----------------"
testableString += "Symbols with reassignments: "
for key in symbolsHavingReassignments
if dependencyInfo.affectedBy.indexOf(key) == -1
dependencyInfo.affectedBy.push key
testableString += key + ", "
testableString += ". "
# print out the symbols that appear in expressions without assignments
if DEBUG then console.log "Symbols in expressions without assignments ----------------"
testableString += "Symbols in expressions without assignments: "
for key in symbolsInExpressionsWithoutAssignments
if dependencyInfo.affectedBy.indexOf(key) == -1
dependencyInfo.affectedBy.push key
testableString += key + ", "
testableString += ". "
# ALL Algebrite code is affected by any pattern changing
dependencyInfo.affectedBy.push "PATTERN_DEPENDENCY"
if patternHasBeenFound
dependencyInfo.affectsVariables.push "PATTERN_DEPENDENCY"
testableString += " - PATTERN_DEPENDENCY inserted - "
# print out all global dependencies as collected by this
# parsing pass
if DEBUG then console.log "All dependencies recursively ----------------"
testableString += "All dependencies recursively: "
scriptEvaluation = ["",""]
generatedCode = ""
readableSummaryOfGeneratedCode = ""
if errorMessage == "" and !dontGenerateCode
try
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
scriptEvaluation = run(stringToBeParsed, true)
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
catch error
if PRINTOUTRESULT then console.log error
errorMessage = error + ""
#debugger
init()
if errorMessage == ""
for key of symbolsDependencies
codeGen = true
if DEBUG then console.log " variable " + key + " is: " + get_binding(usr_symbol(key)).toString()
codeGen = false
if DEBUG then console.log " variable " + key + " depends on: "
testableString += " variable " + key + " depends on: "
recursedDependencies = []
variablesWithCycles = []
cyclesDescriptions = []
recursiveDependencies key, recursedDependencies, [], variablesWithCycles, [], cyclesDescriptions
for i in variablesWithCycles
if DEBUG then console.log " --> cycle through " + i
for i in recursedDependencies
if DEBUG then console.log " " + i
testableString += i + ", "
testableString += "; "
for i in cyclesDescriptions
testableString += " " + i + ", "
if DEBUG then console.log " code generation:" + key + " is: " + get_binding(usr_symbol(key)).toString()
# we really want to make an extra effort
# to generate simplified code, so
# run a "simplify" on the content of each
# variable that we are generating code for.
# Note that the variable
# will still point to un-simplified structures,
# we only simplify the generated code.
push get_binding(usr_symbol(key))
# Since we go and simplify all variables we meet,
# we have to replace each variable passed as a parameter
# with something entirely new, so that there is no chance
# that it might evoke previous values in the external scope
# as in this case:
# a = 2
# f(a) = a+1+b
# we don't want 'a' in the body of f to be simplified to 2
# There are two cases: 1) the variable actually was already in
# the symble table, in which case there is goong to be this new
# one prepended with AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE, and
# we'll have to remove up this variable later.
# OR 2) the variable wasn't already in the symbol table, in which
# case we directly create this one, which means that we'll have
# to rename it later to the correct name without the prepended
# part.
replacementsFrom = []
replacementsTo = []
for eachDependency in recursedDependencies
if eachDependency[0] == "'"
deQuotedDep = eachDependency.substring(1)
originalUserSymbol = usr_symbol(deQuotedDep)
newUserSymbol = usr_symbol("AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE"+deQuotedDep)
replacementsFrom.push originalUserSymbol
replacementsTo.push newUserSymbol
push(originalUserSymbol)
push(newUserSymbol)
subst()
if DEBUG then console.log "after substitution: " + stack[tos-1]
try
simplifyForCodeGeneration()
catch error
if PRINTOUTRESULT then console.log error
errorMessage = error + ""
#debugger
init()
for indexOfEachReplacement in [0...replacementsFrom.length]
#console.log "replacing back " + replacementsTo[indexOfEachReplacement] + " into: " + replacementsFrom[indexOfEachReplacement]
push(replacementsTo[indexOfEachReplacement])
push(replacementsFrom[indexOfEachReplacement])
subst()
clearRenamedVariablesToAvoidBindingToExternalScope()
if errorMessage == ""
toBePrinted = pop()
# we have to get all the variables used on the right side
# here. I.e. to print the arguments it's better to look at the
# actual method body after simplification.
userVariablesMentioned = []
collectUserSymbols(toBePrinted, userVariablesMentioned)
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
codeGen = true
generatedBody = toBePrinted.toString()
codeGen = false
bodyForReadableSummaryOfGeneratedCode = toBePrinted.toString()
if variablesWithCycles.indexOf(key) != -1
generatedCode += "// " + key + " is part of a cyclic dependency, no code generated."
readableSummaryOfGeneratedCode += "#" + key + " is part of a cyclic dependency, no code generated."
else
###
# using this paragraph instead of the following one
# creates methods signatures that
# are slightly less efficient
# i.e. variables compare even if they are
# simplified away.
# In theory these signatures are more stable, but
# in practice signatures vary quite a bit anyways
# depending on previous assignments for example,
# so it's unclear whether going for stability
# is sensible at all..
if recursedDependencies.length != 0
parameters = "("
for i in recursedDependencies
if i.indexOf("'") != 0
parameters += i + ", "
else
if recursedDependencies.indexOf(i.substring(1)) == -1
parameters += i.substring(1) + ", "
###
# remove all native functions from the
# parameters as well.
userVariablesMentioned = userVariablesMentioned.filter (x) ->
predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(x + "") == -1
if userVariablesMentioned.length != 0
parameters = "("
for i in userVariablesMentioned
if i.printname != key
parameters += i.printname + ", "
# eliminate the last ", " for printout clarity
parameters = parameters.replace /, $/gm , ""
parameters += ")"
generatedCode += key + " = function " + parameters + " { return ( " + generatedBody + " ); }"
readableSummaryOfGeneratedCode += key + parameters + " = " + bodyForReadableSummaryOfGeneratedCode
else
generatedCode += key + " = " + generatedBody + ";"
readableSummaryOfGeneratedCode += key + " = " + bodyForReadableSummaryOfGeneratedCode
generatedCode += "\n"
readableSummaryOfGeneratedCode += "\n"
if DEBUG then console.log " " + generatedCode
# eliminate the last new line
generatedCode = generatedCode.replace /\n$/gm , ""
readableSummaryOfGeneratedCode = readableSummaryOfGeneratedCode.replace /\n$/gm , ""
# cleanup
symbolsDependencies = {}
symbolsHavingReassignments = []
patternHasBeenFound = false
symbolsInExpressionsWithoutAssignments = []
if DEBUG then console.log "testable string: " + testableString
if TIMING_DEBUGS
console.log "findDependenciesInScript time for: " + stringToBeRun + " : "+ ((new Date().getTime()) - timeStartFromAlgebra) + "ms"
if ENABLE_CACHING and stringToBeRun != "clearall" and errorMessage == ""
frozen = freeze()
toBeFrozen = [
frozen[0],
frozen[1],
frozen[2],
frozen[3],
frozen[4],
frozen[5],
(new Date().getTime() - timeStartFromAlgebra),
testableString,
scriptEvaluation[0],
generatedCode,
readableSummaryOfGeneratedCode,
scriptEvaluation[1],
errorMessage,
dependencyInfo
]
if CACHE_DEBUGS then console.log "setting cached_findDependenciesInScript on key: " + cacheKey
cached_findDependenciesInScript.set(cacheKey, toBeFrozen)
return [testableString, scriptEvaluation[0], generatedCode, readableSummaryOfGeneratedCode, scriptEvaluation[1], errorMessage, dependencyInfo]
recursiveDependencies = (variableToBeChecked, arrayWhereDependenciesWillBeAdded, variablesAlreadyFleshedOut, variablesWithCycles, chainBeingChecked, cyclesDescriptions) ->
variablesAlreadyFleshedOut.push variableToBeChecked
# recursive dependencies can only be descended if the variable is not bound to a parameter
if symbolsDependencies[chainBeingChecked[chainBeingChecked.length-1]]?
if symbolsDependencies[chainBeingChecked[chainBeingChecked.length-1]].indexOf("'"+variableToBeChecked) != -1
if DEBUG then console.log "can't keep following the chain of " + variableToBeChecked + " because it's actually a variable bound to a parameter"
if arrayWhereDependenciesWillBeAdded.indexOf("'"+variableToBeChecked) == -1 and arrayWhereDependenciesWillBeAdded.indexOf(variableToBeChecked) == -1
arrayWhereDependenciesWillBeAdded.push variableToBeChecked
return arrayWhereDependenciesWillBeAdded
chainBeingChecked.push variableToBeChecked
if !symbolsDependencies[variableToBeChecked]?
# end case: the passed variable has no dependencies
# so there is nothing else to do
if arrayWhereDependenciesWillBeAdded.indexOf(variableToBeChecked) == -1
arrayWhereDependenciesWillBeAdded.push variableToBeChecked
return arrayWhereDependenciesWillBeAdded
else
# recursion case: we have to dig deeper
for i in symbolsDependencies[variableToBeChecked]
# check that there is no recursion in dependencies
# we do that by keeping a list of variables that
# have already been "fleshed-out". If we encounter
# any of those "fleshed-out" variables while
# fleshing out, then there is a cycle
if chainBeingChecked.indexOf(i) != -1
if DEBUG then console.log " found cycle:"
cyclesDescription = ""
for k in chainBeingChecked
if variablesWithCycles.indexOf(k) == -1
variablesWithCycles.push k
if DEBUG then console.log k + " --> "
cyclesDescription += k + " --> "
if DEBUG then console.log " ... then " + i + " again"
cyclesDescription += " ... then " + i + " again"
cyclesDescriptions.push cyclesDescription
#if DEBUG then console.log " --> cycle through " + i
# we want to flesh-out i but it's already been
# fleshed-out, just add it to the variables
# with cycles and move on
# todo refactor this, there are two copies of these two lines
if variablesWithCycles.indexOf(i) == -1
variablesWithCycles.push i
else
# flesh-out i recursively
recursiveDependencies i, arrayWhereDependenciesWillBeAdded, variablesAlreadyFleshedOut, variablesWithCycles, chainBeingChecked, cyclesDescriptions
chainBeingChecked.pop()
#variablesAlreadyFleshedOut.pop()
return arrayWhereDependenciesWillBeAdded
# parses and runs one statement/expression at a time
inited = false
latexErrorSign = "\\rlap{\\large\\color{red}\\bigtriangleup}{\\ \\ \\tiny\\color{red}!}"
turnErrorMessageToLatex = (theErrorMessage) ->
theErrorMessage = theErrorMessage.replace(/\n/g,"")
theErrorMessage = theErrorMessage.replace(/_/g, "} \\\\_ \\text{");
theErrorMessage = theErrorMessage.replace(new RegExp(String.fromCharCode(transpose_unicode), 'g'), "}{}^{T}\\text{");
theErrorMessage = theErrorMessage.replace(new RegExp(String.fromCharCode(dotprod_unicode), 'g'),"}\\cdot \\text{");
theErrorMessage = theErrorMessage.replace("Stop:","} \\quad \\text{Stop:");
theErrorMessage = theErrorMessage.replace("->","} \\rightarrow \\text{");
theErrorMessage = theErrorMessage.replace("?","}\\enspace " + latexErrorSign + " \\enspace \\text{");
theErrorMessage = "$$\\text{" + theErrorMessage.replace(/\n/g,"") + "}$$"
#console.log "theErrorMessage: " + theErrorMessage
return theErrorMessage
# there are around a dozen different unicodes that
# represent some sort of middle dot, let's catch the most
# common and turn them into what we can process
normaliseDots = (stringToNormalise) ->
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(8901), 'g'), String.fromCharCode(dotprod_unicode));
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(8226), 'g'), String.fromCharCode(dotprod_unicode));
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(12539), 'g'), String.fromCharCode(dotprod_unicode));
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(55296), 'g'), String.fromCharCode(dotprod_unicode));
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(65381), 'g'), String.fromCharCode(dotprod_unicode));
return stringToNormalise
CACHE_DEBUGS = false
CACHE_HITSMISS_DEBUGS = false
TIMING_DEBUGS = false
cacheMissPenalty = 0
run = (stringToBeRun, generateLatex = false) ->
timeStart = new Date().getTime()
#stringToBeRun = stringToBeRun + "\n"
stringToBeRun = normaliseDots stringToBeRun
#console.log "run running: " + stringToBeRun
if ENABLE_CACHING and stringToBeRun != "clearall"
currentStateHash = getStateHash()
cacheKey = currentStateHash + " stringToBeRun: " + stringToBeRun
if CACHE_DEBUGS then console.log "cached_runs key: " + cacheKey
possiblyCached = cached_runs.get(cacheKey)
#possiblyCached = null
if possiblyCached?
if CACHE_HITSMISS_DEBUGS then console.log "cached_runs hit on: " + stringToBeRun
unfreeze(possiblyCached)
# return the output string
if TIMING_DEBUGS
totalTime = new Date().getTime() - timeStart
console.log "run time: " + totalTime + "ms, saved " + (possiblyCached[possiblyCached.length-2] - totalTime) + "ms due to cache hit"
return possiblyCached[possiblyCached.length - 1]
else
if CACHE_HITSMISS_DEBUGS then console.log "cached_runs miss on: " + stringToBeRun
if TIMING_DEBUGS
cacheMissPenalty = (new Date().getTime() - timeStart)
if stringToBeRun == "selftest"
selftest()
return
#if (setjmp(stop_return))
# return
if !inited
inited = true
init()
i = 0
n = 0
indexOfPartRemainingToBeParsed = 0
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
while (1)
# while we can keep scanning commands out of the
# passed input AND we can execute them...
try
errorMessage = ""
check_stack()
n = scan(stringToBeRun.substring(indexOfPartRemainingToBeParsed))
p1 = pop()
check_stack()
catch error
if PRINTOUTRESULT then console.log error
#debugger
allReturnedPlainStrings += error.message
if generateLatex
#debugger
theErrorMessage = turnErrorMessageToLatex error.message
allReturnedLatexStrings += theErrorMessage
reset_after_error()
break
if (n == 0)
break
# if debug mode then print the source text
#if (equaln(get_binding(symbol(TRACE)), 1)) {
# for (i = 0 i < n i++)
# if (s[i] != '\r')
# printchar(s[i])
# if (s[n - 1] != '\n') # n is not zero, see above
# printchar('\n')
#}
indexOfPartRemainingToBeParsed += n
push(p1)
#debugger
errorWhileExecution = false
try
stringsEmittedByUserPrintouts = ""
top_level_eval()
#console.log "emitted string after top_level_eval(): >" + stringsEmittedByUserPrintouts + "<"
#console.log "allReturnedPlainStrings string after top_level_eval(): >" + allReturnedPlainStrings + "<"
p2 = pop()
check_stack()
if (isstr(p2))
if DEBUG then console.log(p2.str)
if DEBUG then console.log("\n")
# if the return value is nil there isn't much point
# in adding "nil" to the printout
if (p2 == symbol(NIL))
#collectedPlainResult = stringsEmittedByUserPrintouts
collectedPlainResult = stringsEmittedByUserPrintouts
if generateLatex
collectedLatexResult = "$$" + stringsEmittedByUserPrintouts + "$$"
else
#console.log "emitted string before collectPlainStringFromReturnValue: >" + stringsEmittedByUserPrintouts + "<"
#console.log "allReturnedPlainStrings string before collectPlainStringFromReturnValue: >" + allReturnedPlainStrings + "<"
collectedPlainResult = print_expr(p2)
collectedPlainResult += "\n"
#console.log "collectedPlainResult: >" + collectedPlainResult + "<"
if generateLatex
collectedLatexResult = "$$" + collectLatexStringFromReturnValue(p2) + "$$"
if DEBUG then console.log "collectedLatexResult: " + collectedLatexResult
allReturnedPlainStrings += collectedPlainResult
if generateLatex then allReturnedLatexStrings += collectedLatexResult
if PRINTOUTRESULT
if DEBUG then console.log "printline"
if DEBUG then console.log collectedPlainResult
#alert collectedPlainResult
if PRINTOUTRESULT
if DEBUG then console.log "display:"
print2dascii(p2)
if generateLatex then allReturnedLatexStrings += "\n"
catch error
errorWhileExecution = true
collectedPlainResult = error.message
if generateLatex then collectedLatexResult = turnErrorMessageToLatex error.message
if PRINTOUTRESULT then console.log collectedPlainResult
allReturnedPlainStrings += collectedPlainResult
if collectedPlainResult != ""
allReturnedPlainStrings += "\n"
if generateLatex
allReturnedLatexStrings += collectedLatexResult
allReturnedLatexStrings += "\n"
resetCache()
init()
if allReturnedPlainStrings[allReturnedPlainStrings.length-1] == "\n"
allReturnedPlainStrings = allReturnedPlainStrings.substring(0,allReturnedPlainStrings.length-1)
if generateLatex
if allReturnedLatexStrings[allReturnedLatexStrings.length-1] == "\n"
allReturnedLatexStrings = allReturnedLatexStrings.substring(0,allReturnedLatexStrings.length-1)
if generateLatex
if DEBUG then console.log "allReturnedLatexStrings: " + allReturnedLatexStrings
# TODO handle this case of caching
stringToBeReturned = [allReturnedPlainStrings, allReturnedLatexStrings]
else
stringToBeReturned = allReturnedPlainStrings
if ENABLE_CACHING and stringToBeRun != "clearall" and !errorWhileExecution
frozen = freeze(