algebrite
Version:
Computer Algebra System in Coffeescript
277 lines (229 loc) • 6.76 kB
text/coffeescript
#(docs are generated from top-level comments, keep an eye on the formatting!)
### abs =====================================================================
Tags
----
scripting, JS, internal, treenode, general concept
Parameters
----------
x
General description
-------------------
Returns the absolute value of a real number, the magnitude of a complex number, or the vector length.
###
###
Absolute value of a number,or magnitude of complex z, or norm of a vector
z abs(z)
- ------
a a
-a a
(-1)^a 1
exp(a + i b) exp(a)
a b abs(a) abs(b)
a + i b sqrt(a^2 + b^2)
Notes
1. Handles mixed polar and rectangular forms, e.g. 1 + exp(i pi/3)
2. jean-francois.debroux reports that when z=(a+i*b)/(c+i*d) then
abs(numerator(z)) / abs(denominator(z))
must be used to get the correct answer. Now the operation is
automatic.
###
DEBUG_ABS = false
Eval_abs = ->
push(cadr(p1))
Eval()
abs()
absValFloat = ->
Eval()
absval()
Eval(); # normalize
zzfloat()
# zzfloat of an abs doesn't necessarily result in a double
# , for example if there are variables. But
# in many of the tests there should be indeed
# a float, these two lines come handy to highlight
# when that doesn't happen for those tests.
#if !isdouble(stack[tos-1])
# stop("absValFloat should return a double and instead got: " + stack[tos-1])
abs = ->
save()
p1 = pop()
push(p1)
numerator()
absval()
push(p1)
denominator()
absval()
divide()
restore()
absval = ->
save()
p1 = pop()
input = p1
if DEBUG_ABS then console.log "ABS of " + p1
# handle all the "number" cases first -----------------------------------------
if (iszero(p1))
if DEBUG_ABS then console.log " abs: " + p1 + " just zero"
push(zero)
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
if (isnegativenumber(p1))
if DEBUG_ABS then console.log " abs: " + p1 + " just a negative"
push(p1)
negate()
restore()
return
if (ispositivenumber(p1))
if DEBUG_ABS then console.log " abs: " + p1 + " just a positive"
push(p1)
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
if (p1 == symbol(PI))
if DEBUG_ABS then console.log " abs: " + p1 + " of PI"
push(p1)
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
# ??? should there be a shortcut case here for the imaginary unit?
# now handle decomposition cases ----------------------------------------------
# we catch the "add", "power", "multiply" cases first,
# before falling back to the
# negative/positive cases because there are some
# simplification thay we might be able to do.
# Note that for this routine to give a correct result, this
# must be a sum where a complex number appears.
# If we apply this to "a+b", we get an incorrect result.
if (car(p1) == symbol(ADD) and (
findPossibleClockForm(p1) or
findPossibleExponentialForm(p1) or
Find(p1,imaginaryunit))
)
if DEBUG_ABS then console.log " abs: " + p1 + " is a sum"
if DEBUG_ABS then console.log "abs of a sum"
# sum
push(p1)
rect() # convert polar terms, if any
p1 = pop()
push(p1)
real()
push_integer(2)
power()
push(p1)
imag()
push_integer(2)
power()
add()
push_rational(1, 2)
power()
simplify_trig()
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
if (car(p1) == symbol(POWER) && equaln(cadr(p1), -1))
if DEBUG_ABS then console.log " abs: " + p1 + " is -1 to any power"
# -1 to any power
if evaluatingAsFloats
if DEBUG_ABS then console.log " abs: numeric, so result is 1.0"
push_double(1.0)
else
if DEBUG_ABS then console.log " abs: symbolic, so result is 1"
push_integer(1)
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
# abs(a^b) is equal to abs(a)^b IF b is positive
if (car(p1) == symbol(POWER) && ispositivenumber(caddr(p1)))
if DEBUG_ABS then console.log " abs: " + p1 + " is something to the power of a positive number"
push cadr(p1)
abs()
push caddr(p1)
power()
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
# abs(e^something)
if (car(p1) == symbol(POWER) && cadr(p1) == symbol(E))
if DEBUG_ABS then console.log " abs: " + p1 + " is an exponential"
# exponential
push(caddr(p1))
real()
exponential()
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
if (car(p1) == symbol(MULTIPLY))
if DEBUG_ABS then console.log " abs: " + p1 + " is a product"
# product
push_integer(1)
p1 = cdr(p1)
while (iscons(p1))
push(car(p1))
abs()
multiply()
p1 = cdr(p1)
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
if (car(p1) == symbol(ABS))
if DEBUG_ABS then console.log " abs: " + p1 + " is abs of a abs"
# abs of a abs
push_symbol(ABS)
push cadr(p1)
list(2)
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
###
# Evaluation via zzfloat()
# ...while this is in theory a powerful mechanism, I've commented it
# out because I've refined this method enough to not need this.
# Evaling via zzfloat() is in principle more problematic because it could
# require further evaluations which could end up in further "abs" which
# would end up in infinite loops. Better not use it if not necessary.
# we look directly at the float evaluation of the argument
# to see if we end up with a number, which would mean that there
# is no imaginary component and we can just return the input
# (or its negation) as the result.
push p1
zzfloat()
floatEvaluation = pop()
if (isnegativenumber(floatEvaluation))
if DEBUG_ABS then console.log " abs: " + p1 + " just a negative"
push(p1)
negate()
restore()
return
if (ispositivenumber(floatEvaluation))
if DEBUG_ABS then console.log " abs: " + p1 + " just a positive"
push(p1)
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
return
###
if (istensor(p1))
absval_tensor()
restore()
return
if (isnegativeterm(p1) || (car(p1) == symbol(ADD) && isnegativeterm(cadr(p1))))
push(p1)
negate()
p1 = pop()
if DEBUG_ABS then console.log " abs: " + p1 + " is nothing decomposable"
push_symbol(ABS)
push(p1)
list(2)
if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1]
restore()
# also called the "norm" of a vector
absval_tensor = ->
if (p1.tensor.ndim != 1)
stop("abs(tensor) with tensor rank > 1")
push(p1)
push(p1)
conjugate()
inner()
push_rational(1, 2)
power()
simplify()
Eval()