]> www.ginac.de Git - ginac.git/blobdiff - ginac/normal.cpp
introduce gcd_pf_pow: gcd helper to handle partially factored expressions.
[ginac.git] / ginac / normal.cpp
index 64c34ac290037348d835420d49ff1b2bf019000a..0af5aad856ee2ba95c66bcecf74528ed116de23b 100644 (file)
@@ -6,7 +6,7 @@
  *  computation, square-free factorization and rational function normalization. */
 
 /*
- *  GiNaC Copyright (C) 1999-2005 Johannes Gutenberg University Mainz, Germany
+ *  GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -614,6 +614,72 @@ bool divide(const ex &a, const ex &b, ex &q, bool check_args)
        if (!get_first_symbol(a, x) && !get_first_symbol(b, x))
                throw(std::invalid_argument("invalid expression in divide()"));
 
+       // Try to avoid expanding partially factored expressions.
+       if (is_exactly_a<mul>(b)) {
+       // Divide sequentially by each term
+               ex rem_new, rem_old = a;
+               for (size_t i=0; i < b.nops(); i++) {
+                       if (! divide(rem_old, b.op(i), rem_new, false))
+                               return false;
+                       rem_old = rem_new;
+               }
+               q = rem_new;
+               return true;
+       } else if (is_exactly_a<power>(b)) {
+               const ex& bb(b.op(0));
+               int exp_b = ex_to<numeric>(b.op(1)).to_int();
+               ex rem_new, rem_old = a;
+               for (int i=exp_b; i>0; i--) {
+                       if (! divide(rem_old, bb, rem_new, false))
+                               return false;
+                       rem_old = rem_new;
+               }
+               q = rem_new;
+               return true;
+       } 
+       
+       if (is_exactly_a<mul>(a)) {
+               // Divide sequentially each term. If some term in a is divisible 
+               // by b we are done... and if not, we can't really say anything.
+               size_t i;
+               ex rem_i;
+               bool divisible_p = false;
+               for (i=0; i < a.nops(); ++i) {
+                       if (divide(a.op(i), b, rem_i, false)) {
+                               divisible_p = true;
+                               break;
+                       }
+               }
+               if (divisible_p) {
+                       exvector resv;
+                       resv.reserve(a.nops());
+                       for (size_t j=0; j < a.nops(); j++) {
+                               if (j==i)
+                                       resv.push_back(rem_i);
+                               else
+                                       resv.push_back(a.op(j));
+                       }
+                       q = (new mul(resv))->setflag(status_flags::dynallocated);
+                       return true;
+               }
+       } else if (is_exactly_a<power>(a)) {
+               // The base itself might be divisible by b, in that case we don't
+               // need to expand a
+               const ex& ab(a.op(0));
+               int a_exp = ex_to<numeric>(a.op(1)).to_int();
+               ex rem_i;
+               if (divide(ab, b, rem_i, false)) {
+                       q = rem_i*power(ab, a_exp - 1);
+                       return true;
+               }
+               for (int i=2; i < a_exp; i++) {
+                       if (divide(power(ab, i), b, rem_i, false)) {
+                               q = rem_i*power(ab, a_exp - i);
+                               return true;
+                       }
+               } // ... so we *really* need to expand expression.
+       }
+       
        // Polynomial long division (recursive)
        ex r = a.expand();
        if (r.is_zero()) {
@@ -714,6 +780,31 @@ static bool divide_in_z(const ex &a, const ex &b, ex &q, sym_desc_vec::const_ite
        }
 #endif
 
+       if (is_exactly_a<power>(b)) {
+               const ex& bb(b.op(0));
+               ex qbar = a;
+               int exp_b = ex_to<numeric>(b.op(1)).to_int();
+               for (int i=exp_b; i>0; i--) {
+                       if (!divide_in_z(qbar, bb, q, var))
+                               return false;
+                       qbar = q;
+               }
+               return true;
+       }
+
+       if (is_exactly_a<mul>(b)) {
+               ex qbar = a;
+               for (const_iterator itrb = b.begin(); itrb != b.end(); ++itrb) {
+                       sym_desc_vec sym_stats;
+                       get_symbol_stats(a, *itrb, sym_stats);
+                       if (!divide_in_z(qbar, *itrb, q, sym_stats.begin()))
+                               return false;
+
+                       qbar = q;
+               }
+               return true;
+       }
+
        // Main symbol
        const ex &x = var->sym;
 
@@ -1177,17 +1268,19 @@ class gcdheu_failed {};
  *  polynomials and an iterator to the first element of the sym_desc vector
  *  passed in. This function is used internally by gcd().
  *
- *  @param a  first multivariate polynomial (expanded)
- *  @param b  second multivariate polynomial (expanded)
+ *  @param a  first integer multivariate polynomial (expanded)
+ *  @param b  second integer multivariate polynomial (expanded)
  *  @param ca  cofactor of polynomial a (returned), NULL to suppress
  *             calculation of cofactor
  *  @param cb  cofactor of polynomial b (returned), NULL to suppress
  *             calculation of cofactor
  *  @param var iterator to first element of vector of sym_desc structs
- *  @return the GCD as a new expression
+ *  @param res the GCD (returned)
+ *  @return true if GCD was computed, false otherwise.
  *  @see gcd
  *  @exception gcdheu_failed() */
