#pragma once
#include "util/mpzzp.h"
#include "util/rational.h"
#include "math/polynomial/polynomial.h"
#include "util/z3_exception.h"
#include "util/mpbq.h"
#include "util/rlimit.h"
#define FACTOR_VERBOSE_LVL 1000
namespace upolynomial {
typedef polynomial::factor_params factor_params;
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;
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);
void push_back_swap(numeral_vector & p, unsigned degree);
void swap_factor(unsigned i, numeral_vector & p);
void swap(factors & other) noexcept;
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:
reslimit& m_limit;
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;
static bool is_alias(numeral const * p, numeral_vector & buffer) { return buffer.data() != nullptr && buffer.data() == 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(reslimit& lim, 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; }
reslimit& lim() const { return m_limit; }
bool modular() const { return m().modular(); }
bool field() const { return m().field(); }
numeral const & p() const { return m().p(); }
void set_z() { m().set_z(); }
void set_zp(numeral const & p) { m().set_zp(p); }
void set_zp(uint64_t p) { m().set_zp(p); }
void checkpoint();
void reset(numeral_vector & p);
void trim(numeral_vector & p);
void set_size(unsigned sz, numeral_vector & buffer);
unsigned degree(numeral_vector const & p) {
unsigned sz = p.size();
return sz == 0 ? 0 : sz - 1;
}
bool is_zero(numeral_vector & p) { trim(p); return p.empty(); }
bool is_const(numeral_vector & p) { trim(p); return p.size() <= 1; }
void set(unsigned sz, numeral const * p, numeral_vector & buffer);
void set(numeral_vector & target, numeral_vector const & source) { set(source.size(), source.data(), target); }
void set(unsigned sz, rational const * p, numeral_vector & buffer);
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.data(), pp, cont);
}
void get_primitive(numeral_vector const & f, numeral_vector & pp) {
scoped_numeral cont(m());
get_primitive_and_content(f.size(), f.data(), pp, cont);
}
void neg(unsigned sz, numeral * p);
void neg(numeral_vector & p) { neg(p.size(), p.data()); }
void neg(unsigned sz, numeral const * p, numeral_vector & buffer);
void neg(numeral_vector const & p, numeral_vector & p_neg) { neg(p.size(), p.data(), p_neg); }
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.data(), b.size(), b.data(), c); }
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.data(), b.size(), b.data(), c); }
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.data(), b.size(), b.data(), c); }
void pw(unsigned sz, numeral const * p, unsigned k, numeral_vector & r);
void derivative(unsigned sz1, numeral const * p, numeral_vector & buffer);
void derivative(numeral_vector const & p, numeral_vector & d_p) { derivative(p.size(), p.data(), d_p); }
void normalize(unsigned sz, numeral * p);
void normalize(numeral_vector & p);
void div(unsigned sz, numeral * p, numeral const & b);
void div(numeral_vector & p, numeral const & b) { div(p.size(), p.data(), b); }
void mul(unsigned sz, numeral * p, numeral const & b);
void mul(numeral_vector & p, numeral const & b);
void div_rem_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r);
void div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r);
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.data(), p2.size(), p2.data(), q, r);
}
void div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q);
void rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & r);
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);
}
void srem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r);
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.data(), p2.size(), p2.data()); }
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.data(), p2.size(), p2.data(), q);
}
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.data()); }
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.data(), p2.size(), p2.data(), g);
}
void subresultant_gcd(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & g) {
subresultant_gcd(p1.size(), p1.data(), p2.size(), p2.data(), g);
}
void square_free(unsigned sz, numeral const * p, numeral_vector & g);
bool is_square_free(unsigned sz, numeral const * p);
bool is_square_free(numeral_vector const & p) {
return is_square_free(p.size(), p.data());
}
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);
}
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);
}
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.data(), B.size(), B.data(), 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.data(), p2.size(), p2.data()); }
std::ostream& display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x", bool use_star = false) const;
std::ostream& display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { return display(out, p.size(), p.data(), var_name); }
std::ostream& display_star(std::ostream & out, unsigned sz, numeral const * p) { return display(out, sz, p, "x", true); }
std::ostream& display_star(std::ostream & out, numeral_vector const & p) { return display_star(out, p.size(), p.data()); }
std::ostream& display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x") const;
std::ostream& display_smt2(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const {
return display_smt2(out, p.size(), p.data(), var_name);
}
std::ostream& display_smt2_no_power(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x") const;
};
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_t 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; unsigned_vector m_begins; unsigned_vector m_szs; friend class manager;
public:
void push(unsigned sz, numeral * p);
void push(numeral_manager & m, unsigned sz, numeral const * p);
unsigned size() const { return m_szs.size(); }
numeral const * coeffs(unsigned i) const { return m_seq_coeffs.data() + m_begins[i]; }
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;
sign 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(reslimit& lim, z_numeral_manager & m):core_manager(lim, m) {}
~manager();
void reset(numeral_vector & p) { core_manager::reset(p); }
void reset(upolynomial_sequence & seq);
bool has_zero_roots(unsigned sz, numeral const * p) { SASSERT(sz > 0); return m().is_zero(p[0]); }
void remove_zero_roots(unsigned sz, numeral const * p, numeral_vector & buffer);
bool has_one_half_root(unsigned sz, numeral const * p);
void remove_one_half_root(unsigned sz, numeral const * p, numeral_vector & buffer);
unsigned sign_changes(unsigned sz, numeral const * p);
unsigned descartes_bound(unsigned sz, numeral const * p);
unsigned descartes_bound_0_1(unsigned sz, numeral const * p);
unsigned descartes_bound_a_b(unsigned sz, numeral const * p, mpbq_manager & m, mpbq const & a, mpbq const & b);
void translate(unsigned sz, numeral * p);
void translate(unsigned sz, numeral const * p, numeral_vector & buffer) { set(sz, p, buffer); translate(sz, buffer.data()); }
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.data(), k); }
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.data(), c); }
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.data(), b); }
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.data(), b); }
void compose_2n_p_x_div_2(unsigned sz, numeral * p);
void compose_2kn_p_x_div_2k(unsigned sz, numeral * p, unsigned k);
void compose_p_2k_x(unsigned sz, numeral * p, unsigned k);
void compose_p_b_x(unsigned sz, numeral * p, numeral const & b);
void compose_p_b_x(unsigned sz, numeral * p, mpbq const & b);
void compose_p_q_x(unsigned sz, numeral * p, mpq const & q);
void compose_an_p_x_div_a(unsigned sz, numeral * p, numeral const & a);
void p_minus_x(unsigned sz, numeral * p);
void p_1_div_x(unsigned sz, numeral * p);
sign eval_sign_at(unsigned sz, numeral const * p, mpbq const & b);
sign eval_sign_at(unsigned sz, numeral const * p, mpq const & b);
sign eval_sign_at(unsigned sz, numeral const * p, mpz const & b);
sign eval_sign_at_zero(unsigned sz, numeral const * p);
sign eval_sign_at_plus_inf(unsigned sz, numeral const * p);
sign eval_sign_at_minus_inf(unsigned sz, numeral const * p);
unsigned sign_variations_at_minus_inf(upolynomial_sequence const & seq);
unsigned sign_variations_at_plus_inf(upolynomial_sequence const & seq);
unsigned sign_variations_at_zero(upolynomial_sequence const & seq);
unsigned sign_variations_at(upolynomial_sequence const & seq, mpbq const & b);
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);
unsigned nonzero_root_lower_bound(unsigned sz, numeral const * p);
void sqf_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
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);
void sturm_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq);
void sturm_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq);
void sturm_tarski_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq);
void fourier_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq);
bool isolating2refinable(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b);
bool refine_core(unsigned sz, numeral const * p, sign 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, sign 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);
bool convert_q2bq_interval(unsigned sz, numeral const * p, mpq const & a, mpq const & b, mpbq_manager & bqm, mpbq & c, mpbq & d);
unsigned get_root_id(unsigned sz, numeral const * p, mpbq const & l);
bool normalize_interval_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & m, mpbq & a, mpbq & b);
bool normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq & a, mpbq & b);
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.data(), r, params); }
std::ostream& 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);
}
std::ostream& display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const {
return core_manager::display(out, p, var_name);
}
std::ostream& display(std::ostream & out, upolynomial_sequence const & seq, char const * var_name = "x") const;
};
};