3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-29 03:45:51 +00:00

reorganizing the code

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2012-10-23 21:53:34 -07:00
parent b89d35dd69
commit 9e299b88c4
101 changed files with 16 additions and 16 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,493 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
algebraic_numbers.h
Abstract:
Real Algebraic Numbers
Author:
Leonardo (leonardo) 2011-11-22
Notes:
--*/
#ifndef _ALGEBRAIC_NUMBERS_H_
#define _ALGEBRAIC_NUMBERS_H_
#include"rational.h"
#include"mpq.h"
#include"polynomial.h"
#include"z3_exception.h"
#include"scoped_numeral.h"
#include"scoped_numeral_vector.h"
#include"tptr.h"
#include"statistics.h"
#include"params.h"
class small_object_allocator;
class mpbq_manager;
class mpbq;
namespace algebraic_numbers {
class anum;
class manager;
class algebraic_exception : public default_exception {
public:
algebraic_exception(char const * msg):default_exception(msg) {}
};
class manager {
public:
struct imp;
private:
imp * m_imp;
small_object_allocator * m_allocator;
bool m_own_allocator;
public:
static bool precise() { return true; }
static bool field() { return true; }
typedef anum numeral;
typedef svector<numeral> numeral_vector;
typedef _scoped_numeral<manager> scoped_numeral;
typedef _scoped_numeral_vector<manager> scoped_numeral_vector;
manager(unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0);
~manager();
static void get_param_descrs(param_descrs & r);
static void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
void set_cancel(bool f);
void updt_params(params_ref const & p);
unsynch_mpq_manager & qm() const;
mpbq_manager & bqm() const;
void del(numeral & a);
/**
\brief a <- 0
*/
void reset(numeral & a);
/**
\brief Return true if a is zero.
*/
bool is_zero(numeral const & a);
/**
\brief Return true if a is positive.
*/
bool is_pos(numeral const & a);
/**
\brief Return true if a is negative.
*/
bool is_neg(numeral const & a);
/**
\brief Return true if a is a rational number.
*/
bool is_rational(numeral const & a);
/**
\brief Return true if a is an integer.
*/
bool is_int(numeral const & a);
/**
\brief Degree of the algebraic number.
That is, degree of the polynomial that is used to encode \c a.
*/
unsigned degree(numeral const & a);
/**
\brief Convert a into a rational number.
\pre is_rational(a)
*/
void to_rational(numeral const & a, mpq & r);
/**
\brief Convert a into a rational number.
\pre is_rational(a)
*/
void to_rational(numeral const & a, rational & r);
/**
\brief a <- n
*/
void set(numeral & a, int n);
void set(numeral & a, mpz const & n);
void set(numeral & a, mpq const & n);
void set(numeral & a, numeral const & n);
void swap(numeral & a, numeral & b);
/**
\brief Store in b an integer value smaller than 'a'.
Remark: this is not the floor, but b <= floor(a)
*/
void int_lt(numeral const & a, numeral & b);
/**
\brief Store in b an integer value bigger than 'a'
Remark: this is not the ceil, but b >= ceil(a)
*/
void int_gt(numeral const & a, numeral & b);
/**
\brief Store in result a value in the interval (prev, next)
\pre lt(pre,v next)
*/
void select(numeral const & prev, numeral const & curr, numeral & result);
/**
\brief Isolate the roots of (an univariate polynomial) p, and store them as algebraic numbers in \c root.
That is, p is in Z[x].
*/
void isolate_roots(polynomial_ref const & p, numeral_vector & roots);
/**
\brief Isolate the roots of a multivariate polynomial p such that all but one variable of p is fixed by x2v, and
store them as algebraic numbers in \c root.
That is, we are viewing p as a polynomial in Z[y_1, ..., y_n][x]:
q_n(y_1, ..., y_n)x^n + ... + q_1(y_1, ..., y_n)*x + q_0
And we are returning the roots of
q_n(x2v(y_1), ..., x2v(y_n))x^n + ... + q_1(x2v(y_1), ..., x2v(y_n))*x + q_0
*/
void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots);
/**
\brief Isolate the roots of the given polynomial, and compute its sign between them.
*/
void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector<int> & signs);
/**
\brief Store in r the i-th root of p.
This method throws an exception if p does not have at least i roots.
This method is not really used in the nonlinear procedure.
It is mainly used for debugging purposes, and creating regression tests
\pre i > 0
*/
void mk_root(polynomial_ref const & p, unsigned i, numeral & r);
/**
\brief Store in r the i-th root of p.
This method throws an exception if the s-expression p does not represent
an univariate polynomial, of if p does not have at least i roots.
This method is not really used in the nonlinear procedure.
It is mainly used for debugging purposes, and "reading" root objects in the SMT 2.0 front-end.
\pre i > 0
*/
void mk_root(sexpr const * p, unsigned i, numeral & r);
/**
\brief Return a^{1/k}
Throws an exception if the result is not a real.
That is, (a is negative and k is even) or (k is zero).
*/
void root(numeral const & a, unsigned k, numeral & b);
/**
\brief Return a^k
Throws an exception if 0^0.
*/
void power(numeral const & a, unsigned k, numeral & b);
/**
\brief c <- a + b
*/
void add(numeral const & a, numeral const & b, numeral & c);
void add(numeral const & a, mpz const & b, numeral & c);
/**
\brief c <- a - b
*/
void sub(numeral const & a, numeral const & b, numeral & c);
/**
\brief c <- a * b
*/
void mul(numeral const & a, numeral const & b, numeral & c);
/**
\brief a <- -a
*/
void neg(numeral & a);
/**
\brief a <- 1/a if a != 0
*/
void inv(numeral & a);
/**
\brief c <- a/b if b != 0
*/
void div(numeral const & a, numeral const & b, numeral & c);
/**
Return -1 if a < b
Return 0 if a == b
Return 1 if a > b
*/
int compare(numeral const & a, numeral const & b);
/**
\brief a == b
*/
bool eq(numeral const & a, numeral const & b);
bool eq(numeral const & a, mpq const & b);
bool eq(numeral const & a, mpz const & b);
/**
\brief a != b
*/
bool neq(numeral const & a, numeral const & b) { return !eq(a, b); }
bool neq(numeral const & a, mpq const & b) { return !eq(a, b); }
bool neq(numeral const & a, mpz const & b) { return !eq(a, b); }
/**
\brief a < b
*/
bool lt(numeral const & a, numeral const & b);
bool lt(numeral const & a, mpq const & b);
bool lt(numeral const & a, mpz const & b);
/**
\brief a > b
*/
bool gt(numeral const & a, numeral const & b) { return lt(b, a); }
bool gt(numeral const & a, mpq const & b);
bool gt(numeral const & a, mpz const & b);
/**
\brief a <= b
*/
bool le(numeral const & a, numeral const & b) { return !gt(a, b); }
bool le(numeral const & a, mpq const & b) { return !gt(a, b); }
bool le(numeral const & a, mpz const & b) { return !gt(a, b); }
/**
\brief a >= b
*/
bool ge(numeral const & a, numeral const & b) { return !lt(a, b); }
bool ge(numeral const & a, mpq const & b) { return !lt(a, b); }
bool ge(numeral const & a, mpz const & b) { return !lt(a, b); }
/**
\brief Evaluate the sign of a multivariate polynomial p(x_1, ..., x_n)
at assignment x2v: [x_1 -> alpha_1, ..., x_n -> alpha_n].
\remark forall variable x in p, we have that x2v.contains(x) is true
Return negative number if p(alpha_1, ..., alpha_n) < 0
Return 0 if p(alpha_1, ..., alpha_n) == 0
Return positive number if p(alpha_1, ..., alpha_n) > 0
*/
int eval_sign_at(polynomial_ref const & p, polynomial::var2anum const & x2v);
void get_polynomial(numeral const & a, svector<mpz> & r);
// Procedures for getting lower and upper bounds for irrational numbers
void get_lower(numeral const & a, mpbq & l);
void get_lower(numeral const & a, mpq & l);
void get_lower(numeral const & a, rational & l);
void get_lower(numeral const & a, mpq & l, unsigned precision);
void get_lower(numeral const & a, rational & l, unsigned precision);
void get_upper(numeral const & a, mpbq & u);
void get_upper(numeral const & a, mpq & u);
void get_upper(numeral const & a, rational & u);
void get_upper(numeral const & a, mpq & l, unsigned precision);
void get_upper(numeral const & a, rational & l, unsigned precision);
/**
\brief Display algebraic number as a rational if is_rational(n)
Otherwise, display it as an interval.
*/
void display_interval(std::ostream & out, numeral const & a) const;
/**
\brief Display algebraic number in decimal notation.
A question mark is added based on the precision requested.
*/
void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const;
/**
\brief Display algebraic number as a root object: (p, i)
That is, 'a' is the i-th root of p.
*/
void display_root(std::ostream & out, numeral const & a) const;
/**
\brief Display algebraic number as a root object in SMT 2.0 style: (root-obj p i)
That is, 'a' is the i-th root of p.
*/
void display_root_smt2(std::ostream & out, numeral const & a) const;
/**
\brief Display algebraic number in Mathematica format.
*/
void display_mathematica(std::ostream & out, numeral const & a) const;
void display(std::ostream & out, numeral const & a) { return display_decimal(out, a); }
void reset_statistics();
void collect_statistics(statistics & st) const;
};
struct basic_cell;
struct algebraic_cell;
enum anum_kind { BASIC = 0, ROOT };
class anum {
friend struct manager::imp;
friend class manager;
void * m_cell;
anum(basic_cell * cell):m_cell(TAG(void*, cell, BASIC)) {}
anum(algebraic_cell * cell):m_cell(TAG(void*, cell, ROOT)) {}
bool is_basic() const { return GET_TAG(m_cell) == BASIC; }
basic_cell * to_basic() const { SASSERT(is_basic()); return UNTAG(basic_cell*, m_cell); }
algebraic_cell * to_algebraic() const { SASSERT(!is_basic()); return UNTAG(algebraic_cell*, m_cell); }
public:
anum():m_cell(0) {}
};
};
typedef algebraic_numbers::manager anum_manager;
typedef algebraic_numbers::manager::numeral anum;
typedef algebraic_numbers::manager::numeral_vector anum_vector;
typedef algebraic_numbers::manager::scoped_numeral scoped_anum;
typedef algebraic_numbers::manager::scoped_numeral_vector scoped_anum_vector;
#define AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \
inline bool EXTERNAL(scoped_anum const & a, TYPE const & b) { \
anum_manager & m = a.m(); \
scoped_anum _b(m); \
m.set(_b, b); \
return m.INTERNAL(a, _b); \
}
#define AN_MK_COMPARISON(EXTERNAL, INTERNAL) \
AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \
AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \
AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpq)
AN_MK_COMPARISON(operator==, eq);
AN_MK_COMPARISON(operator!=, neq);
AN_MK_COMPARISON(operator<, lt);
AN_MK_COMPARISON(operator<=, le);
AN_MK_COMPARISON(operator>, gt);
AN_MK_COMPARISON(operator>=, ge);
#undef AN_MK_COMPARISON
#undef AN_MK_COMPARISON_CORE
#define AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \
inline scoped_anum EXTERNAL(scoped_anum const & a, TYPE const & b) { \
anum_manager & m = a.m(); \
scoped_anum _b(m); \
m.set(_b, b); \
scoped_anum r(m); \
m.INTERNAL(a, _b, r); \
return r; \
}
#define AN_MK_BINARY(EXTERNAL, INTERNAL) \
AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \
AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \
AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpq)
AN_MK_BINARY(operator+, add)
AN_MK_BINARY(operator-, sub)
AN_MK_BINARY(operator*, mul)
AN_MK_BINARY(operator/, div)
#undef AN_MK_BINARY
#undef AN_MK_BINARY_CORE
inline scoped_anum root(scoped_anum const & a, unsigned k) {
scoped_anum r(a.m());
a.m().root(a, k, r);
return r;
}
inline scoped_anum power(scoped_anum const & a, unsigned k) {
scoped_anum r(a.m());
a.m().power(a, k, r);
return r;
}
inline scoped_anum operator^(scoped_anum const & a, unsigned k) {
return power(a, k);
}
inline bool is_int(scoped_anum const & a) {
return a.m().is_int(a);
}
inline bool is_rational(scoped_anum const & a) {
return a.m().is_rational(a);
}
struct root_obj_pp {
anum_manager & m;
anum const & n;
root_obj_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {}
root_obj_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {}
};
inline std::ostream & operator<<(std::ostream & out, root_obj_pp const & n) {
n.m.display_root(out, n.n);
return out;
}
struct decimal_pp {
anum_manager & m;
anum const & n;
unsigned prec;
decimal_pp(anum_manager & _m, anum const & _n, unsigned p):m(_m), n(_n), prec(p) {}
decimal_pp(scoped_anum const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {}
};
inline std::ostream & operator<<(std::ostream & out, decimal_pp const & n) {
n.m.display_decimal(out, n.n, n.prec);
return out;
}
struct interval_pp {
anum_manager & m;
anum const & n;
interval_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {}
interval_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {}
};
inline std::ostream & operator<<(std::ostream & out, interval_pp const & n) {
n.m.display_interval(out, n.n);
return out;
}
#endif

View file

@ -0,0 +1,152 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
linear_eq_solver.h
Abstract:
Simple equational solver template for any number field.
No special optimization, just the basics for solving small systems.
It is a solver target to dense system of equations.
Main client: Sparse Modular GCD algorithm.
Author:
Leonardo (leonardo) 2012-01-22
Notes:
--*/
#ifndef _LINEAR_EQ_SOLVER_H_
#define _LINEAR_EQ_SOLVER_H_
template<typename numeral_manager>
class linear_eq_solver {
typedef typename numeral_manager::numeral numeral;
numeral_manager & m;
unsigned n; // number of variables
vector<svector<numeral> > A;
svector<numeral> b;
public:
linear_eq_solver(numeral_manager & _m):m(_m), n(0) { SASSERT(m.field()); }
~linear_eq_solver() { flush(); }
void flush() {
SASSERT(b.size() == A.size());
unsigned sz = A.size();
for (unsigned i = 0; i < sz; i++) {
svector<numeral> & as = A[i];
m.del(b[i]);
SASSERT(as.size() == n);
for (unsigned j = 0; j < n; j++)
m.del(as[j]);
}
A.reset();
b.reset();
n = 0;
}
void resize(unsigned _n) {
if (n != _n) {
flush();
n = _n;
for (unsigned i = 0; i < n; i++) {
A.push_back(svector<numeral>());
svector<numeral> & as = A.back();
for (unsigned j = 0; j < n; j++) {
as.push_back(numeral());
}
b.push_back(numeral());
}
}
}
void reset() {
for (unsigned i = 0; i < n; i++) {
svector<numeral> & A_i = A[i];
for (unsigned j = 0; j < n; j++) {
m.set(A_i[j], 0);
}
m.set(b[i], 0);
}
}
// Set row i with _as[0]*x_0 + ... + _as[n-1]*x_{n-1} = b
void add(unsigned i, numeral const * _as, numeral const & _b) {
SASSERT(i < n);
m.set(b[i], _b);
svector<numeral> & A_i = A[i];
for (unsigned j = 0; j < n; j++) {
m.set(A_i[j], _as[j]);
}
}
// Return true if the system of equations has a solution.
// Return false if the matrix is singular
bool solve(numeral * xs) {
for (unsigned k = 0; k < n; k++) {
TRACE("linear_eq_solver", tout << "iteration " << k << "\n"; display(tout););
// find pivot
unsigned i = k;
for (; i < n; i++) {
if (!m.is_zero(A[i][k]))
break;
}
if (i == n)
return false; // matrix is singular
A[k].swap(A[i]); // swap rows
svector<numeral> & A_k = A[k];
numeral & A_k_k = A_k[k];
SASSERT(!m.is_zero(A_k_k));
// normalize row
for (unsigned i = k+1; i < n; i++)
m.div(A_k[i], A_k_k, A_k[i]);
m.div(b[k], A_k_k, b[k]);
m.set(A_k_k, 1);
// check if first k-1 positions are zero
DEBUG_CODE({ for (unsigned i = 0; i < k; i++) { SASSERT(m.is_zero(A_k[i])); } });
// for all rows below pivot
for (unsigned i = k+1; i < n; i++) {
svector<numeral> & A_i = A[i];
numeral & A_i_k = A_i[k];
for (unsigned j = k+1; j < n; j++) {
m.submul(A_i[j], A_i_k, A_k[j], A_i[j]);
}
m.submul(b[i], A_i_k, b[k], b[i]);
m.set(A_i_k, 0);
}
}
unsigned k = n;
while (k > 0) {
--k;
TRACE("linear_eq_solver", tout << "iteration " << k << "\n"; display(tout););
SASSERT(m.is_one(A[k][k]));
// save result
m.set(xs[k], b[k]);
// back substitute
unsigned i = k;
while (i > 0) {
--i;
m.submul(b[i], A[i][k], b[k], b[i]);
m.set(A[i][k], 0);
}
}
return true;
}
void display(std::ostream & out) const {
for (unsigned i = 0; i < A.size(); i++) {
SASSERT(A[i].size() == n);
for (unsigned j = 0; j < n; j++) {
m.display(out, A[i][j]);
out << " ";
}
m.display(out, b[i]); out << "\n";
}
}
};
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,235 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
polynomial_cache.cpp
Abstract:
"Hash-consing" for polynomials
Author:
Leonardo (leonardo) 2012-01-07
Notes:
--*/
#include"polynomial_cache.h"
#include"chashtable.h"
namespace polynomial {
struct poly_hash_proc {
manager & m;
poly_hash_proc(manager & _m):m(_m) {}
unsigned operator()(polynomial const * p) const { return m.hash(p); }
};
struct poly_eq_proc {
manager & m;
poly_eq_proc(manager & _m):m(_m) {}
bool operator()(polynomial const * p1, polynomial const * p2) const { return m.eq(p1, p2); }
};
struct psc_chain_entry {
polynomial const * m_p;
polynomial const * m_q;
var m_x;
unsigned m_hash;
unsigned m_result_sz;
polynomial ** m_result;
psc_chain_entry(polynomial const * p, polynomial const * q, var x, unsigned h):
m_p(p),
m_q(q),
m_x(x),
m_hash(h),
m_result_sz(0),
m_result(0) {
}
struct hash_proc { unsigned operator()(psc_chain_entry const * entry) const { return entry->m_hash; } };
struct eq_proc {
bool operator()(psc_chain_entry const * e1, psc_chain_entry const * e2) const {
return e1->m_p == e2->m_p && e1->m_q == e2->m_q && e1->m_x == e2->m_x;
}
};
};
struct factor_entry {
polynomial const * m_p;
unsigned m_hash;
unsigned m_result_sz;
polynomial ** m_result;
factor_entry(polynomial const * p, unsigned h):
m_p(p),
m_hash(h),
m_result_sz(0),
m_result(0) {
}
struct hash_proc { unsigned operator()(factor_entry const * entry) const { return entry->m_hash; } };
struct eq_proc {
bool operator()(factor_entry const * e1, factor_entry const * e2) const {
return e1->m_p == e2->m_p;
}
};
};
typedef chashtable<polynomial*, poly_hash_proc, poly_eq_proc> polynomial_table;
typedef chashtable<psc_chain_entry*, psc_chain_entry::hash_proc, psc_chain_entry::eq_proc> psc_chain_cache;
typedef chashtable<factor_entry*, factor_entry::hash_proc, factor_entry::eq_proc> factor_cache;
struct cache::imp {
manager & m;
polynomial_table m_poly_table;
psc_chain_cache m_psc_chain_cache;
factor_cache m_factor_cache;
polynomial_ref_vector m_cached_polys;
svector<char> m_in_cache;
small_object_allocator & m_allocator;
imp(manager & _m):m(_m), m_poly_table(poly_hash_proc(m), poly_eq_proc(m)), m_cached_polys(m), m_allocator(m.allocator()) {
}
~imp() {
reset_psc_chain_cache();
reset_factor_cache();
}
void del_psc_chain_entry(psc_chain_entry * entry) {
if (entry->m_result_sz != 0)
m_allocator.deallocate(sizeof(polynomial*)*entry->m_result_sz, entry->m_result);
entry->~psc_chain_entry();
m_allocator.deallocate(sizeof(psc_chain_entry), entry);
}
void del_factor_entry(factor_entry * entry) {
if (entry->m_result_sz != 0)
m_allocator.deallocate(sizeof(polynomial*)*entry->m_result_sz, entry->m_result);
entry->~factor_entry();
m_allocator.deallocate(sizeof(factor_entry), entry);
}
void reset_psc_chain_cache() {
psc_chain_cache::iterator it = m_psc_chain_cache.begin();
psc_chain_cache::iterator end = m_psc_chain_cache.end();
for (; it != end; ++it) {
del_psc_chain_entry(*it);
}
m_psc_chain_cache.reset();
}
void reset_factor_cache() {
factor_cache::iterator it = m_factor_cache.begin();
factor_cache::iterator end = m_factor_cache.end();
for (; it != end; ++it) {
del_factor_entry(*it);
}
m_factor_cache.reset();
}
unsigned pid(polynomial * p) const { return m.id(p); }
polynomial * mk_unique(polynomial * p) {
if (m_in_cache.get(pid(p), false))
return p;
polynomial * p_prime = m_poly_table.insert_if_not_there(p);
if (p == p_prime) {
m_cached_polys.push_back(p_prime);
m_in_cache.setx(pid(p_prime), true, false);
}
return p_prime;
}
void psc_chain(polynomial * p, polynomial * q, var x, polynomial_ref_vector & S) {
p = mk_unique(p);
q = mk_unique(q);
unsigned h = hash_u_u(pid(p), pid(q));
psc_chain_entry * entry = new (m_allocator.allocate(sizeof(psc_chain_entry))) psc_chain_entry(p, q, x, h);
psc_chain_entry * old_entry = m_psc_chain_cache.insert_if_not_there(entry);
if (entry != old_entry) {
entry->~psc_chain_entry();
m_allocator.deallocate(sizeof(psc_chain_entry), entry);
S.reset();
for (unsigned i = 0; i < old_entry->m_result_sz; i++) {
S.push_back(old_entry->m_result[i]);
}
}
else {
m.psc_chain(p, q, x, S);
unsigned sz = S.size();
entry->m_result_sz = sz;
entry->m_result = static_cast<polynomial**>(m_allocator.allocate(sizeof(polynomial*)*sz));
for (unsigned i = 0; i < sz; i++) {
polynomial * h = mk_unique(S.get(i));
S.set(i, h);
entry->m_result[i] = h;
}
}
}
void factor(polynomial * p, polynomial_ref_vector & distinct_factors) {
distinct_factors.reset();
p = mk_unique(p);
unsigned h = hash_u(pid(p));
factor_entry * entry = new (m_allocator.allocate(sizeof(factor_entry))) factor_entry(p, h);
factor_entry * old_entry = m_factor_cache.insert_if_not_there(entry);
if (entry != old_entry) {
entry->~factor_entry();
m_allocator.deallocate(sizeof(factor_entry), entry);
distinct_factors.reset();
for (unsigned i = 0; i < old_entry->m_result_sz; i++) {
distinct_factors.push_back(old_entry->m_result[i]);
}
}
else {
factors fs(m);
m.factor(p, fs);
unsigned sz = fs.distinct_factors();
entry->m_result_sz = sz;
entry->m_result = static_cast<polynomial**>(m_allocator.allocate(sizeof(polynomial*)*sz));
for (unsigned i = 0; i < sz; i++) {
polynomial * h = mk_unique(fs[i]);
distinct_factors.push_back(h);
entry->m_result[i] = h;
}
}
}
};
cache::cache(manager & m) {
m_imp = alloc(imp, m);
}
cache::~cache() {
dealloc(m_imp);
}
manager & cache::m() const {
return m_imp->m;
}
polynomial * cache::mk_unique(polynomial * p) {
return m_imp->mk_unique(p);
}
void cache::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) {
m_imp->psc_chain(const_cast<polynomial*>(p), const_cast<polynomial*>(q), x, S);
}
void cache::factor(polynomial const * p, polynomial_ref_vector & distinct_factors) {
m_imp->factor(const_cast<polynomial*>(p), distinct_factors);
}
void cache::reset() {
manager & _m = m();
dealloc(m_imp);
m_imp = alloc(imp, _m);
}
};

