Copyright (c) 2011 Microsoft Corporation
Module Name:
upolynomial.cpp
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:
--*/
#include "math/polynomial/upolynomial.h"
#include "math/polynomial/upolynomial_factorization.h"
#include "math/polynomial/polynomial_primes.h"
#include "util/buffer.h"
#include "util/common_msgs.h"
namespace upolynomial {
core_manager::factors::factors(core_manager & upm):
m_upm(upm),
m_total_factors(0),
m_total_degree(0) {
nm().set(m_constant, 1);
}
core_manager::factors::~factors() {
clear();
nm().del(m_constant);
}
void core_manager::factors::clear() {
for (unsigned i = 0; i < m_factors.size(); ++ i) {
m_upm.reset(m_factors[i]);
}
m_factors.reset();
m_degrees.reset();
nm().set(m_constant, 1);
m_total_factors = 0;
m_total_degree = 0;
}
void core_manager::factors::push_back(numeral_vector const & p, unsigned degree) {
SASSERT(degree > 0);
m_factors.push_back(numeral_vector());
m_degrees.push_back(degree);
m_upm.set(p.size(), p.data(), m_factors.back());
m_total_factors += degree;
m_total_degree += m_upm.degree(p)*degree;
}
void core_manager::factors::push_back_swap(numeral_vector & p, unsigned degree) {
SASSERT(degree > 0);
m_factors.push_back(numeral_vector());
m_degrees.push_back(degree);
p.swap(m_factors.back());
m_total_factors += degree;
m_total_degree += m_upm.degree(p)*degree;
}
void core_manager::factors::multiply(numeral_vector & out) const {
m_upm.reset(out);
if (nm().is_zero(m_constant)) {
SASSERT(m_factors.empty());
return;
}
out.push_back(numeral());
nm().set(out.back(), m_constant);
for (unsigned i = 0; i < m_factors.size(); ++ i) {
if (m_degrees[i] > 1) {
numeral_vector power;
m_upm.pw(m_factors[i].size(), m_factors[i].data(), m_degrees[i], power);
m_upm.mul(out.size(), out.data(), power.size(), power.data(), out);
m_upm.reset(power);
} else {
m_upm.mul(out.size(), out.data(), m_factors[i].size(), m_factors[i].data(), out);
}
}
}
void core_manager::factors::display(std::ostream & out) const {
out << nm().to_string(m_constant);
if (!m_factors.empty()) {
for (unsigned i = 0; i < m_factors.size(); ++ i) {
out << " * (";
m_upm.display(out, m_factors[i]);
out << ")^" << m_degrees[i];
}
}
}
numeral_manager & core_manager::factors::nm() const {
return m_upm.m();
}
void core_manager::factors::set_constant(numeral const & constant) {
nm().set(m_constant, constant);
}
void core_manager::factors::set_degree(unsigned i, unsigned degree) {
m_total_degree -= m_upm.degree(m_factors[i])*m_degrees[i];
m_total_factors -= m_degrees[i];
m_degrees[i] = degree;
m_total_factors += degree;
m_total_degree += m_upm.degree(m_factors[i])*degree;
}
void core_manager::factors::swap_factor(unsigned i, numeral_vector & p) {
m_total_degree -= m_upm.degree(m_factors[i])*m_degrees[i];
m_total_degree += m_upm.degree(p)*m_degrees[i];
m_factors[i].swap(p);
}
void core_manager::factors::swap(factors & other) {
m_factors.swap(other.m_factors);
m_degrees.swap(other.m_degrees);
nm().swap(m_constant, other.m_constant);
std::swap(m_total_factors, other.m_total_factors);
std::swap(m_total_degree, other.m_total_degree);
}
core_manager::core_manager(reslimit& lim, unsynch_mpz_manager & m):
m_limit(lim),
m_manager(m) {
}
core_manager::~core_manager() {
reset(m_basic_tmp);
reset(m_div_tmp1);
reset(m_div_tmp2);
reset(m_exact_div_tmp);
reset(m_gcd_tmp1);
reset(m_gcd_tmp2);
reset(m_CRA_tmp);
for (unsigned i = 0; i < UPOLYNOMIAL_MGCD_TMPS; i++) reset(m_mgcd_tmp[i]);
reset(m_sqf_tmp1);
reset(m_sqf_tmp2);
reset(m_pw_tmp);
}
void core_manager::checkpoint() {
if (!m_limit.inc())
throw upolynomial_exception(Z3_CANCELED_MSG);
}
void core_manager::trim(numeral_vector & buffer) {
unsigned sz = buffer.size();
while (sz > 0 && m().is_zero(buffer[sz - 1])) {
m().del(buffer[sz - 1]);
sz--;
}
buffer.shrink(sz);
}
void core_manager::set_size(unsigned sz, numeral_vector & buffer) {
unsigned old_sz = buffer.size();
SASSERT(old_sz >= sz);
for (unsigned i = sz; i < old_sz; i++) {
m().del(buffer[i]);
}
buffer.shrink(sz);
trim(buffer);
}
void core_manager::reset(numeral_vector & buffer) {
set_size(0, buffer);
}
void core_manager::set(unsigned sz, numeral const * p, numeral_vector & buffer) {
if (p != nullptr && buffer.data() == p) {
SASSERT(buffer.size() == sz);
return;
}
buffer.reserve(sz);
for (unsigned i = 0; i < sz; i++) {
m().set(buffer[i], p[i]);
}
set_size(sz, buffer);
}
void core_manager::set(unsigned sz, rational const * p, numeral_vector & buffer) {
buffer.reserve(sz);
for (unsigned i = 0; i < sz; i++) {
SASSERT(p[i].is_int());
m().set(buffer[i], p[i].to_mpq().numerator());
}
set_size(sz, buffer);
}
void core_manager::get_primitive_and_content(unsigned f_sz, numeral const * f, numeral_vector & pp, numeral & cont) {
SASSERT(f_sz > 0);
m().gcd(f_sz, f, cont);
SASSERT(m().is_pos(cont));
if (m().is_one(cont)) {
set(f_sz, f, pp);
}
else {
pp.reserve(f_sz);
for (unsigned i = 0; i < f_sz; i++) {
if (!m().is_zero(f[i])) {
m().div(f[i], cont, pp[i]);
}
else {
m().set(pp[i], 0);
}
}
set_size(f_sz, pp);
}
}
void core_manager::neg(unsigned sz, numeral * p) {
for (unsigned i = 0; i < sz; i++) {
m().neg(p[i]);
}
}
void core_manager::neg_core(unsigned sz, numeral const * p, numeral_vector & buffer) {
SASSERT(!is_alias(p, buffer));
buffer.reserve(sz);
for (unsigned i = 0; i < sz; i++) {
m().set(buffer[i], p[i]);
m().neg(buffer[i]);
}
set_size(sz, buffer);
}
void core_manager::neg(unsigned sz, numeral const * p, numeral_vector & buffer) {
neg_core(sz, p, m_basic_tmp);
buffer.swap(m_basic_tmp);
}
void core_manager::add_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
SASSERT(!is_alias(p1, buffer));
SASSERT(!is_alias(p2, buffer));
unsigned min_sz = std::min(sz1, sz2);
unsigned max_sz = std::max(sz1, sz2);
unsigned i = 0;
buffer.reserve(max_sz);
for (; i < min_sz; i++) {
m().add(p1[i], p2[i], buffer[i]);
}
for (; i < sz1; i++) {
m().set(buffer[i], p1[i]);
}
for (; i < sz2; i++) {
m().set(buffer[i], p2[i]);
}
set_size(max_sz, buffer);
}
void core_manager::add(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
add_core(sz1, p1, sz2, p2, m_basic_tmp);
buffer.swap(m_basic_tmp);
}
void core_manager::sub_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
SASSERT(!is_alias(p1, buffer));
SASSERT(!is_alias(p2, buffer));
unsigned min_sz = std::min(sz1, sz2);
unsigned max_sz = std::max(sz1, sz2);
unsigned i = 0;
buffer.reserve(max_sz);
for (; i < min_sz; i++) {
m().sub(p1[i], p2[i], buffer[i]);
}
for (; i < sz1; i++) {
m().set(buffer[i], p1[i]);
}
for (; i < sz2; i++) {
m().set(buffer[i], p2[i]);
m().neg(buffer[i]);
}
set_size(max_sz, buffer);
}
void core_manager::sub(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
sub_core(sz1, p1, sz2, p2, m_basic_tmp);
buffer.swap(m_basic_tmp);
}
void core_manager::mul_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
SASSERT(!is_alias(p1, buffer));
SASSERT(!is_alias(p2, buffer));
if (sz1 == 0) {
reset(buffer);
}
else if (sz2 == 0) {
reset(buffer);
}
else {
unsigned new_sz = sz1 + sz2 - 1;
buffer.reserve(new_sz);
for (unsigned i = 0; i < new_sz; i++) {
m().reset(buffer[i]);
}
if (sz1 < sz2) {
std::swap(sz1, sz2);
std::swap(p1, p2);
}
for (unsigned i = 0; i < sz1; i++) {
checkpoint();
numeral const & a_i = p1[i];
if (m().is_zero(a_i))
continue;
for (unsigned j = 0; j < sz2; j++) {
numeral const & b_j = p2[j];
if (m().is_zero(b_j))
continue;
m().addmul(buffer[i+j], a_i, b_j, buffer[i+j]);
}
}
set_size(new_sz, buffer);
}
}
void core_manager::mul(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
mul_core(sz1, p1, sz2, p2, m_basic_tmp);
buffer.swap(m_basic_tmp);
}
void core_manager::derivative(unsigned sz, numeral const * p, numeral_vector & buffer) {
if (sz <= 1) {
reset(buffer);
return;
}
buffer.reserve(sz - 1);
for (unsigned i = 1; i < sz; i++) {
numeral d;
m().set(d, i);
m().mul(p[i], d, buffer[i-1]);
}
set_size(sz-1, buffer);
}
void core_manager::normalize(unsigned sz, numeral * p) {
if (sz == 0)
return;
if (sz == 1) {
if (m().is_pos(p[0]))
m().set(p[0], 1);
else
m().set(p[0], -1);
return;
}
scoped_numeral g(m());
m().gcd(sz, p, g);
if (m().is_one(g))
return;
for (unsigned i = 0; i < sz; i++) {
#ifdef Z3DEBUG
scoped_numeral old_p_i(m());
old_p_i = p[i];
#endif
m().div(p[i], g, p[i]);
#ifdef Z3DEBUG
scoped_numeral tmp(m());
m().mul(g, p[i], tmp);
CTRACE("div_bug", !m().eq(tmp, old_p_i), tout << "old(p[i]): " << m().to_string(old_p_i) << ", g: " << m().to_string(g) << ", p[i]: " <<
m().to_string(p[i]) << ", tmp: " << m().to_string(tmp) << "\n";);
SASSERT(tmp == old_p_i);
#endif
}
}
void core_manager::normalize(numeral_vector & p) {
normalize(p.size(), p.data());
}
void core_manager::div(unsigned sz, numeral * p, numeral const & b) {
SASSERT(!m().is_zero(b));
if (m().is_one(b))
return;
for (unsigned i = 0; i < sz; i++) {
CTRACE("upolynomial", !m().divides(b, p[i]), tout << "b: " << m().to_string(b) << ", p[i]: " << m().to_string(p[i]) << "\n";);
SASSERT(m().divides(b, p[i]));
m().div(p[i], b, p[i]);
}
}
void core_manager::mul(unsigned sz, numeral * p, numeral const & b) {
SASSERT(!m().is_zero(b));
if (m().is_one(b))
return;
for (unsigned i = 0; i < sz; i++) {
m().mul(p[i], b, p[i]);
}
}
void core_manager::mul(numeral_vector & p, numeral const & b) {
if (m().is_zero(b)) {
reset(p);
return;
}
mul(p.size(), p.data(), b);
}
void core_manager::div_rem_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2,
unsigned & d, numeral_vector & q, numeral_vector & r) {
SASSERT(!is_alias(p1, q)); SASSERT(!is_alias(p2, q));
SASSERT(!is_alias(p1, r)); SASSERT(!is_alias(p2, r));
d = 0;
SASSERT(sz2 > 0);
if (sz2 == 1) {
set(sz1, p1, q);
if (field()) {
div(q, *p2);
}
reset(r);
return;
}
reset(q);
set(sz1, p1, r);
if (sz1 <= 1)
return;
unsigned qsz;
if (sz1 >= sz2) {
q.reserve(sz1 - sz2 + 1);
qsz = sz1 - sz2 + 1;
}
else {
qsz = 0;
}
numeral const & b_n = p2[sz2-1];
SASSERT(!m().is_zero(b_n));
scoped_numeral a_m(m());
while (true) {
checkpoint();
sz1 = r.size();
if (sz1 < sz2) {
set_size(qsz, q);
return;
}
unsigned m_n = sz1 - sz2;
if (field()) {
numeral & ratio = a_m;
m().div(r[sz1 - 1], b_n, ratio);
m().add(q[m_n], ratio, q[m_n]);
for (unsigned i = 0; i < sz2 - 1; i++) {
m().submul(r[i + m_n], ratio, p2[i], r[i + m_n]);
}
}
else {
d++;
m().set(a_m, r[sz1 - 1]);
for (unsigned i = 0; i < sz1 - 1; i++) {
m().mul(r[i], b_n, r[i]);
}
for (unsigned i = 0; i < qsz; i++) {
m().mul(q[i], b_n, q[i]);
}
m().add(q[m_n], a_m, q[m_n]);
for (unsigned i = 0; i < sz2 - 1; i++) {
m().submul(r[i + m_n], a_m, p2[i], r[i + m_n]);
}
}
set_size(sz1 - 1, r);
}
}
void core_manager::div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d,
numeral_vector & q, numeral_vector & r) {
numeral_vector & _r = m_div_tmp1;
numeral_vector & _q = m_div_tmp2;
div_rem_core(sz1, p1, sz2, p2, d, _q, _r);
r.swap(_r);
q.swap(_q);
}
void core_manager::div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q) {
unsigned d;
numeral_vector & _r = m_div_tmp1;
numeral_vector & _q = m_div_tmp2;
div_rem_core(sz1, p1, sz2, p2, d, _q, _r);
reset(_r);
q.swap(_q);
}
void core_manager::rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & buffer) {
SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer));
d = 0;
SASSERT(sz2 > 0);
if (sz2 == 1) {
reset(buffer);
return;
}
set(sz1, p1, buffer);
if (sz1 <= 1)
return;
numeral const & b_n = p2[sz2-1];
SASSERT(!m().is_zero(b_n));
scoped_numeral a_m(m());
while (m_limit.inc()) {
TRACE("rem_bug", tout << "rem loop, p2:\n"; display(tout, sz2, p2); tout << "\nbuffer:\n"; display(tout, buffer); tout << "\n";);
sz1 = buffer.size();
if (sz1 < sz2) {
TRACE("rem_bug", tout << "finished\n";);
return;
}
unsigned m_n = sz1 - sz2;
if (field()) {
numeral & ratio = a_m;
m().div(buffer[sz1 - 1], b_n, ratio);
for (unsigned i = 0; i < sz2 - 1; i++) {
m().submul(buffer[i + m_n], ratio, p2[i], buffer[i + m_n]);
}
}
else {
d++;
m().set(a_m, buffer[sz1 - 1]);
TRACE("rem_bug", tout << "a_m: " << m().to_string(a_m) << ", b_n: " << m().to_string(b_n) << "\n";);
for (unsigned i = 0; i < sz1 - 1; i++) {
m().mul(buffer[i], b_n, buffer[i]);
}
for (unsigned i = 0; i < sz2 - 1; i++) {
m().submul(buffer[i + m_n], a_m, p2[i], buffer[i + m_n]);
}
}
set_size(sz1 - 1, buffer);
}
}
void core_manager::srem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer));
unsigned d;
rem(sz1, p1, sz2, p2, d, buffer);
if (d % 2 == 0 || (sz2 > 0 && m().is_pos(p2[sz2-1])))
neg(buffer.size(), buffer.data());
}
bool core_manager::divides(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2) {
if (sz2 == 0)
return false;
if (sz1 == 0)
return true;
if (sz2 > sz1 || !m().divides(p2[sz2-1], p1[sz1-1]))
return false;
scoped_numeral b(m());
numeral_vector & _p1 = m_div_tmp1;
set(sz1, p1, _p1);
while (true) {
if (sz1 == 0)
return true;
if (sz2 > sz1 || !m().divides(p2[sz2-1], _p1[sz1-1]))
return false;
unsigned delta = sz1 - sz2;
m().div(_p1[sz1-1], p2[sz2-1], b);
for (unsigned i = 0; i < sz2 - 1; i++) {
if (!m().is_zero(p2[i]))
m().submul(_p1[i+delta], b, p2[i], _p1[i+delta]);
}
m().reset(_p1[sz1-1]);
trim(_p1);
sz1 = _p1.size();
}
return true;
}
bool core_manager::exact_div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r) {
if (sz2 == 0)
return false;
if (sz1 == 0) {
reset(r);
return true;
}
if (sz2 > sz1 || !m().divides(p2[sz2-1], p1[sz1-1]) || !m().divides(p2[0], p1[0]))
return false;
numeral_vector & _r = m_exact_div_tmp;
reset(_r);
unsigned deg = sz1 - sz2;
_r.reserve(deg+1);
numeral_vector & _p1 = m_div_tmp1;
TRACE("factor_bug", tout << "sz1: " << sz1 << " p1: " << p1 << ", _p1.c_ptr(): " << _p1.data() << ", _p1.size(): " << _p1.size() << "\n";);
set(sz1, p1, _p1);
SASSERT(_p1.size() == sz1);
while (true) {
TRACE("upolynomial", tout << "exact_div loop...\n"; display(tout, _p1); tout << "\n"; display(tout, _r); tout << "\n";);
if (sz1 == 0) {
set_size(deg+1, _r);
r.swap(_r);
return true;
}
if (sz2 > sz1 || !m().divides(p2[sz2-1], _p1[sz1-1]) || !m().divides(p2[0], _p1[0])) {
reset(r);
return false;
}
unsigned delta = sz1 - sz2;
numeral & a_r = _r[delta];
m().div(_p1[sz1-1], p2[sz2-1], a_r);
for (unsigned i = 0; i < sz2 - 1; i++) {
if (!m().is_zero(p2[i]))
m().submul(_p1[i+delta], a_r, p2[i], _p1[i+delta]);
}
m().reset(_p1[sz1-1]);
trim(_p1);
sz1 = _p1.size();
}
return true;
}
void core_manager::flip_sign_if_lm_neg(numeral_vector & buffer) {
unsigned sz = buffer.size();
if (sz == 0)
return;
if (m().is_neg(buffer[sz - 1])) {
for (unsigned i = 0; i < sz; i++)
m().neg(buffer[i]);
}
}
void core_manager::CRA_combine_images(numeral_vector const & C1, numeral const & b1, numeral_vector & C2, numeral & b2) {
SASSERT(!m().m().is_even(b1));
SASSERT(!m().m().is_even(b2));
numeral_vector & R = m_CRA_tmp; reset(R);
scoped_numeral inv1(m());
scoped_numeral inv2(m());
scoped_numeral g(m());
m().gcd(b1, b2, inv1, inv2, g);
SASSERT(m().is_one(g));
m().m().mod(inv1, b2, inv1);
m().m().mod(inv2, b1, inv2);
TRACE("CRA", tout << "inv1: " << inv1 << ", inv2: " << inv2 << "\n";);
scoped_numeral a1(m());
scoped_numeral a2(m());
m().mul(b2, inv2, a1);
m().mul(b1, inv1, a2);
TRACE("CRA", tout << "a1: " << a1 << ", a2: " << a2 << "\n";);
scoped_numeral new_bound(m());
m().mul(b1, b2, new_bound);
scoped_numeral lower(m());
scoped_numeral upper(m());
scoped_numeral new_a(m()), tmp1(m()), tmp2(m()), tmp3(m());
m().div(new_bound, 2, upper);
m().set(lower, upper);
m().neg(lower);
TRACE("CRA", tout << "lower: " << lower << ", upper: " << upper << "\n";);
#define ADD(A1, A2) { \
m().mul(A1, a1, tmp1); \
m().mul(A2, a2, tmp2); \
m().add(tmp1, tmp2, tmp3); \
m().m().mod(tmp3, new_bound, new_a); \
if (m().gt(new_a, upper)) \
m().sub(new_a, new_bound, new_a); \
R.push_back(numeral()); \
m().set(R.back(), new_a); \
}
numeral zero(0);
unsigned i = 0;
unsigned sz1 = C1.size();
unsigned sz2 = C2.size();
unsigned sz = std::min(sz1, sz2);
for (; i < sz; i++) {
ADD(C1[i], C2[i]);
}
for (; i < sz1; i++) {
ADD(C1[i], zero);
}
for (; i < sz2; i++) {
ADD(zero, C2[i]);
}
m().set(b2, new_bound);
R.swap(C2);
}
void core_manager::mod_gcd(unsigned sz_u, numeral const * u,
unsigned sz_v, numeral const * v,
numeral_vector & result) {
TRACE("mgcd", tout << "u: "; display_star(tout, sz_u, u); tout << "\nv: "; display_star(tout, sz_v, v); tout << "\n";);
SASSERT(sz_u > 0 && sz_v > 0);
SASSERT(!m().modular());
scoped_numeral c_u(m()), c_v(m());
numeral_vector & pp_u = m_mgcd_tmp[0];
numeral_vector & pp_v = m_mgcd_tmp[1];
get_primitive_and_content(sz_u, u, pp_u, c_u);
get_primitive_and_content(sz_v, v, pp_v, c_v);
scoped_numeral c_g(m());
m().gcd(c_u, c_v, c_g);
unsigned d_u = sz_u - 1;
unsigned d_v = sz_v - 1;
numeral const & lc_u = pp_u[d_u];
numeral const & lc_v = pp_v[d_v];
scoped_numeral lc_g(m());
m().gcd(lc_u, lc_v, lc_g);
scoped_numeral p(m());
scoped_numeral bound(m());
numeral_vector & u_Zp = m_mgcd_tmp[2];
numeral_vector & v_Zp = m_mgcd_tmp[3];
numeral_vector & q = m_mgcd_tmp[4];
numeral_vector & C = m_mgcd_tmp[5];
for (unsigned i = 0; i < NUM_BIG_PRIMES; i++) {
m().set(p, polynomial::g_big_primes[i]);
TRACE("mgcd", tout << "trying prime: " << p << "\n";);
{
scoped_set_zp setZp(*this, p);
set(pp_u.size(), pp_u.data(), u_Zp);
set(pp_v.size(), pp_v.data(), v_Zp);
if (degree(u_Zp) < d_u) {
TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";);
continue;
}
if (degree(v_Zp) < d_v) {
TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";);
continue;
}
euclid_gcd(u_Zp.size(), u_Zp.data(), v_Zp.size(), v_Zp.data(), q);
mk_monic(q.size(), q.data());
scoped_numeral c(m());
m().set(c, lc_g);
mul(q, c);
}
TRACE("mgcd", tout << "new q:\n"; display_star(tout, q); tout << "\n";);
if (is_const(q)) {
TRACE("mgcd", tout << "done, modular gcd is one\n";);
reset(result);
result.push_back(numeral());
m().set(result.back(), c_g);
return;
}
if (i == 0) {
set(q.size(), q.data(), C);
m().set(bound, p);
}
else if (q.size() < C.size() || m().m().is_even(p) || m().m().is_even(bound)) {
TRACE("mgcd", tout << "discarding image\n";);
set(q.size(), q.data(), C);
m().set(bound, p);
}
else {
CRA_combine_images(q, p, C, bound);
TRACE("mgcd", tout << "new combined:\n"; display_star(tout, C); tout << "\n";);
}
numeral_vector & candidate = q;
get_primitive(C, candidate);
TRACE("mgcd", tout << "candidate:\n"; display_star(tout, candidate); tout << "\n";);
SASSERT(candidate.size() > 0);
numeral const & lc_candidate = candidate[candidate.size() - 1];
if (m().divides(lc_candidate, lc_g) &&
divides(pp_u, candidate) &&
divides(pp_v, candidate)) {
TRACE("mgcd", tout << "found GCD\n";);
mul(candidate, c_g);
flip_sign_if_lm_neg(candidate);
candidate.swap(result);
TRACE("mgcd", tout << "r: "; display_star(tout, result); tout << "\n";);
return;
}
}
euclid_gcd(sz_u, u, sz_v, v, result);
}
void core_manager::euclid_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer));
if (sz1 == 0) {
set(sz2, p2, buffer);
flip_sign_if_lm_neg(buffer);
return;
}
if (sz2 == 0) {
set(sz1, p1, buffer);
flip_sign_if_lm_neg(buffer);
return;
}
bool is_field = field();
numeral_vector & A = m_gcd_tmp1;
numeral_vector & B = m_gcd_tmp2;
numeral_vector & R = buffer;
set(sz1, p1, A);
set(sz2, p2, B);
TRACE("upolynomial", tout << "sz1: " << sz1 << ", p1: " << p1 << ", sz2: " << sz2 << ", p2: " << p2 << "\nB.size(): " << B.size() <<
", B.c_ptr(): " << B.data() << "\n";);
while (m_limit.inc()) {
TRACE("upolynomial", tout << "A: "; display(tout, A); tout <<"\nB: "; display(tout, B); tout << "\n";);
if (B.empty()) {
normalize(A);
buffer.swap(A);
if (is_field) {
mk_monic(buffer.size(), buffer.data());
}
else {
flip_sign_if_lm_neg(buffer);
}
TRACE("upolynomial", tout << "GCD\n"; display(tout, sz1, p1); tout << "\n"; display(tout, sz2, p2); tout << "\n--->\n";
display(tout, buffer); tout << "\n";);
return;
}
rem(A.size(), A.data(), B.size(), B.data(), R);
normalize(R);
A.swap(B);
B.swap(R);
}
throw upolynomial_exception(Z3_CANCELED_MSG);
}
void core_manager::gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer));
if (sz1 == 0) {
set(sz2, p2, buffer);
flip_sign_if_lm_neg(buffer);
return;
}
if (sz2 == 0) {
set(sz1, p1, buffer);
flip_sign_if_lm_neg(buffer);
return;
}
if (!modular())
mod_gcd(sz1, p1, sz2, p2, buffer);
else
euclid_gcd(sz1, p1, sz2, p2, buffer);
}
void core_manager::subresultant_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer) {
SASSERT(!is_alias(p1, buffer)); SASSERT(!is_alias(p2, buffer));
if (sz1 == 0) {
set(sz2, p2, buffer);
flip_sign_if_lm_neg(buffer);
return;
}
if (sz2 == 0) {
set(sz1, p1, buffer);
flip_sign_if_lm_neg(buffer);
return;
}
numeral_vector & A = m_gcd_tmp1;
numeral_vector & B = m_gcd_tmp2;
numeral_vector & R = buffer;
scoped_numeral g(m());
scoped_numeral h(m());
scoped_numeral aux(m());
m().set(g, 1);
m().set(h, 1);
unsigned d;
set(sz1, p1, A);
set(sz2, p2, B);
if (A.size() < B.size())
A.swap(B);
while (true) {
SASSERT(A.size() >= B.size());
TRACE("upolynomial", tout << "A: "; display(tout, A); tout <<"\nB: "; display(tout, B); tout << "\n";
tout << "g: " << m().to_string(g) << ", h: " << m().to_string(h) << "\n";);
if (B.empty()) {
normalize(A);
buffer.swap(A);
if (field()) {
mk_monic(buffer.size(), buffer.data());
}
else {
flip_sign_if_lm_neg(buffer);
}
TRACE("upolynomial", tout << "subresultant GCD\n"; display(tout, sz1, p1); tout << "\n"; display(tout, sz2, p2); tout << "\n--->\n";
display(tout, buffer); tout << "\n";);
return;
}
rem(A.size(), A.data(), B.size(), B.data(), d, R);
unsigned pseudo_div_d = A.size() - B.size();
if (d < pseudo_div_d + 1) {
m().power(B[B.size() - 1], pseudo_div_d + 1 - d, aux);
mul(R, aux);
}
d = pseudo_div_d;
TRACE("upolynomial", tout << "R: "; display(tout, R); tout << "\nd: " << d << "\n";);
m().power(h, d, aux);
m().mul(g, aux, aux);
div(R, aux);
A.swap(B);
B.swap(R);
m().set(g, A[A.size() - 1]);
m().power(g, d, aux);
if (d == 0) {
}
else if (d == 1) {
m().set(h, g);
}
else {
SASSERT(d > 1);
d--;
m().power(h, d, h);
SASSERT(m().divides(h, aux));
m().div(aux, h, h);
}
}
}
void core_manager::square_free(unsigned sz, numeral const * p, numeral_vector & buffer) {
SASSERT(!is_alias(p, buffer));
if (sz <= 1) {
set(sz, p, buffer);
return;
}
numeral_vector & p_prime = m_sqf_tmp1;
numeral_vector & g = m_sqf_tmp2;
derivative(sz, p, p_prime);
gcd(sz, p, p_prime.size(), p_prime.data(), g);
if (g.size() <= 1) {
set(sz, p, buffer);
}
else {
div(sz, p, g.size(), g.data(), buffer);
normalize(buffer);
}
}
bool core_manager::is_square_free(unsigned sz, numeral const * p) {
if (sz <= 1) {
return true;
}
numeral_vector & p_prime = m_sqf_tmp1;
numeral_vector & g = m_sqf_tmp2;
derivative(sz, p, p_prime);
gcd(sz, p, p_prime.size(), p_prime.data(), g);
return g.size() <= 1;
}
void core_manager::pw(unsigned sz, numeral const * p, unsigned k, numeral_vector & r) {
if (k == 0) {
SASSERT(sz != 0);
r.reserve(1);
m().set(r[0], 1);
set_size(1, r);
return;
}
if (k == 1 || sz == 0 || (sz == 1 && m().is_one(p[0]))) {
set(sz, p, r);
return;
}
numeral_vector & result = m_pw_tmp;
set(sz, p, result);
for (unsigned i = 1; i < k; i++)
mul(m_pw_tmp.size(), m_pw_tmp.data(), sz, p, m_pw_tmp);
r.swap(result);
#if 0
unsigned mask = 1;
numeral_vector & p2 = m_pw_tmp;
set(sz, p, p2);
reset(r);
r.push_back(numeral(1));
while (mask <= k) {
if (mask & k) {
mul(r.size(), r.c_ptr(), p2.size(), p2.c_ptr(), r);
}
mul(p2.size(), p2.c_ptr(), p2.size(), p2.c_ptr(), p2);
mask = mask << 1;
}
#endif
}
void core_manager::mk_monic(unsigned sz, numeral * p, numeral & lc, numeral & lc_inv) {
m().set(lc, 1);
m().set(lc_inv, 1);
if (sz > 0 && !m().is_one(p[sz-1])) {
int d = sz-1;
m().swap(lc, p[d--]);
m().set(lc_inv, lc);
m().inv(lc_inv);
for (; d >= 0; -- d) {
m().mul(p[d], lc_inv, p[d]);
}
}
}
void core_manager::ext_gcd(unsigned szA, numeral const * A, unsigned szB, numeral const * B,
numeral_vector & U, numeral_vector & V, numeral_vector & D) {
SASSERT(!is_alias(A, U)); SASSERT(!is_alias(A, V)); SASSERT(!is_alias(A, D));
SASSERT(!is_alias(B, U)); SASSERT(!is_alias(B, V)); SASSERT(!is_alias(B, D));
SASSERT(field());
scoped_numeral_vector V1(m()), V3(m()), Q(m()), R(m()), T(m()), V1Q(m());
reset(U); U.push_back(numeral()); m().set(U.back(), 1);
set(szA, A, D);
mk_monic(szA, D.data());
reset(V1);
set(szB, B, V3);
while (true) {
if (V3.empty()) {
numeral_vector & AU = V1;
numeral_vector & D_AU = V3;
mul(szA, A, U.size(), U.data(), AU);
sub(D.size(), D.data(), AU.size(), AU.data(), D_AU);
div(D_AU.size(), D_AU.data(), szB, B, V);
DEBUG_CODE({
scoped_numeral_vector BV(m());
scoped_numeral_vector expected_D(m());
mul(szB, B, V.size(), V.data(), BV);
add(AU.size(), AU.data(), BV.size(), BV.data(), expected_D);
SASSERT(eq(expected_D, D));
});
scoped_numeral lc_inv(m()), lc(m());
mk_monic(D.size(), D.data(), lc, lc_inv);
mul(U, lc_inv);
mul(V, lc_inv);
return;
}
div_rem(D.size(), D.data(), V3.size(), V3.data(), Q, R);
mul(V1.size(), V1.data(), Q.size(), Q.data(), V1Q);
sub(U.size(), U.data(), V1Q.size(), V1Q.data(), T);
U.swap(V1);
D.swap(V3);
V1.swap(T);
V3.swap(R);
}
}
std::ostream& core_manager::display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name, bool use_star) const {
bool displayed = false;
unsigned i = sz;
scoped_numeral a(m());
while (i > 0) {
--i;
if (!m().is_zero(p[i])) {
m().set(a, p[i]);
if (displayed) {
m().abs(a);
if (m().is_pos(p[i]))
out << " + ";
else
out << " - ";
}
displayed = true;
if (i == 0) {
out << m().to_string(a);
}
else {
SASSERT(i > 0);
if (!m().is_one(a)) {
out << m().to_string(a);
if (use_star)
out << "*";
else
out << " ";
}
out << var_name;
if (i > 1)
out << "^" << i;
}
}
}
if (!displayed)
out << "0";
return out;
}
static void display_smt2_mumeral(std::ostream & out, numeral_manager & m, mpz const & n) {
if (m.is_neg(n)) {
out << "(- ";
mpz abs_n;
m.set(abs_n, n);
m.neg(abs_n);
m.display(out, abs_n);
m.del(abs_n);
out << ")";
}
else {
m.display(out, n);
}
}
static void display_smt2_monomial(std::ostream & out, numeral_manager & m, mpz const & n,
unsigned k, char const * var_name) {
if (k == 0) {
display_smt2_mumeral(out, m, n);
}
else if (m.is_one(n)) {
if (k == 1)
out << var_name;
else
out << "(^ " << var_name << " " << k << ")";
}
else {
out << "(* ";
display_smt2_mumeral(out, m, n);
out << " ";
if (k == 1)
out << var_name;
else
out << "(^ " << var_name << " " << k << ")";
out << ")";
}
}
std::ostream& core_manager::display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name) const {
if (sz == 0) {
out << "0";
return out;
}
if (sz == 1) {
display_smt2_mumeral(out, m(), p[0]);
return out;
}
unsigned non_zero_idx = UINT_MAX;
unsigned num_non_zeros = 0;
for (unsigned i = 0; i < sz; i++) {
if (m().is_zero(p[i]))
continue;
non_zero_idx = i;
num_non_zeros ++;
}
if (num_non_zeros == 1) {
SASSERT(non_zero_idx != UINT_MAX && non_zero_idx >= 1);
display_smt2_monomial(out, m(), p[non_zero_idx], non_zero_idx, var_name);
}
out << "(+";
unsigned i = sz;
while (i > 0) {
--i;
if (!m().is_zero(p[i])) {
out << " ";
display_smt2_monomial(out, m(), p[i], i, var_name);
}
}
return out << ")";
}
bool core_manager::eq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2) {
if (sz1 != sz2)
return false;
for (unsigned i = 0; i < sz1; i++) {
if (!m().eq(p1[i], p2[i]))
return false;
}
return true;
}
void upolynomial_sequence::push(unsigned sz, numeral * p) {
m_begins.push_back(m_seq_coeffs.size());
m_szs.push_back(sz);
for (unsigned i = 0; i < sz; i++) {
m_seq_coeffs.push_back(numeral());
swap(m_seq_coeffs.back(), p[i]);
}
}
void upolynomial_sequence::push(numeral_manager & m, unsigned sz, numeral const * p) {
m_begins.push_back(m_seq_coeffs.size());
m_szs.push_back(sz);
for (unsigned i = 0; i < sz; i++) {
m_seq_coeffs.push_back(numeral());
m.set(m_seq_coeffs.back(), p[i]);
}
}
scoped_numeral_vector::scoped_numeral_vector(manager & m):
_scoped_numeral_vector<numeral_manager>(m.m()) {
}
scoped_upolynomial_sequence::~scoped_upolynomial_sequence() {
m_manager.reset(*this);
}
manager::~manager() {
reset(m_db_tmp);
reset(m_dbab_tmp1);
reset(m_dbab_tmp2);
reset(m_tr_tmp);
reset(m_push_tmp);
}
void manager::reset(upolynomial_sequence & seq) {
reset(seq.m_seq_coeffs);
seq.m_szs.reset();
seq.m_begins.reset();
}
void manager::remove_zero_roots(unsigned sz, numeral const * p, numeral_vector & buffer) {
SASSERT(sz > 0);
if (!m().is_zero(p[0])) {
set(sz, p, buffer);
return;
}
unsigned i = 0;
while (true) {
SASSERT(i < sz);
if (!m().is_zero(p[i]))
break;
i++;
}
unsigned new_sz = sz - i;
buffer.reserve(new_sz);
for (unsigned j = 0; j < new_sz; j++) {
m().set(buffer[j], p[j + i]);
}
set_size(new_sz, buffer);
}
bool manager::has_one_half_root(unsigned sz, numeral const * p) {
if (sz == 0)
return true;
if (sz == 1)
return false;
unsigned k = 1;
scoped_numeral r(m());
scoped_numeral ak(m());
m().set(r, p[sz-1]);
unsigned i = sz - 1;
while (i > 0) {
--i;
numeral const & a = p[i];
m().mul2k(a, k, ak);
m().add(r, ak, r);
k++;
}
return m().is_zero(r);
}
void manager::remove_one_half_root(unsigned sz, numeral const * p, numeral_vector & buffer) {
SASSERT(has_one_half_root(sz, p));
numeral two_x_1[2] = { numeral(-1), numeral(2) };
div(sz, p, 2, two_x_1, buffer);
}
sign manager::sign_of(numeral const & c) {
if (m().is_zero(c))
return sign_zero;
if (m().is_pos(c))
return sign_pos;
else
return sign_neg;
}
unsigned manager::sign_changes(unsigned sz, numeral const * p) {
unsigned r = 0;
auto prev_sign = sign_zero;
unsigned i = 0;
for (; i < sz; i++) {
auto sign = sign_of(p[i]);
if (sign == sign_zero)
continue;
if (sign != prev_sign && prev_sign != sign_zero)
r++;
prev_sign = sign;
}
return r;
}
unsigned manager::descartes_bound(unsigned sz, numeral const * p) {
return sign_changes(sz, p);
}
unsigned manager::descartes_bound_0_1(unsigned sz, numeral const * p) {
if (sz <= 1)
return 0;
numeral_vector & Q = m_db_tmp;
set(sz, p, Q);
#if 0
unsigned n = Q.size() - 1;
unsigned i;
for (unsigned i = 1; i <= n; i++) {
for (unsigned k = i; k >= 1; k--) {
m().add(Q[k], Q[k-1], Q[k]);
}
}
return sign_changes(Q.size(), Q.c_ptr());
#endif
sign prev_sign = sign_zero;
unsigned num_vars = 0;
for (unsigned i = 0; i < sz; i++) {
checkpoint();
unsigned k;
for (k = 1; k < sz - i; k++) {
m().add(Q[k], Q[k-1], Q[k]);
}
auto sign = sign_of(Q[k-1]);
if (::is_zero(sign))
continue;
if (sign != prev_sign && !::is_zero(prev_sign)) {
num_vars++;
if (num_vars > 1)
return num_vars;
}
prev_sign = sign;
}
return num_vars;
}
unsigned manager::descartes_bound_a_b(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq const & a, mpbq const & b) {
if (bqm.is_nonneg(a)) {
TRACE("upolynomial", tout << "pos interval... " << bqm.to_string(a) << ", " << bqm.to_string(b) << "\n"; display(tout, sz, p); tout << "\n";);
numeral_vector & p_aux = m_dbab_tmp1;
translate_bq(sz, p, a, p_aux);
TRACE("upolynomial", tout << "after translation\n"; display(tout, p_aux); tout << "\n";);
scoped_mpbq b_a(bqm);
bqm.sub(b, a, b_a);
compose_p_b_x(p_aux.size(), p_aux.data(), b_a);
TRACE("upolynomial", tout << "after composition: " << bqm.to_string(b_a) << "\n"; display(tout, p_aux); tout << "\n";);
unsigned result = descartes_bound_0_1(p_aux.size(), p_aux.data());
return result;
}
else if (bqm.is_nonpos(b)) {
numeral_vector & p_aux = m_dbab_tmp2;
set(sz, p, p_aux);
p_minus_x(p_aux.size(), p_aux.data());
scoped_mpbq mb(bqm);
scoped_mpbq ma(bqm);
bqm.set(mb, b); bqm.neg(mb);
bqm.set(ma, a); bqm.neg(ma);
unsigned result = descartes_bound_a_b(p_aux.size(), p_aux.data(), bqm, mb, ma);
return result;
}
else if (!has_zero_roots(sz, p)) {
mpbq zero(0);
unsigned r1 = descartes_bound_a_b(sz, p, bqm, a, zero);
if (r1 > 1)
return r1;
unsigned r2 = descartes_bound_a_b(sz, p, bqm, zero, b);
if (r1 == 0)
return r2;
if (r2 == 0)
return r1;
return 2;
}
else {
mpbq zero(0);
if (descartes_bound_a_b(sz, p, bqm, a, zero) > 0)
return 2;
if (descartes_bound_a_b(sz, p, bqm, zero, b) > 0)
return 2;
return 1;
}
}
void manager::translate(unsigned sz, numeral * p) {
if (sz <= 1)
return;
unsigned n = sz - 1;
for (unsigned i = 1; i <= n; i++) {
checkpoint();
for (unsigned k = n-i; k <= n-1; k++)
m().add(p[k], p[k+1], p[k]);
}
}
void manager::translate_k(unsigned sz, numeral * p, unsigned k) {
if (sz <= 1)
return;
scoped_numeral aux(m());
unsigned n = sz - 1;
for (unsigned i = 1; i <= n; i++) {
checkpoint();
for (unsigned k = n-i; k <= n-1; k++) {
m().mul2k(p[k+1], k, aux);
m().add(p[k], aux, p[k]);
}
}
}
void manager::translate_z(unsigned sz, numeral * p, numeral const & c) {
if (sz <= 1)
return;
unsigned n = sz - 1;
for (unsigned i = 1; i <= n; i++) {
checkpoint();
for (unsigned k = n-i; k <= n-1; k++)
m().addmul(p[k], c, p[k+1], p[k]);
}
}
void manager::translate_bq(unsigned sz, numeral * p, mpbq const & b) {
if (sz <= 1)
return;
compose_2kn_p_x_div_2k(sz, p, b.k());
TRACE("upolynomial", tout << "after compose 2kn_p_x_div_2k\n"; display(tout, sz, p); tout << "\n";);
numeral const & c = b.numerator();
unsigned n = sz - 1;
for (unsigned i = 1; i <= n; i++) {
checkpoint();
m().addmul(p[n - i], c, p[n - i + 1], p[n - i]);
for (unsigned k = n - i + 1; k <= n - 1; k++) {
m().mul2k(p[k], b.k());
m().addmul(p[k], c, p[k + 1], p[k]);
}
m().mul2k(p[n], b.k());
}
TRACE("upolynomial", tout << "after special translation\n"; display(tout, sz, p); tout << "\n";);
}
void manager::translate_q(unsigned sz, numeral * p, mpq const & b) {
if (sz <= 1)
return;
compose_an_p_x_div_a(sz, p, b.denominator());
TRACE("upolynomial", tout << "after compose_an_p_x_div_a\n"; display(tout, sz, p); tout << "\n";);
numeral const & c = b.numerator();
unsigned n = sz - 1;
for (unsigned i = 1; i <= n; i++) {
checkpoint();
m().addmul(p[n - i], c, p[n - i + 1], p[n - i]);
for (unsigned k = n - i + 1; k <= n - 1; k++) {
m().mul(p[k], b.denominator(), p[k]);
m().addmul(p[k], c, p[k + 1], p[k]);
}
m().mul(p[n], b.denominator(), p[n]);
}
TRACE("upolynomial", tout << "after special translation\n"; display(tout, sz, p); tout << "\n";);
}
void manager::compose_2n_p_x_div_2(unsigned sz, numeral * p) {
if (sz <= 1)
return;
unsigned k = sz-1;
for (unsigned i = 0; i < sz - 1; i++) {
m().mul2k(p[i], k);
k--;
}
}
void manager::p_minus_x(unsigned sz, numeral * p) {
for (unsigned i = 0; i < sz; i++) {
if (m().is_zero(p[i]))
continue;
if (i % 2 == 0)
continue;
m().neg(p[i]);
}
}
void manager::p_1_div_x(unsigned sz, numeral * p) {
if (sz <= 1)
return;
unsigned i = 0;
unsigned j = sz-1;
SASSERT(j >= 1);
while (true) {
swap(p[i], p[j]);
i++; j--;
if (i >= j)
break;
}
}
void manager::compose_p_2k_x(unsigned sz, numeral * p, unsigned k) {
if (sz <= 1)
return;
unsigned k_i = k;
for (unsigned i = 1; i < sz; i++) {
m().mul2k(p[i], k_i);
k_i += k;
}
}
void manager::compose_2kn_p_x_div_2k(unsigned sz, numeral * p, unsigned k) {
if (sz <= 1)
return;
unsigned k_i = k*sz;
for (unsigned i = 0; i < sz; i++) {
k_i -= k;
if (!m().is_zero(p[i]))
m().mul2k(p[i], k_i);
}
}
void manager::compose_an_p_x_div_a(unsigned sz, numeral * p, numeral const & a) {
if (sz <= 1)
return;
unsigned i = sz-1;
scoped_numeral a_i(m());
m().set(a_i, a);
while (i > 0) {
--i;
if (!m().is_zero(p[i]))
m().mul(p[i], a_i, p[i]);
m().mul(a_i, a, a_i);
}
}
void manager::compose_p_b_x(unsigned sz, numeral * p, numeral const & b) {
if (sz <= 1)
return;
scoped_numeral b_i(m());
m().set(b_i, b);
for (unsigned i = 1; i < sz; i++) {
if (!m().is_zero(p[i]))
m().mul(p[i], b_i, p[i]);
m().mul(b_i, b, b_i);
}
}
void manager::compose_p_b_x(unsigned sz, numeral * p, mpbq const & b) {
if (sz <= 1)
return;
unsigned k = b.k();
numeral const & c = b.numerator();
scoped_numeral c_i(m());
m().set(c_i, 1);
unsigned k_i = k*sz;
for (unsigned i = 0; i < sz; i++) {
k_i -= k;
if (!m().is_zero(p[i])) {
m().mul2k(p[i], k_i);
m().mul(p[i], c_i, p[i]);
}
m().mul(c_i, c, c_i);
}
SASSERT(k_i == 0);
}
void manager::compose_p_q_x(unsigned sz, numeral * p, mpq const & q) {
if (sz <= 1)
return;
numeral const & b = q.numerator();
numeral const & c = q.denominator();
scoped_numeral bc(m());
m().power(c, sz-1, bc);
for (unsigned i = 0; i < sz; i++) {
if (!m().is_zero(p[i]))
m().mul(p[i], bc, p[i]);
if (i < sz - 1) {
m().div(bc, c, bc);
m().mul(bc, b, bc);
}
}
}
sign manager::eval_sign_at(unsigned sz, numeral const * p, mpbq const & b) {
if (sz == 0)
return sign_zero;
if (sz == 1)
return sign_of(p[0]);
numeral const & c = b.numerator();
unsigned k = b.k();
unsigned k_i = k;
scoped_numeral r(m());
scoped_numeral ak(m());
m().set(r, p[sz-1]);
unsigned i = sz-1;
while (i > 0) {
--i;
numeral const & a = p[i];
if (m().is_zero(a)) {
m().mul(r, c, r);
}
else {
m().mul2k(a, k_i, ak);
m().addmul(ak, r, c, r);
}
k_i += k;
}
return sign_of(r);
}
sign manager::eval_sign_at(unsigned sz, numeral const * p, mpq const & b) {
if (sz == 0)
return sign_zero;
if (sz == 1)
return sign_of(p[0]);
numeral const & c = b.numerator();
numeral const & d = b.denominator();
scoped_numeral d_i(m());
m().set(d_i, d);
scoped_numeral r(m());
scoped_numeral ak(m());
m().set(r, p[sz-1]);
unsigned i = sz-1;
while (i > 0) {
--i;
numeral const & a = p[i];
if (m().is_zero(a)) {
m().mul(r, c, r);
}
else {
m().mul(a, d_i, ak);
m().addmul(ak, r, c, r);
}
m().mul(d_i, d, d_i);
}
return sign_of(r);
}
sign manager::eval_sign_at(unsigned sz, numeral const * p, mpz const & b) {
if (sz == 0)
return sign_zero;
if (sz == 1)
return sign_of(p[0]);
scoped_numeral r(m());
m().set(r, p[sz-1]);
unsigned i = sz-1;
while (i > 0) {
--i;
numeral const & a = p[i];
if (m().is_zero(a))
m().mul(r, b, r);
else
m().addmul(a, r, b, r);
}
return sign_of(r);
}
sign manager::eval_sign_at_zero(unsigned sz, numeral const * p) {
if (sz == 0)
return sign_zero;
return sign_of(p[0]);
}
sign manager::eval_sign_at_plus_inf(unsigned sz, numeral const * p) {
if (sz == 0)
return sign_zero;
return sign_of(p[sz-1]);
}
sign manager::eval_sign_at_minus_inf(unsigned sz, numeral const * p) {
if (sz == 0)
return sign_zero;
unsigned degree = sz - 1;
if (degree % 2 == 0)
return sign_of(p[sz-1]);
else
return -sign_of(p[sz-1]);
}
template<manager::location loc>
unsigned manager::sign_variations_at_core(upolynomial_sequence const & seq, mpbq const & b) {
unsigned sz = seq.size();
if (sz <= 1)
return 0;
unsigned r = 0;
int sign, prev_sign;
sign = 0;
prev_sign = 0;
unsigned i = 0;
for (; i < sz; i++) {
unsigned psz = seq.size(i);
numeral const * p = seq.coeffs(i);
switch (loc) {
case PLUS_INF:
sign = eval_sign_at_plus_inf(psz, p);
break;
case MINUS_INF:
sign = eval_sign_at_minus_inf(psz, p);
break;
case ZERO:
sign = eval_sign_at_zero(psz, p);
break;
case MPBQ:
sign = eval_sign_at(psz, p, b);
break;
default:
UNREACHABLE();
break;
}
if (sign == 0)
continue;
SASSERT(sign == 1 || sign == -1);
if (sign != prev_sign && prev_sign != 0)
r++;
prev_sign = sign;
}
return r;
}
unsigned manager::sign_variations_at_minus_inf(upolynomial_sequence const & seq) {
mpbq dummy(0);
return sign_variations_at_core<MINUS_INF>(seq, dummy);
}
unsigned manager::sign_variations_at_plus_inf(upolynomial_sequence const & seq) {
mpbq dummy(0);
return sign_variations_at_core<PLUS_INF>(seq, dummy);
}
unsigned manager::sign_variations_at_zero(upolynomial_sequence const & seq) {
mpbq dummy(0);
return sign_variations_at_core<ZERO>(seq, dummy);
}
unsigned manager::sign_variations_at(upolynomial_sequence const & seq, mpbq const & b) {
return sign_variations_at_core<MPBQ>(seq, b);
}
void manager::root_upper_bound(unsigned sz, numeral const * p, numeral & r) {
SASSERT(sz > 0);
SASSERT(!m().is_zero(p[sz - 1]));
bool init = false;
scoped_numeral max(m());
scoped_numeral min(m());
scoped_numeral a_n(m());
scoped_numeral r2(m());
m().set(a_n, p[sz - 1]);
m().abs(a_n);
scoped_numeral c(m());
for (unsigned i = 0; i < sz; i++) {
if (m().is_zero(p[i]))
continue;
m().set(c, p[i]);
m().abs(c);
if (!init) {
m().set(max, c);
m().set(min, c);
init = true;
continue;
}
if (m().gt(c, max))
m().set(max, c);
if (m().lt(c, min))
m().set(min, c);
}
m().add(min, max, r);
m().div(r, min, r);
m().add(r, numeral(1), r);
m().add(a_n, max, r2);
m().div(r2, a_n, r2);
m().add(r2, numeral(1), r2);
if (m().lt(r2, r))
swap(r, r2);
SASSERT(m().le(r, r2));
}
\brief Find positive root upper bound using Knuth's approach.
Given p(x) = a_n * x^n + a_{n-1} * x^{n-1} + ... + a_0
If a_n is positive,
Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} < 0 })
Then, 2*B is a bound for the positive roots
Similarly, if a_n is negative
Let B = max({ (-a_{n-k}/a_n)^{1/k} | 1 <= k <= n, a_{n-k} > 0 })
Then, 2*B is a bound for the positive roots
This procedure returns a k s.t. 2*B <= 2^k
The procedure actually computes
max of log2(abs(a_{n-k})) + 1 - log2(abs(a_n))/k
Proof Sketch:
Let u > 0 be a root of p(x).
If u <= B, then we are done. So, let us assume u > B
Assume, a_n > 0 (the proof is similar for a_n < 0)
Then: a_n * u^n + a_{n-1} * u^{n-1} + ... + a0 = 0
u^n = 1/a_n (-a_{n-1} * u^{n-1} - ... - a_0)
Note that, if we remove the monomials s.t. a_i is positive, then
we are increasing the value of (-a_{n-1} * u^{n-1} - ... - a_0).
Thus,
u^n <= 1/a_n(SUM_{a_i < 0, 0 <= i < n} (-a_i * u^i))
Dividing by u_n which is positive, we get
1 <= 1/a_n(SUM_{a_i < 0, 0 <= i < n} (-a_i * u^i))
By replacing, i = n - k
we have
1 <= 1/a_n(SUM_{a_{n-k} < 0, n >= k > 0} (-a_{n-k} * u^{n-k})) = 1/a_n(SUM_{a_{n-k} < 0, 1 <= k <= n} (-a_i * u^{n-k})) < Sum_{1 <= k < +oo}(B/u)^k
Since u > B, we have that Sum_{1 <= k < +oo}(B/u)^k = B/(u - B)
Thus, we have
1 < B/(u - B)
and u < 2B
*/
unsigned manager::knuth_positive_root_upper_bound(unsigned sz, numeral const * p) {
if (sz == 0)
return 0;
unsigned max = 0;
unsigned n = sz - 1;
bool pos_a_n = m().is_pos(p[n]);
unsigned log2_a_n = pos_a_n ? m().log2(p[n]) : m().mlog2(p[n]);
for (unsigned k = 1; k <= n; k++) {
numeral const & a_n_k = p[n - k];
if (m().is_zero(a_n_k))
continue;
bool pos_a_n_k = m().is_pos(a_n_k);
if (pos_a_n_k == pos_a_n)
continue;
unsigned log2_a_n_k = pos_a_n_k ? m().log2(a_n_k) : m().mlog2(a_n_k);
if (log2_a_n > log2_a_n_k)
continue;
unsigned curr = log2_a_n_k + 1 - log2_a_n;
if (curr % k == 0) {
curr /= k;
}
else {
curr /= k;
curr ++;
}
if (curr > max)
max = curr;
}
return max + 1;
}
It is essentially applying knuth_positive_root_upper_bound for p(-x)
*/
unsigned manager::knuth_negative_root_upper_bound(unsigned sz, numeral const * p) {
numeral * _p = const_cast<numeral *>(p);
p_minus_x(sz, _p);
unsigned r = knuth_positive_root_upper_bound(sz, _p);
p_minus_x(sz, _p);
return r;
}
Return a lower bound for the nonzero roots of p(x).
Let k be the result of this procedure,
Then for any nonzero root alpha of p(x), we have that
|alpha| > 1/2^k
We essentially compute the upper bound for the roots of x^n*p(1/x) where n is the degree of p.
Remark: alpha is a nonzero root of p(x) iff 1/alpha is a root of x^n*p(1/x).
Thus, if 2^k is upper bound for the root of x^n*p(1/x). Then we have that
-2^k < 1/alpha < 2^k
and consequently
alpha < -1/2^k or 1/2^k < alpha
/pre p is not the zero polynomial.
*/
unsigned manager::nonzero_root_lower_bound(unsigned sz, numeral const * p) {
SASSERT(sz > 0);
unsigned i = 0;
while (true) {
SASSERT(i < sz);
if (!m().is_zero(p[i]))
break;
i++;
}
SASSERT(i < sz);
SASSERT(!m().is_zero(p[i]));
unsigned nz_sz = sz - i;
numeral const * nz_p = p + i;
numeral * _nz_p = const_cast<numeral *>(nz_p);
std::reverse(_nz_p, _nz_p + nz_sz);
unsigned k1 = knuth_positive_root_upper_bound(nz_sz, _nz_p);
unsigned k2 = knuth_negative_root_upper_bound(nz_sz, _nz_p);
std::reverse(_nz_p, _nz_p + nz_sz);
return std::max(k1, k2);
}
struct manager::drs_frame {
unsigned m_parent_idx;
unsigned m_size:30;
unsigned m_first:1;
unsigned m_left:1;
drs_frame(unsigned pidx, unsigned sz, bool left):
m_parent_idx(pidx),
m_size(sz),
m_first(true),
m_left(left) {
}
};
void manager::pop_top_frame(numeral_vector & p_stack, svector<drs_frame> & frame_stack) {
SASSERT(!frame_stack.empty());
unsigned sz = frame_stack.back().m_size;
SASSERT(sz <= p_stack.size());
for (unsigned i = 0; i < sz; i++) {
m().del(p_stack.back());
p_stack.pop_back();
}
frame_stack.pop_back();
}
void manager::push_child_frames(unsigned sz, numeral const * p, numeral_vector & p_stack, svector<drs_frame> & frame_stack) {
unsigned parent_idx = frame_stack.empty() ? UINT_MAX : frame_stack.size() - 1;
numeral_vector & p_aux = m_push_tmp;
set(sz, p, p_aux);
compose_2n_p_x_div_2(p_aux.size(), p_aux.data());
normalize(p_aux);
for (unsigned i = 0; i < sz; i++) {
p_stack.push_back(numeral());
m().set(p_stack.back(), p_aux[i]);
}
frame_stack.push_back(drs_frame(parent_idx, sz, true));
translate(sz, p_stack.data() + p_stack.size() - sz, p_aux);
normalize(p_aux);
for (unsigned i = 0; i < sz; i++) {
p_stack.push_back(numeral());
swap(p_stack.back(), p_aux[i]);
}
frame_stack.push_back(drs_frame(parent_idx, sz, false));
}
void manager::add_isolating_interval(svector<drs_frame> const & frame_stack, mpbq_manager & bqm, mpbq_vector & lowers, mpbq_vector & uppers) {
mpbq l(0);
mpbq u(1);
unsigned idx = frame_stack.size() - 1;
while (idx != UINT_MAX) {
drs_frame const & fr = frame_stack[idx];
TRACE("upolynomial",
tout << "normalizing...\n";
tout << "idx: " << idx << ", left: " << fr.m_left << ", l: " << bqm.to_string(l) << ", u: " << bqm.to_string(u) << "\n";);
if (fr.m_left) {
bqm.div2(l);
bqm.div2(u);
}
else {
bqm.add(l, mpz(1), l);
bqm.add(u, mpz(1), u);
bqm.div2(l);
bqm.div2(u);
}
idx = fr.m_parent_idx;
}
TRACE("upolynomial", tout << "adding normalized interval (" << bqm.to_string(l) << ", " << bqm.to_string(u) << ")\n";);
lowers.push_back(mpbq());
uppers.push_back(mpbq());
swap(lowers.back(), l);
swap(uppers.back(), u);
}
void manager::add_root(svector<drs_frame> const & frame_stack, mpbq_manager & bqm, mpbq_vector & roots) {
mpbq u(1,1);
unsigned idx = frame_stack.size() - 1;
while (idx != UINT_MAX) {
drs_frame const & fr = frame_stack[idx];
if (fr.m_left) {
bqm.div2(u);
}
else {
bqm.add(u, mpz(1), u);
bqm.div2(u);
}
idx = fr.m_parent_idx;
}
TRACE("upolynomial", tout << "adding normalized root: " << bqm.to_string(u) << "\n";);
roots.push_back(mpbq());
swap(roots.back(), u);
}
void manager::drs_isolate_0_1_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) {
TRACE("upolynomial", tout << "isolating (0,1) roots of:\n"; display(tout, sz, p); tout << "\n";);
unsigned k = descartes_bound_0_1(sz, p);
if (k == 0) {
TRACE("upolynomial", tout << "polynomial does not have any roots\n";);
return;
}
if (k == 1) {
TRACE("upolynomial", tout << "polynomial has one root in (0, 1)\n";);
lowers.push_back(mpbq(0));
uppers.push_back(mpbq(1));
return;
}
TRACE("upolynomial", tout << "polynomial has more than one root in (0, 1), starting search...\n";);
scoped_numeral_vector q(m());
scoped_numeral_vector p_stack(m());
svector<drs_frame> frame_stack;
if (has_one_half_root(sz, p)) {
TRACE("upolynomial", tout << "polynomial has a 1/2 root\n";);
roots.push_back(mpbq(1, 1));
remove_one_half_root(sz, p, q);
push_child_frames(q.size(), q.data(), p_stack, frame_stack);
}
else {
push_child_frames(sz, p, p_stack, frame_stack);
}
SASSERT(!frame_stack.empty());
while (!frame_stack.empty()) {
checkpoint();
drs_frame & fr = frame_stack.back();
unsigned sz = fr.m_size;
SASSERT(sz <= p_stack.size());
numeral const * p = p_stack.data() + p_stack.size() - sz;
TRACE("upolynomial", tout << "processing frame #" << frame_stack.size() - 1 << "\n"
<< "first: " << fr.m_first << ", left: " << fr.m_left << ", sz: " << fr.m_size << ", parent_idx: ";
if (fr.m_parent_idx == UINT_MAX) tout << "<null>"; else tout << fr.m_parent_idx;
tout << "\np: "; display(tout, sz, p); tout << "\n";);
if (!fr.m_first) {
pop_top_frame(p_stack, frame_stack);
continue;
}
fr.m_first = false;
unsigned k = descartes_bound_0_1(sz, p);
if (k == 0) {
TRACE("upolynomial", tout << "(0, 1) does not have roots\n";);
pop_top_frame(p_stack, frame_stack);
continue;
}
if (k == 1) {
TRACE("upolynomial", tout << "(0, 1) is isolating interval\n";);
add_isolating_interval(frame_stack, bqm, lowers, uppers);
pop_top_frame(p_stack, frame_stack);
continue;
}
TRACE("upolynomial", tout << "polynomial has more than one root in (0, 1) creating child frames...\n";);
if (has_one_half_root(sz, p)) {
TRACE("upolynomial", tout << "1/2 is a root\n";);
add_root(frame_stack, bqm, roots);
remove_one_half_root(sz, p, q);
push_child_frames(q.size(), q.data(), p_stack, frame_stack);
}
else {
push_child_frames(sz, p, p_stack, frame_stack);
}
}
}
static void adjust_pos(mpbq_manager & bqm, mpbq_vector & v, unsigned starting_at, unsigned k) {
unsigned sz = v.size();
for (unsigned i = starting_at; i < sz; i++)
bqm.mul2k(v[i], k);
}
static void adjust_neg(mpbq_manager & bqm, mpbq_vector & v, unsigned starting_at, unsigned k) {
unsigned sz = v.size();
for (unsigned i = starting_at; i < sz; i++) {
bqm.mul2k(v[i], k);
bqm.neg(v[i]);
}
}
static void swap_lowers_uppers(unsigned starting_at, mpbq_vector & lowers, mpbq_vector & uppers) {
SASSERT(lowers.size() == uppers.size());
unsigned sz = lowers.size();
for (unsigned i = starting_at; i < sz; i++) {
swap(lowers[i], uppers[i]);
}
}
void manager::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) {
scoped_numeral_vector aux_p(m());
set(sz, p, aux_p);
pos_k = std::max(neg_k, pos_k);
compose_p_2k_x(sz, aux_p.data(), pos_k);
TRACE("upolynomial", tout << "searching at (0, 1)\n";);
unsigned old_roots_sz = roots.size();
unsigned old_lowers_sz = lowers.size();
drs_isolate_0_1_roots(sz, aux_p.data(), bqm, roots, lowers, uppers);
SASSERT(lowers.size() == uppers.size());
adjust_pos(bqm, roots, old_roots_sz, pos_k);
adjust_pos(bqm, lowers, old_lowers_sz, pos_k);
adjust_pos(bqm, uppers, old_lowers_sz, pos_k);
p_minus_x(sz, p);
compose_p_2k_x(sz, p, neg_k);
TRACE("upolynomial", tout << "searching at (-1, 0) using:\n"; display(tout, sz, p); tout << "\n";);
old_roots_sz = roots.size();
old_lowers_sz = lowers.size();
drs_isolate_0_1_roots(sz, p, bqm, roots, lowers, uppers);
SASSERT(lowers.size() == uppers.size());
adjust_neg(bqm, roots, old_roots_sz, neg_k);
adjust_neg(bqm, lowers, old_lowers_sz, neg_k);
adjust_neg(bqm, uppers, old_lowers_sz, neg_k);
swap_lowers_uppers(old_lowers_sz, lowers, uppers);
}
void manager::drs_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) {
SASSERT(lowers.size() == uppers.size());
SASSERT(!has_zero_roots(sz, p));
scoped_numeral_vector p1(m());
set(sz, p, p1);
normalize(p1);
TRACE("upolynomial",
scoped_numeral U(m());
root_upper_bound(p1.size(), p1.data(), U);
unsigned U_k = m().log2(U) + 1;
tout << "Cauchy U: 2^" << U_k << "\n";
tout << "Knuth pos U: 2^" << knuth_positive_root_upper_bound(sz, p) << "\n";
tout << "Knuth neg U: 2^" << knuth_negative_root_upper_bound(sz, p) << "\n";);
#if 1
unsigned pos_k = knuth_positive_root_upper_bound(sz, p);
unsigned neg_k = knuth_negative_root_upper_bound(sz, p);
#else
scoped_numeral U(m());
root_upper_bound(p1.size(), p1.c_ptr(), U);
unsigned pos_k = m().log2(U) + 1;
unsigned neg_k = pos_k;
#endif
drs_isolate_roots(p1.size(), p1.data(), neg_k, pos_k, bqm, roots, lowers, uppers);
}
struct ss_frame {
mpbq m_lower;
mpbq m_upper;
unsigned m_lower_sv;
unsigned m_upper_sv;
};
class ss_frame_stack : public svector<ss_frame> {
mpbq_manager & m;
public:
ss_frame_stack(mpbq_manager & _m):m(_m) {}
~ss_frame_stack() {
iterator it = begin();
iterator e = end();
for (; it != e; ++it) {
ss_frame & f = *it;
m.del(f.m_lower);
m.del(f.m_upper);
}
}
};
inline void pop_ss_frame(mpbq_manager & m, ss_frame_stack & s) {
m.del(s.back().m_lower);
m.del(s.back().m_upper);
s.pop_back();
}
void ss_add_isolating_interval(mpbq_manager & m, mpbq const & lower, mpbq const & upper, mpbq_vector & lowers, mpbq_vector & uppers) {
lowers.push_back(mpbq());
uppers.push_back(mpbq());
m.set(lowers.back(), lower);
m.set(uppers.back(), upper);
}
void manager::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) {
SASSERT(!has_zero_roots(sz, p));
scoped_upolynomial_sequence seq(*this);
scoped_mpbq mid(bqm);
scoped_mpbq curr_lower(bqm);
scoped_mpbq curr_upper(bqm);
sturm_seq(sz, p, seq);
ss_frame_stack s(bqm);
TRACE("upolynomial", tout << "p: "; display(tout, sz, p); tout << "\nSturm seq:\n"; display(tout, seq); tout << "\n";);
unsigned lower_sv = sign_variations_at_minus_inf(seq);
unsigned zero_sv = sign_variations_at_zero(seq);
unsigned upper_sv = sign_variations_at_plus_inf(seq);
if (upper_sv >= lower_sv)
return;
bqm.power(mpbq(2), neg_k, curr_lower);
bqm.neg(curr_lower);
bqm.power(mpbq(2), pos_k, curr_upper);
#define PUSH_SS_FRAME(LOWER_SV, UPPER_SV, LOWER, UPPER) { \
SASSERT(!bqm.eq(LOWER, UPPER)); \
if (LOWER_SV == UPPER_SV) { \
\
} \
else if (LOWER_SV == UPPER_SV + 1) { \
\
\
if (eval_sign_at(sz, p, UPPER) == 0) { \
\
roots.push_back(mpbq()); \
bqm.set(roots.back(), UPPER); \
} \
else { \
ss_add_isolating_interval(bqm, LOWER, UPPER, lowers, uppers); \
} \
} \
else { \
s.push_back(ss_frame()); \
ss_frame & f = s.back(); \
bqm.set(f.m_lower, LOWER); \
bqm.set(f.m_upper, UPPER); \
f.m_lower_sv = LOWER_SV; \
f.m_upper_sv = UPPER_SV; \
} \
} ((void) 0)
mpbq zero(0);
PUSH_SS_FRAME(lower_sv, zero_sv, curr_lower, zero);
PUSH_SS_FRAME(zero_sv, upper_sv, zero, curr_upper);
while (!s.empty()) {
checkpoint();
ss_frame & f = s.back();
lower_sv = f.m_lower_sv;
upper_sv = f.m_upper_sv;
bqm.swap(curr_lower, f.m_lower);
bqm.swap(curr_upper, f.m_upper);
pop_ss_frame(bqm, s);
SASSERT(lower_sv > upper_sv + 1);
bqm.add(curr_lower, curr_upper, mid);
bqm.div2(mid);
TRACE("upolynomial",
tout << "depth: " << s.size() << "\n";
tout << "lower_sv: " << lower_sv << "\n";
tout << "upper_sv: " << upper_sv << "\n";
tout << "curr_lower: " << curr_lower << "\n";
tout << "curr_upper: " << curr_upper << "\n";
tout << "mid: " << mid << "\n";);
unsigned mid_sv = sign_variations_at(seq, mid);
PUSH_SS_FRAME(lower_sv, mid_sv, curr_lower, mid);
PUSH_SS_FRAME(mid_sv, upper_sv, mid, curr_upper);
}
}
void manager::sturm_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) {
SASSERT(lowers.size() == uppers.size());
scoped_numeral_vector p1(m());
set(sz, p, p1);
normalize(p1);
#if 1
unsigned pos_k = knuth_positive_root_upper_bound(sz, p);
unsigned neg_k = knuth_negative_root_upper_bound(sz, p);
#else
scoped_numeral U(m());
root_upper_bound(p1.size(), p1.c_ptr(), U);
unsigned pos_k = m().log2(U) + 1;
unsigned neg_k = pos_k;
#endif
sturm_isolate_roots_core(p1.size(), p1.data(), neg_k, pos_k, bqm, roots, lowers, uppers);
}
void manager::sqf_nz_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) {
SASSERT(!has_zero_roots(sz, p));
drs_isolate_roots(sz, p, bqm, roots, lowers, uppers);
}
void manager::sqf_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) {
bqm.reset(roots);
bqm.reset(lowers);
bqm.reset(uppers);
if (has_zero_roots(sz, p)) {
roots.push_back(mpbq(0));
scoped_numeral_vector nz_p(m());
remove_zero_roots(sz, p, nz_p);
TRACE("upolynomial", tout << "after removing zero root:\n"; display(tout, nz_p); tout << "\n";);
SASSERT(!has_zero_roots(nz_p.size(), nz_p.data()));
sqf_nz_isolate_roots(nz_p.size(), nz_p.data(), bqm, roots, lowers, uppers);
}
else {
sqf_nz_isolate_roots(sz, p, bqm, roots, lowers, uppers);
}
}
void manager::isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers) {
SASSERT(sz > 0);
TRACE("upolynomial", tout << "isolating roots of:\n"; display(tout, sz, p); tout << "\n";);
scoped_numeral_vector sqf_p(m());
square_free(sz, p, sqf_p);
TRACE("upolynomial", tout << "square free part:\n"; display(tout, sqf_p); tout << "\n";);
sqf_isolate_roots(sqf_p.size(), sqf_p.data(), bqm, roots, lowers, uppers);
}
void manager::sturm_seq_core(upolynomial_sequence & seq) {
scoped_numeral_vector r(m());
while (m_limit.inc()) {
unsigned sz = seq.size();
srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r);
if (is_zero(r))
return;
normalize(r);
seq.push(r.size(), r.data());
}
}
void manager::sturm_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq) {
reset(seq);
seq.push(m(), sz1, p1);
seq.push(m(), sz2, p2);
sturm_seq_core(seq);
}
void manager::sturm_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq) {
reset(seq);
scoped_numeral_vector p_prime(m());
seq.push(m(), sz, p);
derivative(sz, p, p_prime);
seq.push(p_prime.size(), p_prime.data());
sturm_seq_core(seq);
}
void manager::sturm_tarski_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq) {
reset(seq);
scoped_numeral_vector p1p2(m());
seq.push(m(), sz1, p1);
derivative(sz1, p1, p1p2);
mul(p1p2.size(), p1p2.data(), sz2, p2, p1p2);
seq.push(p1p2.size(), p1p2.data());
sturm_seq_core(seq);
}
void manager::fourier_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq) {
reset(seq);
scoped_numeral_vector p_prime(m());
seq.push(m(), sz, p);
if (sz == 0)
return;
unsigned degree = sz - 1;
for (unsigned i = 0; i < degree; i++) {
unsigned sz = seq.size();
derivative(seq.size(sz-1), seq.coeffs(sz-1), p_prime);
normalize(p_prime);
seq.push(p_prime.size(), p_prime.data());
}
}
We say an interval (a, b) of a polynomial p is ISOLATING if p has only one root in the
interval (a, b).
We say an isolating interval (a, b) of a square free polynomial p is REFINABLE if
sign(p(a)) = -sign(p(b))
Not every isolating interval (a, b) of a square free polynomial p is refinable, because
sign(p(a)) or sign(p(b)) may be zero.
Refinable intervals of square free polynomials are useful, because we can increase precision
("squeeze" the interval) by just evaluating p at (a+b)/2
This procedure converts an isolating interval of a square free polynomial p, into
a refinable interval, or an actual root.
The method returns TRUE if it produced a REFINABLE interval (a', b'). The new interval
is stored in input variables a and b.
The method returns FALSE if it found the root a' in the interval (a, b). The root is
stored in the input variable a.
\pre p MUST BE SQUARE FREE.
\warning This method may loop if p is not square free or if (a,b) is not an isolating interval.
*/
bool manager::isolating2refinable(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b) {
int sign_a = eval_sign_at(sz, p, a);
int sign_b = eval_sign_at(sz, p, b);
TRACE("upolynomial", tout << "sign_a: " << sign_a << ", sign_b: " << sign_b << "\n";);
if (sign_a != 0 && sign_b != 0) {
SASSERT(sign_a == -sign_b);
return true;
}
if (sign_a == 0 && sign_b != 0) {
scoped_mpbq new_a(bqm);
bqm.add(a, b, new_a);
bqm.div2(new_a);
while (true) {
TRACE("upolynomial", tout << "CASE 2, a: " << bqm.to_string(a) << ", b: " << bqm.to_string(b) << ", new_a: " << bqm.to_string(new_a) << "\n";);
int sign_new_a = eval_sign_at(sz, p, new_a);
if (sign_new_a != sign_b) {
swap(new_a, a);
SASSERT(sign_new_a == 0 ||
sign_new_a == -sign_b);
return sign_new_a != 0;
}
else {
SASSERT(sign_new_a == sign_b);
swap(b, new_a);
bqm.add(b, a, new_a);
bqm.div2(new_a);
}
}
}
if (sign_a != 0 && sign_b == 0 ) {
scoped_mpbq new_b(bqm);
bqm.add(a, b, new_b);
bqm.div2(new_b);
while (true) {
TRACE("upolynomial", tout << "CASE 3, a: " << bqm.to_string(a) << ", b: " << bqm.to_string(b) << ", new_b: " << bqm.to_string(new_b) << "\n";);
int sign_new_b = eval_sign_at(sz, p, new_b);
if (sign_new_b != sign_a) {
SASSERT(sign_new_b == 0 ||
sign_new_b == -sign_a);
if (sign_new_b == 0)
swap(new_b, a);
else
swap(new_b, b);
return sign_new_b != 0;
}
else {
SASSERT(sign_new_b == sign_a);
swap(a, new_b);
bqm.add(b, a, new_b);
bqm.div2(new_b);
}
}
}
SASSERT(sign_a == 0 && sign_b == 0);
mpbq & a1 = a;
scoped_mpbq b1(bqm);
scoped_mpbq a2(bqm);
mpbq & b2 = b;
scoped_mpbq new_a1(bqm);
scoped_mpbq new_b2(bqm);
bqm.add(a, b, b1);
bqm.div2(b1);
bqm.set(a2, b1);
int sign_b1 = eval_sign_at(sz, p, b1);
int sign_a2 = sign_b1;
bool result;
if (sign_b1 == 0) {
swap(b1, a);
result = false;
goto end;
}
bqm.add(a1, b1, new_a1);
bqm.div2(new_a1);
bqm.add(a2, b2, new_b2);
bqm.div2(new_b2);
while (true) {
TRACE("upolynomial",
tout << "CASE 4\na1: " << bqm.to_string(a1) << ", b1: " << bqm.to_string(b1) << ", new_a1: " << bqm.to_string(new_a1) << "\n";
tout << "a2: " << bqm.to_string(a2) << ", b2: " << bqm.to_string(b2) << ", new_b2: " << bqm.to_string(new_b2) << "\n";);
int sign_new_a1 = eval_sign_at(sz, p, new_a1);
if (sign_new_a1 == 0) {
swap(new_a1, a);
result = false;
goto end;
}
if (sign_new_a1 == -sign_b1) {
swap(new_a1, a);
swap(b1, b);
result = true;
goto end;
}
int sign_new_b2 = eval_sign_at(sz, p, new_b2);
if (sign_new_b2 == 0) {
swap(new_b2, a);
result = false;
goto end;
}
if (sign_new_b2 == -sign_a2) {
swap(a2, a);
swap(new_b2, b);
result = true;
goto end;
}
SASSERT(sign_new_a1 == sign_b1);
swap(b1, new_a1);
bqm.add(b1, a1, new_a1);
bqm.div2(new_a1);
SASSERT(sign_new_b2 == sign_a2);
swap(a2, new_b2);
bqm.add(b2, a2, new_b2);
bqm.div2(new_b2);
}
end:
return result;
}
Given a square-free polynomial p, and a refinable interval (a,b), then "squeeze" (a,b).
That is, return a new interval (a',b') s.t. b' - a' = (b - a)/2
See isolating2refinable for a definition of refinable 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.
The arguments sign_a and sign_b must contain the values returned by
eval_sign_at(sz, p, a) and eval_sign_at(sz, p, b).
*/
bool manager::refine_core(unsigned sz, numeral const * p, sign sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b) {
SASSERT(sign_a == eval_sign_at(sz, p, a));
SASSERT(-sign_a == eval_sign_at(sz, p, b));
SASSERT(sign_a != 0);
scoped_mpbq mid(bqm);
bqm.add(a, b, mid);
bqm.div2(mid);
auto sign_mid = eval_sign_at(sz, p, mid);
if (::is_zero(sign_mid)) {
swap(mid, a);
return false;
}
if (sign_mid == sign_a) {
swap(mid, a);
return true;
}
SASSERT(sign_mid == -sign_a);
swap(mid, b);
return true;
}
bool manager::refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b) {
sign sign_a = eval_sign_at(sz, p, a);
SASSERT(!::is_zero(sign_a));
return refine_core(sz, p, sign_a, bqm, a, b);
}
bool manager::refine_core(unsigned sz, numeral const * p, sign sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k) {
SASSERT(sign_a != sign_zero);
SASSERT(sign_a == eval_sign_at(sz, p, a));
SASSERT(-sign_a == eval_sign_at(sz, p, b));
scoped_mpbq w(bqm);
while (true) {
checkpoint();
bqm.sub(b, a, w);
if (bqm.lt_1div2k(w, prec_k)) {
return true;
}
if (!refine_core(sz, p, sign_a, bqm, a, b)) {
return false;
}
}
}
bool manager::refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k) {
sign sign_a = eval_sign_at(sz, p, a);
SASSERT(eval_sign_at(sz, p, b) == -sign_a);
SASSERT(sign_a != 0);
return refine_core(sz, p, sign_a, bqm, a, b, prec_k);
}
bool manager::convert_q2bq_interval(unsigned sz, numeral const * p, mpq const & a, mpq const & b, mpbq_manager & bqm, mpbq & c, mpbq & d) {
sign sign_a = eval_sign_at(sz, p, a);
sign sign_b = eval_sign_at(sz, p, b);
SASSERT(!::is_zero(sign_a) && !::is_zero(sign_b));
SASSERT(sign_a == -sign_b);
bool found_d = false;
TRACE("convert_bug",
tout << "a: " << m().to_string(a.numerator()) << "/" << m().to_string(a.denominator()) << "\n";
tout << "b: " << m().to_string(b.numerator()) << "/" << m().to_string(b.denominator()) << "\n";
tout << "sign_a: " << sign_a << "\n";
tout << "sign_b: " << sign_b << "\n";);
scoped_mpbq lower(bqm), upper(bqm);
if (bqm.to_mpbq(a, lower)) {
TRACE("convert_bug", tout << "found c: " << lower << "\n";);
swap(c, lower);
SASSERT(bqm.eq(c, a));
}
else {
bqm.set(upper, lower);
bqm.mul2(upper);
if (m_manager.is_neg(a.numerator()))
::swap(lower, upper);
TRACE("convert_bug",
tout << "a: "; m().display(tout, a.numerator()); tout << "/"; m().display(tout, a.denominator()); tout << "\n";
tout << "lower: "; bqm.display(tout, lower); tout << ", upper: "; bqm.display(tout, upper); tout << "\n";);
SASSERT(bqm.lt(lower, a));
SASSERT(bqm.gt(upper, a));
while (bqm.ge(upper, b)) {
bqm.refine_upper(a, lower, upper);
}
SASSERT(bqm.lt(upper, b));
while (true) {
auto sign_upper = eval_sign_at(sz, p, upper);
if (::is_zero(sign_upper)) {
bqm.swap(c, upper);
bqm.del(lower); bqm.del(upper);
return false;
}
else if (sign_upper == sign_a) {
bqm.swap(c, upper);
break;
}
else {
SASSERT(sign_upper == sign_b);
if (!found_d) {
found_d = true;
bqm.set(d, upper);
}
}
bqm.refine_upper(a, lower, upper);
}
}
if (!found_d) {
if (bqm.to_mpbq(b, lower)) {
swap(d, lower);
SASSERT(bqm.eq(d, b));
}
else {
bqm.set(upper, lower);
bqm.mul2(upper);
if (m_manager.is_neg(b.numerator()))
::swap(lower, upper);
SASSERT(bqm.gt(upper, b));
SASSERT(bqm.lt(lower, b));
while (bqm.le(lower, c)) {
bqm.refine_lower(b, lower, upper);
}
SASSERT(bqm.lt(c, lower));
SASSERT(bqm.lt(lower, upper));
SASSERT(bqm.lt(lower, b));
while (true) {
sign sign_lower = eval_sign_at(sz, p, lower);
if (::is_zero(sign_lower)) {
bqm.swap(c, lower);
bqm.del(lower); bqm.del(upper);
return false;
}
else if (sign_lower == sign_b) {
bqm.swap(d, lower);
break;
}
bqm.refine_lower(b, lower, upper);
}
}
}
SASSERT(bqm.lt(c, d));
SASSERT(eval_sign_at(sz, p, c) == -eval_sign_at(sz, p, d));
SASSERT(bqm.ge(c, a));
SASSERT(bqm.le(d, b));
return true;
}
bool manager::normalize_interval_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & m, mpbq & a, mpbq & b) {
if (m.is_neg(a) && m.is_pos(b)) {
if (sign_a == INT_MIN) {
sign_a = eval_sign_at(sz, p, a);
}
else {
SASSERT(sign_a == eval_sign_at(sz, p, a));
}
SASSERT(-sign_a == eval_sign_at(sz, p, b));
SASSERT(sign_a != 0);
if (has_zero_roots(sz, p)) {
return false;
}
auto sign_zero = eval_sign_at_zero(sz, p);
if (sign_a == sign_zero) {
m.reset(a);
}
else {
m.reset(b);
}
SASSERT(eval_sign_at(sz, p, a) == -eval_sign_at(sz, p, b));
}
return true;
}
bool manager::normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq & a, mpbq & b) {
return normalize_interval_core(sz, p, INT_MIN, m, a, b);
}
unsigned manager::get_root_id(unsigned sz, numeral const * p, mpbq const & l) {
scoped_upolynomial_sequence seq(*this);
sturm_seq(sz, p, seq);
unsigned s0 = sign_variations_at_minus_inf(seq);
unsigned s1 = sign_variations_at(seq, l);
SASSERT(s0 >= s1);
return s0 - s1;
}
void manager::flip_sign(factors & r) {
scoped_numeral new_c(m_manager);
m_manager.set(new_c, r.get_constant());
m_manager.neg(new_c);
r.set_constant(new_c);
}
void manager::factor_2_sqf_pp(numeral_vector & p, factors & r, unsigned k) {
SASSERT(p.size() == 3);
TRACE("factor", tout << "factor square free (degree == 2):\n"; display(tout, p); tout << "\n";);
numeral const & a = p[2];
numeral const & b = p[1];
numeral const & c = p[0];
TRACE("factor", tout << "a: " << m().to_string(a) << "\nb: " << m().to_string(b) << "\nc: " << m().to_string(c) << "\n";);
scoped_numeral b2(m());
scoped_numeral ac(m());
scoped_numeral disc(m());
m().power(b, 2, b2);
m().mul(a, c, ac);
m().addmul(b2, numeral(-4), ac, disc);
SASSERT(!m().is_zero(disc));
scoped_numeral disc_sqrt(m());
if (!m().is_perfect_square(disc, disc_sqrt)) {
r.push_back(p, k);
return;
}
TRACE("factor", tout << "disc_sqrt: " << m().to_string(disc_sqrt) << "\n";);
scoped_numeral_vector f1(m());
scoped_numeral_vector f2(m());
f1.reserve(2); f2.reserve(2);
m().sub(b, disc_sqrt, f1[0]);
m().add(b, disc_sqrt, f2[0]);
m().mul(a, numeral(2), f1[1]);
m().mul(a, numeral(2), f2[1]);
set_size(2, f1);
set_size(2, f2);
normalize(f1);
normalize(f2);
TRACE("factor", tout << "f1: "; display(tout, f1); tout << "\nf2: "; display(tout, f2); tout << "\n";);
DEBUG_CODE({
scoped_numeral_vector f1f2(m());
mul(f1, f2, f1f2);
SASSERT(eq(f1f2, p));
});
r.push_back(f1, k);
r.push_back(f2, k);
}
bool manager::factor_sqf_pp(numeral_vector & p, factors & r, unsigned k, factor_params const & params) {
unsigned sz = p.size();
SASSERT(sz == 0 || m().is_pos(p[sz-1]));
if (sz <= 2) {
r.push_back(p, k);
return true;
}
else if (sz == 3) {
factor_2_sqf_pp(p, r, k);
return true;
}
else {
return factor_square_free(*this, p, r, k, params);
}
}
void manager::flip_factor_sign_if_lm_neg(numeral_vector & p, factors & r, unsigned k) {
unsigned sz = p.size();
if (sz == 0)
return;
if (m().is_neg(p[sz - 1])) {
for (unsigned i = 0; i < sz; i++)
m().neg(p[i]);
if (k % 2 == 1)
flip_sign(r);
}
}
bool manager::factor_core(unsigned sz, numeral const * p, factors & r, factor_params const & params) {
if (sz == 0) {
r.set_constant(numeral(0));
return true;
}
if (sz == 1) {
r.set_constant(p[0]);
return true;
}
scoped_numeral content(m());
scoped_numeral_vector pp(m());
get_primitive_and_content(sz, p, pp, content);
r.set_constant(content);
scoped_numeral_vector & C = pp;
scoped_numeral_vector C_prime(m());
derivative(C, C_prime);
scoped_numeral_vector A(m()), B(m()), D(m());
gcd(C, C_prime, B);
bool result = true;
if (is_const(B)) {
SASSERT(!is_const(C));
flip_factor_sign_if_lm_neg(C, r, 1);
if (!factor_sqf_pp(C, r, 1, params))
result = false;
}
else {
VERIFY(exact_div(C, B, A));
TRACE("factor_bug", tout << "C: "; display(tout, C); tout << "\nB: "; display(tout, B); tout << "\nA: "; display(tout, A); tout << "\n";);
unsigned j = 1;
while (!is_const(A)) {
checkpoint();
TRACE("factor", tout << "factor_core main loop j: " << j << "\nA: "; display(tout, A); tout << "\nB: "; display(tout, B); tout << "\n";);
gcd(A, B, D);
VERIFY(exact_div(A, D, C));
if (!is_const(C)) {
flip_factor_sign_if_lm_neg(C, r, j);
if (!factor_sqf_pp(C, r, j, params))
result = false;
}
else {
TRACE("factor", tout << "const C: "; display(tout, C); tout << "\n";);
SASSERT(C.size() == 1);
SASSERT(m().is_one(C[0]) || m().is_minus_one(C[0]));
if (m().is_minus_one(C[0]) && j % 2 == 1)
flip_sign(r);
}
TRACE("factor_bug", tout << "B: "; display(tout, B); tout << "\nD: "; display(tout, D); tout << "\n";);
VERIFY(exact_div(B, D, B));
A.swap(D);
j++;
}
TRACE("factor_bug", tout << "A: "; display(tout, A); tout << "\n";);
SASSERT(A.size() == 1 && m().is_one(A[0]));
}
return result;
}
bool manager::factor(unsigned sz, numeral const * p, factors & r, factor_params const & params) {
bool result = factor_core(sz, p, r, params);
#ifndef _EXTERNAL_RELEASE
IF_VERBOSE(FACTOR_VERBOSE_LVL, verbose_stream() << "(polynomial-factorization :distinct-factors " << r.distinct_factors() << ")" << std::endl;);
#endif
return result;
}
std::ostream& manager::display(std::ostream & out, upolynomial_sequence const & seq, char const * var_name) const {
for (unsigned i = 0; i < seq.size(); i++) {
display(out, seq.size(i), seq.coeffs(i), var_name);
out << "\n";
}
return out;
}
};