* computation, square-free factorization and rational function normalization. */
/*
- * GiNaC Copyright (C) 1999-2005 Johannes Gutenberg University Mainz, Germany
+ * GiNaC Copyright (C) 1999-2006 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
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()) {
}
}
+ 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 is our main variable
+ // 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;
}
// Try to eliminate variables
- if (var->deg_a == 0) {
+ 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) {
+ } 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 (exponent.info(info_flags::posint))
return power(basis.to_rational(repl), exponent);
else if (exponent.info(info_flags::negint))
- return power(replace_with_symbol(power(basis, _ex_1), repl), -exponent);
+ {
+ 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);
}
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;
} else if (is_exactly_a<power>(e)) {
const ex e_exp(e.op(1));
- if (e_exp.info(info_flags::posint)) {
+ 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);