View file

@ -0,0 +1,44 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
polynomial_cache.h
Abstract:
"Hash-consing" for polynomials
Author:
Leonardo (leonardo) 2012-01-07
Notes:
--*/
#ifndef _POLYNOMIAL_CACHE_H_
#define _POLYNOMIAL_CACHE_H_
#include"polynomial.h"
namespace polynomial {
/**
\brief Functor for creating unique polynomials and caching results of operations
*/
class cache {
struct imp;
imp * m_imp;
public:
cache(manager & m);
~cache();
manager & m() const;
manager & pm() const { return m(); }
polynomial * mk_unique(polynomial * p);
void psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S);
void factor(polynomial const * p, polynomial_ref_vector & distinct_factors);
void reset();
};
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
polynomial_factorization.h
Abstract:
Methods for factoring polynomials.
Author:
Dejan (t-dejanj) 2011-11-29
Notes:
[1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal,
46(8-10):18531859, 1967.
[2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third
edition, 1997.
[3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993.
--*/
#pragma once
#if 0
// Disabled for reorg
#include "polynomial.h"
#include "upolynomial.h"
#include "bit_vector.h"
#include "z3_exception.h"
namespace polynomial {
/**
\brief Something to throw when we are in trouble.
*/
class factorization_exception : public default_exception {
public:
factorization_exception(char const * msg) : default_exception(msg) {}
};
/**
\brief Factor the polynomial f from Z[x1, ..., x_n]. Returns the index of the last factor that is completely
factored. I.e., if the method returns m, then f_1, ..., f_m are true irreducible factors, and the rest might
be further reducible.
*/
unsigned factor(polynomial_ref & f, factors & factors);
/**
\brief Factor the square-free primitive polynomial f from Z[x1, ..., x_n]. Returns true if the factorization
was sucesseful, i.e. it was completed and the result is complete. Otherwise the quarantee is that the all but
the last factor are irreducible.
*/
bool factor_square_free_primitive(polynomial_ref const & f, factors & factors);
}
inline std::ostream & operator<<(std::ostream & out, polynomial::factors & factors) {
factors.display(out);
return out;
}
#endif

View file

@ -0,0 +1,72 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
polynomial_primes.h
Abstract:
Some prime numbers for modular computations.
Author:
Leonardo (leonardo) 2011-12-21
Notes:
--*/
#ifndef _POLYNOMIAL_PRIMES_H_
#define _POLYNOMIAL_PRIMES_H_
namespace polynomial {
#define NUM_SMALL_PRIMES 11
const unsigned g_small_primes[NUM_SMALL_PRIMES] = {
13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53
};
#if 0
#define NUM_BIG_PRIMES 77
const unsigned g_big_primes[NUM_BIG_PRIMES] = {
16777259, 16777289, 16777291, 16777331, 16777333, 16777337, 16777381,
16777421, 16777441, 16777447, 16777469, 16777499, 16777507, 16777531,
16777571, 16777577, 16777597, 16777601, 16777619, 16777633, 16777639,
16777643, 16777669, 16777679, 16777681, 16777699, 16777711, 16777721,
16777723, 16777729, 16777751, 16777777, 16777781, 16777807, 16777811,
16777823, 16777829, 16777837, 16777853, 16777903, 16777907, 16777949,
16777967, 16777973, 16777987, 16777991, 16778009, 16778023, 16778071,
16778077, 16778089, 16778123, 16778129, 16778137, 16778147, 16778173,
16778227, 16778231, 16778291, 16778309, 16778357, 16778383, 16778401,
16778413, 16778429, 16778441, 16778449, 16778453, 16778497, 16778521,
16778537, 16778543, 16778549, 16778561, 16778603, 16778623, 16778627
};
#else
#define NUM_BIG_PRIMES 231
const unsigned g_big_primes[NUM_BIG_PRIMES] = {
39103, 39107, 39113, 39119, 39133, 39139, 39157, 39161, 39163, 39181, 39191,
39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301,
39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397,
39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521,
39541, 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667,
39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779,
39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869,
39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, 39979, 39983, 39989,
40009, 40013, 40031, 40037, 40039, 40063, 40087, 40093, 40099, 40111, 40123,
40127, 40129, 40151, 40153, 40163, 40169, 40177, 40189, 40193, 40213, 40231,
40237, 40241, 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387,
40423, 40427, 40429, 40433, 40459, 40471, 40483, 40487, 40493, 40499, 40507,
40519, 40529, 40531, 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627,
40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771,
40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867,
40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, 40993,
41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117,
41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203,
41213, 41221, 41227, 41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299,
41333, 41341, 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443,
41453, 41467, 41479, 41491, 41507, 41513, 41519, 41521, 41539, 41543, 41549
};
#endif
};
#endif

View file

@ -0,0 +1,50 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
polynomial_var2value.h
Abstract:
Simple implementations of the var2value interface.
Author:
Leonardo (leonardo) 2012-01-05
Notes:
--*/
#ifndef _POLYNOMIAL_VAR2VALUE_H_
#define _POLYNOMIAL_VAR2VALUE_H_
#include"polynomial.h"
#include"scoped_numeral_vector.h"
namespace polynomial {
// default implementation used for debugging purposes
template<typename ValManager>
class simple_var2value : public var2value<ValManager, typename ValManager::numeral> {
var_vector m_xs;
_scoped_numeral_vector<ValManager> m_vs;
public:
simple_var2value(ValManager & m):m_vs(m) {}
void push_back(var x, typename ValManager::numeral const & v) { m_xs.push_back(x); m_vs.push_back(v); }
virtual ValManager & m() const { return m_vs.m(); }
virtual bool contains(var x) const { return std::find(m_xs.begin(), m_xs.end(), x) != m_xs.end(); }
virtual typename ValManager::numeral const & operator()(var x) const {
for (unsigned i = 0; i < m_xs.size(); i++)
if (m_xs[i] == x)
return m_vs[i];
UNREACHABLE();
static typename ValManager::numeral zero;
return zero;
}
};
};
#endif

View file

@ -0,0 +1,800 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
rpolynomial.cpp
Abstract:
Goodies for creating and handling polynomials in dense recursive representation.
Author:
Leonardo (leonardo) 2012-06-11
Notes:
--*/
#include"rpolynomial.h"
#include"tptr.h"
#include"buffer.h"
namespace rpolynomial {
typedef void poly_or_num;
inline bool is_poly(poly_or_num * p) { return GET_TAG(p) == 0; }
inline bool is_num(poly_or_num * p) { return GET_TAG(p) == 1; }
polynomial * to_poly(poly_or_num * p) { SASSERT(is_poly(p)); return UNTAG(polynomial*, p); }
manager::numeral * to_num_ptr(poly_or_num * p) { SASSERT(is_num(p)); return (UNTAG(manager::numeral *, p)); }
manager::numeral & to_num(poly_or_num * p) { return *to_num_ptr(p); }
poly_or_num * to_poly_or_num(polynomial * p) { return TAG(poly_or_num*, p, 0); }
poly_or_num * to_poly_or_num(manager::numeral * n) { return TAG(poly_or_num*, n, 1); }
class polynomial {
friend class manager;
friend struct manager::imp;
unsigned m_ref_count;
var m_var;
unsigned m_size;
poly_or_num * m_args[0];
public:
unsigned ref_count() const { return m_ref_count; }
void inc_ref() { m_ref_count++; }
void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; }
static unsigned get_obj_size(unsigned n) { return sizeof(polynomial) + n*sizeof(void*); }
var max_var() const { return m_var; }
unsigned size() const { return m_size; }
poly_or_num * arg(unsigned i) const { SASSERT(i < size()); return m_args[i]; }
};
struct manager::imp {
manager & m_wrapper;
numeral_manager & m_manager;
small_object_allocator * m_allocator;
bool m_own_allocator;
volatile bool m_cancel;
imp(manager & w, numeral_manager & m, small_object_allocator * a):
m_wrapper(w),
m_manager(m),
m_allocator(a),
m_own_allocator(a == 0) {
if (a == 0)
m_allocator = alloc(small_object_allocator, "rpolynomial");
m_cancel = false;
}
~imp() {
if (m_own_allocator)
dealloc(m_allocator);
}
// Remark: recursive calls should be fine since I do not expect to have polynomials with more than 100 variables.
manager & pm() const { return m_wrapper; }
numeral * mk_numeral() {
void * mem = m_allocator->allocate(sizeof(numeral));
return new (mem) numeral();
}
void del_numeral(numeral * n) {
m_manager.del(*n);
m_allocator->deallocate(sizeof(numeral), n);
}
static void inc_ref(polynomial * p) {
if (p)
p->inc_ref();
}
static void inc_ref(poly_or_num * p) {
if (p && is_poly(p))
inc_ref(to_poly(p));
}
void del_poly(polynomial * p) {
SASSERT(p != 0);
ptr_buffer<polynomial> todo;
todo.push_back(p);
while (!todo.empty()) {
p = todo.back();
todo.pop_back();
unsigned sz = p->size();
for (unsigned i = 0; i < sz; i++) {
poly_or_num * pn = p->arg(i);
if (pn == 0)
continue;
if (is_num(pn)) {
del_numeral(to_num_ptr(pn));
}
else {
SASSERT(is_poly(p));
polynomial * p_arg = to_poly(p);
p_arg->dec_ref();
if (p_arg->ref_count() == 0) {
todo.push_back(p_arg);
}
}
}
unsigned obj_sz = polynomial::get_obj_size(sz);
m_allocator->deallocate(obj_sz, p);
}
}
void dec_ref(polynomial * p) {
if (p) {
p->dec_ref();
if (p->ref_count() == 0)
del_poly(p);
}
}
void dec_ref(poly_or_num * p) {
if (p && is_poly(p))
dec_ref(to_poly(p));
}
static bool is_const(polynomial const * p) {
SASSERT(p == 0 || (p->max_var() == null_var) == (p->size() == 1 && p->arg(0) != 0 && is_num(p->arg(0))));
return p == 0 || p->max_var() == null_var;
}
bool is_zero(polynomial const * p) {
return p == 0;
}
static bool is_univariate(polynomial const * p) {
if (is_const(p))
return false;
unsigned sz = p->size();
for (unsigned i = 0; i < sz; i++) {
poly_or_num * pn = p->arg(i);
if (pn == 0)
continue;
if (is_poly(pn))
return false;
}
return true;
}
bool is_monomial(polynomial const * p) {
if (is_const(p))
return true;
unsigned sz = p->size();
SASSERT(sz > 0);
SASSERT(p->arg(sz - 1) != 0);
for (unsigned i = 0; i < sz - 1; i++) {
if (p->arg(i) != 0)
return false;
}
SASSERT(is_poly(p->arg(sz - 1)));
return is_monomial(to_poly(p->arg(sz-1)));
}
unsigned degree(polynomial const * p) {
SASSERT(p != 0);
SASSERT(p->size() > 0);
return p == 0 ? 0 : p->size() - 1;
}
bool eq(polynomial const * p1, polynomial const * p2) {
if (p1 == p2)
return true;
if (p1 == 0 || p2 == 0)
return false;
if (p1->size() != p2->size())
return false;
if (p1->max_var() != p2->max_var())
return false;
unsigned sz = p1->size();
for (unsigned i = 0; i < sz; i++) {
poly_or_num * pn1 = p1->arg(i);
poly_or_num * pn2 = p2->arg(i);
if (pn1 == 0 && pn2 == 0)
continue;
if (pn1 == 0 || pn2 == 0)
return false;
if (is_num(pn1) && is_num(pn2)) {
if (!m_manager.eq(to_num(pn1), to_num(pn2)))
return false;
}
else if (is_poly(pn1) && is_poly(pn2)) {
if (!eq(to_poly(pn1), to_poly(pn2)))
return false;
}
else {
return false;
}
}
return true;
}
void inc_ref_args(unsigned sz, poly_or_num * const * args) {
for (unsigned i = 0; i < sz; i++) {
poly_or_num * pn = args[i];
if (pn == 0 || is_num(pn))
continue;
inc_ref(to_poly(pn));
}
}
void dec_ref_args(unsigned sz, poly_or_num * const * args) {
for (unsigned i = 0; i < sz; i++) {
poly_or_num * pn = args[i];
if (pn == 0 || is_num(pn))
continue;
dec_ref(to_poly(pn));
}
}
unsigned trim(unsigned sz, poly_or_num * const * args) {
while (sz > 0) {
if (args[sz - 1] != 0)
return sz;
sz--;
}
UNREACHABLE();
return UINT_MAX;
}
polynomial * allocate_poly(unsigned sz, poly_or_num * const * args, var max_var) {
SASSERT(sz > 0);
SASSERT((max_var == null_var) == (sz == 1 && is_num(args[0])));
unsigned obj_sz = polynomial::get_obj_size(sz);
void * mem = m_allocator->allocate(obj_sz);
polynomial * new_pol = new (mem) polynomial();
new_pol->m_ref_count = 0;
new_pol->m_var = max_var;
new_pol->m_size = sz;
for (unsigned i = 0; i < sz; i++) {
poly_or_num * pn = args[i];
if (is_poly(pn)) {
inc_ref(to_poly(pn));
new_pol->m_args[i] = pn;
SASSERT(max_var == null_var || to_poly(pn)->max_var() < max_var);
}
else {
SASSERT(!m_manager.is_zero(to_num(pn)));
new_pol->m_args[i] = pn;
}
}
return new_pol;
}
poly_or_num * mk_poly_core(unsigned sz, poly_or_num * const * args, var max_var) {
sz = trim(sz, args);
SASSERT(sz > 0);
if (sz == 1) {
poly_or_num * pn0 = args[0];
SASSERT(!is_num(pn0) || !m_manager.is_zero(to_num(pn0)));
return pn0;
}
SASSERT((max_var == null_var) == (sz == 1 && is_num(args[0])));
SASSERT(sz > 1);
return to_poly_or_num(allocate_poly(sz, args, max_var));
}
polynomial * mk_poly(unsigned sz, poly_or_num * const * args, var max_var) {
poly_or_num * _p = mk_poly_core(sz, args, max_var);
if (_p == 0)
return 0;
else if (is_num(_p))
return allocate_poly(1, &_p, null_var);
else
return to_poly(_p);
}
polynomial * mk_const(numeral const & n) {
if (m_manager.is_zero(n))
return 0;
numeral * a = mk_numeral();
m_manager.set(*a, n);
poly_or_num * _a = to_poly_or_num(a);
return allocate_poly(1, &_a, null_var);
}
polynomial * mk_const(rational const & a) {
SASSERT(a.is_int());
scoped_numeral tmp(m_manager);
m_manager.set(tmp, a.to_mpq().numerator());
return mk_const(tmp);
}
polynomial * mk_polynomial(var x, unsigned k) {
SASSERT(x != null_var);
if (k == 0) {
numeral one;
m_manager.set(one, 1);
return mk_const(one);
}
ptr_buffer<poly_or_num> new_args;
for (unsigned i = 0; i < k; i++)
new_args.push_back(0);
numeral * new_arg = mk_numeral();
m_manager.set(*new_arg, 1);
new_args.push_back(to_poly_or_num(new_arg));
return mk_poly(new_args.size(), new_args.c_ptr(), x);
}
poly_or_num * unpack(polynomial const * p) {
if (p == 0) {
return 0;
}
else if (is_const(p)) {
SASSERT(p->size() == 1);
SASSERT(p->max_var() == null_var);
return p->arg(0);
}
else {
return to_poly_or_num(const_cast<polynomial*>(p));
}
}
polynomial * pack(poly_or_num * p) {
if (p == 0)
return 0;
else if (is_num(p))
return mk_poly(1, &p, null_var);
else
return to_poly(p);
}
poly_or_num * mul_core(numeral const & c, poly_or_num * p) {
if (m_manager.is_zero(c) || p == 0) {
return 0;
}
else if (is_num(p)) {
numeral * r = mk_numeral();
m_manager.mul(c, to_num(p), *r);
return to_poly_or_num(r);
}
else {
polynomial * _p = to_poly(p);
unsigned sz = _p->size();
SASSERT(sz > 1);
ptr_buffer<poly_or_num> new_args;
for (unsigned i = 0; i < sz; i++) {
new_args.push_back(mul_core(c, _p->arg(i)));
}
return mk_poly_core(new_args.size(), new_args.c_ptr(), _p->max_var());
}
}
polynomial * mul(numeral const & c, polynomial const * p) {
return pack(mul_core(c, unpack(p)));
}
polynomial * neg(polynomial const * p) {
numeral minus_one;
m_manager.set(minus_one, -1);
return pack(mul_core(minus_one, unpack(p)));
}
poly_or_num * add_core(numeral const & c, poly_or_num * p) {
if (m_manager.is_zero(c)) {
return p;
}
else if (p == 0) {
numeral * r = mk_numeral();
m_manager.set(*r, c);
return to_poly_or_num(r);
}
else if (is_num(p)) {
numeral a;
m_manager.add(c, to_num(p), a);
if (m_manager.is_zero(a))
return 0;
numeral * new_arg = mk_numeral();
m_manager.swap(*new_arg, a);
return to_poly_or_num(new_arg);
}
else {
polynomial * _p = to_poly(p);
unsigned sz = _p->size();
SASSERT(sz > 1);
ptr_buffer<poly_or_num> new_args;
new_args.push_back(add_core(c, _p->arg(0)));
for (unsigned i = 1; i < sz; i++)
new_args.push_back(_p->arg(1));
return mk_poly_core(new_args.size(), new_args.c_ptr(), _p->max_var());
}
}
polynomial * add(numeral const & c, polynomial const * p) {
return pack(add_core(c, unpack(p)));
}
#if 0
polynomial * add_lt(polynomial const * p1, polynomial const * p2) {
// Add non-constant polynomials p1 and p2 when max_var(p1) < max_var(p2)
SASSERT(p1->max_var() != null_var);
SASSERT(p2->max_var() != null_var);
SASSERT(p1->max_var() < p2->max_var());
unsigned sz = p2->size();
ptr_buffer<poly_or_num> new_args;
poly_or_num * pn0 = p2->arg(0);
if (pn0 == 0) {
new_args.push_back(to_poly_or_num(const_cast<polynomial*>(p1)));
}
else if (is_num(pn0)) {
SASSERT(!is_const(p1));
polynomial * new_arg = add(to_num(pn0), p1);
SASSERT(!is_zero(new_arg));
SASSERT(!is_const(new_arg));
new_args.push_back(to_poly_or_num(new_arg));
}
else {
SASSERT(is_poly(pn0));
polynomial * new_arg = add(p1, to_poly(pn0));
new_args.push_back(to_poly_or_num(new_arg));
}
for (unsigned i = 1; i < sz; i++)
new_args.push_back(p2->arg(i));
return mk_poly(sz, new_args.c_ptr(), p2->max_var());
}
polynomial * add(polynomial const * p1, polynomial const * p2) {
if (is_zero(p1))
return const_cast<polynomial*>(p2);
if (is_zero(p2))
return const_cast<polynomial*>(p1);
var x1 = p1->max_var();
var x2 = p2->max_var();
if (x1 == null_var) {
SASSERT(is_const(p1));
return add(to_num(p1->arg(0)), p2);
}
if (x2 == null_var) {
SASSERT(is_const(p2));
return add(to_num(p2->arg(0)), p1);
}
if (x1 < x2)
return add_lt(p1, p2);
if (x2 < x1)
return add_lt(p2, p1);
SASSERT(x1 == x2);
unsigned sz1 = p1->size();
unsigned sz2 = p2->size();
unsigned msz = std::min(sz1, sz2);
ptr_buffer<poly_or_num> new_args;
for (unsigned i = 0; i < msz; i++) {
poly_or_num * pn1 = p1->arg(i);
poly_or_num * pn2 = p2->arg(i);
if (pn1 == 0) {
new_args.push_back(pn2);
continue;
}
if (pn2 == 0) {
new_args.push_back(pn1);
continue;
}
SASSERT(pn1 != 0 && pn2 != 0);
if (is_num(pn1)) {
if (is_num(pn2)) {
SASSERT(is_num(pn1) && is_num(pn2));
numeral a;
m_manager.add(to_num(pn1), to_num(pn2), a);
if (m_manager.is_zero(a)) {
new_args.push_back(0);
}
else {
numeral * new_arg = mk_numeral();
m_manager.swap(*new_arg, a);
new_args.push_back(to_poly_or_num(new_arg));
}
}
else {
SASSERT(is_num(pn1) && is_poly(pn2));
new_args.push_back(to_poly_or_num(add(to_num(pn1), to_poly(pn2))));
}
}
else {
if (is_num(pn2)) {
SASSERT(is_poly(pn1) && is_num(pn2));
new_args.push_back(to_poly_or_num(add(to_num(pn2), to_poly(pn1))));
}
else {
SASSERT(is_poly(pn1) && is_poly(pn2));
new_args.push_back(to_poly_or_num(add(to_poly(pn1), to_poly(pn2))));
}
}
}
SASSERT(new_args.size() == sz1 || new_args.size() == sz2);
for (unsigned i = msz; i < sz1; i++) {
new_args.push_back(p1->arg(i));
}
for (unsigned i = msz; i < sz2; i++) {
new_args.push_back(p2->arg(i));
}
SASSERT(new_args.size() == std::max(sz1, sz2));
return mk_poly(new_args.size(), new_args.c_ptr(), x1);
}
class resetter_mul_buffer;
friend class resetter_mul_buffer;
class resetter_mul_buffer {
imp & m_owner;
ptr_buffer<poly_or_num> m_buffer;
public:
resetter_mul_buffer(imp & o, ptr_buffer<poly_or_num> & b):m_owner(o), m_buffer(b) {}
~resetter_mul_buffer() {
m_owner.dec_ref_args(m_buffer.size(), m_buffer.c_ptr());
m_buffer.reset();
}
};
void acc_mul_xk(ptr_buffer<poly_or_num> & mul_buffer, unsigned k, polynomial * p) {
if (mul_buffer[k] == 0) {
mul_buffer[k] = to_poly_or_num(p);
inc_ref(p);
}
else {
polynomial * new_p;
if (is_num(mul_buffer[k]))
new_p = add(to_num(mul_buffer.get(k)), p);
else
new_p = add(p, to_poly(mul_buffer.get(k)));
if (is_zero(new_p)) {
dec_ref(mul_buffer[k]);
mul_buffer[k] = 0;
}
else {
inc_ref(new_p);
dec_ref(mul_buffer[k]);
mul_buffer[k] = to_poly_or_num(new_p);
}
}
}
void acc_mul_xk(ptr_buffer<poly_or_num> & mul_buffer, unsigned k, numeral & a) {
if (mul_buffer.get(k) == 0) {
numeral * new_arg = mk_numeral();
m_manager.swap(*new_arg, a);
mul_buffer[k] = to_poly_or_num(new_arg);
}
else {
if (is_num(mul_buffer[k])) {
m_manager.add(to_num(mul_buffer[k]), a, to_num(mul_buffer[k]));
if (m_manager.is_zero(to_num(mul_buffer[k]))) {
del_numeral(to_num_ptr(mul_buffer[k]));
mul_buffer[k] = 0;
}
}
else {
polynomial * new_p = add(a, to_poly(mul_buffer.get(k)));
if (is_zero(new_p)) {
dec_ref(mul_buffer[k]);
mul_buffer[k] = 0;
}
else {
inc_ref(new_p);
dec_ref(mul_buffer[k]);
mul_buffer[k] = to_poly_or_num(new_p);
}
}
}
}
polynomial * mul_lt(polynomial const * p1, polynomial const * p2) {
unsigned sz2 = p2->size();
// TODO
return 0;
}
polynomial * mul(polynomial const * p1, polynomial const * p2) {
var x1 = p1->max_var();
var x2 = p2->max_var();
if (x1 == null_var) {
SASSERT(is_const(p1));
return mul(to_num(p1->arg(0)), p2);
}
if (x2 == null_var) {
SASSERT(is_const(p2));
return mul(to_num(p2->arg(0)), p1);
}
if (x1 < x2)
return mul_lt(p1, p2);
if (x2 < x1)
return mul_lt(p2, p1);
SASSERT(x1 == x2);
if (degree(p1) < degree(p2))
std::swap(p1, p2);
unsigned sz = degree(p1) * degree(p2) + 1;
ptr_buffer<poly_or_num> mul_buffer;
resetter_mul_buffer resetter(*this, mul_buffer);
mul_buffer.resize(sz);
unsigned sz1 = p1->size();
unsigned sz2 = p2->size();
for (unsigned i1 = 0; i1 < sz1; i1++) {
poly_or_num * pn1 = p1->arg(i1);
if (pn1 == 0)
continue;
for (unsigned i2 = 0; i2 < sz2; i2++) {
poly_or_num * pn2 = p2->arg(i2);
if (pn2 == 0)
continue;
unsigned i = i1+i2;
if (is_num(pn1)) {
if (is_num(pn2)) {
SASSERT(is_num(pn1) && is_num(pn2));
scoped_numeral a(m_manager);
m_manager.mul(to_num(pn1), to_num(pn2), a);
acc_mul_xk(mul_buffer, i, a);
}
else {
SASSERT(is_num(pn1) && is_poly(pn2));
polynomial_ref p(pm());
p = mul(to_num(pn1), to_poly(pn2));
acc_mul_xk(mul_buffer, i, p);
}
}
else {
if (is_num(pn2)) {
SASSERT(is_poly(pn1) && is_num(pn2));
polynomial_ref p(pm());
p = mul(to_num(pn2), to_poly(pn1));
acc_mul_xk(mul_buffer, i, p);
}
else {
SASSERT(is_poly(pn1) && is_poly(pn2));
polynomial_ref p(pm());
p = mul(to_poly(pn2), to_poly(pn1));
acc_mul_xk(mul_buffer, i, p);
}
}
}
}
return mk_poly(mul_buffer.size(), mul_buffer.c_ptr(), x1);
}
#endif
void display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) {
var x = p->max_var();
bool first = true;
unsigned i = p->size();
while (i > 0) {
--i;
poly_or_num * pn = p->arg(i);
if (pn == 0)
continue;
if (first)
first = false;
else
out << " + ";
if (is_num(pn)) {
numeral & a = to_num(pn);
if (i == 0) {
m_manager.display(out, a);
}
else {
if (m_manager.is_one(a)) {
proc(out, x);
if (i > 1)
out << "^" << i;
}
else {
m_manager.display(out, a);
if (use_star)
out << "*";
else
out << " ";
proc(out, x);
if (i > 1)
out << "^" << i;
}
}
}
else {
SASSERT(is_poly(pn));
if (i == 0) {
display(out, to_poly(pn), proc, use_star);
}
else {
bool add_paren = false;
if (i > 0)
add_paren = !is_monomial(to_poly(pn));
if (add_paren)
out << "(";
display(out, to_poly(pn), proc, use_star);
if (add_paren)
out << ")";
if (i > 0) {
if (use_star)
out << "*";
else
out << " ";
proc(out, x);
if (i > 1)
out << "^" << i;
}
}
}
}
}
};
manager:: manager(numeral_manager & m, small_object_allocator * a) {
m_imp = alloc(imp, *this, m, a);
}
manager::~manager() {
dealloc(m_imp);
}
bool manager::is_zero(polynomial const * p) {
return p == 0;
}
#if 0
bool manager::is_const(polynomial const * p) {
return imp::is_const(p);
}
bool manager::is_univariate(polynomial const * p) {
return imp::is_univariate(p);
}
bool manager::is_monomial(polynomial const * p) const {
return m_imp->is_monomial(p);
}
bool manager::eq(polynomial const * p1, polynomial const * p2) {
return m_imp->eq(p1, p2);
}
polynomial * manager::mk_zero() {
return m_imp->mk_zero();
}
polynomial * manager::mk_const(numeral const & r) {
return m_imp->mk_const(r);
}
polynomial * manager::mk_const(rational const & a) {
return m_imp->mk_const(a);
}
polynomial * manager::mk_polynomial(var x, unsigned k) {
return m_imp->mk_polynomial(x, k);
}
polynomial * manager::mul(numeral const & r, polynomial const * p) {
return m_imp->mul(r, p);
}
polynomial * manager::neg(polynomial const * p) {
return m_imp->neg(p);
}
polynomial * manager::add(numeral const & r, polynomial const * p) {
return m_imp->add(r, p);
}
polynomial * manager::add(polynomial const * p1, polynomial const * p2) {
return m_imp->add(p1, p2);
}
var manager::max_var(polynomial const * p) {
return p->max_var();
}
unsigned manager::size(polynomial const * p) {
return p->size();
}
void manager::display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) const {
return m_imp->display(out, p, proc, use_star);
}
#endif
};

