[GiNaC-devel] [PATCH] Implement modular multivariate GCD (based on chinese remaindering algorithm).

Alexei Sheplyakov varg at metalica.kh.ua
Mon Jan 19 07:45:38 CET 2009


Both algorithm and implementation are a bit inefficient:

* algorithm does not make use of sparseness of inputs (and most of
  multivariate polynomials are quite sparse)
* GiNaC's expressions are essentially immutable. Thus some simple
  operations (i.e. multiplying a polynomial by an element of the base ring)
  are prohibitively expensive.
* All numbers (i.e. GiNaC::numeric objects) are heap allocated.

Still it's much faster (~5x on bivariate polynomials, ~100x on 3-variate
ones) than (subresultant) PRS algorithm, so gcd() uses modular algorithm
by default.

---
 ginac/Makefile.am                        |   18 ++++
 ginac/normal.cpp                         |   10 ++-
 ginac/normal.h                           |   17 +++-
 ginac/polynomial/chinrem_gcd.cpp         |   14 +++
 ginac/polynomial/chinrem_gcd.h           |   19 ++++
 ginac/polynomial/collect_vargs.cpp       |  164 ++++++++++++++++++++++++++++++
 ginac/polynomial/collect_vargs.h         |   34 ++++++
 ginac/polynomial/debug.hpp               |    1 +
 ginac/polynomial/divide_in_z_p.cpp       |   88 ++++++++++++++++
 ginac/polynomial/divide_in_z_p.h         |   27 +++++
 ginac/polynomial/euclid_gcd_wrap.h       |   56 ++++++++++
 ginac/polynomial/eval_point_finder.h     |   51 +++++++++
 ginac/polynomial/mgcd.cpp                |   96 +++++++++++++++++
 ginac/polynomial/newton_interpolate.h    |   36 +++++++
 ginac/polynomial/optimal_vars_finder.cpp |  134 ++++++++++++++++++++++++
 ginac/polynomial/optimal_vars_finder.h   |   20 ++++
 ginac/polynomial/pgcd.cpp                |  136 +++++++++++++++++++++++++
 ginac/polynomial/pgcd.h                  |   27 +++++
 ginac/polynomial/poly_cra.h              |   38 +++++++
 ginac/polynomial/primes_factory.h        |   60 +++++++++++
 ginac/polynomial/primpart_content.cpp    |   78 ++++++++++++++
 ginac/polynomial/smod_helpers.h          |   72 +++++++++++++
 22 files changed, 1192 insertions(+), 4 deletions(-)
 create mode 100644 ginac/polynomial/chinrem_gcd.cpp
 create mode 100644 ginac/polynomial/chinrem_gcd.h
 create mode 100644 ginac/polynomial/collect_vargs.cpp
 create mode 100644 ginac/polynomial/collect_vargs.h
 create mode 100644 ginac/polynomial/divide_in_z_p.cpp
 create mode 100644 ginac/polynomial/divide_in_z_p.h
 create mode 100644 ginac/polynomial/euclid_gcd_wrap.h
 create mode 100644 ginac/polynomial/eval_point_finder.h
 create mode 100644 ginac/polynomial/mgcd.cpp
 create mode 100644 ginac/polynomial/newton_interpolate.h
 create mode 100644 ginac/polynomial/optimal_vars_finder.cpp
 create mode 100644 ginac/polynomial/optimal_vars_finder.h
 create mode 100644 ginac/polynomial/pgcd.cpp
 create mode 100644 ginac/polynomial/pgcd.h
 create mode 100644 ginac/polynomial/poly_cra.h
 create mode 100644 ginac/polynomial/primes_factory.h
 create mode 100644 ginac/polynomial/primpart_content.cpp
 create mode 100644 ginac/polynomial/smod_helpers.h

diff --git a/ginac/Makefile.am b/ginac/Makefile.am
index 7ab16d6..76e32b2 100644
--- a/ginac/Makefile.am
+++ b/ginac/Makefile.am
@@ -35,6 +35,24 @@ polynomial/interpolate_padic_uvar.h \
 polynomial/sr_gcd_uvar.h \
 polynomial/heur_gcd_uvar.h \
 polynomial/gcd_uvar.cpp \
+polynomial/chinrem_gcd.cpp \
+polynomial/chinrem_gcd.h \
+polynomial/collect_vargs.cpp \
+polynomial/collect_vargs.h \
+polynomial/divide_in_z_p.cpp \
+polynomial/divide_in_z_p.h \
+polynomial/euclid_gcd_wrap.h \
+polynomial/eval_point_finder.h \
+polynomial/mgcd.cpp \
+polynomial/newton_interpolate.h \
+polynomial/optimal_vars_finder.cpp \
+polynomial/optimal_vars_finder.h \
+polynomial/pgcd.cpp \
+polynomial/pgcd.h \
+polynomial/poly_cra.h \
+polynomial/primes_factory.h \
+polynomial/primpart_content.cpp \
+polynomial/smod_helpers.h \
 polynomial/debug.hpp
 
 libginac_la_LDFLAGS = -version-info $(LT_VERSION_INFO) -release $(LT_RELEASE)
diff --git a/ginac/normal.cpp b/ginac/normal.cpp
index 8f5ba73..54fff47 100644
--- a/ginac/normal.cpp
+++ b/ginac/normal.cpp
@@ -44,6 +44,7 @@
 #include "pseries.h"
 #include "symbol.h"
 #include "utils.h"
+#include "polynomial/chinrem_gcd.h"
 
 namespace GiNaC {
 
@@ -1623,8 +1624,15 @@ ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned optio
 		}
 #endif
 	}
+	if (options & gcd_options::use_sr_gcd) {
+		g = sr_gcd(aex, bex, var);
+	} else {
+		exvector vars;
+		for (std::size_t n = sym_stats.size(); n-- != 0; )
+			vars.push_back(sym_stats[n].sym);
+		g = chinrem_gcd(aex, bex, vars);
+	}
 
