algebrite
Version:
Computer Algebra System in Coffeescript
629 lines (520 loc) • 14.5 kB
text/coffeescript
DEBUG_IS = false
# jsBoolToToInt = (p) ->
# if p then 1 else 0
# p is a U
# this routine is a simple check on whether we have
# a basic zero in our hands. It doesn't perform any
# calculations or simplifications.
isZeroAtom = (p) ->
switch (p.k)
when NUM
MZERO(p.q.a)
when DOUBLE
p.d == 0.0
else
false
# p is a U
# this routine is a simple check on whether we have
# a basic zero in our hands. It doesn't perform any
# calculations or simplifications.
isZeroTensor = (p) ->
if p.k != TENSOR
return 0
for i in [0...p.tensor.nelem]
if (!isZeroAtomOrTensor(p.tensor.elem[i]))
return 0
return 1
# p is a U
# this routine is a simple check on whether we have
# a basic zero in our hands. It doesn't perform any
# calculations or simplifications.
isZeroAtomOrTensor = (p) ->
isZeroAtom(p) or isZeroTensor(p)
# This is a key routine to try to determine whether
# the argument looks like zero/false, or non-zero/true,
# or undetermined.
# This is useful in two instances:
# * to determine if a predicate is true/false
# * to determine if particular quantity is zero
# Note that if one wants to check if we have a simple
# zero atom or tensor in our hands, then the isZeroAtomOrTensor
# routine is sufficient.
isZeroLikeOrNonZeroLikeOrUndetermined = (valueOrPredicate) ->
# push the argument
push(valueOrPredicate)
# just like Eval but turns assignments into
# equality checks
Eval_predicate()
evalledArgument = pop()
# OK first check if we already have
# a simple zero (or simple zero tensor)
if isZeroAtomOrTensor evalledArgument
return 0
# also check if we have a simple numeric value, or a tensor
# full of simple numeric values (i.e. straight doubles or fractions).
# In such cases, since we
# just excluded they are zero, then we take it as
# a "true"
if isNumericAtomOrTensor evalledArgument
return 1
# if we are here we are in the case of value that
# is not a zero and not a simple numeric value.
# e.g. stuff like
# 'sqrt(2)', or 'sin(45)' or '1+i', or 'a'
# so in such cases let's try to do a float()
# so we might get down to a simple numeric value
# in some of those cases
push evalledArgument
zzfloat()
evalledArgument = pop()
# anything that could be calculated down to a simple
# numeric value is now indeed either a
# double OR a double with an imaginary component
# e.g. 2.0 or 2.4 + i*5.6
# (Everything else are things that don't have a numeric
# value e.g. 'a+b')
# So, let's take care of the case where we have
# a simple numeric value with NO imaginary component,
# things like sqrt(2) or sin(PI)
# by doing the simple numeric
# values checks again
if isZeroAtomOrTensor evalledArgument
return 0
if isNumericAtomOrTensor evalledArgument
return 1
# here we still have cases of simple numeric values
# WITH an imaginary component e.g. '1+i',
# or things that don't have a numeric value e.g. 'a'
# so now let's take care of the imaginary numbers:
# since we JUST have to spot "zeros" we can just
# calculate the absolute value and re-do all the checks
# we just did
if Find(evalledArgument, imaginaryunit)
push evalledArgument
absValFloat()
Eval_predicate()
evalledArgument = pop()
# re-do the simple-number checks...
if isZeroAtomOrTensor evalledArgument
return 0
if isNumericAtomOrTensor evalledArgument
return 1
# here we have stuff that is not reconducible to any
# numeric value (or tensor with numeric values) e.g.
# 'a+b', so it just means that we just don't know the
# truth value, so we have
# to leave the whole thing unevalled
return null
# p is a U
isnegativenumber = (p) ->
switch (p.k)
when NUM
if (MSIGN(p.q.a) == -1)
return 1
when DOUBLE
if (p.d < 0.0)
return 1
return 0
# p is a U
ispositivenumber = (p) ->
switch (p.k)
when NUM
if (MSIGN(p.q.a) == 1)
return 1
when DOUBLE
if (p.d > 0.0)
return 1
return 0
# p is a U
isplustwo = (p) ->
switch (p.k)
when NUM
if (MEQUAL(p.q.a, 2) && MEQUAL(p.q.b, 1))
return 1
when DOUBLE
if (p.d == 2.0)
return 1
return 0
# p is a U
isplusone = (p) ->
switch (p.k)
when NUM
if (MEQUAL(p.q.a, 1) && MEQUAL(p.q.b, 1))
return 1
when DOUBLE
if (p.d == 1.0)
return 1
return 0
isminusone = (p) ->
switch (p.k)
when NUM
if (MEQUAL(p.q.a, -1) && MEQUAL(p.q.b, 1))
return 1
when DOUBLE
if (p.d == -1.0)
return 1
return 0
isone = (p) ->
return isplusone(p) or isminusone(p)
isinteger = (p) ->
if (p.k == NUM && MEQUAL(p.q.b, 1))
return 1
else
return 0
isintegerorintegerfloat = (p) ->
if p.k == DOUBLE
if (p.d == Math.round(p.d))
return 1
return 0
return isinteger(p)
isnonnegativeinteger = (p) ->
if (isrational(p) && MEQUAL(p.q.b, 1) && MSIGN(p.q.a) == 1)
return 1
else
return 0
isposint = (p) ->
if (isinteger(p) && MSIGN(p.q.a) == 1)
return 1
else
return 0
# --------------------------------------
isunivarpolyfactoredorexpandedform = (p,x) ->
if DEBUG then console.log "isunivarpolyfactoredorexpandedform: p: " + p + " x: " + x
if !x?
push p
guess()
x = pop()
pop()
if ispolyfactoredorexpandedform(p,x) and (Find(p, symbol(SYMBOL_X)) + Find(p, symbol(SYMBOL_Y)) + Find(p, symbol(SYMBOL_Z)) == 1)
return x
else
return 0
# --------------------------------------
# sometimes we want to check if we have a poly in our
# hands, however it's in factored form and we don't
# want to expand it.
ispolyfactoredorexpandedform = (p,x) ->
return ispolyfactoredorexpandedform_factor(p, x)
ispolyfactoredorexpandedform_factor = (p,x) ->
if (car(p) == symbol(MULTIPLY))
p = cdr(p)
while (iscons(p))
if DEBUG then console.log "ispolyfactoredorexpandedform_factor testing " + car(p)
if (!ispolyfactoredorexpandedform_power(car(p), x))
if DEBUG then console.log "... tested negative:" + car(p)
return 0
p = cdr(p)
return 1
else
return ispolyfactoredorexpandedform_power(p, x)
ispolyfactoredorexpandedform_power = (p,x) ->
if (car(p) == symbol(POWER))
if DEBUG then console.log "ispolyfactoredorexpandedform_power (isposint(caddr(p)) " + (isposint(caddr(p))
if DEBUG then console.log "ispolyfactoredorexpandedform_power ispolyexpandedform_expr(cadr(p), x)) " + ispolyexpandedform_expr(cadr(p), x))
return (isposint(caddr(p)) and ispolyexpandedform_expr(cadr(p), x))
else
if DEBUG then console.log "ispolyfactoredorexpandedform_power not a power, testing if this is exp form: " + p
return ispolyexpandedform_expr(p, x)
# --------------------------------------
ispolyexpandedform = (p,x) ->
if (Find(p, x))
return ispolyexpandedform_expr(p, x)
else
return 0
ispolyexpandedform_expr = (p,x) ->
if (car(p) == symbol(ADD))
p = cdr(p)
while (iscons(p))
if (!ispolyexpandedform_term(car(p), x))
return 0
p = cdr(p)
return 1
else
return ispolyexpandedform_term(p, x)
ispolyexpandedform_term = (p,x) ->
if (car(p) == symbol(MULTIPLY))
p = cdr(p)
while (iscons(p))
if (!ispolyexpandedform_factor(car(p), x))
return 0
p = cdr(p)
return 1
else
return ispolyexpandedform_factor(p, x)
ispolyexpandedform_factor = (p,x) ->
if (equal(p, x))
return 1
if (car(p) == symbol(POWER) && equal(cadr(p), x))
if (isposint(caddr(p)))
return 1
else
return 0
if (Find(p, x))
return 0
else
return 1
# --------------------------------------
isnegativeterm = (p) ->
if (isnegativenumber(p))
return 1
else if (car(p) == symbol(MULTIPLY) && isnegativenumber(cadr(p)))
return 1
else
return 0
hasNegativeRationalExponent = (p) ->
if (car(p) == symbol(POWER) \
&& isrational(car(cdr(cdr(p)))) \
&& isnegativenumber(car(cdr(p))))
if DEBUG_IS then console.log "hasNegativeRationalExponent: " + p.toString() + " has imaginary component"
return 1
else
if DEBUG_IS then console.log "hasNegativeRationalExponent: " + p.toString() + " has NO imaginary component"
return 0
isimaginarynumberdouble = (p) ->
if ((car(p) == symbol(MULTIPLY) \
&& length(p) == 3 \
&& isdouble(cadr(p)) \
&& hasNegativeRationalExponent(caddr(p))) \
|| equal(p, imaginaryunit))
return 1
else
return 0
isimaginarynumber = (p) ->
if ((car(p) == symbol(MULTIPLY) \
&& length(p) == 3 \
&& isNumericAtom(cadr(p)) \
&& equal(caddr(p), imaginaryunit)) \
|| equal(p, imaginaryunit) \
|| hasNegativeRationalExponent(caddr(p))
)
if DEBUG_IS then console.log "isimaginarynumber: " + p.toString() + " is imaginary number"
return 1
else
if DEBUG_IS then console.log "isimaginarynumber: " + p.toString() + " isn't an imaginary number"
return 0
iscomplexnumberdouble = (p) ->
if ((car(p) == symbol(ADD) \
&& length(p) == 3 \
&& isdouble(cadr(p)) \
&& isimaginarynumberdouble(caddr(p))) \
|| isimaginarynumberdouble(p))
return 1
else
return 0
iscomplexnumber = (p) ->
if DEBUG_IS then debugger
if ((car(p) == symbol(ADD) \
&& length(p) == 3 \
&& isNumericAtom(cadr(p)) \
&& isimaginarynumber(caddr(p))) \
|| isimaginarynumber(p))
if DEBUG then console.log "iscomplexnumber: " + p.toString() + " is imaginary number"
return 1
else
if DEBUG then console.log "iscomplexnumber: " + p.toString() + " is imaginary number"
return 0
iseveninteger = (p) ->
if isinteger(p) && p.q.a.isEven()
return 1
else
return 0
isnegative = (p) ->
if (car(p) == symbol(ADD) && isnegativeterm(cadr(p)))
return 1
else if (isnegativeterm(p))
return 1
else
return 0
# returns 1 if there's a symbol somewhere.
# not used anywhere.
# NOTE: PI and POWER are symbols,
# so for example 2^3 would be symbolic
# while -1^(1/2) i.e. 'i' is not, so this can
# be tricky to use.
issymbolic = (p) ->
if (issymbol(p))
return 1
else
while (iscons(p))
if (issymbolic(car(p)))
return 1
p = cdr(p)
return 0
# i.e. 2, 2^3, etc.
isintegerfactor = (p) ->
isinteger(p) || # note that and takes precedence over or
car(p) == symbol(POWER) and
isinteger(cadr(p)) and
isinteger(caddr(p))
isNumberOneOverSomething = (p) ->
isfraction(p) and
MEQUAL(p.q.a.abs(), 1)
isoneover = (p) ->
car(p) == symbol(POWER) and
isminusone(caddr(p))
isfraction = (p) ->
p.k == NUM && !MEQUAL(p.q.b, 1)
# p is a U, n an int
equaln = (p,n) ->
switch (p.k)
when NUM
MEQUAL(p.q.a, n) && MEQUAL(p.q.b, 1)
when DOUBLE
p.d == n
else
false
# p is a U, a and b ints
equalq = (p, a, b) ->
switch (p.k)
when NUM
MEQUAL(p.q.a, a) && MEQUAL(p.q.b, b)
when DOUBLE
p.d == a / b
else
false
# 1/2 ?
isoneovertwo = (p) ->
equalq(p, 1, 2)
# -1/2 ?
isminusoneovertwo = (p) ->
equalq(p, -1, 2)
# 1/sqrt(2) ?
isoneoversqrttwo = (p) ->
car(p) == symbol(POWER) and
equaln(cadr(p), 2) and
equalq(caddr(p), -1, 2)
# -1/sqrt(2) ?
isminusoneoversqrttwo = (p) ->
car(p) == symbol(MULTIPLY) and
equaln(cadr(p), -1) and
isoneoversqrttwo(caddr(p)) and
length(p) == 3
# sqrt(3)/2 ?
issqrtthreeovertwo = (p) ->
car(p) == symbol(MULTIPLY) and
isoneovertwo(cadr(p)) and
issqrtthree(caddr(p)) and
length(p) == 3
# -sqrt(3)/2 ?
isminussqrtthreeovertwo = (p) ->
car(p) == symbol(MULTIPLY) and
isminusoneovertwo(cadr(p)) and
issqrtthree(caddr(p)) and
length(p) == 3
# p == sqrt(3) ?
issqrtthree = (p) ->
car(p) == symbol(POWER) and
equaln(cadr(p), 3) and
isoneovertwo(caddr(p))
isfloating = (p) ->
if p.k == DOUBLE or p == symbol(FLOATF)
return 1
while (iscons(p))
if (isfloating(car(p)))
return 1
p = cdr(p)
return 0
isimaginaryunit = (p) ->
if (equal(p, imaginaryunit))
return 1
else
return 0
# n/2 * i * pi ?
# return value:
# 0 no
# 1 1
# 2 -1
# 3 i
# 4 -i
isquarterturn = (p) ->
n = 0
minussign = 0
if (car(p) != symbol(MULTIPLY))
return 0
if (equal(cadr(p), imaginaryunit))
if (caddr(p) != symbol(PI))
return 0
if (length(p) != 3)
return 0
return 2
if (!isNumericAtom(cadr(p)))
return 0
if (!equal(caddr(p), imaginaryunit))
return 0
if (cadddr(p) != symbol(PI))
return 0
if (length(p) != 4)
return 0
push(cadr(p))
push_integer(2)
multiply()
n = pop_integer()
if (isNaN(n))
return 0
if (n < 1)
minussign = 1
n = -n
switch (n % 4)
when 0
n = 1
when 1
if (minussign)
n = 4
else
n = 3
when 2
n = 2
when 3
if (minussign)
n = 3
else
n = 4
return n
# special multiple of pi?
# returns for the following multiples of pi...
# -4/2 -3/2 -2/2 -1/2 1/2 2/2 3/2 4/2
# 4 1 2 3 1 2 3 4
isnpi = (p) ->
n = 0
if (p == symbol(PI))
return 2
if (car(p) == symbol(MULTIPLY) \
&& isNumericAtom(cadr(p)) \
&& caddr(p) == symbol(PI) \
&& length(p) == 3)
doNothing = 0
else
return 0
push(cadr(p))
push_integer(2)
multiply()
n = pop_integer()
if (isNaN(n))
return 0
if (n < 0)
n = 4 - (-n) % 4
else
n = 1 + (n - 1) % 4
return n
$.isZeroAtomOrTensor = isZeroAtomOrTensor
$.isnegativenumber = isnegativenumber
$.isplusone = isplusone
$.isminusone = isminusone
$.isinteger = isinteger
$.isnonnegativeinteger = isnonnegativeinteger
$.isposint = isposint
$.isnegativeterm = isnegativeterm
$.isimaginarynumber = isimaginarynumber
$.iscomplexnumber = iscomplexnumber
$.iseveninteger = iseveninteger
$.isnegative = isnegative
$.issymbolic = issymbolic
$.isintegerfactor = isintegerfactor
$.isoneover = isoneover
$.isfraction = isfraction
$.isoneoversqrttwo = isoneoversqrttwo
$.isminusoneoversqrttwo = isminusoneoversqrttwo
$.isfloating = isfloating
$.isimaginaryunit = isimaginaryunit
$.isquarterturn = isquarterturn
$.isnpi = isnpi