#include "numeric.h"
#include "power.h"
#include "relational.h"
+#include "matrix.h"
#include "pseries.h"
#include "symbol.h"
#include "utils.h"
// Add symbol the sym_desc_vec (used internally by get_symbol_stats())
static void add_symbol(const symbol *s, sym_desc_vec &v)
{
- sym_desc_vec::iterator it = v.begin(), itend = v.end();
+ sym_desc_vec::const_iterator it = v.begin(), itend = v.end();
while (it != itend) {
if (it->sym->compare(*s) == 0) // If it's already in there, don't add it a second time
return;
- it++;
+ ++it;
}
sym_desc d;
d.sym = s;
it->max_lcnops = std::max(a.lcoeff(*(it->sym)).nops(), b.lcoeff(*(it->sym)).nops());
it->ldeg_a = a.ldegree(*(it->sym));
it->ldeg_b = b.ldegree(*(it->sym));
- it++;
+ ++it;
}
- sort(v.begin(), v.end());
+ std::sort(v.begin(), v.end());
#if 0
std::clog << "Symbols:\n";
it = v.begin(); itend = v.end();
while (it != itend) {
std::clog << " " << *it->sym << ": deg_a=" << it->deg_a << ", deg_b=" << it->deg_b << ", ldeg_a=" << it->ldeg_a << ", ldeg_b=" << it->ldeg_b << ", max_deg=" << it->max_deg << ", max_lcnops=" << it->max_lcnops << endl;
std::clog << " lcoeff_a=" << a.lcoeff(*(it->sym)) << ", lcoeff_b=" << b.lcoeff(*(it->sym)) << endl;
- it++;
+ ++it;
}
#endif
}
static ex multiply_lcm(const ex &e, const numeric &lcm)
{
if (is_ex_exactly_of_type(e, mul)) {
- ex c = _ex1();
+ unsigned num = e.nops();
+ exvector v; v.reserve(num + 1);
numeric lcm_accum = _num1();
for (unsigned i=0; i<e.nops(); i++) {
numeric op_lcm = lcmcoeff(e.op(i), _num1());
- c *= multiply_lcm(e.op(i), op_lcm);
+ v.push_back(multiply_lcm(e.op(i), op_lcm));
lcm_accum *= op_lcm;
}
- c *= lcm / lcm_accum;
- return c;
+ v.push_back(lcm / lcm_accum);
+ return (new mul(v))->setflag(status_flags::dynallocated);
} else if (is_ex_exactly_of_type(e, add)) {
- ex c = _ex0();
- for (unsigned i=0; i<e.nops(); i++)
- c += multiply_lcm(e.op(i), lcm);
- return c;
+ unsigned num = e.nops();
+ exvector v; v.reserve(num);
+ for (unsigned i=0; i<num; i++)
+ v.push_back(multiply_lcm(e.op(i), lcm));
+ return (new add(v))->setflag(status_flags::dynallocated);
} else if (is_ex_exactly_of_type(e, power)) {
if (is_ex_exactly_of_type(e.op(0), symbol))
return e * lcm;
throw(std::invalid_argument("quo: arguments must be polynomials over the rationals"));
// Polynomial long division
- ex q = _ex0();
ex r = a.expand();
if (r.is_zero())
return r;
int rdeg = r.degree(x);
ex blcoeff = b.expand().coeff(x, bdeg);
bool blcoeff_is_numeric = is_ex_exactly_of_type(blcoeff, numeric);
+ exvector v; v.reserve(rdeg - bdeg + 1);
while (rdeg >= bdeg) {
ex term, rcoeff = r.coeff(x, rdeg);
if (blcoeff_is_numeric)
return (new fail())->setflag(status_flags::dynallocated);
}
term *= power(x, rdeg - bdeg);
- q += term;
+ v.push_back(term);
r -= (term * b).expand();
if (r.is_zero())
break;
rdeg = r.degree(x);
}
- return q;
+ return (new add(v))->setflag(status_flags::dynallocated);
}
if (is_ex_exactly_of_type(b, numeric))
return _ex0();
else
- return b;
+ return a;
}
#if FAST_COMPARE
if (a.is_equal(b))
}
+/** Decompose rational function a(x)=N(x)/D(x) into P(x)+n(x)/D(x)
+ * with degree(n, x) < degree(D, x).
+ *
+ * @param a rational function in x
+ * @param x a is a function of x
+ * @return decomposed function. */
+ex decomp_rational(const ex &a, const symbol &x)
+{
+ ex nd = numer_denom(a);
+ ex numer = nd.op(0), denom = nd.op(1);
+ ex q = quo(numer, denom, x);
+ if (is_ex_exactly_of_type(q, fail))
+ return a;
+ else
+ return q + rem(numer, denom, x) / denom;
+}
+
+
/** Pseudo-remainder of polynomials a(x) and b(x) in Z[x].
*
* @param a first polynomial in x (dividend)
* @param check_args check whether a and b are polynomials with rational
* coefficients (defaults to "true")
* @return sparse pseudo-remainder of a(x) and b(x) in Z[x] */
-
ex sprem(const ex &a, const ex &b, const symbol &x, bool check_args)
{
if (b.is_zero())
int rdeg = r.degree(*x);
ex blcoeff = b.expand().coeff(*x, bdeg);
bool blcoeff_is_numeric = is_ex_exactly_of_type(blcoeff, numeric);
+ exvector v; v.reserve(rdeg - bdeg + 1);
while (rdeg >= bdeg) {
ex term, rcoeff = r.coeff(*x, rdeg);
if (blcoeff_is_numeric)
if (!divide(rcoeff, blcoeff, term, false))
return false;
term *= power(*x, rdeg - bdeg);
- q += term;
+ v.push_back(term);
r -= (term * b).expand();
- if (r.is_zero())
+ if (r.is_zero()) {
+ q = (new add(v))->setflag(status_flags::dynallocated);
return true;
+ }
rdeg = r.degree(*x);
}
return false;
int rdeg = adeg;
ex eb = b.expand();
ex blcoeff = eb.coeff(*x, bdeg);
+ exvector v; v.reserve(rdeg - bdeg + 1);
while (rdeg >= bdeg) {
ex term, rcoeff = r.coeff(*x, rdeg);
if (!divide_in_z(rcoeff, blcoeff, term, var+1))
break;
term = (term * power(*x, rdeg - bdeg)).expand();
- q += term;
+ v.push_back(term);
r -= (term * eb).expand();
if (r.is_zero()) {
+ q = (new add(v))->setflag(status_flags::dynallocated);
#if USE_REMEMBER
dr_remember[ex2(a, b)] = exbool(q, true);
#endif
/** xi-adic polynomial interpolation */
-static ex interpolate(const ex &gamma, const numeric &xi, const symbol &x)
+static ex interpolate(const ex &gamma, const numeric &xi, const symbol &x, int degree_hint = 1)
{
- ex g = _ex0();
+ exvector g; g.reserve(degree_hint);
ex e = gamma;
numeric rxi = xi.inverse();
for (int i=0; !e.is_zero(); i++) {
ex gi = e.smod(xi);
- g += gi * power(x, i);
+ g.push_back(gi * power(x, i));
e = (e - gi) * rxi;
}
- return g;
+ return (new add(g))->setflag(status_flags::dynallocated);
}
/** Exception thrown by heur_gcd() to signal failure. */
heur_gcd_called++;
#endif
- // Algorithms only works for non-vanishing input polynomials
+ // Algorithm only works for non-vanishing input polynomials
if (a.is_zero() || b.is_zero())
return (new fail())->setflag(status_flags::dynallocated);
numeric rgc = gc.inverse();
ex p = a * rgc;
ex q = b * rgc;
- int maxdeg = std::max(p.degree(x),q.degree(x));
+ int maxdeg = std::max(p.degree(x), q.degree(x));
// Find evaluation point
numeric mp = p.max_coefficient();
if (!is_ex_exactly_of_type(gamma, fail)) {
// Reconstruct polynomial from GCD of mapped polynomials
- ex g = interpolate(gamma, xi, x);
+ ex g = interpolate(gamma, xi, x, maxdeg);
// Remove integer content
g /= g.integer_content();
if (is_ex_exactly_of_type(b, mul) && b.nops() > a.nops())
goto factored_b;
factored_a:
- ex g = _ex1();
- ex acc_ca = _ex1();
+ unsigned num = a.nops();
+ exvector g; g.reserve(num);
+ exvector acc_ca; acc_ca.reserve(num);
ex part_b = b;
- for (unsigned i=0; i<a.nops(); i++) {
+ for (unsigned i=0; i<num; i++) {
ex part_ca, part_cb;
- g *= gcd(a.op(i), part_b, &part_ca, &part_cb, check_args);
- acc_ca *= part_ca;
+ g.push_back(gcd(a.op(i), part_b, &part_ca, &part_cb, check_args));
+ acc_ca.push_back(part_ca);
part_b = part_cb;
}
if (ca)
- *ca = acc_ca;
+ *ca = (new mul(acc_ca))->setflag(status_flags::dynallocated);
if (cb)
*cb = part_b;
- return g;
+ return (new mul(g))->setflag(status_flags::dynallocated);
} else if (is_ex_exactly_of_type(b, mul)) {
if (is_ex_exactly_of_type(a, mul) && a.nops() > b.nops())
goto factored_a;
factored_b:
- ex g = _ex1();
- ex acc_cb = _ex1();
+ unsigned num = b.nops();
+ exvector g; g.reserve(num);
+ exvector acc_cb; acc_cb.reserve(num);
ex part_a = a;
- for (unsigned i=0; i<b.nops(); i++) {
+ for (unsigned i=0; i<num; i++) {
ex part_ca, part_cb;
- g *= gcd(part_a, b.op(i), &part_ca, &part_cb, check_args);
- acc_cb *= part_cb;
+ g.push_back(gcd(part_a, b.op(i), &part_ca, &part_cb, check_args));
+ acc_cb.push_back(part_cb);
part_a = part_ca;
}
if (ca)
*ca = part_a;
if (cb)
- *cb = acc_cb;
- return g;
+ *cb = (new mul(acc_cb))->setflag(status_flags::dynallocated);
+ return (new mul(g))->setflag(status_flags::dynallocated);
}
#if FAST_COMPARE
} while (!z.is_zero());
return res;
}
+
/** Compute square-free factorization of multivariate polynomial in Q[X].
*
* @param a multivariate polynomial over Q[X]
if (is_ex_of_type(a,numeric) || // algorithm does not trap a==0
is_ex_of_type(a,symbol)) // shortcut
return a;
+
// If no lst of variables to factorize in was specified we have to
// invent one now. Maybe one can optimize here by reversing the order
// or so, I don't know.
if (l.nops()==0) {
sym_desc_vec sdv;
get_symbol_stats(a, _ex0(), sdv);
- for (sym_desc_vec::iterator it=sdv.begin(); it!=sdv.end(); ++it)
+ sym_desc_vec::const_iterator it = sdv.begin(), itend = sdv.end();
+ while (it != itend) {
args.append(*it->sym);
+ ++it;
+ }
} else {
args = l;
}
+
// Find the symbol to factor in at this stage
if (!is_ex_of_type(args.op(0), symbol))
throw (std::runtime_error("sqrfree(): invalid factorization variable"));
const symbol x = ex_to<symbol>(args.op(0));
+
// convert the argument from something in Q[X] to something in Z[X]
numeric lcm = lcm_of_coefficients_denominators(a);
ex tmp = multiply_lcm(a,lcm);
+
// find the factors
exvector factors = sqrfree_yun(tmp,x);
+
// construct the next list of symbols with the first element popped
- lst newargs;
- for (int i=1; i<args.nops(); ++i)
- newargs.append(args.op(i));
+ lst newargs = args;
+ newargs.remove_first();
+
// recurse down the factors in remaining vars
if (newargs.nops()>0) {
- for (exvector::iterator i=factors.begin(); i!=factors.end(); ++i)
+ exvector::iterator i = factors.begin(), end = factors.end();
+ while (i != end) {
*i = sqrfree(*i, newargs);
+ ++i;
+ }
}
+
// Done with recursion, now construct the final result
ex result = _ex1();
- exvector::iterator it = factors.begin();
- for (int p = 1; it!=factors.end(); ++it, ++p)
+ exvector::const_iterator it = factors.begin(), itend = factors.end();
+ for (int p = 1; it!=itend; ++it, ++p)
result *= power(*it, p);
+
// Yun's algorithm does not account for constant factors. (For
// univariate polynomials it works only in the monic case.) We can
// correct this by inserting what has been lost back into the result:
return result * lcm.inverse();
}
+/** Compute square-free partial fraction decomposition of rational function
+ * a(x).
+ *
+ * @param a rational function over Z[x], treated as univariate polynomial
+ * in x
+ * @param x variable to factor in
+ * @return decomposed rational function */
+ex sqrfree_parfrac(const ex & a, const symbol & x)
+{
+ // Find numerator and denominator
+ ex nd = numer_denom(a);
+ ex numer = nd.op(0), denom = nd.op(1);
+//clog << "numer = " << numer << ", denom = " << denom << endl;
+
+ // Convert N(x)/D(x) -> Q(x) + R(x)/D(x), so degree(R) < degree(D)
+ ex red_poly = quo(numer, denom, x), red_numer = rem(numer, denom, x).expand();
+//clog << "red_poly = " << red_poly << ", red_numer = " << red_numer << endl;
+
+ // Factorize denominator and compute cofactors
+ exvector yun = sqrfree_yun(denom, x);
+//clog << "yun factors: " << exprseq(yun) << endl;
+ unsigned num_yun = yun.size();
+ exvector factor; factor.reserve(num_yun);
+ exvector cofac; cofac.reserve(num_yun);
+ for (unsigned i=0; i<num_yun; i++) {
+ if (!yun[i].is_equal(_ex1())) {
+ for (unsigned j=0; j<=i; j++) {
+ factor.push_back(pow(yun[i], j+1));
+ ex prod = _ex1();
+ for (unsigned k=0; k<num_yun; k++) {
+ if (k == i)
+ prod *= pow(yun[k], i-j);
+ else
+ prod *= pow(yun[k], k+1);
+ }
+ cofac.push_back(prod.expand());
+ }
+ }
+ }
+ unsigned num_factors = factor.size();
+//clog << "factors : " << exprseq(factor) << endl;
+//clog << "cofactors: " << exprseq(cofac) << endl;
+
+ // Construct coefficient matrix for decomposition
+ int max_denom_deg = denom.degree(x);
+ matrix sys(max_denom_deg + 1, num_factors);
+ matrix rhs(max_denom_deg + 1, 1);
+ for (int i=0; i<=max_denom_deg; i++) {
+ for (unsigned j=0; j<num_factors; j++)
+ sys(i, j) = cofac[j].coeff(x, i);
+ rhs(i, 0) = red_numer.coeff(x, i);
+ }
+//clog << "coeffs: " << sys << endl;
+//clog << "rhs : " << rhs << endl;
+
+ // Solve resulting linear system
+ matrix vars(num_factors, 1);
+ for (unsigned i=0; i<num_factors; i++)
+ vars(i, 0) = symbol();
+ matrix sol = sys.solve(vars, rhs);
+
+ // Sum up decomposed fractions
+ ex sum = 0;
+ for (unsigned i=0; i<num_factors; i++)
+ sum += sol(i, 0) / factor[i];
+
+ return red_poly + sum;
+}
+
/*
* Normal form of rational functions
* the information that (a+b) is the numerator and 3 is the denominator.
*/
+
/** Create a symbol for replacing the expression "e" (or return a previously
* assigned symbol). The symbol is appended to sym_lst and returned, the
* expression is appended to repl_lst.
return es;
}
-/** Default implementation of ex::normal(). It replaces the object with a
- * temporary symbol.
+
+/** Function object to be applied by basic::normal(). */
+struct normal_map_function : public map_function {
+ int level;
+ normal_map_function(int l) : level(l) {}
+ ex operator()(const ex & e) { return normal(e, level); }
+};
+
+/** Default implementation of ex::normal(). It normalizes the children and
+ * replaces the object with a temporary symbol.
* @see ex::normal */
ex basic::normal(lst &sym_lst, lst &repl_lst, int level) const
{
- return (new lst(replace_with_symbol(*this, sym_lst, repl_lst), _ex1()))->setflag(status_flags::dynallocated);
+ if (nops() == 0)
+ return (new lst(replace_with_symbol(*this, sym_lst, repl_lst), _ex1()))->setflag(status_flags::dynallocated);
+ else {
+ if (level == 1)
+ return (new lst(replace_with_symbol(*this, sym_lst, repl_lst), _ex1()))->setflag(status_flags::dynallocated);
+ else if (level == -max_recursion_level)
+ throw(std::runtime_error("max recursion level reached"));
+ else {
+ normal_map_function map_normal(level - 1);
+ return (new lst(replace_with_symbol(map(map_normal), sym_lst, repl_lst), _ex1()))->setflag(status_flags::dynallocated);
+ }
+ }
}
throw(std::runtime_error("max recursion level reached"));
// Normalize children, separate into numerator and denominator
- ex num = _ex1();
- ex den = _ex1();
+ exvector num; num.reserve(seq.size());
+ exvector den; den.reserve(seq.size());
ex n;
epvector::const_iterator it = seq.begin(), itend = seq.end();
while (it != itend) {
n = recombine_pair_to_ex(*it).bp->normal(sym_lst, repl_lst, level-1);
- num *= n.op(0);
- den *= n.op(1);
+ num.push_back(n.op(0));
+ den.push_back(n.op(1));
it++;
}
n = overall_coeff.bp->normal(sym_lst, repl_lst, level-1);
- num *= n.op(0);
- den *= n.op(1);
+ num.push_back(n.op(0));
+ den.push_back(n.op(1));
// Perform fraction cancellation
- return frac_cancel(num, den);
+ return frac_cancel((new mul(num))->setflag(status_flags::dynallocated),
+ (new mul(den))->setflag(status_flags::dynallocated));
}
ex pseries::normal(lst &sym_lst, lst &repl_lst, int level) const
{
epvector newseq;
- for (epvector::const_iterator i=seq.begin(); i!=seq.end(); ++i) {
+ epvector::const_iterator i = seq.begin(), end = seq.end();
+ while (i != end) {
ex restexp = i->rest.normal();
if (!restexp.is_zero())
newseq.push_back(expair(restexp, i->coeff));
+ ++i;
}
ex n = pseries(relational(var,point), newseq);
return (new lst(replace_with_symbol(n, sym_lst, repl_lst), _ex1()))->setflag(status_flags::dynallocated);
}
-/** Implementation of ex::normal() for relationals. It normalizes both sides.
- * @see ex::normal */
-ex relational::normal(lst &sym_lst, lst &repl_lst, int level) const
-{
- return (new lst(relational(lh.normal(), rh.normal(), o), _ex1()))->setflag(status_flags::dynallocated);
-}
-
-
/** Normalization of rational functions.
* This function converts an expression to its normal form
* "numerator/denominator", where numerator and denominator are (relatively
{
epvector s;
s.reserve(seq.size());
- for (epvector::const_iterator it=seq.begin(); it!=seq.end(); ++it) {
- s.push_back(split_ex_to_pair(recombine_pair_to_ex(*it).to_rational(repl_lst)));
- // s.push_back(combine_ex_with_coeff_to_pair((*it).rest.to_rational(repl_lst),
+ epvector::const_iterator i = seq.begin(), end = seq.end();
+ while (i != end) {
+ s.push_back(split_ex_to_pair(recombine_pair_to_ex(*i).to_rational(repl_lst)));
+ ++i;
}
ex oc = overall_coeff.to_rational(repl_lst);
if (oc.info(info_flags::numeric))
return thisexpairseq(s, overall_coeff);
- else s.push_back(combine_ex_with_coeff_to_pair(oc,_ex1()));
+ else
+ s.push_back(combine_ex_with_coeff_to_pair(oc, _ex1()));
return thisexpairseq(s, default_overall_coeff());
}