-static ex heur_gcd(const ex &a, const ex &b, ex *ca, ex *cb, sym_desc_vec::const_iterator var)
+static bool heur_gcd_z(ex& res, const ex &a, const ex &b, ex *ca, ex *cb,
+                      sym_desc_vec::const_iterator var)
 {
 #if STATISTICS
        heur_gcd_called++;
@@ -1195,7 +1288,7 @@ static ex heur_gcd(const ex &a, const ex &b, ex *ca, ex *cb, sym_desc_vec::const
 
        // Algorithm only works for non-vanishing input polynomials
        if (a.is_zero() || b.is_zero())
-               return (new fail())->setflag(status_flags::dynallocated);
+               return false;
 
        // GCD of two numeric values -> CLN
        if (is_exactly_a<numeric>(a) && is_exactly_a<numeric>(b)) {
@@ -1204,7 +1297,8 @@ static ex heur_gcd(const ex &a, const ex &b, ex *ca, ex *cb, sym_desc_vec::const
                        *ca = ex_to<numeric>(a) / g;
                if (cb)
                        *cb = ex_to<numeric>(b) / g;
-               return g;
+               res = g;
+               return true;
        }
 
        // The first symbol is our main variable
@@ -1234,9 +1328,13 @@ static ex heur_gcd(const ex &a, const ex &b, ex *ca, ex *cb, sym_desc_vec::const
 
                // Apply evaluation homomorphism and calculate GCD
                ex cp, cq;
-               ex gamma = heur_gcd(p.subs(x == xi, subs_options::no_pattern), q.subs(x == xi, subs_options::no_pattern), &cp, &cq, var+1).expand();
-               if (!is_exactly_a<fail>(gamma)) {
-
+               ex gamma;
+               bool found = heur_gcd_z(gamma,
+                                       p.subs(x == xi, subs_options::no_pattern),
+                                       q.subs(x == xi, subs_options::no_pattern),
+                                       &cp, &cq, var+1);
+               if (found) {
+                       gamma = gamma.expand();
                        // Reconstruct polynomial from GCD of mapped polynomials
                        ex g = interpolate(gamma, xi, x, maxdeg);
 
@@ -1247,16 +1345,79 @@ static ex heur_gcd(const ex &a, const ex &b, ex *ca, ex *cb, sym_desc_vec::const
                        ex dummy;
                        if (divide_in_z(p, g, ca ? *ca : dummy, var) && divide_in_z(q, g, cb ? *cb : dummy, var)) {
                                g *= gc;
-                               return g;
+                               res = g;
+                               return true;
                        }
                }
 
                // Next evaluation point
                xi = iquo(xi * isqrt(isqrt(xi)) * numeric(73794), numeric(27011));
        }
-       return (new fail())->setflag(status_flags::dynallocated);
+       return false;
 }
 
+/** Compute GCD of multivariate polynomials using the heuristic GCD algorithm.
+ *  get_symbol_stats() must have been called previously with the input
+ *  polynomials and an iterator to the first element of the sym_desc vector
+ *  passed in. This function is used internally by gcd().
+ *
+ *  @param a  first rational multivariate polynomial (expanded)
+ *  @param b  second rational multivariate polynomial (expanded)
+ *  @param ca  cofactor of polynomial a (returned), NULL to suppress
+ *             calculation of cofactor
+ *  @param cb  cofactor of polynomial b (returned), NULL to suppress
+ *             calculation of cofactor
+ *  @param var iterator to first element of vector of sym_desc structs
+ *  @param res the GCD (returned)
+ *  @return true if GCD was computed, false otherwise.
+ *  @see heur_gcd_z
+ *  @see gcd
+ */
+static bool heur_gcd(ex& res, const ex& a, const ex& b, ex *ca, ex *cb,
+                    sym_desc_vec::const_iterator var)
+{
+       if (a.info(info_flags::integer_polynomial) && 
+           b.info(info_flags::integer_polynomial)) {
+               try {
+                       return heur_gcd_z(res, a, b, ca, cb, var);
+               } catch (gcdheu_failed) {
+                       return false;
+               }
+       }
+
+       // convert polynomials to Z[X]
+       const numeric a_lcm = lcm_of_coefficients_denominators(a);
+       const numeric ab_lcm = lcmcoeff(b, a_lcm);
+
+       const ex ai = a*ab_lcm;
+       const ex bi = b*ab_lcm;
+       if (!ai.info(info_flags::integer_polynomial))
+               throw std::logic_error("heur_gcd: not an integer polynomial [1]");
+
+       if (!bi.info(info_flags::integer_polynomial))
+               throw std::logic_error("heur_gcd: not an integer polynomial [2]");
+
+       bool found = false;
+       try {
+               found = heur_gcd_z(res, ai, bi, ca, cb, var);
+       } catch (gcdheu_failed) {
+               return false;
+       }
+       
+       // GCD is not unique, it's defined up to a unit (i.e. invertible
+       // element). If the coefficient ring is a field, every its element is
+       // invertible, so one can multiply the polynomial GCD with any element
+       // of the coefficient field. We use this ambiguity to make cofactors
+       // integer polynomials.
+       if (found)
+               res /= ab_lcm;
+       return found;
+}
+
+
+// gcd helper to handle partially factored polynomials (to avoid expanding
+// large expressions). At least one of the arguments should be a power.
+static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args);
 
 /** Compute GCD (Greatest Common Divisor) of multivariate polynomials a(X)
  *  and b(X) in Z[X]. Optionally also compute the cofactors of a and b,
@@ -1269,7 +1430,7 @@ static ex heur_gcd(const ex &a, const ex &b, ex *ca, ex *cb, sym_desc_vec::const
  *  @param check_args  check whether a and b are polynomials with rational
  *         coefficients (defaults to "true")
  *  @return the GCD as a new expression */
-ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args)
+ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned options)
 {
 #if STATISTICS
        gcd_called++;
@@ -1341,7 +1502,172 @@ factored_b:
        }
 
 #if FAST_COMPARE
-       // Input polynomials of the form poly^n are sometimes also trivial
+       if (is_exactly_a<power>(a) || is_exactly_a<power>(b))
+               return gcd_pf_pow(a, b, ca, cb, check_args);
+#endif
+
+       // Some trivial cases
+       ex aex = a.expand(), bex = b.expand();
+       if (aex.is_zero()) {
+               if (ca)
+                       *ca = _ex0;
+               if (cb)
+                       *cb = _ex1;
+               return b;
+       }
+       if (bex.is_zero()) {
+               if (ca)
+                       *ca = _ex1;
+               if (cb)
+                       *cb = _ex0;
+               return a;
+       }
+       if (aex.is_equal(_ex1) || bex.is_equal(_ex1)) {
+               if (ca)
+                       *ca = a;
+               if (cb)
+                       *cb = b;
+               return _ex1;
+       }
+#if FAST_COMPARE
+       if (a.is_equal(b)) {
+               if (ca)
+                       *ca = _ex1;
+               if (cb)
+                       *cb = _ex1;
+               return a;
+       }
+#endif
+
+       if (is_a<symbol>(aex)) {
+               if (! bex.subs(aex==_ex0, subs_options::no_pattern).is_zero()) {
+                       if (ca)
+                               *ca = a;
+                       if (cb)
+                               *cb = b;
+                       return _ex1;
+               }
+       }
+
+       if (is_a<symbol>(bex)) {
+               if (! aex.subs(bex==_ex0, subs_options::no_pattern).is_zero()) {
+                       if (ca)
+                               *ca = a;
+                       if (cb)
+                               *cb = b;
+                       return _ex1;
+               }
+       }
+
+       if (is_exactly_a<numeric>(aex)) {
+               numeric bcont = bex.integer_content();
+               numeric g = gcd(ex_to<numeric>(aex), bcont);
+               if (ca)
+                       *ca = ex_to<numeric>(aex)/g;
+               if (cb)
+                       *cb = bex/g;
+               return g;
+       }
+
+       if (is_exactly_a<numeric>(bex)) {
+               numeric acont = aex.integer_content();
+               numeric g = gcd(ex_to<numeric>(bex), acont);
+               if (ca)
+                       *ca = aex/g;
+               if (cb)
+                       *cb = ex_to<numeric>(bex)/g;
+               return g;
+       }
+
+       // Gather symbol statistics
+       sym_desc_vec sym_stats;
+       get_symbol_stats(a, b, sym_stats);
+
+       // The symbol with least degree which is contained in both polynomials
+       // is our main variable
+       sym_desc_vec::iterator vari = sym_stats.begin();
+       while ((vari != sym_stats.end()) && 
+              (((vari->ldeg_b == 0) && (vari->deg_b == 0)) ||
+               ((vari->ldeg_a == 0) && (vari->deg_a == 0))))
+               vari++;
+
+       // No common symbols at all, just return 1:
+       if (vari == sym_stats.end()) {
+               // N.B: keep cofactors factored
+               if (ca)
+                       *ca = a;
+               if (cb)
+                       *cb = b;
+               return _ex1;
+       }
+       // move symbols which contained only in one of the polynomials
+       // to the end:
+       rotate(sym_stats.begin(), vari, sym_stats.end());
+
+       sym_desc_vec::const_iterator var = sym_stats.begin();
+       const ex &x = var->sym;
+
+       // Cancel trivial common factor
+       int ldeg_a = var->ldeg_a;
+       int ldeg_b = var->ldeg_b;
+       int min_ldeg = std::min(ldeg_a,ldeg_b);
+       if (min_ldeg > 0) {
+               ex common = power(x, min_ldeg);
+               return gcd((aex / common).expand(), (bex / common).expand(), ca, cb, false) * common;
+       }
+
+       // Try to eliminate variables
+       if (var->deg_a == 0 && var->deg_b != 0 ) {
+               ex bex_u, bex_c, bex_p;
+               bex.unitcontprim(x, bex_u, bex_c, bex_p);
+               ex g = gcd(aex, bex_c, ca, cb, false);
+               if (cb)
+                       *cb *= bex_u * bex_p;
+               return g;
+       } else if (var->deg_b == 0 && var->deg_a != 0) {
+               ex aex_u, aex_c, aex_p;
+               aex.unitcontprim(x, aex_u, aex_c, aex_p);
+               ex g = gcd(aex_c, bex, ca, cb, false);
+               if (ca)
+                       *ca *= aex_u * aex_p;
+               return g;
+       }
+
+       // Try heuristic algorithm first, fall back to PRS if that failed
+       ex g;
+       bool found = heur_gcd(g, aex, bex, ca, cb, var);
+       if (!found) {
+#if STATISTICS
+               heur_gcd_failed++;
+#endif
+               g = sr_gcd(aex, bex, var);
+               if (g.is_equal(_ex1)) {
+                       // Keep cofactors factored if possible
+                       if (ca)
+                               *ca = a;
+                       if (cb)
+                               *cb = b;
+               } else {
+                       if (ca)
+                               divide(aex, g, *ca, false);
+                       if (cb)
+                               divide(bex, g, *cb, false);
+               }
+       } else {
+               if (g.is_equal(_ex1)) {
+                       // Keep cofactors factored if possible
+                       if (ca)
+                               *ca = a;
+                       if (cb)
+                               *cb = b;
+               }
+       }
+
+       return g;
+}
+
+static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args)
+{
        if (is_exactly_a<power>(a)) {
                ex p = a.op(0);
                const ex& exp_a = a.op(1);
@@ -1443,113 +1769,8 @@ factored_b:
                        return p_gcd*gcd(apart_co, power(p_gcd, exp_b-1)*power(p_co, exp_b), ca, cb, false);
                } // p_gcd.is_equal(_ex1)
        }