View file

@ -0,0 +1,208 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
rpolynomial.h
Abstract:
Goodies for creating and handling polynomials in dense recursive representation.
Author:
Leonardo (leonardo) 2012-06-11
Notes:
--*/
#ifndef _RPOLYNOMIAL_H_
#define _RPOLYNOMIAL_H_
#include"mpz.h"
#include"rational.h"
#include"obj_ref.h"
#include"ref_vector.h"
#include"z3_exception.h"
#include"polynomial.h"
namespace rpolynomial {
typedef polynomial::var var;
const var null_var = polynomial::null_var;
typedef polynomial::var_vector var_vector;
typedef polynomial::display_var_proc display_var_proc;
typedef polynomial::polynomial som_polynomial;
class polynomial;
class manager;
typedef obj_ref<polynomial, manager> polynomial_ref;
typedef ref_vector<polynomial, manager> polynomial_ref_vector;
typedef ptr_vector<polynomial> polynomial_vector;
class manager {
public:
typedef unsynch_mpz_manager numeral_manager;
typedef numeral_manager::numeral numeral;
typedef svector<numeral> numeral_vector;
typedef _scoped_numeral<numeral_manager> scoped_numeral;
typedef _scoped_numeral_vector<numeral_manager> scoped_numeral_vector;
struct imp;
private:
imp * m_imp;
public:
manager(numeral_manager & m, small_object_allocator * a = 0);
~manager();
numeral_manager & m() const;
small_object_allocator & allocator() const;
void set_cancel(bool f);
/**
\brief Create a new variable.
*/
var mk_var();
/**
\brief Return the number of variables in the manager.
*/
unsigned num_vars() const;
/**
\brief Return true if x is a valid variable in this manager.
*/
bool is_valid(var x) const { return x < num_vars(); }
/**
\brief Increment reference counter.
*/
void inc_ref(polynomial * p);
/**
\brief Decrement reference counter.
*/
void dec_ref(polynomial * p);
/**
\brief Return true if \c p is the zero polynomial.
*/
bool is_zero(polynomial const * p);
/**
\brief Return true if p1 == p2.
*/
bool eq(polynomial const * p1, polynomial const * p2);
/**
\brief Return true if \c p is the constant polynomial.
*/
static bool is_const(polynomial const * p);
/**
\brief Return true if \c p is an univariate polynomial.
*/
static bool is_univariate(polynomial const * p);
/**
\brief Return true if \c p is a monomial.
*/
bool is_monomial(polynomial const * p) const;
/**
\brief Return the maximal variable occurring in p.
Return null_var if p is a constant polynomial.
*/
static var max_var(polynomial const * p);
/**
\brief Return the size of the polynomail p.
It is the degree(p) on max_var(p) + 1.
*/
static unsigned size(polynomial const * p);
/**
\brief Return a polynomial h that is the coefficient of max_var(p)^k in p.
if p does not contain any monomial containing max_var(p)^k, then return 0.
*/
polynomial * coeff(polynomial const * p, unsigned k);
/**
\brief Create the zero polynomial.
*/
polynomial * mk_zero();
/**
\brief Create the constant polynomial \c r.
\warning r is a number managed by the numeral_manager in the polynomial manager
\warning r is reset.
*/
polynomial * mk_const(numeral const & r);
/**
\brief Create the constant polynomial \c r.
\pre r must be an integer
*/
polynomial * mk_const(rational const & r);
/**
\brief Create the polynomial x^k
*/
polynomial * mk_polynomial(var x, unsigned k = 1);
polynomial * mul(numeral const & r, polynomial const * p);
polynomial * neg(polynomial const * p);
polynomial * add(numeral const & r, polynomial const * p);
polynomial * add(int c, polynomial const * p);
polynomial * add(polynomial const * p1, polynomial const * p2);
/**
\brief Convert the given polynomial in sum-of-monomials form into a polynomial in dense recursive form.
*/
polynomial * translate(som_polynomial const * p);
void display(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc(), bool use_star = false) const;
void display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc()) const;
friend std::ostream & operator<<(std::ostream & out, polynomial_ref const & p) {
p.m().display(out, p);
return out;
}
};
};
typedef rpolynomial::polynomial_ref rpolynomial_ref;
typedef rpolynomial::polynomial_ref_vector rpolynomial_ref_vector;
inline rpolynomial_ref neg(rpolynomial_ref const & p) {
rpolynomial::manager & m = p.m();
return rpolynomial_ref(m.neg(p), m);
}
inline rpolynomial_ref operator-(rpolynomial_ref const & p) {
rpolynomial::manager & m = p.m();
return rpolynomial_ref(m.neg(p), m);
}
inline rpolynomial_ref operator+(int a, rpolynomial_ref const & p) {
rpolynomial::manager & m = p.m();
return rpolynomial_ref(m.add(a, p), m);
}
inline rpolynomial_ref operator+(rpolynomial_ref const & p, int a) {
rpolynomial::manager & m = p.m();
return rpolynomial_ref(m.add(a, p), m);
}
#endif

