/* * Mathomatic simplifying and general polynomial routines. * Includes polynomial and smart division, polynomial factoring, etc. * Globals tlhs[] and trhs[] are used and wiped out by most of these routines. * * The polynomial division and GCD routines here are not recursive, * due to the global static expression storage areas. * This limits the polynomial GCD routines to mostly univariate operation. * This also does not allow their use during solving. * So far, these limitations have been a good thing, * making Mathomatic faster and more stable and reliable. * Making polynomial GCD calculations partially recursive * with one level of recursion would enable multivariate operation for most cases, I think. * * Mathomatic has proved it is practical and efficient to do * generalized polynomial operations. By generalized, I mean that * the coefficients of polynomials are not specially treated or stored in any way. * They are not separated out from the main variable of the polynomial. * Maximum simplification of all expressions is not possible unless everything is generalized. * * Copyright (C) 1987-2012 George Gesslein II. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA The chief copyright holder can be contacted at gesslein@mathomatic.org, or George Gesslein II, P.O. Box 224, Lansing, NY 14882-0224 USA. */ #include "includes.h" #define REMAINDER_IS_ZERO() (n_trhs == 1 && trhs[0].kind == CONSTANT && trhs[0].token.constant == 0.0) /* * The following static expression storage areas are of non-standard size * and must only be used for temporary storage. * Most Mathomatic expression manipulation and simplification routines should not be used * on non-standard or constant size expression storage areas. * Standard size expression storage areas that may be * manipulated or simplified are the equation spaces, tlhs[], trhs[], and tes[] only. */ token_type divisor[DIVISOR_SIZE]; /* static expression storage areas for polynomial and smart division */ int n_divisor; /* length of expression in divisor[] */ token_type quotient[DIVISOR_SIZE]; int n_quotient; /* length of expression in quotient[] */ token_type gcd_divisor[DIVISOR_SIZE]; /* static expression storage area for polynomial GCD routine */ int len_d; /* length of expression in gcd_divisor[] */ static int pf_recurse(token_type *equation, int *np, int loc, int level, int do_repeat); static int pf_sub(token_type *equation, int *np, int loc, int len, int level, int do_repeat); static int save_factors(token_type *equation, int *np, int loc1, int len, int level); static int do_gcd(long *vp); static int mod_recurse(token_type *equation, int *np, int loc, int level); static int polydiv_recurse(token_type *equation, int *np, int loc, int level); static int pdiv_recurse(token_type *equation, int *np, int loc, int level, int code); static int poly_div_sub(token_type *d1, int len1, token_type *d2, int len2, long *vp); static int find_highest_count(token_type *p1, int n1, token_type *p2, int n2, long *vp1); /* * Compare function for qsort(3). */ static int vcmp(p1, p2) sort_type *p1, *p2; { if (p2->count == p1->count) { if (p1->v < p2->v) return -1; if (p1->v == p2->v) return 0; return 1; } return(p2->count - p1->count); } /* * Return true if passed expression is strictly a single polynomial term in variable v. * The general form of a polynomial term is c*(v^d) * The coefficient (c) and exponent (d) may be any expression, * as long as it doesn't contain the variable v. */ int poly_in_v_sub(p1, n, v, allow_divides) token_type *p1; /* expression pointer */ int n; /* expression length */ long v; /* Mathomatic variable */ int allow_divides; /* if true, allow division by variable */ { int i, k; int level, vlevel; int count; level = min_level(p1, n); for (i = 0, count = 0; i < n; i += 2) { if (p1[i].kind == VARIABLE && p1[i].token.variable == v) { count++; if (count > 1) return false; vlevel = p1[i].level; if (vlevel == level || vlevel == (level + 1)) { for (k = 1; k < n; k += 2) { if (p1[k].level == level) { switch (p1[k].token.operatr) { case DIVIDE: if (!allow_divides && k == (i - 1)) return false; case TIMES: continue; case POWER: if (k == (i + 1)) continue; default: return false; } } } if (vlevel == (level + 1)) { if ((i + 1) < n && p1[i+1].level == vlevel && p1[i+1].token.operatr == POWER) { continue; } } else { continue; } } return false; } } return true; } /* * Return true if passed expression is a polynomial in the given variable v. * The coefficients and exponents may be anything, as long as they don't contain the variable v. * The passed expression should be fully unfactored, for a proper determination. */ int poly_in_v(p1, n, v, allow_divides) token_type *p1; /* expression pointer */ int n; /* expression length */ long v; /* Mathomatic variable */ int allow_divides; /* allow variable to be right of a divide (negative exponents) as a polynomial term */ { int i, j; for (i = 1, j = 0;; i += 2) { if (i >= n || (p1[i].level == 1 && (p1[i].token.operatr == PLUS || p1[i].token.operatr == MINUS))) { if (!poly_in_v_sub(&p1[j], i - j, v, allow_divides)) { return false; } j = i + 1; } if (i >= n) break; } return true; } /* * Factor polynomials by calling pf_sub() for every additive sub-expression. * Factors repeated factor polynomials (like (x+1)^5) if do_repeat is true. * And always factors multivariate polynomials with symbolic factors (like (x+a)*(x+b)). * * Does not factor polynomials with different numeric factors (like (x+1)*(x+2)). * * Because of accumulated floating point round-off error, * this routine does not succeed at factoring most large polynomials. * * Return true if equation side was modified (factored). */ int poly_factor(equation, np, do_repeat) token_type *equation; /* pointer to the beginning of equation side */ int *np; /* pointer to length of equation side */ int do_repeat; /* factor repeated factors flag */ { return pf_recurse(equation, np, 0, 1, do_repeat); } static int pf_recurse(equation, np, loc, level, do_repeat) token_type *equation; int *np, loc, level; int do_repeat; { int modified = false; int i; int count = 0, level_count = 0; for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) { switch (equation[i].token.operatr) { case PLUS: case MINUS: count++; if (equation[i].level == level) { level_count++; } } } if (level_count && count > 1) { /* so we don't factor expressions with only one additive operator */ /* try to factor the sub-expression */ modified = pf_sub(equation, np, loc, i - loc, level, do_repeat); } for (i = loc; i < *np && equation[i].level >= level;) { if (equation[i].level > level) { modified |= pf_recurse(equation, np, i, level + 1, do_repeat); i++; for (; i < *np && equation[i].level > level; i += 2) ; continue; } i++; } return modified; } /* * Polynomial factoring subroutine. * It can't factor everything, but it usually can factor polynomials * if it will make the expression size smaller. * * Return true if equation side was modified (factored). */ static int pf_sub(equation, np, loc, len, level, do_repeat) token_type *equation; /* equation side holding the possible polynomial to factor */ int *np, /* pointer to length of equation side */ loc, /* index of start of polynomial in equation side */ len; /* length of polynomial */ int level; /* level of additive operators in polynomial */ int do_repeat; /* factor repeated factors flag */ { token_type *p1; int modified = false, symbolic_modified = false; int i, j, k; long v = 0, v1, last_v; int len_first = 0; int loc1, loc2, len2 = 0; int loct, lent; int count; jmp_buf save_save; int div_flag = 3; int vc, cnt; sort_type va[MAX_VARS]; double d; int old_partial; debug_string(3, "Entering pf_sub()."); old_partial = partial_flag; loc2 = loc1 = loc; find_greatest_power(&equation[loc1], len, &v, &d, &j, &k, &div_flag); if (v == 0) return false; blt(save_save, jmp_save, sizeof(jmp_save)); if ((i = setjmp(jmp_save)) != 0) { /* trap errors */ partial_flag = old_partial; blt(jmp_save, save_save, sizeof(jmp_save)); if (i == 13) { /* critical error code */ longjmp(jmp_save, i); } return(modified || symbolic_modified); } /* First factor polynomials with repeated factors */ /* using poly_gcd(polynomial, v * differentiate(polynomial, v)) to discover the factors: */ for (count = 1; do_repeat; count++) { blt(trhs, &equation[loc1], len * sizeof(token_type)); n_trhs = len; partial_flag = false; uf_simp(trhs, &n_trhs); partial_flag = old_partial; if (level1_plus_count(trhs, n_trhs) < 2) { /* must be at least 2 level 1 additive operators to be factorable */ goto skip_factor; } /* create a variable list with counts of the number of times each variable occurs: */ last_v = 0; for (vc = 0; vc < ARR_CNT(va);) { cnt = 0; v1 = -1; for (i = 0; i < n_trhs; i += 2) { if (trhs[i].kind == VARIABLE && trhs[i].token.variable > last_v) { if (v1 == -1 || trhs[i].token.variable < v1) { v1 = trhs[i].token.variable; cnt = 1; } else if (trhs[i].token.variable == v1) { cnt++; } } } if (v1 == -1) break; last_v = v1; va[vc].v = v1; va[vc].count = cnt; vc++; } side_debug(3, &equation[loc1], len); side_debug(3, trhs, n_trhs); /* Find a valid polynomial base variable "v": */ cnt = -1; if (v) { if (vc > 1 && !poly_in_v(trhs, n_trhs, v, true)) { v = 0; } } for (i = 0; i < vc; i++) { if ((va[i].v & VAR_MASK) <= SIGN) { continue; } if (v == 0) { if (poly_in_v(trhs, n_trhs, va[i].v, true)) { v = va[i].v; } } if (cnt < 0 || va[i].count < cnt) { cnt = va[i].count; } } if (cnt <= 1) /* A polynomial requires 2 or more instances of every normal variable to be factorable. */ goto skip_factor; if (v == 0) { #if 1 goto skip_factor; #else /* no polynomial variables found, try differentiating with respect to each normal variable until one works */ for (i = 0; i < vc; i++) { if ((va[i].v & VAR_MASK) <= SIGN) { continue; } v = va[i].v; blt(tlhs, trhs, n_trhs * sizeof(token_type)); n_tlhs = n_trhs; if (differentiate(tlhs, &n_tlhs, v)) { break; } v = 0; } if (v == 0) { goto skip_factor; } #endif } else { blt(tlhs, trhs, n_trhs * sizeof(token_type)); n_tlhs = n_trhs; if (!differentiate(tlhs, &n_tlhs, v)) { break; } } #if !SILENT if (debug_level >= 3) { list_var(v, 0); fprintf(gfp, _("Differentiation successful using variable %s.\n"), var_str); } #endif simp_loop(tlhs, &n_tlhs); if ((n_tlhs + 2) > min(DIVISOR_SIZE, n_tokens)) break; for (i = 0; i < n_tlhs; i++) tlhs[i].level++; tlhs[n_tlhs].kind = OPERATOR; tlhs[n_tlhs].level = 1; tlhs[n_tlhs].token.operatr = TIMES; n_tlhs++; tlhs[n_tlhs].kind = VARIABLE; tlhs[n_tlhs].level = 1; tlhs[n_tlhs].token.variable = v; n_tlhs++; uf_simp(tlhs, &n_tlhs); if (poly_gcd(&equation[loc1], len, tlhs, n_tlhs, v) <= 0) break; if (level1_plus_count(tlhs, n_tlhs) == 0) break; if (!save_factors(equation, np, loc1, len, level)) break; loc1 += n_tlhs + 1; len = n_trhs; switch (count) { case 1: debug_string(1, "Polynomial with repeated factor factored."); len_first = n_tlhs; loc2 = loc1; break; case 2: len2 = n_tlhs; break; } modified = true; } /* Now factor polynomials with symbolic factors by grouping: */ if (!modified) { last_v = 0; next_v: p1 = &equation[loc1]; blt(trhs, p1, len * sizeof(token_type)); n_trhs = len; uf_simp_no_repeat(trhs, &n_trhs); if (level1_plus_count(trhs, n_trhs) < 2) { /* must be at least 2 level 1 additive operators to be factorable */ goto skip_factor; } for (;;) { v = -1; for (i = 0; i < len; i += 2) { if (p1[i].kind == VARIABLE && p1[i].token.variable > last_v) { if (v == -1 || p1[i].token.variable < v) { v = p1[i].token.variable; } } } if (v == -1) { break; } last_v = v; /* make sure there is more than one "v" raised to the highest power: */ if (find_greatest_power(trhs, n_trhs, &v, &d, &j, &k, &div_flag) <= 1) { continue; } blt(tlhs, trhs, n_trhs * sizeof(token_type)); n_tlhs = n_trhs; /* do the grouping: */ while (factor_plus(tlhs, &n_tlhs, v, 0.0)) { simp_loop(tlhs, &n_tlhs); } /* extract the highest power group: */ if (find_greatest_power(tlhs, n_tlhs, &v, &d, &j, &k, &div_flag) != 1) { continue; } if (j) { blt(tlhs, &tlhs[j], k * sizeof(token_type)); } n_tlhs = k; #if !SILENT if (debug_level >= 3) { fprintf(gfp, _("Trying factor: ")); list_proc(tlhs, n_tlhs, false); fprintf(gfp, "\n"); } #endif if (poly_gcd(&equation[loc1], len, tlhs, n_tlhs, 0L) <= 0) goto next_v; if (level1_plus_count(tlhs, n_tlhs) == 0) goto next_v; if (!symbolic_modified) { debug_string(1, "Symbolic polynomial factored."); } else { debug_string(1, "Found another symbolic factor."); } if (!save_factors(equation, np, loc1, len, level)) break; len = n_tlhs; symbolic_modified = true; last_v = 0; goto next_v; } } skip_factor: blt(jmp_save, save_save, sizeof(jmp_save)); if (modified) { /* Repeated factor was factored out. */ /* See if we can factor out more of the repeated factor. */ if (len2) { loct = loc2; lent = len2; } else { loct = loc; lent = len_first; } if (poly_gcd(&equation[loc1], len, &equation[loct], lent, v) > 0) { if (save_factors(equation, np, loc1, len, level)) { loc1 += n_tlhs + 1; len = n_trhs; } } if (len2) { loc1 = loc2; len = len2; } if (poly_gcd(&equation[loc], len_first, &equation[loc1], len, 0L) > 0) { save_factors(equation, np, loc, len_first, level); } } if (modified || symbolic_modified) { for (i = loc; i < *np && equation[i].level >= level; i++) ; #if DEBUG if ((i & 1) != 1) { error_bug("Error in result of pf_sub()."); } #endif debug_string(1, "Resulting factors of pf_sub():"); side_debug(1, &equation[loc], i - loc); } return(modified || symbolic_modified); } static int save_factors(equation, np, loc1, len, level) token_type *equation; int *np, loc1, len, level; { int i, j; i = n_tlhs + 1 + n_trhs; if (i > (len * 3)) goto rejected; if ((*np + (i - len)) > n_tokens) goto rejected; blt(&equation[loc1+i], &equation[loc1+len], (*np - (loc1 + len)) * sizeof(token_type)); *np += i - len; blt(&equation[loc1], tlhs, n_tlhs * sizeof(token_type)); i = loc1 + n_tlhs; equation[i].level = 0; equation[i].kind = OPERATOR; equation[i].token.operatr = TIMES; i++; blt(&equation[i], trhs, n_trhs * sizeof(token_type)); i += n_trhs; for (j = loc1; j < i; j++) equation[j].level += level; return true; rejected: debug_string(1, "Polynomial factor rejected because too large."); return false; } /* * Remove level 1 trivial factors and divides from tlhs[]. * Makes it a simpler factor that looks a lot nicer and is easier to work with. * * Return true if result is a level 1 additive expression. * If this returns false, nothing is removed. */ int remove_factors(void) { int i, j, k; int plus_flag = false, divide_flag = false; int op; debug_string(3, "Entering remove_factors() with: "); side_debug(3, tlhs, n_tlhs); do { simp_ssub(tlhs, &n_tlhs, 0L, 1.0, false, true, 4); } while (uf_power(tlhs, &n_tlhs)); for (i = 1, j = 0, k = 0;; i += 2) { if (i >= n_tlhs) { if (plus_flag && !divide_flag) { if (k > 0) j--; blt(&scratch[k], &tlhs[j], (i - j) * sizeof(token_type)); k += i - j; } if (k <= 0) { debug_string(3, "Leaving remove_factors() with false return and no change."); return false; } blt(tlhs, scratch, k * sizeof(token_type)); n_tlhs = k; debug_string(3, "Leaving remove_factors() with success and: "); side_debug(3, tlhs, n_tlhs); return true; } op = tlhs[i].token.operatr; switch (tlhs[i].level) { case 1: switch (op) { case PLUS: case MINUS: plus_flag = true; continue; case TIMES: case DIVIDE: break; default: debug_string(3, "Leaving remove_factors() with false return and no change."); return false; } if (plus_flag && !divide_flag) { if (k > 0) j--; blt(&scratch[k], &tlhs[j], (i - j) * sizeof(token_type)); k += i - j; } plus_flag = false; divide_flag = (op == DIVIDE); j = i + 1; break; case 2: switch (op) { case PLUS: case MINUS: plus_flag = true; } break; } } } /* * This is the Euclidean GCD algorithm applied to polynomials. * It needs to be made multivariate by making it recursive. * * Return the number of iterations (divisions), if successful, * with the polynomial GCD result in gcd_divisor[] and len_d. * Else return 0 or negative on failure. If 0, might work if operands switched, * If a negative number, switching operands won't work either. */ static int do_gcd(vp) long *vp; /* polynomial base variable pointer */ { int i; int count; for (count = 1; count < 50; count++) { switch (poly_div(trhs, n_trhs, gcd_divisor, len_d, vp)) { case 0: /* divide failed */ return(1 - count); case 2: /* Total success! Remainder is zero. */ debug_string(2, "Found raw polynomial GCD:"); side_debug(2, gcd_divisor, len_d); return count; } /* Do the Euclidean shuffle: swap trhs[] (remainder) and gcd_divisor[] */ if (len_d > n_tokens || n_trhs > DIVISOR_SIZE) return 0; blt(scratch, trhs, n_trhs * sizeof(token_type)); blt(trhs, gcd_divisor, len_d * sizeof(token_type)); blt(gcd_divisor, scratch, n_trhs * sizeof(token_type)); i = n_trhs; n_trhs = len_d; len_d = i; } return 0; } /* * Compute the simplified and normalized polynomial Greatest Common Divisor * of the expressions in "larger" and "smaller". * This polynomial GCD routine is used for polynomial factoring, etc. * * Return a positive integer if successful. * Return the GCD in trhs[]. * Return larger/GCD in tlhs[]. * The results are unfactored and simplified. */ int poly_gcd(larger, llen, smaller, slen, v) token_type *larger; /* larger polynomial */ int llen; /* larger polynomial length */ token_type *smaller; /* smaller polynomial */ int slen; /* smaller polynomial length */ long v; /* polynomial base variable */ { int count; debug_string(3, "Entering poly_gcd():"); side_debug(3, larger, llen); side_debug(3, smaller, slen); if (llen > n_tokens || slen > min(ARR_CNT(gcd_divisor), n_tokens)) return 0; if (trhs != larger) { blt(trhs, larger, llen * sizeof(token_type)); } n_trhs = llen; if (tlhs != smaller) { blt(tlhs, smaller, slen * sizeof(token_type)); } n_tlhs = slen; if (!remove_factors()) return 0; if (n_tlhs > ARR_CNT(gcd_divisor)) return 0; blt(gcd_divisor, tlhs, n_tlhs * sizeof(token_type)); len_d = n_tlhs; count = do_gcd(&v); if (count <= 0) return 0; if (count > 1) { if (len_d > n_tokens) return 0; blt(tlhs, gcd_divisor, len_d * sizeof(token_type)); n_tlhs = len_d; if (!remove_factors()) return 0; if (n_tlhs > ARR_CNT(gcd_divisor)) return 0; blt(gcd_divisor, tlhs, n_tlhs * sizeof(token_type)); len_d = n_tlhs; if (poly_div(larger, llen, gcd_divisor, len_d, &v) != 2) { debug_string(1, "Polynomial GCD found, but larger divide failed in poly_gcd()."); return 0; } } if (len_d > n_tokens) return 0; blt(trhs, gcd_divisor, len_d * sizeof(token_type)); n_trhs = len_d; uf_simp(tlhs, &n_tlhs); uf_simp(trhs, &n_trhs); debug_string(3, "poly_gcd() successful."); return(count); } /* * Compute the polynomial Greatest Common Divisor of the expressions in "larger" and "smaller". * This polynomial GCD routine is used by the division simplifiers. * * Return a positive integer if successful. * Return larger/GCD in tlhs[]. * Return smaller/GCD in trhs[]. */ int poly2_gcd(larger, llen, smaller, slen, v, require_additive) token_type *larger; /* larger polynomial */ int llen; /* larger polynomial length */ token_type *smaller; /* smaller polynomial */ int slen; /* smaller polynomial length */ long v; /* polynomial base variable */ int require_additive; /* require the GCD to contain addition or subtraction */ { int i; int count; #if 0 jmp_buf save_save; #endif if (require_additive) { /* Require an additive operator in both polynomials to continue. */ count = 0; for (i = 1; i < llen; i += 2) { if (larger[i].token.operatr == PLUS || larger[i].token.operatr == MINUS) { count++; break; } } if (count == 0) return 0; count = 0; for (i = 1; i < slen; i += 2) { if (smaller[i].token.operatr == PLUS || smaller[i].token.operatr == MINUS) { count++; } } if (count == 0 /* || count > 200 */) return 0; } debug_string(3, "Entering poly2_gcd():"); side_debug(3, larger, llen); side_debug(3, smaller, slen); if (llen > n_tokens || slen > min(ARR_CNT(gcd_divisor), n_tokens)) return 0; blt(trhs, larger, llen * sizeof(token_type)); n_trhs = llen; blt(tlhs, smaller, slen * sizeof(token_type)); n_tlhs = slen; #if 0 if (require_additive) { /* Require a level 1 additive operator in the divisor. */ blt(save_save, jmp_save, sizeof(jmp_save)); if ((i = setjmp(jmp_save)) != 0) { /* trap errors */ blt(jmp_save, save_save, sizeof(jmp_save)); if (i == 13) { /* critical error code */ longjmp(jmp_save, i); } return 0; } uf_simp(tlhs, &n_tlhs); blt(jmp_save, save_save, sizeof(jmp_save)); if (level1_plus_count(tlhs, n_tlhs) == 0) return 0; } #endif if (n_tlhs > ARR_CNT(gcd_divisor)) return 0; blt(gcd_divisor, tlhs, n_tlhs * sizeof(token_type)); len_d = n_tlhs; count = do_gcd(&v); if (count <= 0) return count; if (count > 1) { if (require_additive && level1_plus_count(gcd_divisor, len_d) == 0) return 0; if (poly_div(smaller, slen, gcd_divisor, len_d, &v) != 2) { debug_string(1, "Polynomial GCD found, but smaller divide failed in poly2_gcd()."); return 0; } blt(trhs, gcd_divisor, len_d * sizeof(token_type)); n_trhs = len_d; if (n_tlhs > ARR_CNT(gcd_divisor)) return 0; blt(gcd_divisor, tlhs, n_tlhs * sizeof(token_type)); len_d = n_tlhs; blt(tlhs, trhs, n_trhs * sizeof(token_type)); n_tlhs = n_trhs; if (poly_div(larger, llen, tlhs, n_tlhs, &v) != 2) { debug_string(1, "Polynomial GCD found, but larger divide failed in poly2_gcd()."); return 0; } blt(trhs, gcd_divisor, len_d * sizeof(token_type)); n_trhs = len_d; } else { n_trhs = 1; trhs[0] = one_token; } debug_string(3, "poly2_gcd() successful."); return count; } /* * This function returns true if the passed Mathomatic variable * is of type integer. * * Integer variable names start with "integer". */ int is_integer_var(v) long v; { char *cp; int (*strncmpfunc)(); if (case_sensitive_flag) { strncmpfunc = strncmp; } else { strncmpfunc = strncasecmp; } cp = var_name(v); if (cp && strncmpfunc(cp, V_INTEGER_PREFIX, strlen(V_INTEGER_PREFIX)) == 0) return true; else return false; } /* * This function is a strict test that * returns true if passed expression is entirely integer and all variables * are either "integer" or "sign". * * The result of evaluating the expression must be integer if this returns true. * Should first be unfactored with uf_pplus() for a proper determination. */ int is_integer_expr(p1, n) token_type *p1; /* expression pointer */ int n; /* length of expression */ { int i; long v; #if DEBUG if (p1 == NULL || n < 1) { error_bug("(p1 == NULL || n < 1) in is_integer_expr()."); } #endif for (i = 0; i < n; i++) { switch (p1[i].kind) { case OPERATOR: if (p1[i].token.operatr == DIVIDE) return false; break; case CONSTANT: if (fmod(p1[i].token.constant, 1.0) != 0.0) return false; break; case VARIABLE: v = labs(p1[i].token.variable); if (!is_integer_var(v) && (v & VAR_MASK) != SIGN) return false; break; } } return true; } /* * This routine is the modulus operator (%) simplifier for equation sides. * Using "integer" variables will allow more simplification here. * Globals tlhs[] and trhs[] are wiped out by the polynomial division. * * Return true if equation side was modified. */ int mod_simp(equation, np) token_type *equation; /* pointer to the beginning of equation side to simplify */ int *np; /* pointer to length of the equation side */ { return mod_recurse(equation, np, 0, 1); } static int mod_recurse(equation, np, loc, level) token_type *equation; int *np, loc, level; { int modified = false; int i, j, k; int i1, i2, i3, i4, i5; int op, last_op2; int len1, len2, len3; int diff_sign; for (i = loc; i < *np && equation[i].level >= level;) { if (equation[i].level > level) { modified |= mod_recurse(equation, np, i, level + 1); i++; for (; i < *np && equation[i].level > level; i += 2) ; continue; } i++; } if (modified) /* make sure the deepest levels are completed, first */ return true; for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) { if (!(equation[i].level == level && equation[i].token.operatr == MODULUS)) continue; for (k = i + 2;; k += 2) { if (k >= *np || equation[k].level <= level) break; } len1 = k - (i + 1); last_op2 = 0; for (j = loc; j < *np && equation[j].level >= level; j++) { if (equation[j].level == level && equation[j].kind == OPERATOR) { last_op2 = equation[j].token.operatr; continue; } if (last_op2 == MODULUS) { continue; } last_op2 = MODULUS; op = 0; for (i1 = k = j + 1; k < *np && equation[k].level > level; k += 2) { if (equation[k].level == (level + 1)) { op = equation[k].token.operatr; i1 = k; } } len2 = k - j; switch (op) { case MODULUS: /* simplify (x%n)%n to x%n */ len3 = k - (i1 + 1); if (se_compare(&equation[i+1], len1, &equation[i1+1], len3, &diff_sign)) { blt(&equation[i1], &equation[k], (*np - k) * sizeof(token_type)); *np -= len3 + 1; return true; } break; case TIMES: if (!is_integer_expr(&equation[j], len2)) break; /* simplify (i%n*j)%n to (i*j)%n if j is integer */ for (i2 = i1 = j + 1;; i1 += 2) { if (i1 >= k || equation[i1].level == (level + 1)) { for (; i2 < i1; i2 += 2) { if (equation[i2].level == (level + 2) && equation[i2].token.operatr == MODULUS) { len3 = i1 - (i2 + 1); if (se_compare(&equation[i+1], len1, &equation[i2+1], len3, &diff_sign)) { blt(&equation[i2], &equation[i1], (*np - i1) * sizeof(token_type)); *np -= len3 + 1; return true; } } } } if (i1 >= k) break; } break; case PLUS: case MINUS: /* simplify (i%n+j)%n to (i+j)%n */ /* and ((i%n)*j+k)%n to (i*j+k)%n, when j is integer */ for (i2 = i1 = j + 1, i3 = j - 1;; i1 += 2) { if (i1 >= k || equation[i1].level == (level + 1)) { for (; i2 < i1; i2 += 2) { if (equation[i2].level == (level + 2)) { switch (equation[i2].token.operatr) { case MODULUS: len3 = i1 - (i2 + 1); if (se_compare(&equation[i+1], len1, &equation[i2+1], len3, &diff_sign)) { blt(&equation[i2], &equation[i1], (*np - i1) * sizeof(token_type)); *np -= len3 + 1; return true; } break; case TIMES: i2 = i1 - 2; if (!is_integer_expr(&equation[i3+1], i1 - (i3 + 1))) { break; } for (i4 = i3 + 2; i4 < i1; i4 += 2) { if (equation[i4].level == (level + 3) && equation[i4].token.operatr == MODULUS) { for (i5 = i4 + 2; i5 < i1 && equation[i5].level > (level + 3); i5 += 2) ; len3 = i5 - (i4 + 1); if (se_compare(&equation[i+1], len1, &equation[i4+1], len3, &diff_sign)) { blt(&equation[i4], &equation[i5], (*np - i5) * sizeof(token_type)); *np -= len3 + 1; return true; } } } break; } } } i3 = i1; } if (i1 >= k) break; } break; } #if 1 /* Remove integer*n multiples in x for x%n by doing */ /* polynomial division x/n and replacing with remainder%n. */ /* Globals tlhs[] and trhs[] are wiped out by the polynomial division here. */ if (poly_div(&equation[j], len2, &equation[i+1], len1, NULL)) { uf_pplus(tlhs, &n_tlhs); /* so integer%(integer^integer) isn't simplified to 0 */ if (is_integer_expr(tlhs, n_tlhs)) { if (n_trhs < len2 || REMAINDER_IS_ZERO()) { /* if it will make it smaller */ if ((*np + (n_trhs - len2)) > n_tokens) error_huge(); for (k = 0; k < n_trhs; k++) trhs[k].level += level; blt(&equation[j+n_trhs], &equation[j+len2], (*np - (j + len2)) * sizeof(token_type)); *np += n_trhs - len2; blt(&equation[j], trhs, n_trhs * sizeof(token_type)); debug_string(2, "Polynomial division successful in modulus simplification. The result is:"); side_debug(2, equation, *np); return true; } } } #endif } } return modified; } /* * This routine is a division simplifier for equation sides. * It reduces algebraic fractions. * * Return true if any polynomial Greatest Common Divisor (GCD) * between a numerator and denominator was found and * the expression was simplified by dividing the numerator * and denominator by the GCD. */ int poly_gcd_simp(equation, np) token_type *equation; int *np; { return polydiv_recurse(equation, np, 0, 1); } static int polydiv_recurse(equation, np, loc, level) token_type *equation; int *np, loc, level; { int modified = false; int i, j, k; int last_op2; int len1, len2; int rv; for (i = loc; i < *np && equation[i].level >= level;) { if (equation[i].level > level) { modified |= polydiv_recurse(equation, np, i, level + 1); i++; for (; i < *np && equation[i].level > level; i += 2) ; continue; } i++; } start: for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) { #if DEBUG if (equation[i].kind != OPERATOR) error_bug("Bug in poly_gcd_simp()."); #endif if (equation[i].level == level && equation[i].token.operatr == DIVIDE) { for (k = i + 2;; k += 2) { if (k >= *np || equation[k].level <= level) break; } len1 = k - (i + 1); last_op2 = 0; for (j = loc; j < *np && equation[j].level >= level; j++) { if (equation[j].level == level && equation[j].kind == OPERATOR) { last_op2 = equation[j].token.operatr; continue; } switch (last_op2) { case DIVIDE: continue; case 0: case TIMES: break; default: error_bug("Expression is corrupt in poly_gcd_simp()."); } last_op2 = DIVIDE; for (k = j + 1;; k += 2) { if (k >= *np || equation[k].level <= level) break; } len2 = k - j; if ((rv = poly2_gcd(&equation[i+1], len1, &equation[j], len2, 0L, true)) > 0) { store_code: for (k = 0; k < n_tlhs; k++) tlhs[k].level += level; for (k = 0; k < n_trhs; k++) trhs[k].level += level; if (((*np + (n_trhs - len2)) > n_tokens) || ((*np + (n_trhs - len2) + (n_tlhs - len1)) > n_tokens)) error_huge(); blt(&equation[j+n_trhs], &equation[j+len2], (*np - (j + len2)) * sizeof(token_type)); *np += n_trhs - len2; if (i > j) i += n_trhs - len2; blt(&equation[j], trhs, n_trhs * sizeof(token_type)); blt(&equation[i+n_tlhs+1], &equation[i+1+len1], (*np - (i + 1 + len1)) * sizeof(token_type)); *np += n_tlhs - len1; blt(&equation[i+1], tlhs, n_tlhs * sizeof(token_type)); debug_string(1, _("Division simplified with polynomial GCD.")); modified = true; goto start; } if (rv == 0 && poly2_gcd(&equation[j], len2, &equation[i+1], len1, 0L, true) > 0) { k = j - 1; j = i + 1; i = k; k = len1; len1 = len2; len2 = k; goto store_code; } } } } return modified; } /* * This routine is a division simplifier for equation sides. * Check for divides and do polynomial and smart division. * For example: (c - b + a)/(b - a) will be converted to (c/(b - a)) - 1. * Will only make expressions with algebraic fractions smaller, * otherwise it would go into an infinite loop. * An expression with less operators is judged to be smaller. * Complex fractions (fractions within fractions) are sometimes * created by this, especially if quick_flag is false. * * Return true if expression was simplified. */ int div_remainder(equation, np, poly_flag, quick_flag) token_type *equation; int *np; int poly_flag; /* if true, try polynomial division first, then smart division */ int quick_flag; /* if true, keep algebraic fractions simpler */ { int rv = false; debug_string(3, "Entering div_remainder()."); if (quick_flag) group_proc(equation, np); rv = pdiv_recurse(equation, np, 0, 1, poly_flag); if (quick_flag) organize(equation, np); debug_string(3, "Leaving div_remainder()."); return rv; } static int pdiv_recurse(equation, np, loc, level, code) token_type *equation; int *np, loc, level, code; { int modified = false; int i, j, k; int op, op2, last_op2; int len1, len2, real_len1; int rv = 0; int flag, power_flag, zero_remainder; for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) { if (!(equation[i].level == level && equation[i].token.operatr == DIVIDE)) continue; for (k = i + 2;; k += 2) { if (k >= *np || equation[k].level <= level) break; } len1 = real_len1 = k - (i + 1); last_op2 = 0; for (j = loc; j < *np && equation[j].level >= level; j++) { if (equation[j].level == level && equation[j].kind == OPERATOR) { last_op2 = equation[j].token.operatr; continue; } if (last_op2 == DIVIDE) { continue; } last_op2 = DIVIDE; op = 0; for (k = j + 1; k < *np && equation[k].level > level; k += 2) { if (equation[k].level == (level + 1)) { op = equation[k].token.operatr; } } if (op != PLUS && op != MINUS) { continue; } len2 = k - j; flag = code; power_flag = false; op = 0; op2 = 0; for (k = i + 2; k < *np && equation[k].level > level; k += 2) { if (equation[k].level == level + 3) { switch (equation[k].token.operatr) { case PLUS: case MINUS: op2 = PLUS; } } else if (equation[k].level == level + 2) { op = equation[k].token.operatr; } else if (equation[k].level == level + 1) { if (equation[k].token.operatr == POWER && (op == PLUS || op == MINUS || (op == TIMES && op2 == PLUS))) { power_flag = true; len1 = k - (i + 1); } break; } } next_thingy: if (!power_flag) { len1 = real_len1; } if (flag || power_flag) { rv = poly_div(&equation[j], len2, &equation[i+1], len1, NULL); } else { rv = smart_div(&equation[j], len2, &equation[i+1], len1); } zero_remainder = (rv > 0 && REMAINDER_IS_ZERO()); if (power_flag && !zero_remainder) { rv = 0; } if (rv > 0) { /* if successful and the result is smaller than the original: */ if ((n_tlhs + 2 + n_trhs + len1) > n_tokens) error_huge(); for (k = 0; k < n_tlhs; k++) tlhs[k].level++; tlhs[n_tlhs].level = 1; tlhs[n_tlhs].kind = OPERATOR; tlhs[n_tlhs].token.operatr = PLUS; n_tlhs++; for (k = 0; k < n_trhs; k++) trhs[k].level += 2; blt(&tlhs[n_tlhs], trhs, n_trhs * sizeof(token_type)); n_tlhs += n_trhs; tlhs[n_tlhs].level = 2; tlhs[n_tlhs].kind = OPERATOR; tlhs[n_tlhs].token.operatr = DIVIDE; n_tlhs++; k = n_tlhs; blt(&tlhs[n_tlhs], &equation[i+1], len1 * sizeof(token_type)); n_tlhs += len1; for (; k < n_tlhs; k++) tlhs[k].level += 2; side_debug(3, &equation[j], len2); side_debug(3, &equation[i+1], len1); simpb_side(tlhs, &n_tlhs, false, true, 3); /* parameters should match what's used in simpa_side() */ side_debug(3, tlhs, n_tlhs); if (power_flag) { k = (var_count(tlhs, n_tlhs) <= var_count(&equation[j], len2)); } else { k = ((var_count(tlhs, n_tlhs) + (n_tlhs >= len1 + 1 + len2)) <= (var_count(&equation[j], len2) + var_count(&equation[i+1], len1))); } if (k) { for (k = 0; k < n_tlhs; k++) tlhs[k].level += level; if (power_flag) { if ((*np - len2 + n_tlhs + 2) > n_tokens) error_huge(); for (k = i + 2 + len1; k <= i + real_len1; k++) { equation[k].level++; } blt(&equation[i+real_len1+3], &equation[k], (*np - k) * sizeof(token_type)); *np += 2; equation[k].level = level + 2; equation[k].kind = OPERATOR; equation[k].token.operatr = MINUS; k++; equation[k].level = level + 2; equation[k].kind = CONSTANT; equation[k].token.constant = 1.0; if (i < j) { j += 2; } } else { if ((*np - (len1 + 1 + len2) + n_tlhs) > n_tokens) error_huge(); blt(&equation[i], &equation[i+1+len1], (*np - (i + 1 + len1)) * sizeof(token_type)); *np -= len1 + 1; if (i < j) { j -= len1 + 1; } } blt(&equation[j+n_tlhs], &equation[j+len2], (*np - (j + len2)) * sizeof(token_type)); *np -= len2 - n_tlhs; blt(&equation[j], tlhs, n_tlhs * sizeof(token_type)); if (flag || power_flag) { debug_string(1, _("Polynomial division successful.")); } else { debug_string(1, _("Smart division successful.")); } side_debug(3, equation, *np); return true; } } if (power_flag) { power_flag = false; goto next_thingy; } if (flag == code) { flag = !flag; goto next_thingy; } } } for (i = loc; i < *np && equation[i].level >= level;) { if (equation[i].level > level) { modified |= pdiv_recurse(equation, np, i, level + 1, code); i++; for (; i < *np && equation[i].level > level; i += 2) ; continue; } i++; } return modified; } /* * Do generalized and accurate polynomial division. * This is the main polynomial division function for Mathomatic. * Works with any number of variables and any pair of expressions. * * Returns non-zero if successful: 2 if remainder is 0, * 1 if result is smaller than the original two expressions, * negative if result is larger. * Quotient is returned in tlhs[] and remainder in trhs[]. * * If *vp is 0, automatically select the best polynomial base variable and return it in *vp. */ int poly_div(d1, len1, d2, len2, vp) token_type *d1; /* pointer to dividend */ int len1; /* length of dividend */ token_type *d2; /* pointer to divisor */ int len2; /* length of divisor */ long *vp; /* pointer to polynomial base variable */ { int i; int rv; int old_partial; jmp_buf save_save; old_partial = partial_flag; partial_flag = false; /* We want full unfactoring during polynomial division. */ blt(save_save, jmp_save, sizeof(jmp_save)); if ((i = setjmp(jmp_save)) != 0) { /* Trap errors so we almost always return normally. */ blt(jmp_save, save_save, sizeof(jmp_save)); partial_flag = old_partial; if (i == 13) { /* critical error code */ longjmp(jmp_save, i); } return false; } rv = poly_div_sub(d1, len1, d2, len2, vp); blt(jmp_save, save_save, sizeof(jmp_save)); partial_flag = old_partial; return rv; } #define MAX_GREATEST_POWER_TERMS 50 /* a limit to keep polynomial division from taking too long */ /* * Do the actual polynomial division using a simple, * generalized, polynomial long division algorithm. */ static int poly_div_sub(d1, len1, d2, len2, vp) token_type *d1; int len1; token_type *d2; int len2; long *vp; { int i; int t1, len_t1; int t2, len_t2; int sign; int divide_flag; double last_power, divisor_power, d; int sum_size; int last_count, count; /* dividend number of terms raised to the highest power */ int divisor_count; /* divisor number of terms raised to the highest power */ long tmp_v = 0; if (vp == NULL) vp = &tmp_v; if (len1 > n_tokens || len2 > n_tokens) return false; /* Copy the source polynomials to where we want them (tlhs and trhs). */ if (trhs != d1) { blt(trhs, d1, len1 * sizeof(token_type)); } n_trhs = len1; if (tlhs != d2) { blt(tlhs, d2, len2 * sizeof(token_type)); } n_tlhs = len2; /* Do the basic unfactoring and simplification of the dividend and divisor. */ uf_simp(trhs, &n_trhs); uf_simp(tlhs, &n_tlhs); if (*vp == 0) { /* Select the best polynomial base variable. */ if (!find_highest_count(trhs, n_trhs, tlhs, n_tlhs, vp)) return false; } #if !SILENT /* Display debugging info. */ if (debug_level >= 3) { list_var(*vp, 0); fprintf(gfp, _("poly_div() starts using base variable %s:\n"), var_str); side_debug(3, trhs, n_trhs); side_debug(3, tlhs, n_tlhs); } #endif /* Determine divide_flag and if the polynomials can be divided. */ divide_flag = 2; last_count = find_greatest_power(trhs, n_trhs, vp, &last_power, &t1, &len_t1, ÷_flag); divisor_count = find_greatest_power(tlhs, n_tlhs, vp, &divisor_power, &t2, &len_t2, ÷_flag); if (divisor_power <= 0 || last_power < divisor_power) { divide_flag = !divide_flag; last_count = find_greatest_power(trhs, n_trhs, vp, &last_power, &t1, &len_t1, ÷_flag); divisor_count = find_greatest_power(tlhs, n_tlhs, vp, &divisor_power, &t2, &len_t2, ÷_flag); if (divisor_power <= 0 || last_power < divisor_power) { return false; } } if (divisor_count > 1 || last_count > MAX_GREATEST_POWER_TERMS) /* Need recursion and factoring to do more than this. */ return false; /* Initialize the quotient. */ n_quotient = 1; quotient[0] = zero_token; /* Store the divisor. */ if (n_tlhs > ARR_CNT(divisor)) return false; blt(divisor, tlhs, n_tlhs * sizeof(token_type)); n_divisor = n_tlhs; sum_size = n_trhs + n_quotient; /* Loop until polynomial division is finished. */ for (;;) { if (t1 > 0 && trhs[t1-1].token.operatr == MINUS) sign = MINUS; else sign = PLUS; if (t2 > 0 && divisor[t2-1].token.operatr == MINUS) { if (sign == MINUS) sign = PLUS; else sign = MINUS; } if ((len_t1 + len_t2 + 1) > n_tokens) return false; blt(tlhs, &trhs[t1], len_t1 * sizeof(token_type)); n_tlhs = len_t1; for (i = 0; i < n_tlhs; i++) tlhs[i].level++; tlhs[n_tlhs].level = 1; tlhs[n_tlhs].kind = OPERATOR; tlhs[n_tlhs].token.operatr = DIVIDE; n_tlhs++; blt(&tlhs[n_tlhs], &divisor[t2], len_t2 * sizeof(token_type)); i = n_tlhs; n_tlhs += len_t2; for (; i < n_tlhs; i++) tlhs[i].level++; if (!simp_loop(tlhs, &n_tlhs)) return false; if ((n_quotient + 1 + n_tlhs) > min(ARR_CNT(quotient), n_tokens)) return false; for (i = 0; i < n_tlhs; i++) tlhs[i].level++; quotient[n_quotient].level = 1; quotient[n_quotient].kind = OPERATOR; quotient[n_quotient].token.operatr = sign; n_quotient++; blt("ient[n_quotient], tlhs, n_tlhs * sizeof(token_type)); n_quotient += n_tlhs; if ((n_trhs + n_tlhs + n_divisor + 2) > n_tokens) return false; blt(&trhs[t1+1], &trhs[t1+len_t1], (n_trhs - (t1 + len_t1)) * sizeof(token_type)); n_trhs -= len_t1 - 1; trhs[t1] = zero_token; for (i = 0; i < n_trhs; i++) trhs[i].level++; trhs[n_trhs].level = 1; trhs[n_trhs].kind = OPERATOR; if (sign == PLUS) trhs[n_trhs].token.operatr = MINUS; else trhs[n_trhs].token.operatr = PLUS; n_trhs++; blt(&trhs[n_trhs], tlhs, n_tlhs * sizeof(token_type)); i = n_trhs; n_trhs += n_tlhs; for (; i < n_trhs; i++) trhs[i].level++; trhs[n_trhs].level = 2; trhs[n_trhs].kind = OPERATOR; trhs[n_trhs].token.operatr = TIMES; n_trhs++; i = n_trhs; blt(&trhs[n_trhs], divisor, t2 * sizeof(token_type)); n_trhs += t2; trhs[n_trhs] = zero_token; n_trhs++; blt(&trhs[n_trhs], &divisor[t2+len_t2], (n_divisor - (t2 + len_t2)) * sizeof(token_type)); n_trhs += (n_divisor - (t2 + len_t2)); for (; i < n_trhs; i++) trhs[i].level += 2; side_debug(3, trhs, n_trhs); uf_repeat(trhs, &n_trhs); uf_tsimp(trhs, &n_trhs); side_debug(4, trhs, n_trhs); count = find_greatest_power(trhs, n_trhs, vp, &d, &t1, &len_t1, ÷_flag); if (d < divisor_power) { /* Success! Polynomial division ends here. */ debug_string(3, "Successful polynomial division!"); blt(tlhs, quotient, n_quotient * sizeof(token_type)); n_tlhs = n_quotient; debug_string(3, "Quotient:"); side_debug(3, tlhs, n_tlhs); debug_string(3, "Remainder:"); side_debug(3, trhs, n_trhs); if (REMAINDER_IS_ZERO()) return 2; if ((n_trhs + n_quotient) >= (sum_size /* - (sum_size / 10) */)) { if ((n_trhs + 1) > sum_size && n_trhs > n_divisor) return -2; else return -1; } return 1; } else if (d < last_power) { last_power = d; last_count = count; if (last_count > MAX_GREATEST_POWER_TERMS) /* Need factoring to do more than this. */ return false; } else if (d > last_power) { return false; } else { if (count >= last_count) { return false; } last_count = count; } } } /* * Do smart division. * * Smart division is heuristic division much like polynomial division, * however instead of basing the division on the highest powers of a base variable, * every term in the dividend is tried, and if a trial makes the * expression smaller, we go with that. * * Only one term in the divisor is tried, to save time. * Currently, the divisor term used is the one with the least number * of variables, not including any terms with no variables. * * Returns true if successful. * Quotient is returned in tlhs[] and remainder in trhs[]. */ int smart_div(d1, len1, d2, len2) token_type *d1; /* pointer to dividend */ int len1; /* length of dividend */ token_type *d2; /* pointer to divisor */ int len2; /* length of divisor */ { int i, j, k; int t1, len_t1; int t2 = 0, len_t2 = 0; int sign; int old_n_quotient; int trhs_size; int term_size = 0, term_count; int term_pos = 0, skip_terms[100]; int skip_count; token_type *qp; int q_size; int sum_size; int count; int dcount = 0; /* divisor term count */ int flag; blt(trhs, d1, len1 * sizeof(token_type)); n_trhs = len1; blt(tlhs, d2, len2 * sizeof(token_type)); n_tlhs = len2; /* Do the basic unfactoring and simplification of the dividend and divisor. */ uf_simp_no_repeat(trhs, &n_trhs); uf_simp_no_repeat(tlhs, &n_tlhs); /* Display debugging info. */ debug_string(3, "smart_div() starts:"); side_debug(3, trhs, n_trhs); side_debug(3, tlhs, n_tlhs); /* Find which divisor term to use. */ for (i = 0, j = 0, k = 0, flag = false;; i++) { if (i >= n_tlhs || (tlhs[i].kind == OPERATOR && tlhs[i].level == 1 && (tlhs[i].token.operatr == PLUS || tlhs[i].token.operatr == MINUS))) { dcount++; if (flag) { if (len_t2 == 0 || var_count(&tlhs[j], i - j) < k) { len_t2 = i - j; t2 = j; k = var_count(&tlhs[t2], len_t2); } } flag = false; j = i + 1; } else if (tlhs[i].kind == VARIABLE && tlhs[i].token.variable != IMAGINARY) { flag = true; } if (i >= n_tlhs) break; } if (len_t2 <= 0) return false; /* Initialize the quotient. */ n_quotient = 1; quotient[0] = zero_token; if (n_tlhs > ARR_CNT(divisor)) return false; blt(divisor, tlhs, n_tlhs * sizeof(token_type)); n_divisor = n_tlhs; try_one: trhs_size = n_trhs; for (skip_count = 0, count = 0;;) { sum_size = n_trhs + n_quotient; for (term_count = 1, q_size = 0;; term_count++) { if (!get_term(trhs, n_trhs, term_count, &t1, &len_t1)) break; flag = false; for (i = 0; i < skip_count; i++) { if (skip_terms[i] == t1) { flag = true; break; } } if (flag) continue; if ((len_t1 + len_t2 + 1) > n_tokens) return false; blt(tlhs, &trhs[t1], len_t1 * sizeof(token_type)); n_tlhs = len_t1; for (i = 0; i < n_tlhs; i++) tlhs[i].level++; tlhs[n_tlhs].level = 1; tlhs[n_tlhs].kind = OPERATOR; tlhs[n_tlhs].token.operatr = DIVIDE; n_tlhs++; blt(&tlhs[n_tlhs], &divisor[t2], len_t2 * sizeof(token_type)); i = n_tlhs; n_tlhs += len_t2; for (; i < n_tlhs; i++) tlhs[i].level++; if (!simp_loop(tlhs, &n_tlhs)) continue; if (basic_size(tlhs, n_tlhs) <= basic_size(&trhs[t1], len_t1)) { q_size = n_tlhs; term_pos = t1; term_size = len_t1; break; } } if (q_size <= 0) { if (count <= 0) { if (dcount > 1) { dcount = 1; t2 = 0; len_t2 = n_divisor; goto try_one; /* Try using the whole divisor as the divisor term. */ } return false; } end_div: if (dcount > 1) { if (n_quotient + n_trhs >= trhs_size + 1) { return false; } } end_div2: blt(tlhs, quotient, n_quotient * sizeof(token_type)); n_tlhs = n_quotient; side_debug(3, tlhs, n_tlhs); side_debug(3, trhs, n_trhs); return true; /* Success! */ } t1 = term_pos; len_t1 = term_size; if (t1 > 0 && trhs[t1-1].token.operatr == MINUS) sign = MINUS; else sign = PLUS; if (t2 > 0 && divisor[t2-1].token.operatr == MINUS) { if (sign == MINUS) sign = PLUS; else sign = MINUS; } if ((len_t1 + len_t2 + 1) > n_tokens) return false; blt(tlhs, &trhs[t1], len_t1 * sizeof(token_type)); n_tlhs = len_t1; for (i = 0; i < n_tlhs; i++) tlhs[i].level++; tlhs[n_tlhs].level = 1; tlhs[n_tlhs].kind = OPERATOR; tlhs[n_tlhs].token.operatr = DIVIDE; n_tlhs++; blt(&tlhs[n_tlhs], &divisor[t2], len_t2 * sizeof(token_type)); i = n_tlhs; n_tlhs += len_t2; for (; i < n_tlhs; i++) tlhs[i].level++; simp_loop(tlhs, &n_tlhs); if ((n_quotient + 1 + n_tlhs) > min(ARR_CNT(quotient), n_tokens)) return false; for (i = 0; i < n_tlhs; i++) tlhs[i].level++; old_n_quotient = n_quotient; quotient[n_quotient].level = 1; quotient[n_quotient].kind = OPERATOR; quotient[n_quotient].token.operatr = sign; n_quotient++; qp = "ient[n_quotient]; q_size = n_tlhs; blt("ient[n_quotient], tlhs, n_tlhs * sizeof(token_type)); n_quotient += n_tlhs; if ((n_trhs + q_size + n_divisor + 2) > n_tokens) return false; blt(tlhs, trhs, n_trhs * sizeof(token_type)); n_tlhs = n_trhs; blt(&trhs[t1+1], &trhs[t1+len_t1], (n_trhs - (t1 + len_t1)) * sizeof(token_type)); n_trhs -= len_t1 - 1; trhs[t1] = zero_token; for (i = 0; i < n_trhs; i++) trhs[i].level++; trhs[n_trhs].level = 1; trhs[n_trhs].kind = OPERATOR; if (sign == PLUS) trhs[n_trhs].token.operatr = MINUS; else trhs[n_trhs].token.operatr = PLUS; n_trhs++; blt(&trhs[n_trhs], qp, q_size * sizeof(token_type)); i = n_trhs; n_trhs += q_size; for (; i < n_trhs; i++) trhs[i].level++; trhs[n_trhs].level = 2; trhs[n_trhs].kind = OPERATOR; trhs[n_trhs].token.operatr = TIMES; n_trhs++; i = n_trhs; blt(&trhs[n_trhs], divisor, t2 * sizeof(token_type)); n_trhs += t2; trhs[n_trhs] = zero_token; n_trhs++; blt(&trhs[n_trhs], &divisor[t2+len_t2], (n_divisor - (t2 + len_t2)) * sizeof(token_type)); n_trhs += (n_divisor - (t2 + len_t2)); for (; i < n_trhs; i++) trhs[i].level += 2; side_debug(3, trhs, n_trhs); uf_tsimp(trhs, &n_trhs); side_debug(4, trhs, n_trhs); if (REMAINDER_IS_ZERO()) goto end_div2; if (dcount > 1) { if ((n_trhs + n_quotient) >= sum_size) { if (skip_count >= ARR_CNT(skip_terms)) { if (count == 0) { return false; } else { n_quotient = old_n_quotient; blt(trhs, tlhs, n_tlhs * sizeof(token_type)); n_trhs = n_tlhs; goto end_div; } } skip_terms[skip_count] = term_pos; skip_count++; n_quotient = old_n_quotient; blt(trhs, tlhs, n_tlhs * sizeof(token_type)); n_trhs = n_tlhs; debug_string(3, "Skipping last operation."); continue; } } if (n_trhs == 1 && trhs[0].kind == CONSTANT) goto end_div; skip_count = 0; count++; } } /* * Return the size of a sub-expression, * minus any constant multiplier. */ int basic_size(p1, len) token_type *p1; int len; { int i, j; int level; int rv; int constant_flag = true; rv = len; level = min_level(p1, len); for (i = 0, j = -1; i < len; i++) { if (p1[i].kind == OPERATOR) { if (p1[i].level == level && (p1[i].token.operatr == TIMES || p1[i].token.operatr == DIVIDE)) { if (constant_flag) { rv -= i - j; } j = i; constant_flag = true; } } else if (p1[i].kind != CONSTANT) constant_flag = false; } if (constant_flag) { rv -= i - j; } return rv; } int get_term(p1, n1, count, tp1, lentp1) token_type *p1; int n1; int count; int *tp1, *lentp1; { int i, j; int no; for (no = 0, i = 1, j = 0;; i += 2) { if (i >= n1 || (p1[i].level == 1 && (p1[i].token.operatr == PLUS || p1[i].token.operatr == MINUS))) { no++; if (no >= count) { *tp1 = j; *lentp1 = i - j; return true; } j = i + 1; } if (i >= n1) return false; } } /* * This routine automatically finds the best variable to do polynomial division with. * * Returns 0 if nothing was found, * otherwise the selected polynomial base variable is returned in *vp1, * and the number of times it occurs in the dividend is returned. */ static int find_highest_count(p1, n1, p2, n2, vp1) token_type *p1; /* pointer to dividend expression */ int n1; /* length of dividend */ token_type *p2; /* pointer to divisor expression */ int n2; /* length of divisor */ long *vp1; /* variable pointer to return base variable with */ { int i; int vc, cnt; long v1, last_v, cv; /* Mathomatic variables */ sort_type va[MAX_VARS]; int divide_flag; int t1, len_t1; int t2, len_t2; double d1, d2; int count1, count2; last_v = 0; for (vc = 0; vc < ARR_CNT(va);) { cnt = 0; v1 = -1; for (i = 0; i < n1; i += 2) { if (p1[i].kind == VARIABLE && p1[i].token.variable > last_v) { if (v1 == -1 || p1[i].token.variable < v1) { v1 = p1[i].token.variable; cnt = 1; } else if (p1[i].token.variable == v1) { cnt++; } } } if (v1 == -1) break; last_v = v1; va[vc].v = v1; va[vc].count = cnt; vc++; } if (vc <= 0) return 0; qsort((char *) va, vc, sizeof(*va), vcmp); for (cv = IMAGINARY + 1; cv > 0; cv--) { for (i = 0; i < vc; i++) { if ((cv > IMAGINARY) ? ((va[i].v & VAR_MASK) <= SIGN) : (va[i].v != cv)) { continue; } *vp1 = va[i].v; divide_flag = 2; count1 = find_greatest_power(p1, n1, vp1, &d1, &t1, &len_t1, ÷_flag); count2 = find_greatest_power(p2, n2, vp1, &d2, &t2, &len_t2, ÷_flag); if (d2 <= 0 || d1 < d2 || count2 > count1) { divide_flag = !divide_flag; count1 = find_greatest_power(p1, n1, vp1, &d1, &t1, &len_t1, ÷_flag); count2 = find_greatest_power(p2, n2, vp1, &d2, &t2, &len_t2, ÷_flag); if (d2 <= 0 || d1 < d2 || count2 > count1) { continue; } } return va[i].count; } #if 1 break; #endif } return 0; } #define VALUE_CNT 3 void term_value(dp, p1, n1, loc) double *dp; token_type *p1; int n1, loc; { int i, j, k; int divide_flag = false; int level, div_level = 0; double d, sub_count, sub_sum; for (i = 0; i < VALUE_CNT; i++) dp[i] = 0.0; for (i = loc; i < n1; i++) { level = p1[i].level; if (p1[i].kind == VARIABLE) { if (divide_flag) { dp[0] -= 1.0; dp[1] -= p1[i].token.variable; dp[2] -= p1[i].token.variable; } else { dp[0] += 1.0; dp[1] += p1[i].token.variable; dp[2] += p1[i].token.variable; } } else if (p1[i].kind == OPERATOR) { if (level == 1 && (p1[i].token.operatr == PLUS || p1[i].token.operatr == MINUS)) break; if (p1[i].token.operatr == DIVIDE) { if (divide_flag && level >= div_level) continue; div_level = level; divide_flag = true; } else if (divide_flag && level <= div_level) { divide_flag = false; } } } divide_flag = false; for (j = loc + 1; j < i; j += 2) { level = p1[j].level; if (p1[j].token.operatr == DIVIDE) { if (divide_flag && level >= div_level) continue; div_level = level; divide_flag = true; } else if (divide_flag && level <= div_level) { divide_flag = false; } if (p1[j].token.operatr == POWER && level == p1[j+1].level && p1[j+1].kind == CONSTANT) { d = p1[j+1].token.constant - 1.0; sub_count = 0.0; sub_sum = 0.0; for (k = j - 1; k >= loc && p1[k].level >= level; k--) { if (p1[k].kind == VARIABLE) { sub_count += 1; sub_sum += p1[k].token.variable; } } if (divide_flag) { dp[0] -= d * sub_count; dp[2] -= d * sub_sum; } else { dp[0] += d * sub_count; dp[2] += d * sub_sum; } } } } /* * This routine finds the additive term raised the highest power, * with ordering and much flexibility. * * The number of terms raised to the highest power is returned, unless *vp1 == 0, * then 0 is returned and *vp1 is set to a valid polynomial base variable, if any. */ int find_greatest_power(p1, n1, vp1, pp1, tp1, lentp1, dcodep) token_type *p1; /* pointer to expression */ int n1; /* length of expression */ long *vp1; /* polynomial base variable */ double *pp1; /* returned power of the returned term, which will be the highest power */ int *tp1, *lentp1; /* the returned term index and length */ int *dcodep; /* divide flag pointer indicates if term is a denominator; */ /* for example: for x^5 it is false, for 1/x^5 it is true. */ /* If equal to 2, set it; if equal to 3, ignore it. */ { int i, j, k, ii; double d; int flag, divide_flag = false; int level, div_level = 0; long v = 0; int was_power = false; double last_va[VALUE_CNT]; double va[VALUE_CNT]; int rv; int count = 0; CLEAR_ARRAY(last_va); *pp1 = 0.0; *tp1 = -1; *lentp1 = 0; rv = *dcodep; for (j = 0, i = 1;; i += 2) { if (i >= n1 || ((p1[i].token.operatr == PLUS || p1[i].token.operatr == MINUS) && p1[i].level == 1)) { divide_flag = false; if (!was_power && *pp1 <= 1.0) { for (k = j; k < i; k++) { if (p1[k].kind == VARIABLE) { if (*dcodep <= 1 && *dcodep != divide_flag) continue; if (*vp1) { if (p1[k].token.variable == *vp1) { term_value(va, p1, n1, j); flag = (*pp1 == 1.0 && (rv > divide_flag)); if (*pp1 == 1.0 && rv == divide_flag) { if (*tp1 != j) count++; for (ii = 0; ii < ARR_CNT(va); ii++) { if (va[ii] == last_va[ii]) continue; if (va[ii] < last_va[ii]) flag = true; break; } } else if (*pp1 < 1.0 || flag) { count = 1; } if (*pp1 < 1.0 || flag) { blt(last_va, va, sizeof(last_va)); *pp1 = 1.0; *tp1 = j; rv = divide_flag; } break; } } else if ((p1[k].token.variable & VAR_MASK) > SIGN) { v = p1[k].token.variable; *pp1 = 1.0; *tp1 = j; rv = divide_flag; break; } } else if (p1[k].kind == OPERATOR) { if (p1[k].token.operatr == DIVIDE) { if (divide_flag && p1[k].level >= div_level) continue; div_level = p1[k].level; divide_flag = true; } else if (divide_flag && p1[k].level <= div_level) { divide_flag = false; } if (p1[k].token.operatr == POWER) { level = p1[k].level; do { k += 2; } while (k < i && p1[k].level > level); k--; } } } } if (i >= n1) break; j = i + 1; was_power = false; divide_flag = false; continue; } level = p1[i].level; if (p1[i].token.operatr == DIVIDE) { if (divide_flag && level >= div_level) continue; div_level = level; divide_flag = true; } else if (divide_flag && level <= div_level) { divide_flag = false; } if (p1[i].token.operatr == POWER && p1[i+1].kind == CONSTANT && (*vp1 || level == p1[i+1].level)) { if (*dcodep <= 1 && *dcodep != divide_flag) continue; d = p1[i+1].token.constant; for (k = i;;) { if (p1[k-1].kind == VARIABLE) { if (*vp1) { if (p1[k-1].token.variable == *vp1) { was_power = true; term_value(va, p1, n1, j); flag = (d == *pp1 && (rv > divide_flag)); if (d == *pp1 && rv == divide_flag) { if (*tp1 != j) count++; for (ii = 0; ii < ARR_CNT(va); ii++) { if (va[ii] == last_va[ii]) continue; if (va[ii] < last_va[ii]) flag = true; break; } } else if (d > *pp1 || flag) { count = 1; } if (d > *pp1 || flag) { blt(last_va, va, sizeof(last_va)); *pp1 = d; *tp1 = j; rv = divide_flag; } break; } } else if ((p1[k-1].token.variable & VAR_MASK) > SIGN) { was_power = true; if (d > *pp1) { v = p1[k-1].token.variable; *pp1 = d; *tp1 = j; rv = divide_flag; } break; } } k -= 2; if (k <= j) break; if (p1[k].level <= level) break; } } } if (*vp1 == 0) { *vp1 = v; } if (*tp1 >= 0) { for (i = *tp1 + 1; i < n1; i += 2) { if ((p1[i].token.operatr == PLUS || p1[i].token.operatr == MINUS) && p1[i].level == 1) { break; } } *lentp1 = i - *tp1; } if (*dcodep == 2) *dcodep = rv; return count; }