171 lines
2.6 KiB
CoffeeScript
171 lines
2.6 KiB
CoffeeScript
|
|
|
|
|
|
factorial = ->
|
|
n = 0
|
|
save()
|
|
p1 = pop()
|
|
push(p1)
|
|
n = pop_integer()
|
|
if (n < 0 || isNaN(n))
|
|
push_symbol(FACTORIAL)
|
|
push(p1)
|
|
list(2)
|
|
restore()
|
|
return
|
|
bignum_factorial(n)
|
|
restore()
|
|
|
|
|
|
# simplification rules for factorials (m < n)
|
|
#
|
|
# (e + 1) * factorial(e) -> factorial(e + 1)
|
|
#
|
|
# factorial(e) / e -> factorial(e - 1)
|
|
#
|
|
# e / factorial(e) -> 1 / factorial(e - 1)
|
|
#
|
|
# factorial(e + n)
|
|
# ---------------- -> (e + m + 1)(e + m + 2)...(e + n)
|
|
# factorial(e + m)
|
|
#
|
|
# factorial(e + m) 1
|
|
# ---------------- -> --------------------------------
|
|
# factorial(e + n) (e + m + 1)(e + m + 2)...(e + n)
|
|
|
|
# this function is not actually used, but
|
|
# all these simplifications
|
|
# do happen automatically via simplify
|
|
simplifyfactorials = ->
|
|
x = 0
|
|
|
|
save()
|
|
|
|
x = expanding
|
|
expanding = 0
|
|
|
|
p1 = pop()
|
|
|
|
if (car(p1) == symbol(ADD))
|
|
push(zero)
|
|
p1 = cdr(p1)
|
|
while (iscons(p1))
|
|
push(car(p1))
|
|
simplifyfactorials()
|
|
add()
|
|
p1 = cdr(p1)
|
|
expanding = x
|
|
restore()
|
|
return
|
|
|
|
if (car(p1) == symbol(MULTIPLY))
|
|
sfac_product()
|
|
expanding = x
|
|
restore()
|
|
return
|
|
|
|
push(p1)
|
|
|
|
expanding = x
|
|
restore()
|
|
|
|
sfac_product = ->
|
|
i = 0
|
|
j = 0
|
|
n = 0
|
|
|
|
s = tos
|
|
|
|
p1 = cdr(p1)
|
|
n = 0
|
|
while (iscons(p1))
|
|
push(car(p1))
|
|
p1 = cdr(p1)
|
|
n++
|
|
|
|
for i in [0...(n - 1)]
|
|
if (stack[s + i] == symbol(NIL))
|
|
continue
|
|
for j in [(i + 1)...n]
|
|
if (stack[s + j] == symbol(NIL))
|
|
continue
|
|
sfac_product_f(s, i, j)
|
|
|
|
push(one)
|
|
|
|
for i in [0...n]
|
|
if (stack[s+i] == symbol(NIL))
|
|
continue
|
|
push(stack[s+i])
|
|
multiply()
|
|
|
|
p1 = pop()
|
|
|
|
moveTos tos - n
|
|
|
|
push(p1)
|
|
|
|
sfac_product_f = (s,a,b) ->
|
|
i = 0
|
|
n = 0
|
|
|
|
p1 = stack[s + a]
|
|
p2 = stack[s + b]
|
|
|
|
if (ispower(p1))
|
|
p3 = caddr(p1)
|
|
p1 = cadr(p1)
|
|
else
|
|
p3 = one
|
|
|
|
if (ispower(p2))
|
|
p4 = caddr(p2)
|
|
p2 = cadr(p2)
|
|
else
|
|
p4 = one
|
|
|
|
if (isfactorial(p1) && isfactorial(p2))
|
|
|
|
# Determine if the powers cancel.
|
|
|
|
push(p3)
|
|
push(p4)
|
|
add()
|
|
yyexpand()
|
|
n = pop_integer()
|
|
if (n != 0)
|
|
return
|
|
|
|
# Find the difference between the two factorial args.
|
|
|
|
# For example, the difference between (a + 2)! and a! is 2.
|
|
|
|
push(cadr(p1))
|
|
push(cadr(p2))
|
|
subtract()
|
|
yyexpand(); # to simplify
|
|
|
|
n = pop_integer()
|
|
if (n == 0 || isNaN(n))
|
|
return
|
|
if (n < 0)
|
|
n = -n
|
|
p5 = p1
|
|
p1 = p2
|
|
p2 = p5
|
|
p5 = p3
|
|
p3 = p4
|
|
p4 = p5
|
|
|
|
push(one)
|
|
|
|
for i in [1..n]
|
|
push(cadr(p2))
|
|
push_integer(i)
|
|
add()
|
|
push(p3)
|
|
power()
|
|
multiply()
|
|
stack[s+a] = pop()
|
|
stack[s+b] = symbol(NIL)
|