View file

@ -0,0 +1,112 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sexpr2upolynomial.cpp
Abstract:
sexpr to upolynomial converter
Author:
Leonardo (leonardo) 2011-12-28
Notes:
--*/
#include"sexpr2upolynomial.h"
#include"sexpr.h"
sexpr2upolynomial_exception::sexpr2upolynomial_exception(char const * msg, sexpr const * s):
cmd_exception(msg, s->get_line(), s->get_pos()) {
}
#define MAX_POLYNOMIAL_DEPTH 1 << 16
// Simple recursive parser
void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p, unsigned depth) {
if (depth > MAX_POLYNOMIAL_DEPTH)
throw sexpr2upolynomial_exception("invalid univariate polynomial, too complex", s);
if (s->is_composite()) {
unsigned num = s->get_num_children();
if (num == 0)
throw sexpr2upolynomial_exception("invalid univariate polynomial, symbol expected", s);
sexpr * h = s->get_child(0);
if (!h->is_symbol())
throw sexpr2upolynomial_exception("invalid univariate polynomial, symbol expected", s);
symbol op = h->get_symbol();
if (op == "+") {
if (num <= 1)
throw sexpr2upolynomial_exception("invalid univariate polynomial, '+' operator expects at least one argument", s);
sexpr2upolynomial(m, s->get_child(1), p, depth+1);
upolynomial::scoped_numeral_vector arg(m);
for (unsigned i = 2; i < num; i++) {
m.reset(arg);
sexpr2upolynomial(m, s->get_child(i), arg, depth+1);
m.add(arg.size(), arg.c_ptr(), p.size(), p.c_ptr(), p);
}
}
else if (op == "-") {
if (num <= 1)
throw sexpr2upolynomial_exception("invalid univariate polynomial, '-' operator expects at least one argument", s);
sexpr2upolynomial(m, s->get_child(1), p, depth+1);
if (num == 2) {
m.neg(p);
return;
}
upolynomial::scoped_numeral_vector arg(m);
for (unsigned i = 2; i < num; i++) {
m.reset(arg);
sexpr2upolynomial(m, s->get_child(i), arg, depth+1);
m.sub(p.size(), p.c_ptr(), arg.size(), arg.c_ptr(), p);
}
}
else if (op == "*") {
if (num <= 1)
throw sexpr2upolynomial_exception("invalid univariate polynomial, '*' operator expects at least one argument", s);
sexpr2upolynomial(m, s->get_child(1), p, depth+1);
upolynomial::scoped_numeral_vector arg(m);
for (unsigned i = 2; i < num; i++) {
m.reset(arg);
sexpr2upolynomial(m, s->get_child(i), arg, depth+1);
m.mul(arg.size(), arg.c_ptr(), p.size(), p.c_ptr(), p);
}
}
else if (op == "^") {
if (num != 3)
throw sexpr2upolynomial_exception("invalid univariate polynomial, '^' operator expects two arguments", s);
sexpr2upolynomial(m, s->get_child(1), p, depth+1);
sexpr * arg2 = s->get_child(2);
if (!arg2->is_numeral() || !arg2->get_numeral().is_unsigned())
throw sexpr2upolynomial_exception("invalid univariate polynomial, exponent must be an unsigned integer", arg2);
unsigned k = arg2->get_numeral().get_unsigned();
m.pw(p.size(), p.c_ptr(), k, p);
}
else {
throw sexpr2upolynomial_exception("invalid univariate polynomial, '+', '-', '^' or '*' expected", s);
}
}
else if (s->is_numeral()) {
// make constant polynomial
rational a = s->get_numeral();
if (!a.is_int())
throw sexpr2upolynomial_exception("invalid univariate polynomial, integer coefficient expected", s);
m.set(1, &a, p);
}
else if (s->is_symbol()) {
if (s->get_symbol() != "x")
throw sexpr2upolynomial_exception("invalid univariate polynomial, variable 'x' expected", s);
// make identity
rational as[2] = { rational(0), rational(1) };
m.set(2, as, p);
}
else {
throw sexpr2upolynomial_exception("invalid univariate polynomial, unexpected ", s);
}
}
void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p) {
sexpr2upolynomial(m, s, p, 0);
}

