336 lines
6.3 KiB
CoffeeScript
336 lines
6.3 KiB
CoffeeScript
###
|
|
Symbolic addition
|
|
|
|
Terms in a sum are combined if they are identical modulo rational
|
|
coefficients.
|
|
|
|
For example, A + 2A becomes 3A.
|
|
|
|
However, the sum A + sqrt(2) A is not modified.
|
|
|
|
Combining terms can lead to second-order effects.
|
|
|
|
For example, consider the case of
|
|
|
|
1/sqrt(2) A + 3/sqrt(2) A + sqrt(2) A
|
|
|
|
The first two terms are combined to yield 2 sqrt(2) A.
|
|
|
|
This result can now be combined with the third term to yield
|
|
|
|
3 sqrt(2) A
|
|
###
|
|
|
|
|
|
|
|
flag = 0
|
|
|
|
Eval_add = ->
|
|
h = tos
|
|
p1 = cdr(p1)
|
|
while (iscons(p1))
|
|
push(car(p1))
|
|
Eval()
|
|
p2 = pop()
|
|
push_terms(p2)
|
|
p1 = cdr(p1)
|
|
add_terms(tos - h)
|
|
|
|
# Add n terms, returns one expression on the stack.
|
|
|
|
stackAddsCount = 0
|
|
add_terms = (n) ->
|
|
stackAddsCount++
|
|
i = 0
|
|
|
|
h = tos - n
|
|
|
|
s = h
|
|
|
|
# ensure no infinite loop, use "for"
|
|
|
|
if DEBUG then console.log "stack before adding terms #" + stackAddsCount
|
|
#if stackAddsCount == 137
|
|
# debugger
|
|
|
|
if DEBUG
|
|
for i in [0...tos]
|
|
console.log print_list stack[i]
|
|
|
|
for i in [0...10]
|
|
|
|
if (n < 2)
|
|
break
|
|
|
|
flag = 0
|
|
|
|
|
|
#qsort(s, n, sizeof (U *), cmp_terms)
|
|
subsetOfStack = stack.slice(h,h+n)
|
|
subsetOfStack.sort(cmp_terms)
|
|
stack = stack.slice(0,h).concat(subsetOfStack).concat(stack.slice(h+n))
|
|
|
|
if (flag == 0)
|
|
break
|
|
|
|
n = combine_terms(h, n)
|
|
|
|
moveTos h + n
|
|
|
|
switch (n)
|
|
when 0
|
|
if evaluatingAsFloats then push_double(0.0) else push(zero)
|
|
when 1
|
|
else
|
|
list(n)
|
|
p1 = pop()
|
|
push_symbol(ADD)
|
|
push(p1)
|
|
cons()
|
|
|
|
if DEBUG then console.log "stack after adding terms #" + stackAddsCount
|
|
#if stackAddsCount == 5
|
|
# debugger
|
|
|
|
if DEBUG
|
|
for i in [0...tos]
|
|
console.log print_list stack[i]
|
|
|
|
# Compare terms for order, clobbers p1 and p2.
|
|
|
|
cmp_terms_count = 0
|
|
cmp_terms = (p1, p2) ->
|
|
cmp_terms_count++
|
|
#if cmp_terms_count == 52
|
|
# debugger
|
|
|
|
i = 0
|
|
# numbers can be combined
|
|
|
|
if (isNumericAtom(p1) && isNumericAtom(p2))
|
|
flag = 1
|
|
#if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 0"
|
|
return 0
|
|
|
|
# congruent tensors can be combined
|
|
|
|
if (istensor(p1) && istensor(p2))
|
|
if (p1.tensor.ndim < p2.tensor.ndim)
|
|
#if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns -1"
|
|
return -1
|
|
if (p1.tensor.ndim > p2.tensor.ndim)
|
|
#if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 1"
|
|
return 1
|
|
for i in [0...p1.tensor.ndim]
|
|
if (p1.tensor.dim[i] < p2.tensor.dim[i])
|
|
#if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns -1"
|
|
return -1
|
|
if (p1.tensor.dim[i] > p2.tensor.dim[i])
|
|
#if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 1"
|
|
return 1
|
|
flag = 1
|
|
#if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 0"
|
|
return 0
|
|
|
|
if (car(p1) == symbol(MULTIPLY))
|
|
p1 = cdr(p1)
|
|
if (isNumericAtom(car(p1)))
|
|
p1 = cdr(p1)
|
|
if (cdr(p1) == symbol(NIL))
|
|
p1 = car(p1)
|
|
|
|
if (car(p2) == symbol(MULTIPLY))
|
|
p2 = cdr(p2)
|
|
if (isNumericAtom(car(p2)))
|
|
p2 = cdr(p2)
|
|
if (cdr(p2) == symbol(NIL))
|
|
p2 = car(p2)
|
|
|
|
t = cmp_expr(p1, p2)
|
|
|
|
if (t == 0)
|
|
flag = 1
|
|
|
|
#if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns " + t
|
|
return t
|
|
|
|
###
|
|
Compare adjacent terms in s[] and combine if possible.
|
|
|
|
Returns the number of terms remaining in s[].
|
|
|
|
n number of terms in s[] initially
|
|
###
|
|
|
|
combine_terms = (s, n) ->
|
|
|
|
#debugger
|
|
|
|
# I had to turn the coffeescript for loop into
|
|
# a more mundane while loop because the i
|
|
# variable was changed from within the body,
|
|
# which is something that is not supposed to
|
|
# happen in the coffeescript 'vector' form.
|
|
# Also this means I had to add a 'i++' jus before
|
|
# the end of the body and before the "continue"s
|
|
i=0
|
|
while i < (n-1)
|
|
check_esc_flag()
|
|
|
|
p3 = stack[s+i]
|
|
p4 = stack[s+i + 1]
|
|
|
|
if (istensor(p3) && istensor(p4))
|
|
push(p3)
|
|
push(p4)
|
|
tensor_plus_tensor()
|
|
p1 = pop()
|
|
if (p1 != symbol(NIL))
|
|
stack[s+i] = p1
|
|
for j in [(i + 1)...(n - 1)]
|
|
stack[s+j] = stack[s+j + 1]
|
|
n--
|
|
i--
|
|
|
|
i++
|
|
continue
|
|
|
|
if (istensor(p3) || istensor(p4))
|
|
|
|
i++
|
|
continue
|
|
|
|
if (isNumericAtom(p3) && isNumericAtom(p4))
|
|
push(p3)
|
|
push(p4)
|
|
add_numbers()
|
|
p1 = pop()
|
|
if (isZeroAtomOrTensor(p1))
|
|
for j in [i...(n - 2)]
|
|
stack[s+j] = stack[s+j + 2]
|
|
n -= 2
|
|
else
|
|
stack[s+i] = p1
|
|
for j in [(i + 1)...(n - 1)]
|
|
stack[s+j] = stack[s+j + 1]
|
|
n--
|
|
i--
|
|
|
|
i++
|
|
continue
|
|
|
|
if (isNumericAtom(p3) || isNumericAtom(p4))
|
|
|
|
i++
|
|
continue
|
|
|
|
if evaluatingAsFloats
|
|
p1 = one_as_double
|
|
p2 = one_as_double
|
|
else
|
|
p1 = one
|
|
p2 = one
|
|
|
|
t = 0
|
|
|
|
if (car(p3) == symbol(MULTIPLY))
|
|
p3 = cdr(p3)
|
|
t = 1; # p3 is now denormal
|
|
if (isNumericAtom(car(p3)))
|
|
p1 = car(p3)
|
|
p3 = cdr(p3)
|
|
if (cdr(p3) == symbol(NIL))
|
|
p3 = car(p3)
|
|
t = 0
|
|
|
|
if (car(p4) == symbol(MULTIPLY))
|
|
p4 = cdr(p4)
|
|
if (isNumericAtom(car(p4)))
|
|
p2 = car(p4)
|
|
p4 = cdr(p4)
|
|
if (cdr(p4) == symbol(NIL))
|
|
p4 = car(p4)
|
|
|
|
if (!equal(p3, p4))
|
|
|
|
i++
|
|
continue
|
|
|
|
push(p1)
|
|
push(p2)
|
|
add_numbers()
|
|
|
|
p1 = pop()
|
|
|
|
if (isZeroAtomOrTensor(p1))
|
|
for j in [i...(n - 2)]
|
|
stack[s+j] = stack[s+j + 2]
|
|
n -= 2
|
|
i--
|
|
|
|
i++
|
|
continue
|
|
|
|
push(p1)
|
|
|
|
if (t)
|
|
push(symbol(MULTIPLY))
|
|
push(p3)
|
|
cons()
|
|
else
|
|
push(p3)
|
|
|
|
multiply()
|
|
|
|
stack[s+i] = pop()
|
|
|
|
for j in [(i + 1)...(n - 1)]
|
|
stack[s+j] = stack[s+j + 1]
|
|
|
|
n--
|
|
i--
|
|
|
|
# this i++ is to match the while
|
|
i++
|
|
|
|
return n
|
|
|
|
push_terms = (p) ->
|
|
if (car(p) == symbol(ADD))
|
|
p = cdr(p)
|
|
while (iscons(p))
|
|
push(car(p))
|
|
p = cdr(p)
|
|
# omit zeroes
|
|
else if (!isZeroAtom(p))
|
|
push(p)
|
|
|
|
# add two expressions
|
|
|
|
add = ->
|
|
save()
|
|
p2 = pop()
|
|
p1 = pop()
|
|
h = tos
|
|
push_terms(p1)
|
|
push_terms(p2)
|
|
add_terms(tos - h)
|
|
restore()
|
|
|
|
add_all = (k) ->
|
|
i = 0
|
|
save()
|
|
s = tos - k
|
|
h = tos
|
|
for i in [0...k]
|
|
push_terms(stack[s+i])
|
|
add_terms(tos - h)
|
|
p1 = pop()
|
|
moveTos tos - k
|
|
push(p1)
|
|
restore()
|
|
|
|
subtract = ->
|
|
negate()
|
|
add()
|