UNPKG

algebrite

Version:

Computer Algebra System in Coffeescript

622 lines (493 loc) 12.6 kB
# Factor a polynomial #define POLY p1 #define X p2 #define Z p3 #define A p4 #define B p5 #define Q p6 #define RESULT p7 #define FACTOR p8 polycoeff = 0 factpoly_expo = 0 factorpoly = -> save() p2 = pop() p1 = pop() if (!Find(p1, p2)) push(p1) restore() return if (!ispoly(p1, p2)) push(p1) restore() return if (!issymbol(p2)) push(p1) restore() return push(p1) push(p2) yyfactorpoly() restore() #----------------------------------------------------------------------------- # # Input: tos-2 true polynomial # # tos-1 free variable # # Output: factored polynomial on stack # #----------------------------------------------------------------------------- yyfactorpoly = -> h = 0 i = 0 save() p2 = pop() p1 = pop() h = tos if (isfloating(p1)) stop("floating point numbers in polynomial") polycoeff = tos push(p1) push(p2) factpoly_expo = coeff() - 1 rationalize_coefficients(h) # for univariate polynomials we could do factpoly_expo > 1 whichRootsAreWeFinding = "real" remainingPoly = null while (factpoly_expo > 0) if (iszero(stack[polycoeff+0])) push_integer(1) p4 = pop() push_integer(0) p5 = pop() else #console.log("trying to find a " + whichRootsAreWeFinding + " root") if whichRootsAreWeFinding == "real" foundRealRoot = get_factor_from_real_root() else if whichRootsAreWeFinding == "complex" foundComplexRoot = get_factor_from_complex_root(remainingPoly) if whichRootsAreWeFinding == "real" if foundRealRoot == 0 whichRootsAreWeFinding = "complex" continue else # build the 1-degree polynomial out of the # real solution that was just found. push(p4) # A push(p2) # x multiply() push(p5) # B add() p8 = pop() if (DEBUG) console.log("success\nFACTOR=" + p8) # factor out negative sign (not req'd because p4 > 1) #if 0 ### if (isnegativeterm(p4)) push(p8) negate() p8 = pop() push(p7) negate_noexpand() p7 = pop() ### #endif # p7 is the part of the polynomial that was factored so far, # add the newly found factor to it. Note that we are not actually # multiplying the polynomials fully, we are just leaving them # expressed as (P1)*(P2), we are not expanding the product. push(p7) push(p8) multiply_noexpand() p7 = pop() # ok now on stack we have the coefficients of the # remaining part of the polynomial still to factor. # Divide it by the newly-found factor so that # the stack then contains the coefficients of the # polynomial part still left to factor. yydivpoly() while (factpoly_expo and iszero(stack[polycoeff+factpoly_expo])) factpoly_expo-- push(zero) for i in [0..factpoly_expo] push(stack[polycoeff+i]) push(p2) # the free variable push_integer(i) power() multiply() add() remainingPoly = pop() #console.log("real branch remainingPoly: " + remainingPoly) else if whichRootsAreWeFinding == "complex" if foundComplexRoot == 0 break else # build the 2-degree polynomial out of the # real solution that was just found. push(p4) # A push(p2) # x subtract() #console.log("first factor: " + stack[tos-1].toString()) push(p4) # A conjugate() push(p2) # x subtract() #console.log("second factor: " + stack[tos-1].toString()) multiply() #if (factpoly_expo > 0 && isnegativeterm(stack[polycoeff+factpoly_expo])) # negate() # negate_noexpand() p8 = pop() if (DEBUG) console.log("success\nFACTOR=" + p8) # factor out negative sign (not req'd because p4 > 1) #if 0 ### if (isnegativeterm(p4)) push(p8) negate() p8 = pop() push(p7) negate_noexpand() p7 = pop() ### #endif # p7 is the part of the polynomial that was factored so far, # add the newly found factor to it. Note that we are not actually # multiplying the polynomials fully, we are just leaving them # expressed as (P1)*(P2), we are not expanding the product. push(p7) previousFactorisation = pop() #console.log("previousFactorisation: " + previousFactorisation) push(p7) push(p8) multiply_noexpand() p7 = pop() #console.log("new prospective factorisation: " + p7) # build the polynomial of the unfactored part #console.log("build the polynomial of the unfactored part factpoly_expo: " + factpoly_expo) if !remainingPoly? push(zero) for i in [0..factpoly_expo] push(stack[polycoeff+i]) push(p2) # the free variable push_integer(i) power() multiply() add() remainingPoly = pop() #console.log("original polynomial (dividend): " + remainingPoly) dividend = remainingPoly #push(dividend) #degree() #startingDegree = pop() push(dividend) #console.log("dividing " + stack[tos-1].toString() + " by " + p8) push(p8) # divisor push(p2) # X divpoly() remainingPoly = pop() push(remainingPoly) push(p8) # divisor multiply() checkingTheDivision = pop() if !equal(checkingTheDivision, dividend) #push(dividend) #gcd_expr() #console.log("gcd top of stack: " + stack[tos-1].toString()) if DEBUG then console.log("we found a polynomial based on complex root and its conj but it doesn't divide the poly, quitting") if DEBUG then console.log("so just returning previousFactorisation times dividend: " + previousFactorisation + " * " + dividend) push(previousFactorisation) push(dividend) prev_expanding = expanding expanding = 0 yycondense() expanding = prev_expanding multiply_noexpand() p7 = pop() stack[h] = p7 moveTos h + 1 restore() return #console.log("result: (still to be factored) " + remainingPoly) #push(remainingPoly) #degree() #remainingDegree = pop() ### if compare_numbers(startingDegree, remainingDegree) # ok even if we found a complex root that # together with the conjugate generates a poly in Z, # that doesn't mean that the division would end up in Z. # Example: 1+x^2+x^4+x^6 has +i and -i as one of its roots # so a factor is 1+x^2 ( = (x+i)*(x-i)) # BUT ### for i in [0..factpoly_expo] pop() push(remainingPoly) push(p2) coeff() factpoly_expo -= 2 #console.log("factpoly_expo: " + factpoly_expo) # build the remaining unfactored part of the polynomial push(zero) for i in [0..factpoly_expo] push(stack[polycoeff+i]) push(p2) # the free variable push_integer(i) power() multiply() add() p1 = pop() if (DEBUG) console.log("POLY=" + p1) push(p1) prev_expanding = expanding expanding = 0 yycondense() expanding = prev_expanding p1 = pop() #console.log("new poly with extracted common factor: " + p1) #debugger # factor out negative sign if (factpoly_expo > 0 && isnegativeterm(stack[polycoeff+factpoly_expo])) push(p1) #prev_expanding = expanding #expanding = 1 negate() #expanding = prev_expanding p1 = pop() push(p7) negate_noexpand() p7 = pop() push(p7) push(p1) multiply_noexpand() p7 = pop() if (DEBUG) console.log("RESULT=" + p7) stack[h] = p7 moveTos h + 1 restore() rationalize_coefficients = (h) -> i = 0 # LCM of all polynomial coefficients p7 = one for i in [h...tos] push(stack[i]) denominator() push(p7) lcm() p7 = pop() # multiply each coefficient by RESULT for i in [h...tos] push(p7) push(stack[i]) multiply() stack[i] = pop() # reciprocate RESULT push(p7) reciprocate() p7 = pop() if DEBUG then console.log("rationalize_coefficients result") #console.log print_list(p7) get_factor_from_real_root = -> i = 0 j = 0 h = 0 a0 = 0 an = 0 na0 = 0 nan = 0 if (DEBUG) push(zero) for i in [0..factpoly_expo] push(stack[polycoeff+i]) push(p2) push_integer(i) power() multiply() add() p1 = pop() console.log("POLY=" + p1) h = tos an = tos push(stack[polycoeff+factpoly_expo]) divisors_onstack() nan = tos - an a0 = tos push(stack[polycoeff+0]) divisors_onstack() na0 = tos - a0 if (DEBUG) console.log("divisors of base term") for i in [0...na0] console.log(", " + stack[a0 + i]) console.log("divisors of leading term") for i in [0...nan] console.log(", " + stack[an + i]) # try roots for rootsTries_i in [0...nan] for rootsTries_j in [0...na0] #if DEBUG then console.log "nan: " + nan + " na0: " + na0 + " i: " + rootsTries_i + " j: " + rootsTries_j p4 = stack[an + rootsTries_i] p5 = stack[a0 + rootsTries_j] push(p5) push(p4) divide() negate() p3 = pop() Evalpoly() if (DEBUG) console.log("try A=" + p4) console.log(", B=" + p5) console.log(", root " + p2) console.log("=-B/A=" + p3) console.log(", POLY(" + p3) console.log(")=" + p6) if (iszero(p6)) moveTos h if DEBUG then console.log "get_factor_from_real_root returning 1" return 1 push(p5) negate() p5 = pop() push(p3) negate() p3 = pop() Evalpoly() if (DEBUG) console.log("try A=" + p4) console.log(", B=" + p5) console.log(", root " + p2) console.log("=-B/A=" + p3) console.log(", POLY(" + p3) console.log(")=" + p6) if (iszero(p6)) moveTos h if DEBUG then console.log "get_factor_from_real_root returning 1" return 1 moveTos h if DEBUG then console.log "get_factor_from_real_root returning 0" return 0 get_factor_from_complex_root = (remainingPoly) -> i = 0 j = 0 h = 0 a0 = 0 an = 0 na0 = 0 nan = 0 if factpoly_expo <= 2 if DEBUG then console.log("no more factoring via complex roots to be found in polynomial of degree <= 2") return 0 p1 = remainingPoly if DEBUG then console.log("complex root finding for POLY=" + p1) h = tos an = tos # trying -1^(2/3) which generates a polynomial in Z # generates x^2 + 2x + 1 push_integer(-1) push_rational(2,3) power() rect() p4 = pop() if DEBUG then console.log("complex root finding: trying with " + p4) push(p4) p3 = pop() push(p3) Evalpoly() if DEBUG then console.log("complex root finding result: " + p6) if (iszero(p6)) moveTos h if DEBUG then console.log "get_factor_from_complex_root returning 1" return 1 # trying 1^(2/3) which generates a polynomial in Z # http://www.wolframalpha.com/input/?i=(1)%5E(2%2F3) # generates x^2 - 2x + 1 push_integer(1) push_rational(2,3) power() rect() p4 = pop() if DEBUG then console.log("complex root finding: trying with " + p4) push(p4) p3 = pop() push(p3) Evalpoly() if DEBUG then console.log("complex root finding result: " + p6) if (iszero(p6)) moveTos h if DEBUG then console.log "get_factor_from_complex_root returning 1" return 1 # trying some simple complex numbers. All of these # generate polynomials in Z for rootsTries_i in [-10..10] for rootsTries_j in [1..5] push_integer(rootsTries_i) push_integer(rootsTries_j) push(imaginaryunit) multiply() add() rect() p4 = pop() #console.log("complex root finding: trying simple complex combination: " + p4) push(p4) p3 = pop() push(p3) Evalpoly() #console.log("complex root finding result: " + p6) if (iszero(p6)) moveTos h if DEBUG then console.log "found complex root: " + p6 return 1 moveTos h if DEBUG then console.log "get_factor_from_complex_root returning 0" return 0 #----------------------------------------------------------------------------- # # Divide a polynomial by Ax+B # # Input: on stack: polycoeff Dividend coefficients # # factpoly_expo Degree of dividend # # A (p4) As above # # B (p5) As above # # Output: on stack: polycoeff Contains quotient coefficients # #----------------------------------------------------------------------------- yydivpoly = -> i = 0 p6 = zero for i in [factpoly_expo...0] push(stack[polycoeff+i]) stack[polycoeff+i] = p6 push(p4) divide() p6 = pop() push(stack[polycoeff+i - 1]) push(p6) push(p5) multiply() subtract() stack[polycoeff+i - 1] = pop() stack[polycoeff+0] = p6 if DEBUG then console.log("yydivpoly Q:") #console.log print_list(p6) Evalpoly = -> i = 0 push(zero) for i in [factpoly_expo..0] push(p3) multiply() push(stack[polycoeff+i]) if DEBUG console.log("Evalpoly top of stack:") console.log print_list(stack[tos-i]) add() p6 = pop()