View file

@ -0,0 +1,33 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sexpr2upolynomial.h
Abstract:
sexpr to upolynomial converter
Author:
Leonardo (leonardo) 2011-12-28
Notes:
--*/
#ifndef _SEXPR2UPOLYNOMIAL_H_
#define _SEXPR2UPOLYNOMIAL_H_
#include"upolynomial.h"
#include"cmd_context_types.h"
class sexpr;
class sexpr2upolynomial_exception : public cmd_exception {
public:
sexpr2upolynomial_exception(char const * msg, sexpr const * s);
};
void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,922 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
upolynomial.h
Abstract:
Goodies for creating and handling univariate polynomials.
A dense representation is much better for Root isolation algorithms,
encoding algebraic numbers, factorization, etc.
We also use integers as the coefficients of univariate polynomials.
Author:
Leonardo (leonardo) 2011-11-29
Notes:
--*/
#ifndef _UPOLYNOMIAL_H_
#define _UPOLYNOMIAL_H_
#include"mpzzp.h"
#include"rational.h"
#include"polynomial.h"
#include"z3_exception.h"
#include"mpbq.h"
#define FACTOR_VERBOSE_LVL 1000
namespace upolynomial {
typedef polynomial::factor_params factor_params;
// It is used only for signing cancellation.
class upolynomial_exception : public default_exception {
public:
upolynomial_exception(char const * msg):default_exception(msg) {}
};
typedef mpz numeral;
typedef mpzzp_manager numeral_manager;
typedef mpzzp_manager zp_numeral_manager;
typedef unsynch_mpz_manager z_numeral_manager;
typedef svector<numeral> numeral_vector;
class core_manager {
public:
typedef _scoped_numeral_vector<numeral_manager> scoped_numeral_vector;
typedef _scoped_numeral<numeral_manager> scoped_numeral;
/**
\brief Convenient vector of polynomials that manages its own memory and keeps the degree, of each polynomial.
Polynomial is c*f_1^k_1*...*f_n^k_n.
*/
class factors {
private:
vector<numeral_vector> m_factors;
svector<unsigned> m_degrees;
core_manager & m_upm;
numeral m_constant;
unsigned m_total_factors;
unsigned m_total_degree;
public:
factors(core_manager & upm);
~factors();
core_manager & upm() const { return m_upm; }
core_manager & pm() const { return m_upm; }
numeral_manager & nm() const;
unsigned distinct_factors() const { return m_factors.size(); }
unsigned total_factors() const { return m_total_factors; }
void clear();
void reset() { clear(); }
numeral_vector const & operator[](unsigned i) const { return m_factors[i]; }
numeral const & get_constant() const { return m_constant; }
void set_constant(numeral const & constant);
unsigned get_degree() const { return m_total_degree; }
unsigned get_degree(unsigned i) const { return m_degrees[i]; }
void set_degree(unsigned i, unsigned degree);
void push_back(numeral_vector const & p, unsigned degree);
// push p to vectors and kill it
void push_back_swap(numeral_vector & p, unsigned degree);
void swap_factor(unsigned i, numeral_vector & p);
void swap(factors & other);
void multiply(numeral_vector & out) const;
void display(std::ostream & out) const;
friend std::ostream & operator<<(std::ostream & out, factors const & fs) {
fs.display(out);
return out;
}
};
protected:
numeral_manager m_manager;
numeral_vector m_basic_tmp;
numeral_vector m_div_tmp1;
numeral_vector m_div_tmp2;
numeral_vector m_exact_div_tmp;
numeral_vector m_gcd_tmp1;
numeral_vector m_gcd_tmp2;
numeral_vector m_CRA_tmp;
#define UPOLYNOMIAL_MGCD_TMPS 6
numeral_vector m_mgcd_tmp[UPOLYNOMIAL_MGCD_TMPS];
numeral_vector m_sqf_tmp1;
numeral_vector m_sqf_tmp2;
numeral_vector m_pw_tmp;
volatile bool m_cancel;
static bool is_alias(numeral const * p, numeral_vector & buffer) { return buffer.c_ptr() != 0 && buffer.c_ptr() == p; }
void neg_core(unsigned sz1, numeral const * p1, numeral_vector & buffer);
void add_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
void sub_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
void mul_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
void flip_sign_if_lm_neg(numeral_vector & buffer);
void mod_gcd(unsigned sz_u, numeral const * u, unsigned sz_v, numeral const * v, numeral_vector & result);
void CRA_combine_images(numeral_vector const & q, numeral const & p, numeral_vector & C, numeral & bound);
public:
core_manager(z_numeral_manager & m);
~core_manager();
z_numeral_manager & zm() const { return m_manager.m(); }
numeral_manager & m() const { return const_cast<core_manager*>(this)->m_manager; }
/**
\brief Return true if Z_p[X]
*/
bool modular() const { return m().modular(); }
bool field() const { return m().field(); }
/**
\brief Return p in Z_p[X]
\pre modular
*/
numeral const & p() const { return m().p(); }
/**
\brief Set manager as Z[X]
*/
void set_z() { m().set_z(); }
/**
\brief Set manager as Z_p[X]
*/
void set_zp(numeral const & p) { m().set_zp(p); }
void set_zp(uint64 p) { m().set_zp(p); }
void checkpoint();
void set_cancel(bool f);
/**
\brief set p size to 0. That is, p is the zero polynomial after this operation.
*/
void reset(numeral_vector & p);
/**
\brief Remove zero leading coefficients.
After applying this method, we have that p is empty() or p[p.size()-1] is not zero.
*/
void trim(numeral_vector & p);
void set_size(unsigned sz, numeral_vector & buffer);
/**
\brief Return the actual degree of p.
*/
unsigned degree(numeral_vector const & p) {
unsigned sz = p.size();
return sz == 0 ? 0 : sz - 1;
}
/**
\brief Return true if p is the zero polynomial.
*/
bool is_zero(numeral_vector & p) { trim(p); return p.empty(); }
/**
\brief Return true if p is a constant polynomial
*/
bool is_const(numeral_vector & p) { trim(p); return p.size() <= 1; }
/**
\brief Copy p to buffer.
*/
void set(unsigned sz, numeral const * p, numeral_vector & buffer);
void set(numeral_vector & target, numeral_vector const & source) { set(source.size(), source.c_ptr(), target); }
/**
\brief Copy p to buffer.
Coefficients of p must be integer.
*/
void set(unsigned sz, rational const * p, numeral_vector & buffer);
/**
\brief Compute the primitive part and the content of f (pp can alias f).
*/
void get_primitive_and_content(unsigned f_sz, numeral const * f, numeral_vector & pp, numeral & cont);
void get_primitive_and_content(numeral_vector const & f, numeral_vector & pp, numeral & cont) {
get_primitive_and_content(f.size(), f.c_ptr(), pp, cont);
}
void get_primitive(numeral_vector const & f, numeral_vector & pp) {
scoped_numeral cont(m());
get_primitive_and_content(f.size(), f.c_ptr(), pp, cont);
}
/**
\brief p := -p
*/
void neg(unsigned sz, numeral * p);
void neg(numeral_vector & p) { neg(p.size(), p.c_ptr()); }
/**
\brief buffer := -p
*/
void neg(unsigned sz, numeral const * p, numeral_vector & buffer);
void neg(numeral_vector const & p, numeral_vector & p_neg) { neg(p.size(), p.c_ptr(), p_neg); }
/**
\brief buffer := p1 + p2
*/
void add(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
void add(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { add(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); }
/**
\brief buffer := p1 - p2
*/
void sub(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
void sub(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { sub(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); }
/**
\brief buffer := p1 * p2
*/
void mul(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
void mul(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { mul(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); }
/**
\brief r := p^k
*/
void pw(unsigned sz, numeral const * p, unsigned k, numeral_vector & r);
/**
\brief buffer := dp/dx
*/
void derivative(unsigned sz1, numeral const * p, numeral_vector & buffer);
void derivative(numeral_vector const & p, numeral_vector & d_p) { derivative(p.size(), p.c_ptr(), d_p); }
/**
\brief Divide coeffients of p by their GCD
*/
void normalize(unsigned sz, numeral * p);
/**
\brief Divide coeffients of p by their GCD
*/
void normalize(numeral_vector & p);
/**
\brief Divide the coefficients of p by b.
This method assumes that every coefficient of p is a multiple of b, and b != 0.
*/
void div(unsigned sz, numeral * p, numeral const & b);
void div(numeral_vector & p, numeral const & b) { div(p.size(), p.c_ptr(), b); }
/**
\brief Multiply the coefficients of p by b.
This method assume b != 0.
*/
void mul(unsigned sz, numeral * p, numeral const & b);
/**
\brief Multiply the coefficients of p by b.
If b == 0, it is equivalent to a reset()
*/
void mul(numeral_vector & p, numeral const & b);
/**
\brief Similar to div_rem but p1 and p2 must not be q and r.
*/
void div_rem_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r);
/**
\brief If numeral is a field, then
return q and r s.t. p1 = q*p2 + r
And degree(r) < degree(p2).
If numeral is not a field, then
return q and r s.t. (b_m)^d * p1 = q * p2 + r
where b_m is the leading coefficient of p2 and d <= sz1 - sz2 + 1
if sz1 >= sz2.
The output value d is irrelevant if numeral is a field.
*/
void div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r);
/**
\see div_rem
*/
void div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q, numeral_vector & r) {
unsigned d = 0;
div_rem(sz1, p1, sz2, p2, d, q, r);
}
void div_rem(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & q, numeral_vector & r) {
div_rem(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), q, r);
}
/**
\see div_rem
*/
void div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q);
/**
\see div_rem
*/
void rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & r);
/**
\see div_rem
*/
void rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r) {
unsigned d = 0;
rem(sz1, p1, sz2, p2, d, r);
}
/**
\brief Signed pseudo-remainder.
Alias for rem(sz1, p1, sz2, p2, r); neg(r);
*/
void srem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r);
/**
\brief Return true if p2 divides p1.
*/
bool divides(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2);
bool divides(numeral_vector const & p1, numeral_vector const & p2) { return divides(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr()); }
/**
\brief Return true if p2 divides p1, and store the quotient in q.
*/
bool exact_div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q);
bool exact_div(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & q) {
return exact_div(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), q);
}
/**
\brief Assuming that we can, make the polynomial monic by dividing with the leading coefficient. It
puts the leading coefficient into lc, and it's inverse into lc_inv.
*/
void mk_monic(unsigned sz, numeral * p, numeral & lc, numeral & lc_inv);
void mk_monic(unsigned sz, numeral * p, numeral & lc) { numeral lc_inv; mk_monic(sz, p, lc, lc_inv); m().del(lc_inv); }
void mk_monic(unsigned sz, numeral * p) { numeral lc, lc_inv; mk_monic(sz, p, lc, lc_inv); m().del(lc); m().del(lc_inv); }
void mk_monic(numeral_vector & p) { mk_monic(p.size(), p.c_ptr()); }
/**
\brief g := gcd(p1, p2)
If in a field the coefficients don't matter, so we also make sure that D is monic.
*/
void gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g);
void euclid_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g);
void subresultant_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g);
void gcd(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & g) {
gcd(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), g);
}
void subresultant_gcd(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & g) {
subresultant_gcd(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), g);
}
/**
\brief g := square free part of p
*/
void square_free(unsigned sz, numeral const * p, numeral_vector & g);
/**
\brief Return true if p is a square-free polynomial.
*/
bool is_square_free(unsigned sz, numeral const * p);
/**
\brief Return true if p is a square-free polynomial.
*/
bool is_square_free(numeral_vector const & p) {
return is_square_free(p.size(), p.c_ptr());
}
/**
\brief Convert a multi-variate polynomial (that is) actually representing an univariate polynomial
into a vector of coefficients.
*/
template<typename polynomial_ref>
void to_numeral_vector(polynomial_ref const & p, numeral_vector & r) {
typename polynomial_ref::manager & pm = p.m();
SASSERT(pm.is_univariate(p));
polynomial_ref np(pm);
np = pm.normalize(p);
unsigned sz = pm.size(p);
unsigned deg = pm.total_degree(p);
r.reserve(deg+1);
for (unsigned i = 0; i <= deg; i++) {
m().reset(r[i]);
}
for (unsigned i = 0; i < sz; i++) {
unsigned k = pm.total_degree(pm.get_monomial(p, i));
SASSERT(k <= deg);
m().set(r[k], pm.coeff(p, i));
}
set_size(deg+1, r);
}
/**
\brief Convert a multi-variate polynomial in [x, y1, ..., yn] to a univariate polynomial in just x
by removing everything multivariate.
*/
template<typename polynomial_ref>
void to_numeral_vector(polynomial_ref const & p, polynomial::var x, numeral_vector & r) {
typename polynomial_ref::manager & pm = p.m();
polynomial_ref np(pm);
np = pm.normalize(p);
unsigned sz = pm.size(p);
unsigned deg = pm.degree(p, x);
r.reserve(deg+1);
for (unsigned i = 0; i <= deg; i++) {
m().reset(r[i]);
}
for (unsigned i = 0; i < sz; i++) {
typename polynomial::monomial * mon = pm.get_monomial(p, i);
if (pm.size(mon) == 0) {
m().set(r[0], pm.coeff(p, i));
} else if (pm.size(mon) == 1 && pm.get_var(mon, 0) == x) {
unsigned m_deg_x = pm.degree(mon, 0);
m().set(r[m_deg_x], pm.coeff(p, i));
}
}
set_size(deg+1, r);
}
/**
\brief Extended GCD
This method assumes that numeral is a field.
It determines U, V, D such that
A*U + B*V = D and D is the GCD of A and B.
Since in a field the coefficients don't matter, we also make sure that D is monic.
*/
void ext_gcd(unsigned szA, numeral const * A, unsigned szB, numeral const * B, numeral_vector & U, numeral_vector & V, numeral_vector & D);
void ext_gcd(numeral_vector const & A, numeral_vector const & B, numeral_vector & U, numeral_vector & V, numeral_vector & D) {
ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D);
}
bool eq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2);
bool eq(numeral_vector const & p1, numeral_vector const & p2) { return eq(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr()); }
void display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x", bool use_star = false) const;
void display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { display(out, p.size(), p.c_ptr(), var_name); }
void display_star(std::ostream & out, unsigned sz, numeral const * p) { display(out, sz, p, "x", true); }
void display_star(std::ostream & out, numeral_vector const & p) { display_star(out, p.size(), p.c_ptr()); }
void display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x") const;
void display_smt2(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const {
return display_smt2(out, p.size(), p.c_ptr(), var_name);
}
};
class scoped_set_z {
core_manager & m;
bool m_modular;
core_manager::scoped_numeral m_p;
public:
scoped_set_z(core_manager & _m):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_z(); }
~scoped_set_z() { if (m_modular) m.set_zp(m_p); }
};
class scoped_set_zp {
core_manager & m;
bool m_modular;
core_manager::scoped_numeral m_p;
public:
scoped_set_zp(core_manager & _m, numeral const & p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); }
scoped_set_zp(core_manager & _m, uint64 p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); }
~scoped_set_zp() { if (m_modular) m.set_zp(m_p); else m.set_z(); }
};
class manager;
typedef core_manager z_manager;
typedef core_manager zp_manager;
typedef z_manager::factors factors;
typedef zp_manager::factors zp_factors;
typedef svector<numeral> numeral_vector;
class scoped_numeral_vector : public _scoped_numeral_vector<numeral_manager> {
public:
scoped_numeral_vector(numeral_manager & m):_scoped_numeral_vector<numeral_manager>(m) {}
scoped_numeral_vector(manager & m);
};
class upolynomial_sequence {
numeral_vector m_seq_coeffs; // coefficients of all polynomials in the sequence
unsigned_vector m_begins; // start position (in m_seq_coeffs) of each polynomial in the sequence
unsigned_vector m_szs; // size of each polynomial in the sequence
friend class manager;
public:
/**
\brief Add a new polynomial to the sequence.
The contents of p is erased.
*/
void push(unsigned sz, numeral * p);
/**
\brief Add a new polynomial to the sequence.
The contents of p is preserved.
*/
void push(numeral_manager & m, unsigned sz, numeral const * p);
/**
\brief Return the number of polynomials in the sequence.
*/
unsigned size() const { return m_szs.size(); }
/**
\brief Return the vector of coefficients for the i-th polynomial in the sequence.
*/
numeral const * coeffs(unsigned i) const { return m_seq_coeffs.c_ptr() + m_begins[i]; }
/**
\brief Return the size of the i-th polynomial in the sequence.
*/
unsigned size(unsigned i) const { return m_szs[i]; }
};
class scoped_upolynomial_sequence : public upolynomial_sequence {
manager & m_manager;
public:
scoped_upolynomial_sequence(manager & m):m_manager(m) {}
~scoped_upolynomial_sequence();
};
class manager : public core_manager {
numeral_vector m_db_tmp;
numeral_vector m_dbab_tmp1;
numeral_vector m_dbab_tmp2;
numeral_vector m_tr_tmp;
numeral_vector m_push_tmp;
int sign_of(numeral const & c);
struct drs_frame;
void pop_top_frame(numeral_vector & p_stack, svector<drs_frame> & frame_stack);
void push_child_frames(unsigned sz, numeral const * p, numeral_vector & p_stack, svector<drs_frame> & frame_stack);
void add_isolating_interval(svector<drs_frame> const & frame_stack, mpbq_manager & bqm, mpbq_vector & lowers, mpbq_vector & uppers);
void add_root(svector<drs_frame> const & frame_stack, mpbq_manager & bqm, mpbq_vector & roots);
void drs_isolate_0_1_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
void drs_isolate_roots(unsigned sz, numeral * p, numeral & U, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
void drs_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
void sqf_nz_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
void sturm_seq_core(upolynomial_sequence & seq);
enum location { PLUS_INF, MINUS_INF, ZERO, MPBQ };
template<location loc>
unsigned sign_variations_at_core(upolynomial_sequence const & seq, mpbq const & b);
void flip_sign(factors & r);
void flip_factor_sign_if_lm_neg(numeral_vector & p, factors & r, unsigned k);
void factor_2_sqf_pp(numeral_vector & p, factors & r, unsigned k);
bool factor_sqf_pp(numeral_vector & p, factors & r, unsigned k, factor_params const & params);
bool factor_core(unsigned sz, numeral const * p, factors & r, factor_params const & params);
public:
manager(z_numeral_manager & m):core_manager(m) {}
~manager();
void reset(numeral_vector & p) { core_manager::reset(p); }
void reset(upolynomial_sequence & seq);
/**
\brief Return true if 0 is a root of p.
*/
bool has_zero_roots(unsigned sz, numeral const * p) { SASSERT(sz > 0); return m().is_zero(p[0]); }
/**
\brief Store in buffer a polynomial that has the same roots of p but the zero roots.
We have that:
forall u, p(u) = 0 and u != 0 implies buffer(u) = 0
forall u, buffer(u) = 0 implies p(u) = 0
This method assumes p is not the zero polynomial
*/
void remove_zero_roots(unsigned sz, numeral const * p, numeral_vector & buffer);
/**
\brief Return true if 1/2 is a root of p.
*/
bool has_one_half_root(unsigned sz, numeral const * p);
/**
\brief Store in buffer a polynomial that has the same roots of p, but a 1/2 root is removed.
This method assumes that 1/2 is a root of p.
*/
void remove_one_half_root(unsigned sz, numeral const * p, numeral_vector & buffer);
/**
\brief Return the number of sign changes in the coefficients of p.
Zero coefficients are ignored.
*/
unsigned sign_changes(unsigned sz, numeral const * p);
/**
\brief Return the descartes bound for the number of roots of p in the interval (0, +oo)
Result:
0 - p has no roots in (0,1)
1 - p has one root in (0,1)
>1 - p has more than one root in (0,1)
*/
unsigned descartes_bound(unsigned sz, numeral const * p);
/**
\brief Return the descartes bound for the number of roots of p in the interval (0, 1)
\see descartes_bound
*/
unsigned descartes_bound_0_1(unsigned sz, numeral const * p);
/**
\brief Return the descartes bound for the number of roots of p in the interval (a, b)
\see descartes_bound
*/
unsigned descartes_bound_a_b(unsigned sz, numeral const * p, mpbq_manager & m, mpbq const & a, mpbq const & b);
/**
\brief p(x) := p(x+1)
*/
void translate(unsigned sz, numeral * p);
void translate(unsigned sz, numeral const * p, numeral_vector & buffer) { set(sz, p, buffer); translate(sz, buffer.c_ptr()); }
/**
\brief p(x) := p(x+2^k)
*/
void translate_k(unsigned sz, numeral * p, unsigned k);
void translate_k(unsigned sz, numeral const * p, unsigned k, numeral_vector & buffer) { set(sz, p, buffer); translate_k(sz, buffer.c_ptr(), k); }
/**
\brief p(x) := p(x+c)
*/
void translate_z(unsigned sz, numeral * p, numeral const & c);
void translate_z(unsigned sz, numeral const * p, numeral const & c, numeral_vector & buffer) { set(sz, p, buffer); translate_z(sz, buffer.c_ptr(), c); }
/**
\brief p(x) := p(x+b) where b = c/2^k
buffer := (2^k)^n * p(x + c/(2^k))
*/
void translate_bq(unsigned sz, numeral * p, mpbq const & b);
void translate_bq(unsigned sz, numeral const * p, mpbq const & b, numeral_vector & buffer) { set(sz, p, buffer); translate_bq(sz, buffer.c_ptr(), b); }
/**
\brief p(x) := p(x+b) where b = c/d
buffer := d^n * p(x + c/d)
*/
void translate_q(unsigned sz, numeral * p, mpq const & b);
void translate_q(unsigned sz, numeral const * p, mpq const & b, numeral_vector & buffer) { set(sz, p, buffer); translate_q(sz, buffer.c_ptr(), b); }
/**
\brief p(x) := 2^n*p(x/2) where n = sz-1
*/
void compose_2n_p_x_div_2(unsigned sz, numeral * p);
/**
\brief p(x) := (2^k)^n * p(x/(2^k))
*/
void compose_2kn_p_x_div_2k(unsigned sz, numeral * p, unsigned k);
/**
\brief p(x) := p(2^k * x)
If u is a root of old(p), then u/2^k is a root of p
*/
void compose_p_2k_x(unsigned sz, numeral * p, unsigned k);
/**
\brief p(x) := p(b * x)
If u is a root of old(p), then u/b is a root of p
*/
void compose_p_b_x(unsigned sz, numeral * p, numeral const & b);
/**
\brief p(x) := p(b * x)
If u is a root of old(p), then u/b is a root of p
Let b be of the form c/(2^k), then this operation is equivalent to:
(2^k)^n*p(c*x/(2^k))
Let old(p) be of the form:
a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0
Then p is of the form:
a_n * c^n * x^n + a_{n-1} * c^{n-1} * 2^k * x^{n-1} + ... + a_1 * c * (2^k)^(n-1) * x + a_0
*/
void compose_p_b_x(unsigned sz, numeral * p, mpbq const & b);
/**
\brief p(x) := p(q*x)
*/
void compose_p_q_x(unsigned sz, numeral * p, mpq const & q);
/**
\brief p(x) := a^n * p(x/a)
*/
void compose_an_p_x_div_a(unsigned sz, numeral * p, numeral const & a);
/**
\brief p(x) := p(-x)
*/
void p_minus_x(unsigned sz, numeral * p);
/**
\brief p(x) := x^n * p(1/x)
*/
void p_1_div_x(unsigned sz, numeral * p);
/**
\brief Evaluate the sign of p(b)
*/
int eval_sign_at(unsigned sz, numeral const * p, mpbq const & b);
/**
\brief Evaluate the sign of p(b)
*/
int eval_sign_at(unsigned sz, numeral const * p, mpq const & b);
/**
\brief Evaluate the sign of p(b)
*/
int eval_sign_at(unsigned sz, numeral const * p, mpz const & b);
/**
\brief Evaluate the sign of p(0)
*/
int eval_sign_at_zero(unsigned sz, numeral const * p);
/**
\brief Evaluate the sign of p(+oo)
*/
int eval_sign_at_plus_inf(unsigned sz, numeral const * p);
/**
\brief Evaluate the sign of p(-oo)
*/
int eval_sign_at_minus_inf(unsigned sz, numeral const * p);
/**
\brief Evaluate the sign variations in the polynomial sequence at -oo
*/
unsigned sign_variations_at_minus_inf(upolynomial_sequence const & seq);
/**
\brief Evaluate the sign variations in the polynomial sequence at +oo
*/
unsigned sign_variations_at_plus_inf(upolynomial_sequence const & seq);
/**
\brief Evaluate the sign variations in the polynomial sequence at 0
*/
unsigned sign_variations_at_zero(upolynomial_sequence const & seq);
/**
\brief Evaluate the sign variations in the polynomial sequence at b
*/
unsigned sign_variations_at(upolynomial_sequence const & seq, mpbq const & b);
/**
\brief Return an upper bound U for all roots of p.
U is a positive value.
We have that if u is a root of p, then |u| < U
*/
void root_upper_bound(unsigned sz, numeral const * p, numeral & U);
unsigned knuth_positive_root_upper_bound(unsigned sz, numeral const * p);
unsigned knuth_negative_root_upper_bound(unsigned sz, numeral const * p);
/**
\brief Return k s.t. for any nonzero root alpha of p(x):
|alpha| > 1/2^k
*/
unsigned nonzero_root_lower_bound(unsigned sz, numeral const * p);
/**
\brief Isolate roots of a square free polynomial p.
The result is stored in three vectors: roots, lowers and uppers.
The vector roots contains actual roots of p.
The vectors lowers and uppers have the same size, and
For all i in [0, lowers.size()), we have that there is only and only one root of p in the interval (lowers[i], uppers[i]).
Every root of p in roots or in an interval (lowers[i], uppers[i])
The total number of roots of p is roots.size() + lowers.size()
\pre p is not the zero polynomial, that is, sz > 0
*/
void sqf_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
/**
\brief Isolate roots of an arbitrary polynomial p.
\see sqf_isolate_roots.
\pre p is not the zero polynomial, that is, sz > 0
*/
void isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
void drs_isolate_roots(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k,
mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
void sturm_isolate_roots_core(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k,
mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
void sturm_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
/**
\brief Compute the sturm sequence for p1 and p2.
*/
void sturm_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq);
/**
\brief Compute the sturm sequence for p and p'.
*/
void sturm_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq);
/**
\brief Compute the sturm tarski sequence for p1 and p1'*p2.
*/
void sturm_tarski_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq);
/**
\brief Compute the Fourier sequence for p.
*/
void fourier_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq);
/**
\brief Convert an isolating interval into a refinable one.
See comments in upolynomial.cpp.
*/
bool isolating2refinable(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b);
//
// Interval refinement procedures
// They all assume p is square free and (a, b) is a refinable isolating interval.
//
// Return TRUE, if interval was squeezed, and new interval is stored in (a,b).
// Return FALSE, if the actual root was found, it is stored in a.
//
// See upolynomial.cpp for additional comments
bool refine_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b);
bool refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b);
bool refine_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k);
bool refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k);
/////////////////////
/**
\brief Convert a isolating (refinable) rational interval into a
isolating refinable binary rational interval.
Return TRUE, if interval was found and the result is stored in (c, d).
Return FALSE, if the actual root was found, it is stored in c.
*/
bool convert_q2bq_interval(unsigned sz, numeral const * p, mpq const & a, mpq const & b, mpbq_manager & bqm, mpbq & c, mpbq & d);
/**
\brief Given a polynomial p, and a lower bound l. Return
the root id i. That is, the first root u > l is the i-th root of p.
*/
unsigned get_root_id(unsigned sz, numeral const * p, mpbq const & l);
/**
\brief Make sure that isolating interval (a, b) for p does not contain zero.
Return TRUE, if updated (a, b) does not contain zero.
Return FALSE, if zero is a root of p
*/
bool normalize_interval_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & m, mpbq & a, mpbq & b);
/**
\brief Similar to normalize_interval_core, but sign_a does not need to be provided.
*/
bool normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq & a, mpbq & b);
/**
\brief Return true if all irreducible factors were found.
That is, if the result if false, there is no guarantee that the factors in r are irreducible.
This can happen when limits (e.g., on the search space size) are set in params.
*/
bool factor(unsigned sz, numeral const * p, factors & r, factor_params const & params = factor_params());
bool factor(numeral_vector const & p, factors & r, factor_params const & params = factor_params()) { return factor(p.size(), p.c_ptr(), r, params); }
void display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x", bool use_star = false) const {
return core_manager::display(out, sz, p, var_name);
}
void display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const {
return core_manager::display(out, p, var_name);
}
void display(std::ostream & out, upolynomial_sequence const & seq, char const * var_name = "x") const;
};
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,96 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
upolynomial_factorization.h
Abstract:
Methods for factoring polynomials.
Author:
Dejan (t-dejanj) 2011-11-29
Notes:
[1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal,
46(8-10):18531859, 1967.
[2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third
edition, 1997.
[3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993.
--*/
#ifndef _UPOLYNOMIAL_FACTORIZATION_H_
#define _UPOLYNOMIAL_FACTORIZATION_H_
#include"upolynomial.h"
#include"polynomial.h"
#include"bit_vector.h"
#include"z3_exception.h"
namespace upolynomial {
typedef manager::scoped_numeral scoped_numeral;
/**
\breif Factor f into f = f_1^k_1 * ... * p_n^k_n, such that p_i are square-free and coprime.
*/
void zp_square_free_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & sq_free_factors);
/**
\brief Factor the monic square-free polynomial f from Z_p[x]. Returns true if factorization was sucesseful, or false
if f is an irreducible square-free polynomial in Z_p[x].
*/
bool zp_factor_square_free(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors);
inline bool zp_factor_square_free(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors, factor_params const & params) {
return zp_factor_square_free(zp_upm, f, factors);
}
/**
\brief Factor the monic square-free polynomial f from Z_p[x] using the Berlekamp algorithm. If randomized is true
the factor splitting is done randomly [3], otherwise it is done as in the original Berlekamp [1].
*/
bool zp_factor_square_free_berlekamp(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors, bool randomized = true);
/**
\brief Factor the polynomial f from Z_p[x]. Returns true if factorization was sucesseful, or false if f is
an irreducible polynomial in Z_p[x]
*/
bool zp_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors);
/**
\brief Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and and a = p^{a_k}, b = p^{b_k},
r = (a, b), with the following assumptions:
* UA + VB = 1 (mod a)
* C = AB (mod b)
* (l(A), r) = 1 (importand in order to divide by A, i.e. to invert l(A))
the output of is two polynomials A1, B1 (replacing A and B) such that A1 = A (mod b), B1 = B (mod b),
l(A1) = l(A), deg(A1) = deg(A), deg(B1) = deg(B) and C = A1 B1 (mod b*r). Such A1, B1 are unique if
r is prime. See [3] p. 138.
The method will also change the zp_manager's module from b to b*r
*/
void hensel_lift(z_manager & upm, numeral const & a, numeral const & b, numeral const & r,
numeral_vector const & U, numeral_vector const & A, numeral_vector const & V, numeral_vector const & B,
numeral_vector const & C, numeral_vector & A_lifted, numeral_vector & B_lifted);
/**
\brief Performs the Hensel lift for the (monic!) factors_p of f in Z_p to Z_{p^e}.
*/
void hensel_lift(z_manager & upm, numeral_vector const & f, zp_factors const & factors_p, unsigned e, zp_factors & factors_pe);
/**
\brief Factor the square-free polynomial f from Z[x]. Returns true if factorization was sucesseful, or false if
f is an irreducible polynomial in Z[x]. The vector of factors is cleared.
*/
bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, factor_params const & ps = factor_params());
/**
Similar to factor_square_free, but it is used to factor the k-th component f^k of a polynomial.
That is, the factors of f are inserted as factors of degree k into fs.
*/
bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, unsigned k, factor_params const & ps = factor_params());
};
#endif

