algebrite
Version:
Computer Algebra System in Coffeescript
384 lines (305 loc) • 5.63 kB
text/coffeescript
### Power function
Input: push Base
push Exponent
Output: Result on stack
###
Eval_power = ->
push(cadr(p1))
Eval()
push(caddr(p1))
Eval()
power()
power = ->
save()
yypower()
restore()
yypower = ->
n = 0
p2 = pop()
p1 = pop()
# both base and exponent are rational numbers?
if (isrational(p1) && isrational(p2))
push(p1)
push(p2)
qpow()
return
# both base and exponent are either rational or double?
if (isnum(p1) && isnum(p2))
push(p1)
push(p2)
dpow()
return
if (istensor(p1))
power_tensor()
return
if (p1 == symbol(E) && car(p2) == symbol(LOG))
push(cadr(p2))
return
if (p1 == symbol(E) && isdouble(p2))
push_double(exp(p2.d))
return
# 1 ^ a -> 1
# a ^ 0 -> 1
if (equal(p1, one) || iszero(p2))
push(one)
return
# a ^ 1 -> a
if (equal(p2, one))
push(p1)
return
# (a * b) ^ c -> (a ^ c) * (b ^ c)
if (car(p1) == symbol(MULTIPLY))
p1 = cdr(p1)
push(car(p1))
push(p2)
power()
p1 = cdr(p1)
while (iscons(p1))
push(car(p1))
push(p2)
power()
multiply()
p1 = cdr(p1)
return
# (a ^ b) ^ c -> a ^ (b * c)
if (car(p1) == symbol(POWER))
push(cadr(p1))
push(caddr(p1))
push(p2)
multiply()
power()
return
# (a + b) ^ n -> (a + b) * (a + b) ...
if (expanding && isadd(p1) && isnum(p2))
push(p2)
n = pop_integer()
if (n > 1 && n != 0x80000000)
power_sum(n)
return
# sin(x) ^ 2n -> (1 - cos(x) ^ 2) ^ n
if (trigmode == 1 && car(p1) == symbol(SIN) && iseveninteger(p2))
push_integer(1)
push(cadr(p1))
cosine()
push_integer(2)
power()
subtract()
push(p2)
push_rational(1, 2)
multiply()
power()
return
# cos(x) ^ 2n -> (1 - sin(x) ^ 2) ^ n
if (trigmode == 2 && car(p1) == symbol(COS) && iseveninteger(p2))
push_integer(1)
push(cadr(p1))
sine()
push_integer(2)
power()
subtract()
push(p2)
push_rational(1, 2)
multiply()
power()
return
# complex number? (just number, not expression)
if (iscomplexnumber(p1))
# integer power?
# n will be negative here, positive n already handled
if (isinteger(p2))
# / \ n
# -n | a - ib |
# (a + ib) = | -------- |
# | 2 2 |
# \ a + b /
push(p1)
conjugate()
p3 = pop()
push(p3)
push(p3)
push(p1)
multiply()
divide()
push(p2)
negate()
power()
return
# noninteger or floating power?
if (isnum(p2))
push(p1)
mag()
push(p2)
power()
push_integer(-1)
push(p1)
arg()
push(p2)
multiply()
push(symbol(PI))
divide()
power()
multiply()
return
###
push(p1)
mag()
push(p2)
power()
push(symbol(E))
push(p1)
arg()
push(p2)
multiply()
push(imaginaryunit)
multiply()
power()
multiply()
###
if (simplify_polar())
return
push_symbol(POWER)
push(p1)
push(p2)
list(3)
#-----------------------------------------------------------------------------
#
# Compute the power of a sum
#
# Input: p1 sum
#
# n exponent
#
# Output: Result on stack
#
# Note:
#
# Uses the multinomial series (see Math World)
#
# n n! n1 n2 nk
# (a1 + a2 + ... + ak) = sum (--------------- a1 a2 ... ak )
# n1! n2! ... nk!
#
# The sum is over all n1 ... nk such that n1 + n2 + ... + nk = n.
#
#-----------------------------------------------------------------------------
# first index is the term number 0..k-1, second index is the exponent 0..n
#define A(i, j) frame[(i) * (n + 1) + (j)]
power_sum = (n) ->
a = []
i = 0
j = 0
k = 0
# number of terms in the sum
k = length(p1) - 1
# local frame
push_frame(k * (n + 1))
# array of powers
p1 = cdr(p1)
for i in [0...k]
for j in [0..n]
push(car(p1))
push_integer(j)
power()
stack[frame + (i) * (n + 1) + (j)] = pop()
p1 = cdr(p1)
push_integer(n)
factorial()
p1 = pop()
for i in [0...k]
a[i] = 0
push(zero)
multinomial_sum(k, n, a, 0, n)
pop_frame(k * (n + 1))
#-----------------------------------------------------------------------------
#
# Compute multinomial sum
#
# Input: k number of factors
#
# n overall exponent
#
# a partition array
#
# i partition array index
#
# m partition remainder
#
# p1 n!
#
# A factor array
#
# Output: Result on stack
#
# Note:
#
# Uses recursive descent to fill the partition array.
#
#-----------------------------------------------------------------------------
#int k, int n, int *a, int i, int m
multinomial_sum = (k,n,a,i,m) ->
j = 0
if (i < k - 1)
for j in [0..m]
a[i] = j
multinomial_sum(k, n, a, i + 1, m - j)
return
a[i] = m
# coefficient
push(p1)
for j in [0...k]
push_integer(a[j])
factorial()
divide()
# factors
for j in [0...k]
push(stack[frame + (j) * (n + 1) + a[j]])
multiply()
add()
# exp(n/2 i pi) ?
# p2 is the exponent expression
# clobbers p3
simplify_polar = ->
n = 0
n = isquarterturn(p2)
switch(n)
when 0
doNothing = 1
when 1
push_integer(1)
return 1
when 2
push_integer(-1)
return 1
when 3
push(imaginaryunit)
return 1
when 4
push(imaginaryunit)
negate()
return 1
if (car(p2) == symbol(ADD))
p3 = cdr(p2)
while (iscons(p3))
n = isquarterturn(car(p3))
if (n)
break
p3 = cdr(p3)
switch (n)
when 0
return 0
when 1
push_integer(1)
when 2
push_integer(-1)
when 3
push(imaginaryunit)
when 4
push(imaginaryunit)
negate()
push(p2)
push(car(p3))
subtract()
exponential()
multiply()
return 1
return 0