UNPKG

algebrite

Version:

Computer Algebra System in Coffeescript

629 lines (520 loc) 14.5 kB
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