-#endif
-
-       // Some trivial cases
-       ex aex = a.expand(), bex = b.expand();
-       if (aex.is_zero()) {
-               if (ca)
-                       *ca = _ex0;
-               if (cb)
-                       *cb = _ex1;
-               return b;
-       }
-       if (bex.is_zero()) {
-               if (ca)
-                       *ca = _ex1;
-               if (cb)
-                       *cb = _ex0;
-               return a;
-       }
-       if (aex.is_equal(_ex1) || bex.is_equal(_ex1)) {
-               if (ca)
-                       *ca = a;
-               if (cb)
-                       *cb = b;
-               return _ex1;
-       }
-#if FAST_COMPARE
-       if (a.is_equal(b)) {
-               if (ca)
-                       *ca = _ex1;
-               if (cb)
-                       *cb = _ex1;
-               return a;
-       }
-#endif
-
-       // Gather symbol statistics
-       sym_desc_vec sym_stats;
-       get_symbol_stats(a, b, sym_stats);
-
-       // The symbol with least degree is our main variable
-       sym_desc_vec::const_iterator var = sym_stats.begin();
-       const ex &x = var->sym;
-
-       // Cancel trivial common factor
-       int ldeg_a = var->ldeg_a;
-       int ldeg_b = var->ldeg_b;
-       int min_ldeg = std::min(ldeg_a,ldeg_b);
-       if (min_ldeg > 0) {
-               ex common = power(x, min_ldeg);
-               return gcd((aex / common).expand(), (bex / common).expand(), ca, cb, false) * common;
-       }
-
-       // Try to eliminate variables
-       if (var->deg_a == 0) {
-               ex bex_u, bex_c, bex_p;
-               bex.unitcontprim(x, bex_u, bex_c, bex_p);
-               ex g = gcd(aex, bex_c, ca, cb, false);
-               if (cb)
-                       *cb *= bex_u * bex_p;
-               return g;
-       } else if (var->deg_b == 0) {
-               ex aex_u, aex_c, aex_p;
-               aex.unitcontprim(x, aex_u, aex_c, aex_p);
-               ex g = gcd(aex_c, bex, ca, cb, false);
-               if (ca)
-                       *ca *= aex_u * aex_p;
-               return g;
-       }
-
-       // Try heuristic algorithm first, fall back to PRS if that failed
-       ex g;
-       try {
-               g = heur_gcd(aex, bex, ca, cb, var);
-       } catch (gcdheu_failed) {
-               g = fail();
-       }
-       if (is_exactly_a<fail>(g)) {
-#if STATISTICS
-               heur_gcd_failed++;
-#endif
-               g = sr_gcd(aex, bex, var);
-               if (g.is_equal(_ex1)) {
-                       // Keep cofactors factored if possible
-                       if (ca)
-                               *ca = a;
-                       if (cb)
-                               *cb = b;
-               } else {
-                       if (ca)
-                               divide(aex, g, *ca, false);
-                       if (cb)
-                               divide(bex, g, *cb, false);
-               }
-       } else {
-               if (g.is_equal(_ex1)) {
-                       // Keep cofactors factored if possible
-                       if (ca)
-                               *ca = a;
-                       if (cb)
-                               *cb = b;
-               }
-       }
-
-       return g;
 }
 