-	g = sr_gcd(aex, bex, var);
 	if (g.is_equal(_ex1)) {
 		// Keep cofactors factored if possible
 		if (ca)
diff --git a/ginac/normal.h b/ginac/normal.h
index 5a735e3..a352be8 100644
--- a/ginac/normal.h
+++ b/ginac/normal.h
@@ -37,8 +37,12 @@ struct gcd_options
 {
 	enum {
 		/**
-		 * Usually GiNaC tries heuristic GCD algorithm before PRS.
-		 * Some people don't like this, so here's a flag to disable it.
+		 * Usually GiNaC tries heuristic GCD first, because typically
+		 * it's much faster than anything else. Even if heuristic
+		 * algorithm fails, the overhead is negligible w.r.t. cost
+		 * of computing the GCD by some other method. However, some
+		 * people dislike it, so here's a flag which tells GiNaC
+		 * to NOT use the heuristic algorithm.
 		 */
 		no_heur_gcd = 2,
 		/**
@@ -48,7 +52,14 @@ struct gcd_options
 		 * factored polynomials. DON'T SET THIS unless you *really*
 		 * know what are you doing!
 		 */
-		no_part_factored = 4
+		no_part_factored = 4,
+		/**
+		 * By default GiNaC uses modular GCD algorithm. Typically
+		 * it's much faster than PRS (pseudo remainder sequence)
+		 * algorithm. This flag forces GiNaC to use PRS algorithm
+		 */
+		use_sr_gcd = 8
+
 	};
 };
 
diff --git a/ginac/polynomial/chinrem_gcd.cpp b/ginac/polynomial/chinrem_gcd.cpp
new file mode 100644
index 0000000..4048ee0
--- /dev/null
+++ b/ginac/polynomial/chinrem_gcd.cpp
@@ -0,0 +1,14 @@
+#include "chinrem_gcd.h"
+#include "optimal_vars_finder.h"
+
+namespace GiNaC
+{
+
+ex chinrem_gcd(const ex& A, const ex& B)
+{
+	const exvector vars = gcd_optimal_variables_order(A, B);
+	ex g = chinrem_gcd(A, B, vars);
+	return g;
+}
+} // namespace GiNaC
+
diff --git a/ginac/polynomial/chinrem_gcd.h b/ginac/polynomial/chinrem_gcd.h
new file mode 100644
index 0000000..6c58a9f
--- /dev/null
+++ b/ginac/polynomial/chinrem_gcd.h
@@ -0,0 +1,19 @@
+#ifndef GINAC_CHINREM_GCD_H
+#define GINAC_CHINREM_GCD_H
+#include "ex.h"
+
+namespace GiNaC
+{
+
+extern ex chinrem_gcd(const ex& A_, const ex& B_, const exvector& vars);
+extern ex chinrem_gcd(const ex& A, const ex& B);
+
+struct chinrem_gcd_failed
+{
+	virtual ~chinrem_gcd_failed() { }
+};
+
+}
+
+#endif /* GINAC_CHINREM_GCD_H */
+
diff --git a/ginac/polynomial/collect_vargs.cpp b/ginac/polynomial/collect_vargs.cpp
new file mode 100644
index 0000000..5649e4a
--- /dev/null
+++ b/ginac/polynomial/collect_vargs.cpp
@@ -0,0 +1,164 @@
+#include <iterator>
+#include <map>
+#include <algorithm>
+#include <stdexcept>
+#include <string>
+#include <ginac/ginac.h>
+#include "collect_vargs.h"
+#include <cln/integer.h>
+#include "smod_helpers.h"
+#include "debug.hpp"
+
+namespace GiNaC
+{
+
+typedef std::map<exp_vector_t, ex> ex_collect_priv_t;
+
+static void 
+collect_vargs(ex_collect_priv_t& ec, ex e, const exvector& vars);
+static void
+collect_term(ex_collect_priv_t& ec, const ex& e, const exvector& vars);
+static void wipe_out_zeros(ex_collect_priv_t& ec);
+
+template<typename T, typename CoeffCMP>
+struct compare_terms
+{
+	const CoeffCMP& coeff_cmp;
+	explicit compare_terms(const CoeffCMP& coeff_cmp_) : coeff_cmp(coeff_cmp_)
+	{ }
+	inline bool operator()(const T& t1, const T& t2) const
+	{
+		bool exponent_is_less =
+			std::lexicographical_compare(t1.first.rbegin(),
+						     t1.first.rend(),
+						     t2.first.rbegin(),
+						     t2.first.rend());
+		if (exponent_is_less)
+			return true;
+
+		if ((t1.first == t2.first) &&
+				coeff_cmp(t2.second, t2.second))
+			return true;
+		return false;
+	}
+};
+
+template<typename T, typename CoeffCMP>
+static struct compare_terms<T, CoeffCMP>
+make_compare_terms(const T& dummy, const CoeffCMP& coeff_cmp)
+{
+	return compare_terms<T, CoeffCMP>(coeff_cmp);
+}
+
+void collect_vargs(ex_collect_t& ec, const ex& e, const exvector& vars)
+{
+	ex_collect_priv_t ecp;
+	collect_vargs(ecp, e, vars);
+	ec.reserve(ecp.size());
+	std::copy(ecp.begin(), ecp.end(), std::back_inserter(ec));
+	std::sort(ec.begin(), ec.end(),
+		  make_compare_terms(*ec.begin(), ex_is_less()));
+}
+
+static void 
+collect_vargs(ex_collect_priv_t& ec, ex e, const exvector& vars)
+{
+	e = e.expand();
+	if (e.is_zero()) {
+		ec.clear();
+		return;
+	}
+
+	if (!is_a<add>(e)) {
+		collect_term(ec, e, vars);
+		return;
+	}
+
+	for (const_iterator i = e.begin(); i != e.end(); ++i)
+		collect_term(ec, *i, vars);
+
+	wipe_out_zeros(ec);
+}
+
+static void
+collect_term(ex_collect_priv_t& ec, const ex& e, const exvector& vars)
+{
+	if (e.is_zero())
+		return;
+	static const ex ex1(1);
+	exp_vector_t key(vars.size());
+	ex pre_coeff = e;
+	for (std::size_t i = 0; i < vars.size(); ++i) {
+		const int var_i_pow = pre_coeff.degree(vars[i]);
+		key[i] = var_i_pow;
+		pre_coeff = pre_coeff.coeff(vars[i], var_i_pow);
+	}
+	ex_collect_priv_t::iterator i = ec.find(key);
+	if (i != ec.end())
+		i->second += pre_coeff;
+	else
+		ec.insert(ex_collect_priv_t::value_type(key, pre_coeff));
+}
+
+static void wipe_out_zeros(ex_collect_priv_t& m)
+{
+	ex_collect_priv_t::iterator i = m.begin();
+	while (i != m.end()) {
+		// be careful to not invalide iterator, use post-increment
+		// for that, see e.g.
+		// http://coding.derkeiler.com/Archive/C_CPP/comp.lang.cpp/2004-02/0502.html
+		if (i->second.is_zero())
+			m.erase(i++);
+		else
+			++i;
+	}
+}
+
+GiNaC::ex
+ex_collect_to_ex(const ex_collect_t& ec, const GiNaC::exvector& vars)
+{
+	exvector ev;
+	ev.reserve(ec.size());
+	for (std::size_t i = 0; i < ec.size(); ++i) {
+		exvector tv;
+		tv.reserve(vars.size() + 1);
+		for (std::size_t j = 0; j < vars.size(); ++j) {
+			if (ec[i].first[j] != 0)
+				tv.push_back(power(vars[j], ec[i].first[j]));
+		}
+		tv.push_back(ec[i].second);
+		ex tmp = (new mul(tv))->setflag(status_flags::dynallocated);
+		ev.push_back(tmp);
+	}
+	ex ret = (new add(ev))->setflag(status_flags::dynallocated);
+	return ret;
+}
+
+ex lcoeff_wrt(ex e, const exvector& x)
+{
+	static const ex ex0(0);
+	e = e.expand();
+	if (e.is_zero())
+		return ex0;
+
+	ex_collect_t ec;
+	collect_vargs(ec, e, x);
+	return ec.rbegin()->second;
+}
+
+cln::cl_I integer_lcoeff(const ex& e, const exvector& vars)
+{
+	ex_collect_t ec;
+	collect_vargs(ec, e, vars);
+	if (ec.size() == 0)
+		return cln::cl_I(0);
+	ex lc = ec.rbegin()->second;
+	bug_on(!is_a<numeric>(lc), "leading coefficient is not an integer");
+	bug_on(!lc.info(info_flags::integer),
+		"leading coefficient is not an integer");
+
+	return to_cl_I(lc);
+}
+
+} // namespace GiNaC
+
diff --git a/ginac/polynomial/collect_vargs.h b/ginac/polynomial/collect_vargs.h
new file mode 100644
index 0000000..4dd16ba
--- /dev/null
+++ b/ginac/polynomial/collect_vargs.h
@@ -0,0 +1,34 @@
+#ifndef GINAC_COLLECT_VARGS_HPP
+#define GINAC_COLLECT_VARGS_HPP
+#include "ex.h"
+#include <cln/integer.h>
+#include <vector>
+#include <utility> // std::pair
+
+namespace GiNaC
+{
+
+typedef std::vector<int> exp_vector_t;
+typedef std::vector<std::pair<exp_vector_t, ex> > ex_collect_t;
+
+extern void
+collect_vargs(ex_collect_t& ec, const ex& e, const exvector& x);
+extern ex
+ex_collect_to_ex(const ex_collect_t& ec, const exvector& x);
+
+/**
+ * Leading coefficient of a multivariate polynomial e, considering it
+ * as a multivariate polynomial in x_0, \ldots x_{n-1} with coefficients
+ * being univariate polynomials in R[x_n] (where R is some ring)
+ */
+extern ex lcoeff_wrt(ex e, const exvector& x);
+
+/**
+ * Leading coefficient c \in R (where R = Z or Z_p) of a multivariate
+ * polynomial e \in R[x_0, \ldots, x_n]
+ */
+extern cln::cl_I integer_lcoeff(const ex& e, const exvector& vars);
+
+} // namespace GiNaC
+
+#endif /* GINAC_COLLECT_VARGS_HPP */
diff --git a/ginac/polynomial/debug.hpp b/ginac/polynomial/debug.hpp
index 901ef87..48659fd 100644
--- a/ginac/polynomial/debug.hpp
+++ b/ginac/polynomial/debug.hpp
@@ -4,6 +4,7 @@
 #include <string>
 #include <stdexcept>
 #include <sstream>
+#include "compiler.h"
 
 #define DEBUG_PREFIX __func__ << ':' << __LINE__ << ": "
 #define EXCEPTION_PREFIX std::string(__func__) + std::string(": ") +
diff --git a/ginac/polynomial/divide_in_z_p.cpp b/ginac/polynomial/divide_in_z_p.cpp
new file mode 100644
index 0000000..dff30a9
--- /dev/null
+++ b/ginac/polynomial/divide_in_z_p.cpp
@@ -0,0 +1,88 @@
+#include <ginac/ginac.h>
+#include "smod_helpers.h"
+
+namespace GiNaC
+{
+/** 
+ * Exact polynomial division of a, b \in Z_p[x_0, \ldots, x_n]
+ * It doesn't check whether the inputs are proper polynomials, so be careful
+ * of what you pass in.
+ *  
+ * @param a  first multivariate polynomial (dividend)
+ * @param b  second multivariate polynomial (divisor)
+ * @param q  quotient (returned)
+ * @param var variables X iterator to first element of vector of symbols
+ *
+ * @return "true" when exact division succeeds (the quotient is returned in
+ *          q), "false" otherwise.
+ * @note @a p = 0 means the base ring is Z
+ */
+bool divide_in_z_p(const ex &a, const ex &b, ex &q, const exvector& vars, const long p)
+{
+	static const ex _ex1(1);
+	if (b.is_zero())
+		throw(std::overflow_error("divide_in_z: division by zero"));
+	if (b.is_equal(_ex1)) {
+		q = a;
+		return true;
+	}
+	if (is_exactly_a<numeric>(a)) {
+		if (is_exactly_a<numeric>(b)) {
+			// p == 0 means division in Z
+			if (p == 0) {
+				const numeric tmp = ex_to<numeric>(a/b);
+				if (tmp.is_integer()) {
+					q = tmp;
+					return true;
+				} else
+					return false;
+			} else {
+				q = (a*recip(ex_to<numeric>(b), p)).smod(numeric(p));
+				return true;
+			}
+		} else
+			return false;
+	}
+	if (a.is_equal(b)) {
+		q = _ex1;
+		return true;
+	}
+
+	// Main symbol
+	const ex &x = vars.back();
+
+	// Compare degrees
+	int adeg = a.degree(x), bdeg = b.degree(x);
+	if (bdeg > adeg)
+		return false;
+
+	// Polynomial long division (recursive)
+	ex r = a.expand();
+	if (r.is_zero())
+		return true;
+	int rdeg = adeg;
+	ex eb = b.expand();
+	ex blcoeff = eb.coeff(x, bdeg);
+	exvector v;
+	v.reserve(std::max(rdeg - bdeg + 1, 0));
+	exvector rest_vars(vars);
+	rest_vars.pop_back();
+	while (rdeg >= bdeg) {
+		ex term, rcoeff = r.coeff(x, rdeg);
+		if (!divide_in_z_p(rcoeff, blcoeff, term, rest_vars, p))
+			break;
+		term = (term*power(x, rdeg - bdeg)).expand();
+		v.push_back(term);
+		r = (r - term*eb).expand();
+		if (p != 0)
+			r = r.smod(numeric(p));
+		if (r.is_zero()) {
+			q = (new add(v))->setflag(status_flags::dynallocated);
+			return true;
+		}
+		rdeg = r.degree(x);
+	}
+	return false;
+}
+
+}
diff --git a/ginac/polynomial/divide_in_z_p.h b/ginac/polynomial/divide_in_z_p.h
new file mode 100644
index 0000000..f790fcd
--- /dev/null
+++ b/ginac/polynomial/divide_in_z_p.h
@@ -0,0 +1,27 @@
+#ifndef GINAC_CHINREM_GCD_DIVIDE_IN_Z_P_H
+#define GINAC_CHINREM_GCD_DIVIDE_IN_Z_P_H
+
+#include <ginac/ginac.h>
+namespace GiNaC
+{
+
+/** 
+ * Exact polynomial division of a, b \in Z_p[x_0, \ldots, x_n]
+ * It doesn't check whether the inputs are proper polynomials, so be careful
+ * of what you pass in.
+ *  
+ * @param a  first multivariate polynomial (dividend)
+ * @param b  second multivariate polynomial (divisor)
+ * @param q  quotient (returned)
+ * @param var variables X iterator to first element of vector of symbols
+ *
+ * @return "true" when exact division succeeds (the quotient is returned in
+ *          q), "false" otherwise.
+ */
+extern bool
+divide_in_z_p(const ex &a, const ex &b, ex &q, const exvector& vars, const long p);
+
+} // namespace GiNaC
+
+#endif /* GINAC_CHINREM_GCD_DIVIDE_IN_Z_P_H */
+
diff --git a/ginac/polynomial/euclid_gcd_wrap.h b/ginac/polynomial/euclid_gcd_wrap.h
new file mode 100644
index 0000000..d77f71c
--- /dev/null
+++ b/ginac/polynomial/euclid_gcd_wrap.h
@@ -0,0 +1,56 @@
+#ifndef GINAC_PGCD_EUCLID_GCD_H
+#define GINAC_PGCD_EUCLID_GCD_H
+#include "upoly.hpp"
+#include "gcd_euclid.tcc"
+#include "smod_helpers.h"
+#include <ginac/ginac.h>
+#include "debug.hpp"
+
+namespace GiNaC
+{
+
+static void ex2upoly(umodpoly& u, ex e, const ex& var, const long p)
+{
+	e = e.expand();
+	cln::cl_modint_ring R = cln::find_modint_ring(cln::cl_I(p));
+	u.resize(e.degree(var) + 1);
+	for (int i = 0; i <= e.degree(var); ++i) {
+		ex ce = e.coeff(var, i);
+		bug_on(!is_a<numeric>(ce), "i = " << i << ", " <<
+			"coefficient is not a number: " << ce);
+		const cln::cl_I c = to_cl_I(ce);
+		u[i] = R->canonhom(c);
+	}
+}
+
+static ex umodpoly2ex(const umodpoly& a, const ex& var, const long p)
+{
+	cln::cl_modint_ring R = cln::find_modint_ring(cln::cl_I(p));
+	const numeric pnum(p);
+	exvector ev(a.size());
+	for (std::size_t i = a.size(); i-- != 0; ) {
+		const cln::cl_I c = smod(R->retract(a[i]), p);
+		const ex term = numeric(c)*power(var, i);
+		ev.push_back(term);
+	}
+	ex ret = (new add(ev))->setflag(status_flags::dynallocated);
+	return ret;
+}
+	
+static ex euclid_gcd(ex A, ex B, const ex& var, const long p)
+{
+	A = A.expand();
+	B = B.expand();
+
+	umodpoly a, b;
+	ex2upoly(a, A, var, p);
+	ex2upoly(b, B, var, p);
+	umodpoly g;
+	gcd_euclid(g, a, b);
+	ex ge = umodpoly2ex(g, var, p);
+	return ge;
+}
+
+} // namespace GiNaC
+
+#endif
diff --git a/ginac/polynomial/eval_point_finder.h b/ginac/polynomial/eval_point_finder.h
new file mode 100644
index 0000000..8ceadd1
--- /dev/null
+++ b/ginac/polynomial/eval_point_finder.h
@@ -0,0 +1,51 @@
+#ifndef GINAC_PGCD_EVAL_POINT_FINDER_H
+#define GINAC_PGCD_EVAL_POINT_FINDER_H
+#include <ginac/ginac.h>
+#include <set>
+
+namespace GiNaC
+{
+
+/// Find a `good' evaluation point b \in Z_p for a pair of multivariate
+/// polynomials A, B \in Z_p[x_n][x_0, \ldots, x_n]. Here `good' means
+/// that b is not a root of GCD of contents of A and B. N.B. content
+/// is univariate polynomial \in Z_p[x_n]
+struct eval_point_finder
+{
+	typedef long value_type;
+	const value_type p;
+	std::set<value_type> points;
+	const random_modint modint_generator;
+	bool operator()(value_type& b, const ex& g, const ex& x);
+	eval_point_finder(const value_type& p_) : p(p_), modint_generator(p)
+	{ }
+};
+
+bool eval_point_finder::operator()(value_type& b, const ex& lc, const ex& x)
+{
+	random_modint modint_generator(p);
+	// Search for a new element of field
+	while (points.size() < p - 1) {
+		value_type b_ = modint_generator();
+		// check if this evaluation point was already used
+		if (points.find(b_) != points.end())
+			continue;
+
+		// mark found value as used, even if it's a root of lc
+		// (so we don't need to do the check once more)
+		points.insert(b_);
+		// Now make sure it's NOT the root of GCD's leading coeffient
+		if (lc.subs(x == numeric(b_)).smod(numeric(p)).is_zero())
+			continue;
+		// Nice, it's our next evaluation point
+		b = b_;
+		return true;
+	}
+	// All possible evaluation points were used.
+	return false;
+}
+
+}
+
+#endif /* GINAC_PGCD_EVAL_POINT_FINDER_H */
+
diff --git a/ginac/polynomial/mgcd.cpp b/ginac/polynomial/mgcd.cpp
new file mode 100644
index 0000000..b39475c
--- /dev/null
+++ b/ginac/polynomial/mgcd.cpp
@@ -0,0 +1,96 @@
+#include "chinrem_gcd.h"
+#include <cln/integer.h>
+#include "pgcd.h"
+#include "collect_vargs.h"
+#include "primes_factory.h"
+#include "divide_in_z_p.h"
+#include "poly_cra.h"
+
+namespace GiNaC
+{
+
+static cln::cl_I extract_integer_content(ex& Apr, const ex& A)
+{
+	static const cln::cl_I n1(1);
+	const numeric icont_ = A.integer_content();
+	const cln::cl_I icont = cln::the<cln::cl_I>(icont_.to_cl_N());
+	if (icont != 1) {
+		Apr = (A/icont_).expand();
+		return icont;
+	} else {
+		Apr = A;
+		return n1;
+	}
+}
+
+ex chinrem_gcd(const ex& A_, const ex& B_, const exvector& vars)
+{
+	ex A, B;
+	const cln::cl_I a_icont = extract_integer_content(A, A_);
+	const cln::cl_I b_icont = extract_integer_content(B, B_);
+	const cln::cl_I c = cln::gcd(a_icont, b_icont);
+
+	const cln::cl_I a_lc = integer_lcoeff(A, vars);
+	const cln::cl_I b_lc = integer_lcoeff(B, vars);
+	const cln::cl_I g_lc = cln::gcd(a_lc, b_lc);
+
+	const ex& x(vars.back());
+	int n = std::min(A.degree(x), B.degree(x));
+	const cln::cl_I A_max_coeff = to_cl_I(A.max_coefficient()); 
+	const cln::cl_I B_max_coeff = to_cl_I(B.max_coefficient());
+	const cln::cl_I lcoeff_limit = (cln::cl_I(1) << n)*cln::abs(g_lc)*
+		std::min(A_max_coeff, B_max_coeff);
+
+	cln::cl_I q = 0;
+	ex H = 0;
+
+	long p;
+	primes_factory pfactory;
+	while (true) {
+		bool has_primes = pfactory(p, g_lc);
+		if (!has_primes)
+			throw chinrem_gcd_failed();
+
+		const numeric pnum(p);
+		ex Ap = A.smod(pnum);
+		ex Bp = B.smod(pnum);
+		ex Cp = pgcd(Ap, Bp, vars, p);
+
+		const cln::cl_I g_lcp = smod(g_lc, p); 
+		const cln::cl_I Cp_lc = integer_lcoeff(Cp, vars);
+		const cln::cl_I nlc = smod(recip(Cp_lc, p)*g_lcp, p);
+		Cp = (Cp*numeric(nlc)).expand().smod(pnum);
+		int cp_deg = Cp.degree(x);
+		if (cp_deg == 0)
+			return numeric(g_lc);
+		if (zerop(q)) {
+			H = Cp;
+			n = cp_deg;
+			q = p;
+		} else {
+			if (cp_deg == n) {
+				ex H_next = chinese_remainder(H, q, Cp, p);
+				q = q*cln::cl_I(p);
+				H = H_next;
+			} else if (cp_deg < n) {
+				// all previous homomorphisms are unlucky
+				q = p;
+				H = Cp;
+				n = cp_deg;
+			} else {
+				// dp_deg > d_deg: current prime is bad
+			}
+		}
+		if (q < lcoeff_limit)
+			continue; // don't bother to do division checks
+		ex C, dummy1, dummy2;
+		extract_integer_content(C, H);
+		if (divide_in_z_p(A, C, dummy1, vars, 0) && 
+				divide_in_z_p(B, C, dummy2, vars, 0))
+			return (numeric(c)*C).expand();
+		// else: try more primes
+	}
+}
+
+} // namespace GiNaC
+
diff --git a/ginac/polynomial/newton_interpolate.h b/ginac/polynomial/newton_interpolate.h
new file mode 100644
index 0000000..e80687d
--- /dev/null
+++ b/ginac/polynomial/newton_interpolate.h
@@ -0,0 +1,36 @@
+#ifndef GINAC_PGCD_NEWTON_INTERPOLATE_H
+#define GINAC_PGCD_NEWTON_INTERPOLATE_H
+#include "ex.h"
+#include "numeric.h"
+#include "smod_helpers.h"
+namespace GiNaC
+{
+
+/**
+ * Newton interpolation -- incremental form.
+ *
+ * Given a polynomials e1 \in Z_p[Y], prev \in Z_p[x][Y], and evaluation
+ * points pt1, prevpts \in Z_p compute a polynomial P \in Z_[x][Y] such
+ * that P(x = pt1) = e1, P(x = pt \in prevpts) = prev(x = pt).
+ *
+ * @var{prevpts} enumerates previous evaluation points, should have a form
+ * (x - pt_2) (x - pt_3) \ldots (x - pt_n).
+ * @var{prev} encodes the result of previous interpolations.
+ */
+ex newton_interp(const ex& e1, const long pt1,
+		 const ex& prev, const ex& prevpts,
+		 const ex& x, const long p)
+{
+	const numeric pnum(p);
+	const numeric nc = ex_to<numeric>(prevpts.subs(x == numeric(pt1)).smod(pnum));
+	const numeric nc_1 = recip(nc, p);
+	// result = prev + prevpts (e1 - prev|_{x = pt_1})/prevpts|_{x = pt_1)
+	ex tmp = prev.subs(x == numeric(pt1)).smod(pnum);
+	tmp = (((e1 - tmp).expand().smod(pnum))*nc_1).smod(pnum);
+	tmp = (prevpts*tmp).expand().smod(pnum);
+	tmp = (prev + tmp).expand().smod(pnum);
+	return tmp;
+}
+
+}
+#endif /* GINAC_PGCD_NEWTON_INTERPOLATE_H */
diff --git a/ginac/polynomial/optimal_vars_finder.cpp b/ginac/polynomial/optimal_vars_finder.cpp
new file mode 100644
index 0000000..4903699
--- /dev/null
+++ b/ginac/polynomial/optimal_vars_finder.cpp
@@ -0,0 +1,134 @@
+#include <algorithm>
+#include <cstddef>
+#include "optimal_vars_finder.h"
+#include "add.h"
+#include "mul.h"
+#include "power.h"
+#include "symbol.h"
+#include "numeric.h"
+
+namespace GiNaC
+{
+namespace
+{
+// XXX: copy-pasted from normal.cpp.
+/*
+ *  Statistical information about symbols in polynomials
+ */
+
+/** This structure holds information about the highest and lowest degrees
+ *  in which a symbol appears in two multivariate polynomials "a" and "b".
+ *  A vector of these structures with information about all symbols in
+ *  two polynomials can be created with the function get_symbol_stats().
+ *
+ *  @see get_symbol_stats */
+struct sym_desc 
+{
+	/** Reference to symbol */
+	ex sym;
+
+	/** Highest degree of symbol in polynomial "a" */
+	int deg_a;
+
+	/** Highest degree of symbol in polynomial "b" */
+	int deg_b;
+
+	/** Lowest degree of symbol in polynomial "a" */
+	int ldeg_a;
+
+	/** Lowest degree of symbol in polynomial "b" */
+	int ldeg_b;
+
+	/** Maximum of deg_a and deg_b (Used for sorting) */
+	int max_deg;
+
+	/** Maximum number of terms of leading coefficient of symbol in both polynomials */
+	std::size_t max_lcnops;
+
+	/** Commparison operator for sorting */
+	bool operator<(const sym_desc &x) const
+	{
+		if (max_deg == x.max_deg)
+			return max_lcnops < x.max_lcnops;
+		else
+			return max_deg < x.max_deg;
+	}
+};
+
+// Vector of sym_desc structures
+typedef std::vector<sym_desc> sym_desc_vec;
+
+// Add symbol the sym_desc_vec (used internally by get_symbol_stats())
+static void add_symbol(const ex &s, sym_desc_vec &v)
+{
+	sym_desc_vec::const_iterator it = v.begin(), itend = v.end();
+	while (it != itend) {
+		if (it->sym.is_equal(s))  // If it's already in there, don't add it a second time
+			return;
+		++it;
+	}
+	sym_desc d;
+	d.sym = s;
+	v.push_back(d);
+}
+
+// Collect all symbols of an expression (used internally by get_symbol_stats())
+static void collect_symbols(const ex &e, sym_desc_vec &v)
+{
+	if (is_a<symbol>(e)) {
+		add_symbol(e, v);
+	} else if (is_exactly_a<add>(e) || is_exactly_a<mul>(e)) {
+		for (size_t i=0; i<e.nops(); i++)
+			collect_symbols(e.op(i), v);
+	} else if (is_exactly_a<power>(e)) {
+		collect_symbols(e.op(0), v);
+	}
+}
+
+/**
+ * @brief Find the order of variables which is optimal for GCD computation.
+ *
+ * Collects statistical information about the highest and lowest degrees
+ * of all variables that appear in two polynomials. Sorts the variables
+ * by minimum degree (lowest to highest). The information gathered by
+ * this function is used by GCD routines to find out the main variable
+ * for GCD computation.
+ *
+ *  @param a  first multivariate polynomial
+ *  @param b  second multivariate polynomial
+ *  @param v  vector of sym_desc structs (filled in) */
+static void get_symbol_stats(const ex &a, const ex &b, sym_desc_vec &v)
+{
+	collect_symbols(a, v);
+	collect_symbols(b, v);
+	sym_desc_vec::iterator it = v.begin(), itend = v.end();
+	while (it != itend) {
+		int deg_a = a.degree(it->sym);
+		int deg_b = b.degree(it->sym);
+		it->deg_a = deg_a;
+		it->deg_b = deg_b;
+		it->max_deg = std::max(deg_a, deg_b);
+		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;
+	}
+	std::sort(v.begin(), v.end());
+}
+// XXX: end copy-paste from normal.cpp
+
+} // anonymous namespace of helper functions
+
+exvector gcd_optimal_variables_order(const ex& a, const ex& b)
+{
+	sym_desc_vec v;
+	get_symbol_stats(a, b, v);
+	exvector vars;
+	vars.reserve(v.size());
+	for (std::size_t i = v.size(); i-- != 0; )
+		vars.push_back(v[i].sym);
+	return vars;
+}
+
+} // namespace GiNaC
+
diff --git a/ginac/polynomial/optimal_vars_finder.h b/ginac/polynomial/optimal_vars_finder.h
new file mode 100644
index 0000000..9715a47
--- /dev/null
+++ b/ginac/polynomial/optimal_vars_finder.h
@@ -0,0 +1,20 @@
+#ifndef GINAC_CHINREM_GCD_OPTIMAL_SYMBOL_FINDER_H
+#define GINAC_CHINREM_GCD_OPTIMAL_SYMBOL_FINDER_H
+#include <vector>
+#include "ex.h"
+
+namespace GiNaC
+{
+/**
+ * @brief Find the order of variables which is optimal for GCD computation.
+ *
+ * Collects statistical information about the highest and lowest degrees
+ * of all variables that appear in two polynomials. Sorts the variables
+ * by minimum degree (lowest to highest). The information gathered by
+ * this function is used by GCD routines to find out the main variable
+ * for GCD computation.
+ */
+extern exvector gcd_optimal_variables_order(const ex& A, const ex& B);
+}
+#endif /* GINAC_CHINREM_GCD_OPTIMAL_SYMBOL_FINDER_H */
+
diff --git a/ginac/polynomial/pgcd.cpp b/ginac/polynomial/pgcd.cpp
new file mode 100644
index 0000000..ddc3b8c
--- /dev/null
+++ b/ginac/polynomial/pgcd.cpp
@@ -0,0 +1,136 @@
+#include "pgcd.h"
+#include "collect_vargs.h"
+#include "smod_helpers.h"
+#include "euclid_gcd_wrap.h"
+#include "eval_point_finder.h"
+#include "newton_interpolate.h"
+#include "divide_in_z_p.h"
+
+namespace GiNaC
+{
+
+extern void
+primpart_content(ex& pp, ex& c, ex e, const exvector& vars, const long p);
+
+// Computes the GCD of two polynomials over a prime field.
+// Based on Algorithm 7.2 from "Algorithms for Computer Algebra"
+// A and B are considered as Z_p[x_n][x_0, \ldots, x_{n-1}], that is,
+// as a polynomials in variables x_0, \ldots x_{n-1} having coefficients
+// from the ring Z_p[x_n]
+ex pgcd(const ex& A, const ex& B, const exvector& vars, const long p)
+{
+	static const ex ex1(1);
+	if (A.is_zero())
+		return B;
+
+	if (B.is_zero())
+		return A;
+
+	if (is_a<numeric>(A))
+		return ex1;
+
+	if (is_a<numeric>(B))
+		return ex1;
+
+	// Checks for univariate polynomial
+	if (vars.size() == 1) {
+		ex ret = euclid_gcd(A, B, vars[0], p); // Univariate GCD
+		return ret;
+	}
+	const ex& mainvar(vars.back());
+
+	// gcd of the contents
+	ex H = 0, Hprev = 0; // GCD candidate
+	ex newton_poly = 1;  // for Newton Interpolation
+
+	// Contents and primparts of A and B
+	ex Aprim, contA;
+	primpart_content(Aprim, contA, A, vars, p);
+	ex Bprim, contB;
+	primpart_content(Bprim, contB, B, vars, p);
+	// gcd of univariate polynomials
+	const ex cont_gcd = euclid_gcd(contA, contB, mainvar, p);
+
+	exvector restvars = vars;
+	restvars.pop_back();
+	const ex AL = lcoeff_wrt(Aprim, restvars);
+	const ex BL = lcoeff_wrt(Bprim, restvars);
+	// gcd of univariate polynomials
+	const ex lc_gcd = euclid_gcd(AL, BL, mainvar, p);
+
+	// The estimate of degree of the gcd of Ab and Bb
+	int gcd_deg = std::min(degree(Aprim, mainvar), degree(Bprim, mainvar));
+	eval_point_finder::value_type b;
+
+	eval_point_finder find_eval_point(p);
+	const numeric pn(p);
+	do {
+		// Find a `good' evaluation point b.
+		bool has_more_pts = find_eval_point(b, lc_gcd, mainvar);
+		// If there are no more possible evaluation points, bail out
+		if (!has_more_pts)
+			throw pgcd_failed();
+
+		const numeric bn(b);
+		// Evaluate the polynomials in b
+		ex Ab = Aprim.subs(mainvar == bn).smod(pn);
+		ex Bb = Bprim.subs(mainvar == bn).smod(pn);
+		ex Cb = pgcd(Ab, Bb, restvars, p);
+
+		// Set the correct the leading coefficient
+		const cln::cl_I lcb_gcd =
+			smod(to_cl_I(lc_gcd.subs(mainvar == bn)), p);
+		const cln::cl_I Cblc = integer_lcoeff(Cb, restvars);
+		const cln::cl_I correct_lc = smod(lcb_gcd*recip(Cblc, p), p);
+		Cb = (Cb*numeric(correct_lc)).smod(pn);
+
+		// Test for unlucky homomorphisms
+		const int img_gcd_deg = Cb.degree(restvars.back());
+		if (img_gcd_deg < gcd_deg) {
+			// The degree decreased, previous homomorphisms were
+			// bad, so we have to start it all over.
+			H = Cb;
+			newton_poly = mainvar - numeric(b);
+			Hprev = 0;
+			gcd_deg  = img_gcd_deg;
+			continue;
+		} 
+		if (img_gcd_deg > gcd_deg) {
+			// The degree of images GCD is too high, this
+			// evaluation point is bad. Skip it.
+			continue;
+		}
+
+		// Image has the same degree as the previous one
+		// (or at least not higher than the limit)
+		Hprev = H;
+		H = newton_interp(Cb, b, H, newton_poly, mainvar, p);
+		newton_poly = newton_poly*(mainvar - b);
+
+		// try to reduce the number of division tests.
+		const ex H_lcoeff = lcoeff_wrt(H, restvars);
+
+		if (H_lcoeff.is_equal(lc_gcd)) {
+			if ((Hprev-H).expand().smod(pn).is_zero())
+				continue;
+			ex C /* primitive part of H */, contH /* dummy */;
+			primpart_content(C, contH, H, vars, p);
+			// Normalize GCD so that leading coefficient is 1
+			const cln::cl_I Clc = recip(integer_lcoeff(C, vars), p);
+			C = (C*numeric(Clc)).expand().smod(pn);
+
+			ex dummy1, dummy2;
+
+			if (divide_in_z_p(Aprim, C, dummy1, vars, p) &&
+					divide_in_z_p(Bprim, C, dummy2, vars, p))
+				return (cont_gcd*C).expand().smod(pn);
+			else if (img_gcd_deg == 0)
+				return cont_gcd;
+			// else continue building the candidate
+		} 
+	} while(true);
+	throw pgcd_failed();
+}
+
+} // namespace GiNaC
+
diff --git a/ginac/polynomial/pgcd.h b/ginac/polynomial/pgcd.h
new file mode 100644
index 0000000..5e72038
--- /dev/null
+++ b/ginac/polynomial/pgcd.h
@@ -0,0 +1,27 @@
+#ifndef GINAC_CHINREM_GCD_PGCD_H
+#define GINAC_CHINREM_GCD_PGCD_H
+#include "ex.h"
+
+namespace GiNaC
+{
+
+/// Exception to be thrown when modular GCD algorithm fails
+struct pgcd_failed
+{
+	virtual ~pgcd_failed() { }
+};
+
+/**
+ * @brief Compute the GCD of two polynomials over a prime field Z_p
+ * 
+ * @param vars variables
+ * @param p designates the coefficient field Z_p
+ * @param A polynomial \in Z_p[vars]
+ * @param B second polynomial \in Z_p[vars]
+ */
+extern ex
+pgcd(const ex& A, const ex& B, const exvector& vars, const long p);
+
+} // namespace GiNaC
+
+#endif
diff --git a/ginac/polynomial/poly_cra.h b/ginac/polynomial/poly_cra.h
new file mode 100644
index 0000000..cbcff7b
--- /dev/null
+++ b/ginac/polynomial/poly_cra.h
@@ -0,0 +1,38 @@
+#ifndef GINAC_POLY_CRA_H
+#define GINAC_POLY_CRA_H
+#include "ex.h"
+#include <cln/integer.h>
+#include "smod_helpers.h"
+
+namespace GiNaC
+{
+
+/**
+ * @brief Chinese reamainder algorithm for polynomials.
+ *
+ * Given two polynomials \f$e_1 \in Z_{q_1}[x_1, \ldots, x_n]\f$ and
+ * \f$e_2 \in Z_{q_2}[x_1, \ldots, x_n]\f$, compute the polynomial
+ * \f$r \in Z_{q_1 q_2}[x_1, \ldots, x_n]\f$ such that \f$ r mod q_1 = e_1\f$
+ * and \f$ r mod q_2 = e_2 \f$ 
+ */
+ex chinese_remainder(const ex& e1, const cln::cl_I& q1,
+		     const ex& e2, const long q2)
+{
+	// res = v_1 + v_2 q_1
+	// v_1 = e_1 mod q_1
+	// v_2 = (e_2 - v_1)/q_1 mod q_2
+	const numeric q2n(q2);
+	const numeric q1n(q1);
+	ex v1 = e1.smod(q1n);
+	ex u = v1.smod(q2n);
+	ex v2 = (e2.smod(q2n) - v1.smod(q2n)).expand().smod(q2n);
+	const numeric q1_1(recip(q1, q2)); // 1/q_1 mod q_2
+	v2 = (v2*q1_1).smod(q2n);
+	ex ret = (v1 + v2*q1_1).expand();
+	return ret;
+}
+
+} // namespace GiNaC
+
+#endif /* GINAC_POLY_CRA_H */
+
diff --git a/ginac/polynomial/primes_factory.h b/ginac/polynomial/primes_factory.h
new file mode 100644
index 0000000..093c973
--- /dev/null
+++ b/ginac/polynomial/primes_factory.h
@@ -0,0 +1,60 @@
+#ifndef GINAC_CHINREM_GCD_PRIMES_FACTORY_H
+#define GINAC_CHINREM_GCD_PRIMES_FACTORY_H
+#include <cln/integer.h>
+#include <cln/numtheory.h>
+#include <limits>
+#include "smod_helpers.h"
+#include "debug.hpp"
+
+namespace GiNaC
+{
+
+/**
+ * Find a `big' prime p such that lc mod p != 0. Helper class used by modular
+ * GCD algorithm.
+ */
+class primes_factory
+{
+private:
+	// These primes need to be large enough, so that the number of images
+	// we need to reconstruct the GCD (in Z) is reasonable. On the other
+	// hand, they should be as small as possible, so that operations on
+	// coefficients are efficient. Practically this means we coefficients
+	// should be native integers. (N.B.: as of now chinrem_gcd uses cl_I
+	// or even numeric. Eventually this will be fixed).
+	cln::cl_I last;
+	// This ensures coefficients are immediate.
+	static const int immediate_bits = 8*sizeof(void *) - __alignof__(void *);
+	static const long opt_hint = (1L << (immediate_bits >> 1)) - 1;
+public:
+	primes_factory()
+	{
+		last = cln::nextprobprime(cln::cl_I(opt_hint));
+	}
+
+	bool operator()(long& p, const cln::cl_I& lc)
+	{
+		static const cln::cl_I maxval(std::numeric_limits<long>::max());
+		while (last < maxval) {
+			long p_ = cln::cl_I_to_long(last);
+			last = cln::nextprobprime(last + 1);
+
+			if (!zerop(smod(lc, p_))) {
+				p = p_;
+				return true;
+			}
+		}
+		return false;
+	}
+
+	bool has_primes() const
+	{
+		static const cln::cl_I maxval(std::numeric_limits<long>::max());
+		return last < maxval;
+	}
+};
+
+} // namespace GiNaC
+
+#endif /* GINAC_CHINREM_GCD_PRIMES_FACTORY_H */
+
diff --git a/ginac/polynomial/primpart_content.cpp b/ginac/polynomial/primpart_content.cpp
new file mode 100644
index 0000000..9d36a95
--- /dev/null
+++ b/ginac/polynomial/primpart_content.cpp
@@ -0,0 +1,78 @@
+#include "ex.h"
+#include "numeric.h"
+#include "collect_vargs.h"
+#include "euclid_gcd_wrap.h"
+#include "divide_in_z_p.h"
+#include "debug.hpp"
+
+namespace GiNaC
+{
+
+/**
+ * Compute the primitive part and the content of a modular multivariate
+ * polynomial e \in Z_p[x_n][x_0, \ldots, x_{n-1}], i.e. e is considered
+ * a polynomial in variables x_0, \ldots, x_{n-1} with coefficients being
+ * modular polynomials Z_p[x_n]
+ * @param e polynomial to operate on
+ * @param pp primitive part of @a e, will be computed by this function
+ * @param c content (in the sense described above) of @a e, will be
+ *        computed by this function
+ * @param vars variables x_0, \ldots, x_{n-1}, x_n
+ * @param p modulus
+ */
+void primpart_content(ex& pp, ex& c, ex e, const exvector& vars,
+		      const long p)
+{
+	static const ex ex1(1);
+	static const ex ex0(0);
+	e = e.expand();
+	if (e.is_zero()) {
+		pp = ex0;
+		c = ex1;
+		return;
+	}
+	exvector rest_vars = vars;
+	rest_vars.pop_back();
+	ex_collect_t ec;
+	collect_vargs(ec, e, rest_vars);
+
+	if (ec.size() == 1) {
+		// the input polynomial factorizes into 
+		// p_1(x_n) p_2(x_0, \ldots, x_{n-1})
+		c = ec.rbegin()->second;
+		ec.rbegin()->second = ex1;
+		pp = ex_collect_to_ex(ec, vars).expand().smod(numeric(p));
+		return;
+	}
+
+	// Start from the leading coefficient (which is stored as a last
+	// element of the terms array)
+	ex_collect_t::reverse_iterator i = ec.rbegin();
+	ex g = i->second;
+	// there are at least two terms, so it's safe to...
+	++i;
+	while (i != ec.rend() && !g.is_equal(ex1)) {
+		g = euclid_gcd(i->second, g, vars.back(), p);
+		++i;
+	}
+	if (g.is_equal(ex1)) {
+		pp = e;
+		c = ex1;
+		return;
+	}
+	exvector mainvar;
+	mainvar.push_back(vars.back());
+	for (i = ec.rbegin(); i != ec.rend(); ++i) {
+		ex tmp(0);
+		if (!divide_in_z_p(i->second, g, tmp, mainvar, p))
+			throw std::logic_error(std::string(__func__) +
+					": bogus division failure");
+		i->second = tmp;
+	}
+
+	pp = ex_collect_to_ex(ec, rest_vars).expand().smod(numeric(p));
+	c = g;
+}
+
+} // namespace GiNaC
+
diff --git a/ginac/polynomial/smod_helpers.h b/ginac/polynomial/smod_helpers.h
new file mode 100644
index 0000000..81370ba
--- /dev/null
+++ b/ginac/polynomial/smod_helpers.h
@@ -0,0 +1,72 @@
+#ifndef GINAC_POLYNOMIAL_SMOD_HELPERS_H
+#define GINAC_POLYNOMIAL_SMOD_HELPERS_H
+#include <cln/integer.h>
+#include <cln/integer_io.h>
+#include "ex.h"
+#include "numeric.h"
+#include "debug.hpp"
+
+namespace GiNaC
+{
+
+/// Z -> Z_p (in the symmetric representation)
+static inline cln::cl_I smod(const cln::cl_I& a, long p)
+{
+	const cln::cl_I p2 = cln::cl_I(p >> 1);
+	const cln::cl_I m = cln::mod(a, p);
+	const cln::cl_I m_p = m - cln::cl_I(p);
+	const cln::cl_I ret = m > p2 ? m_p : m;
+	return ret;
+}
+
+static inline cln::cl_I recip(const cln::cl_I& a, long p_)
+{
+	cln::cl_I p(p_);
+	cln::cl_I u, v;
+	const cln::cl_I g = xgcd(a, p, &u, &v);
+	cln::cl_I ret = smod(u, p_);
+	cln::cl_I chck = smod(a*ret, p_);
+	bug_on(chck != 1, "miscomputed recip(" << a << " (mod " << p_ << "))");
+	return ret;
+
+}
+
+static inline numeric recip(const numeric& a_, long p)
+{
+	const cln::cl_I a = cln::the<cln::cl_I>(a_.to_cl_N());
+	const cln::cl_I ret = recip(a, p);
+	return numeric(ret);
+}
+
+static inline cln::cl_I to_cl_I(const ex& e)
+{
+	bug_on(!is_a<numeric>(e), "argument should be an integer");
+	bug_on(!e.info(info_flags::integer),
+		"argument should be an integer");
+	return cln::the<cln::cl_I>(ex_to<numeric>(e).to_cl_N());
+}
+
+struct random_modint
+{
+	typedef long value_type;
+	const value_type p;
+	const value_type p_2;
+
+	random_modint(const value_type& p_) : p(p_), p_2((p >> 1))
+	{ }
+	value_type operator()() const
+	{
+		do {
+			cln::cl_I tmp_ = cln::random_I(p);
+			value_type tmp = cln::cl_I_to_long(tmp_);
+			if (tmp > p_2)
+				tmp -= p;
+			return tmp;
+		} while (true);
+	}
+
+};
+
+} // namespace GiNaC
+
+#endif // GINAC_POLYNOMIAL_SMOD_HELPERS_H
-- 
1.5.6.5

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: Digital signature
Url : http://www.cebix.net/pipermail/ginac-devel/attachments/20090119/28b09c12/attachment-0001.sig 


More information about the GiNaC-devel mailing list