View file

@ -0,0 +1,420 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
upolynomial_factorization_int.h
Abstract:
(Internal) header file for univariate polynomial factorization.
This classes are exposed for debugging purposes only.
Author:
Dejan (t-dejanj) 2011-11-29
Notes:
[1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal,
46(8-10):18531859, 1967.
[2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third
edition, 1997.
[3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993.
--*/
#ifndef _UPOLYNOMIAL_FACTORIZATION_INT_H_
#define _UPOLYNOMIAL_FACTORIZATION_INT_H_
#include"upolynomial_factorization.h"
namespace upolynomial {
// copy p from some manager to zp_p in Z_p[x]
inline void to_zp_manager(zp_manager & zp_upm, numeral_vector & p) {
zp_numeral_manager & zp_nm(zp_upm.m());
for (unsigned i = 0; i < p.size(); ++ i) {
zp_nm.p_normalize(p[i]);
}
zp_upm.trim(p);
}
// copy p from some manager to zp_p in Z_p[x]
inline void to_zp_manager(zp_manager & zp_upm, numeral_vector const & p, numeral_vector & zp_p) {
zp_numeral_manager & zp_nm(zp_upm.m());
zp_upm.reset(zp_p);
for (unsigned i = 0; i < p.size(); ++ i) {
numeral p_i; // no need to delete, we keep it pushed in zp_p
zp_nm.set(p_i, p[i]);
zp_p.push_back(p_i);
}
zp_upm.trim(zp_p);
}
/**
\brief Contains all possible degrees of a factorization of a polynomial.
If
p = p1^{k_1} * ... * pn^{k_n} with p_i of degree d_i
then it is represents numbers of the for \sum a_i*d_i, where a_i <= k_i. Two numbers always in the set are
deg(p) and 0.
*/
class factorization_degree_set {
// the set itself, a (m_max_degree)-binary number
bit_vector m_set;
public:
factorization_degree_set() { }
factorization_degree_set(zp_factors const & factors)
{
zp_manager & upm = factors.upm();
// the set contains only {0}
m_set.push_back(true);
for (unsigned i = 0; i < factors.distinct_factors(); ++ i) {
unsigned degree = upm.degree(factors[i]);
unsigned multiplicity = factors.get_degree(i);
for (unsigned k = 0; k < multiplicity; ++ k) {
bit_vector tmp(m_set);
m_set.shift_right(degree);
m_set |= tmp;
}
}
SASSERT(in_set(0) && in_set(factors.get_degree()));
}
unsigned max_degree() const { return m_set.size() - 1; }
void swap(factorization_degree_set & other) {
m_set.swap(other.m_set);
}
bool is_trivial() const {
// check if set = {0, n}
for (int i = 1; i < (int) m_set.size() - 1; ++ i) {
if (m_set.get(i)) return false;
}
return true;
}
void remove(unsigned k) {
m_set.set(k, false);
}
bool in_set(unsigned k) const {
return m_set.get(k);
}
void intersect(const factorization_degree_set& other) {
m_set &= other.m_set;
}
void display(std::ostream & out) const {
out << "[0";
for (unsigned i = 1; i <= max_degree(); ++ i) {
if (in_set(i)) {
out << ", " << i;
}
}
out << "] represented by " << m_set;
}
};
/**
\brief A to iterate through all combinations of factors. This is only needed for the factorization, and we
always iterate through the
*/
template <typename factors_type>
class factorization_combination_iterator_base {
protected:
// total size of available factors
int m_total_size;
// maximal size of the selection
int m_max_size;
// the factors to select from
factors_type const & m_factors;
// which factors are enabled
svector<bool> m_enabled;
// the size of the current selection
int m_current_size;
// the current selection: indices at positions < m_current_size, other values are maxed out
svector<int> m_current;
/**
Assuming a valid selection m_current[0], ..., m_current[position], try to find the next option for
m_current[position], i.e. the first bigger one that's enabled.
*/
int find(int position, int upper_bound) {
int current = m_current[position] + 1;
while (current < upper_bound && !m_enabled[current]) {
current ++;
}
if (current == upper_bound) {
return -1;
} else {
return current;
}
}
public:
factorization_combination_iterator_base(factors_type const & factors)
: m_total_size(factors.distinct_factors()),
m_max_size(factors.distinct_factors()/2),
m_factors(factors)
{
SASSERT(factors.total_factors() > 1);
SASSERT(factors.total_factors() == factors.distinct_factors());
// enable all to start with
m_enabled.resize(m_factors.distinct_factors(), true);
// max out the m_current so that it always fits
m_current.resize(m_factors.distinct_factors()+1, m_factors.distinct_factors());
m_current_size = 0;
}
/**
\brief Returns the factors we are enumerating through.
*/
factors_type const & get_factors() const {
return m_factors;
}
/**
\brief Computes the next combination of factors and returns true if it exists. If remove current is true
it will eliminate the current selected elements from any future selection.
*/
bool next(bool remove_current) {
int max_upper_bound = m_factors.distinct_factors();
do {
// the index we are currently trying to fix
int current_i = m_current_size - 1;
// the value we found as plausable (-1 we didn't find anything)
int current_value = -1;
if (remove_current) {
SASSERT(m_current_size > 0);
// disable the elements of the current selection from ever appearing again
for (current_i = m_current_size - 1; current_i > 0; -- current_i) {
SASSERT(m_enabled[m_current[current_i]]);
m_enabled[m_current[current_i]] = false;
m_current[current_i] = max_upper_bound;
}
// the last one
SASSERT(m_enabled[m_current[0]]);
m_enabled[m_current[0]] = false;
// not removing current anymore
remove_current = false;
// out max size is also going down
m_total_size -= m_current_size;
m_max_size = m_total_size/2;
}
// we go back to the first one that can be increased (if removing current go all the way)
while (current_i >= 0) {
current_value = find(current_i, m_current[current_i + 1]);
if (current_value >= 0) {
// found one
m_current[current_i] = current_value;
break;
} else {
// go back some more
current_i --;
}
}
do {
if (current_value == -1) {
// we couldn't find any options, we have to increse size and start from the first one of that size
if (m_current_size >= m_max_size) {
return false;
} else {
m_current_size ++;
m_current[0] = -1;
current_i = 0;
current_value = find(current_i, max_upper_bound);
// if we didn't find any, we are done
if (current_value == -1) {
return false;
} else {
m_current[current_i] = current_value;
}
}
}
// ok we have a new selection for the current one
for (current_i ++; current_i < m_current_size; ++ current_i) {
// start from the previous one
m_current[current_i] = m_current[current_i-1];
current_value = find(current_i, max_upper_bound);
if (current_value == -1) {
// screwed, didn't find the next one, this means we need to increase the size
m_current[0] = -1;
break;
} else {
m_current[current_i] = current_value;
}
}
} while (current_value == -1);
} while (filter_current());
// found the next one, hurray
return true;
}
/**
\brief A function that returns true if the current combination should be ignored.
*/
virtual bool filter_current() const = 0;
/**
\brief Returns the size of the current selection (cardinality)
*/
unsigned left_size() const {
return m_current_size;
}
/**
\brief Returns the size of the rest of the current selection (cardinality)
*/
unsigned right_size() const {
return m_total_size - m_current_size;
}
void display(std::ostream& out) const {
out << "[ ";
for (unsigned i = 0; i < m_current.size(); ++ i) {
out << m_current[i] << " ";
}
out << "] from [ ";
for (unsigned i = 0; i < m_factors.distinct_factors(); ++ i) {
if (m_enabled[i]) {
out << i << " ";
}
}
out << "]" << std::endl;
}
};
class ufactorization_combination_iterator : public factorization_combination_iterator_base<zp_factors> {
// the degree sets to choose from
factorization_degree_set const & m_degree_set;
public:
ufactorization_combination_iterator(zp_factors const & factors, factorization_degree_set const & degree_set)
: factorization_combination_iterator_base<zp_factors>(factors),
m_degree_set(degree_set)
{}
/**
\brief Filter the ones not in the degree set.
*/
bool filter_current() const {
// select only the ones that have degrees in the degree set
if (!m_degree_set.in_set(current_degree())) {
return true;
}
return false;
}
/**
\brief Returns the degree of the current selection.
*/
unsigned current_degree() const {
unsigned degree = 0;
zp_manager & upm = m_factors.pm();
for (unsigned i = 0; i < left_size(); ++ i) {
degree += upm.degree(m_factors[m_current[i]]);
}
return degree;
}
void left(numeral_vector & out) const {
SASSERT(m_current_size > 0);
zp_manager & upm = m_factors.upm();
upm.set(m_factors[m_current[0]].size(), m_factors[m_current[0]].c_ptr(), out);
for (int i = 1; i < m_current_size; ++ i) {
upm.mul(out.size(), out.c_ptr(), m_factors[m_current[i]].size(), m_factors[m_current[i]].c_ptr(), out);
}
}
void get_left_tail_coeff(numeral const & m, numeral & out) {
zp_numeral_manager & nm = m_factors.upm().m();
nm.set(out, m);
for (int i = 0; i < m_current_size; ++ i) {
nm.mul(out, m_factors[m_current[i]][0], out);
}
}
void get_right_tail_coeff(numeral const & m, numeral & out) {
zp_numeral_manager & nm = m_factors.upm().m();
nm.set(out, m);
unsigned current = 0;
unsigned selection_i = 0;
// selection is ordered, so we just take the ones in between that are not disable
while (current < m_factors.distinct_factors()) {
if (!m_enabled[current]) {
// by skipping the disabled we never skip a selected one
current ++;
} else {
if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) {
SASSERT(m_factors.get_degree(current) == 1);
nm.mul(out, m_factors[current][0], out);
current ++;
} else {
current ++;
selection_i ++;
}
}
}
}
void right(numeral_vector & out) const {
SASSERT(m_current_size > 0);
zp_manager & upm = m_factors.upm();
upm.reset(out);
unsigned current = 0;
unsigned selection_i = 0;
// selection is ordered, so we just take the ones in between that are not disable
while (current < m_factors.distinct_factors()) {
if (!m_enabled[current]) {
// by skipping the disabled we never skip a selected one
current ++;
} else {
if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) {
SASSERT(m_factors.get_degree(current) == 1);
if (out.size() == 0) {
upm.set(m_factors[current].size(), m_factors[current].c_ptr(), out);
} else {
upm.mul(out.size(), out.c_ptr(), m_factors[current].size(), m_factors[current].c_ptr(), out);
}
current ++;
} else {
current ++;
selection_i ++;
}
}
}
}
};
};
#endif