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:
parent
b89d35dd69
commit
9e299b88c4
101 changed files with 16 additions and 16 deletions
3117
src/math/polynomial/algebraic_numbers.cpp
Normal file
3117
src/math/polynomial/algebraic_numbers.cpp
Normal file
File diff suppressed because it is too large
Load diff
493
src/math/polynomial/algebraic_numbers.h
Normal file
493
src/math/polynomial/algebraic_numbers.h
Normal 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
|
152
src/math/polynomial/linear_eq_solver.h
Normal file
152
src/math/polynomial/linear_eq_solver.h
Normal 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
|
7386
src/math/polynomial/polynomial.cpp
Normal file
7386
src/math/polynomial/polynomial.cpp
Normal file
File diff suppressed because it is too large
Load diff
1380
src/math/polynomial/polynomial.h
Normal file
1380
src/math/polynomial/polynomial.h
Normal file
File diff suppressed because it is too large
Load diff
235
src/math/polynomial/polynomial_cache.cpp
Normal file
235
src/math/polynomial/polynomial_cache.cpp
Normal 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);
|
||||
}
|
||||
};
|
44
src/math/polynomial/polynomial_cache.h
Normal file
44
src/math/polynomial/polynomial_cache.h
Normal 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
|
1143
src/math/polynomial/polynomial_factorization.cpp
Normal file
1143
src/math/polynomial/polynomial_factorization.cpp
Normal file
File diff suppressed because it is too large
Load diff
65
src/math/polynomial/polynomial_factorization.h
Normal file
65
src/math/polynomial/polynomial_factorization.h
Normal 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):1853–1859, 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
|
72
src/math/polynomial/polynomial_primes.h
Normal file
72
src/math/polynomial/polynomial_primes.h
Normal 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
|
50
src/math/polynomial/polynomial_var2value.h
Normal file
50
src/math/polynomial/polynomial_var2value.h
Normal 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
|
800
src/math/polynomial/rpolynomial.cpp
Normal file
800
src/math/polynomial/rpolynomial.cpp
Normal 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
|
||||
|
||||
};
|
208
src/math/polynomial/rpolynomial.h
Normal file
208
src/math/polynomial/rpolynomial.h
Normal 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
|
112
src/math/polynomial/sexpr2upolynomial.cpp
Normal file
112
src/math/polynomial/sexpr2upolynomial.cpp
Normal 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);
|
||||
}
|
33
src/math/polynomial/sexpr2upolynomial.h
Normal file
33
src/math/polynomial/sexpr2upolynomial.h
Normal 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
|
3131
src/math/polynomial/upolynomial.cpp
Normal file
3131
src/math/polynomial/upolynomial.cpp
Normal file
File diff suppressed because it is too large
Load diff
922
src/math/polynomial/upolynomial.h
Normal file
922
src/math/polynomial/upolynomial.h
Normal 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
|
1300
src/math/polynomial/upolynomial_factorization.cpp
Normal file
1300
src/math/polynomial/upolynomial_factorization.cpp
Normal file
File diff suppressed because it is too large
Load diff
96
src/math/polynomial/upolynomial_factorization.h
Normal file
96
src/math/polynomial/upolynomial_factorization.h
Normal 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):1853–1859, 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
|
420
src/math/polynomial/upolynomial_factorization_int.h
Normal file
420
src/math/polynomial/upolynomial_factorization_int.h
Normal 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):1853–1859, 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
|
Loading…
Add table
Add a link
Reference in a new issue