-
 /** Compute LCM (Least Common Multiple) of multivariate polynomials in Z[X].
  *
  *  @param a  first multivariate polynomial
@@ -2343,6 +2564,17 @@ ex power::to_polynomial(exmap & repl) const
 {
        if (exponent.info(info_flags::posint))
                return power(basis.to_rational(repl), exponent);
+       else if (exponent.info(info_flags::negint))
+       {
+               ex basis_pref = collect_common_factors(basis);
+               if (is_exactly_a<mul>(basis_pref) || is_exactly_a<power>(basis_pref)) {
+                       // (A*B)^n will be automagically transformed to A^n*B^n
+                       ex t = power(basis_pref, exponent);
+                       return t.to_polynomial(repl);
+               }
+               else
+                       return power(replace_with_symbol(power(basis, _ex_1), repl), -exponent);
+       } 
        else
                return replace_with_symbol(*this, repl);
 }
@@ -2400,7 +2632,7 @@ static ex find_common_factor(const ex & e, ex & factor, exmap & repl)
                for (size_t i=0; i<num; i++) {
                        ex x = e.op(i).to_polynomial(repl);
 
-                       if (is_exactly_a<add>(x) || is_exactly_a<mul>(x)) {
+                       if (is_exactly_a<add>(x) || is_exactly_a<mul>(x) || is_a<power>(x)) {
                                ex f = 1;
                                x = find_common_factor(x, f, repl);
                                x *= f;
@@ -2459,8 +2691,16 @@ term_done:       ;
                return (new mul(v))->setflag(status_flags::dynallocated);
 
        } else if (is_exactly_a<power>(e)) {
-
-               return e.to_polynomial(repl);
+               const ex e_exp(e.op(1));
+               if (e_exp.info(info_flags::integer)) {
+                       ex eb = e.op(0).to_polynomial(repl);
+                       ex factor_local(_ex1);
+                       ex pre_res = find_common_factor(eb, factor_local, repl);
+                       factor *= power(factor_local, e_exp);
+                       return power(pre_res, e_exp);
+                       
+               } else
+                       return e.to_polynomial(repl);
 
        } else
                return e;
@@ -2471,7 +2711,7 @@ term_done:        ;
  *  'a*(b*x+b*y)' to 'a*b*(x+y)'. */
 ex collect_common_factors(const ex & e)
 {
-       if (is_exactly_a<add>(e) || is_exactly_a<mul>(e)) {
+       if (is_exactly_a<add>(e) || is_exactly_a<mul>(e) || is_exactly_a<power>(e)) {
 
                exmap repl;
                ex factor = 1;