Copyright (c) 2013 Microsoft Corporation
Module Name:
realclosure.cpp
Abstract:
Package for computing with elements of the realclosure of a field containing
- all rationals
- extended with computable transcendental real numbers (e.g., pi and e)
- infinitesimals
Author:
Leonardo (leonardo) 2013-01-02
Notes:
--*/
#include "math/realclosure/realclosure.h"
#include "math/realclosure/rcf_params.hpp"
#include "util/array.h"
#include "util/mpbq.h"
#include "math/realclosure/mpz_matrix.h"
#include "math/interval/interval_def.h"
#include "util/obj_ref.h"
#include "util/ref_vector.h"
#include "util/ref_buffer.h"
#include "util/common_msgs.h"
#ifndef REALCLOSURE_INI_BUFFER_SIZE
#define REALCLOSURE_INI_BUFFER_SIZE 32
#endif
#ifndef REALCLOSURE_INI_SEQ_SIZE
#define REALCLOSURE_INI_SEQ_SIZE 256
#endif
#ifndef REALCLOSURE_INI_DIV_PRECISION
#define REALCLOSURE_INI_DIV_PRECISION 24
#endif
namespace realclosure {
struct mpbq_config {
struct numeral_manager : public mpbq_manager {
static bool precise() { return false; }
static bool field() { return true; }
unsigned m_div_precision;
bool m_to_plus_inf;
numeral_manager(unsynch_mpq_manager & qm):mpbq_manager(qm), m_div_precision(REALCLOSURE_INI_DIV_PRECISION), m_to_plus_inf(true) {
}
void div(mpbq const & a, mpbq const & b, mpbq & c) {
approx_div(a, b, c, m_div_precision, m_to_plus_inf);
}
void inv(mpbq & a) {
mpbq one(1);
scoped_mpbq r(*this);
approx_div(one, a, r, m_div_precision, m_to_plus_inf);
swap(a, r);
}
};
typedef mpbq numeral;
numeral_manager & m_manager;
struct interval {
numeral m_lower;
numeral m_upper;
unsigned char m_lower_inf;
unsigned char m_upper_inf;
unsigned char m_lower_open;
unsigned char m_upper_open;
interval():m_lower_inf(true), m_upper_inf(true), m_lower_open(true), m_upper_open(true) {}
interval(numeral & l, numeral & u):m_lower_inf(false), m_upper_inf(false), m_lower_open(true), m_upper_open(true) {
swap(m_lower, l);
swap(m_upper, u);
}
numeral & lower() { return m_lower; }
numeral & upper() { return m_upper; }
void set_lower_is_inf(bool f) { m_lower_inf = f; }
void set_upper_is_inf(bool f) { m_upper_inf = f; }
void set_lower_is_open(bool f) { m_lower_open = f; }
void set_upper_is_open(bool f) { m_upper_open = f; }
numeral const & lower() const { return m_lower; }
numeral const & upper() const { return m_upper; }
bool lower_is_inf() const { return m_lower_inf != 0; }
bool upper_is_inf() const { return m_upper_inf != 0; }
bool lower_is_open() const { return m_lower_open != 0; }
bool upper_is_open() const { return m_upper_open != 0; }
};
void set_rounding(bool to_plus_inf) { m_manager.m_to_plus_inf = to_plus_inf; }
void round_to_minus_inf() { set_rounding(false); }
void round_to_plus_inf() { set_rounding(true); }
numeral const & lower(interval const & a) const { return a.m_lower; }
numeral const & upper(interval const & a) const { return a.m_upper; }
numeral & lower(interval & a) { return a.m_lower; }
numeral & upper(interval & a) { return a.m_upper; }
bool lower_is_open(interval const & a) const { return a.lower_is_open(); }
bool upper_is_open(interval const & a) const { return a.upper_is_open(); }
bool lower_is_inf(interval const & a) const { return a.lower_is_inf(); }
bool upper_is_inf(interval const & a) const { return a.upper_is_inf(); }
void set_lower(interval & a, numeral const & n) { m_manager.set(a.m_lower, n); }
void set_upper(interval & a, numeral const & n) { m_manager.set(a.m_upper, n); }
void set_lower_is_open(interval & a, bool v) { a.m_lower_open = v; }
void set_upper_is_open(interval & a, bool v) { a.m_upper_open = v; }
void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; }
void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; }
numeral_manager & m() const { return m_manager; }
mpbq_config(numeral_manager & m):m_manager(m) {}
};
typedef interval_manager<mpbq_config> mpbqi_manager;
typedef mpbqi_manager::interval mpbqi;
void swap(mpbqi & a, mpbqi & b) {
swap(a.m_lower, b.m_lower);
swap(a.m_upper, b.m_upper);
std::swap(a.m_lower_inf, b.m_lower_inf);
std::swap(a.m_upper_inf, b.m_upper_inf);
std::swap(a.m_lower_open, b.m_lower_open);
std::swap(a.m_upper_open, b.m_upper_open);
}
struct value {
unsigned m_ref_count;
bool m_rational;
mpbqi m_interval;
mpbqi * m_old_interval;
value(bool rat):m_ref_count(0), m_rational(rat), m_old_interval(nullptr) {}
bool is_rational() const { return m_rational; }
mpbqi const & interval() const { return m_interval; }
mpbqi & interval() { return m_interval; }
};
struct rational_value : public value {
mpq m_value;
rational_value():value(true) {}
};
typedef ptr_array<value> polynomial;
struct extension;
bool rank_lt(extension * r1, extension * r2);
struct rational_function_value : public value {
polynomial m_numerator;
polynomial m_denominator;
extension * m_ext;
bool m_depends_on_infinitesimals;
rational_function_value(extension * ext):value(false), m_ext(ext), m_depends_on_infinitesimals(false) {}
polynomial const & num() const { return m_numerator; }
polynomial & num() { return m_numerator; }
polynomial const & den() const { return m_denominator; }
polynomial & den() { return m_denominator; }
extension * ext() const { return m_ext; }
bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; }
void set_depends_on_infinitesimals(bool f) { m_depends_on_infinitesimals = f; }
};
typedef int sign;
typedef std::pair<polynomial, sign> p2s;
typedef sarray<p2s> signs;
struct extension {
enum kind {
TRANSCENDENTAL = 0,
INFINITESIMAL = 1,
ALGEBRAIC = 2
};
unsigned m_ref_count;
unsigned m_kind:2;
unsigned m_idx:30;
mpbqi m_interval;
mpbqi * m_old_interval;
extension(kind k, unsigned idx):m_ref_count(0), m_kind(k), m_idx(idx), m_old_interval(nullptr) {}
unsigned idx() const { return m_idx; }
kind knd() const { return static_cast<kind>(m_kind); }
bool is_algebraic() const { return knd() == ALGEBRAIC; }
bool is_infinitesimal() const { return knd() == INFINITESIMAL; }
bool is_transcendental() const { return knd() == TRANSCENDENTAL; }
mpbqi const & interval() const { return m_interval; }
mpbqi & interval() { return m_interval; }
};
bool rank_lt(extension * r1, extension * r2) {
return r1->knd() < r2->knd() || (r1->knd() == r2->knd() && r1->idx() < r2->idx());
}
bool rank_eq(extension * r1, extension * r2) {
return r1->knd() == r2->knd() && r1->idx() == r2->idx();
}
struct rank_lt_proc {
bool operator()(extension * r1, extension * r2) const {
return rank_lt(r1, r2);
}
};
\brief Sign condition object, it encodes one conjunct of a sign assignment.
If has to keep following m_prev to obtain the whole sign condition
*/
struct sign_condition {
unsigned m_q_idx:31;
unsigned m_mark:1;
int m_sign;
sign_condition * m_prev;
sign_condition():m_q_idx(0), m_mark(false), m_sign(0), m_prev(nullptr) {}
sign_condition(unsigned qidx, int sign, sign_condition * prev):m_q_idx(qidx), m_mark(false), m_sign(sign), m_prev(prev) {}
sign_condition * prev() const { return m_prev; }
unsigned qidx() const { return m_q_idx; }
int sign() const { return m_sign; }
};
struct sign_det {
unsigned m_ref_count;
mpz_matrix M_s;
array<polynomial> m_prs;
array<int> m_taqrs;
array<sign_condition *> m_sign_conditions;
array<polynomial> m_qs;
sign_det():m_ref_count(0) {}
array<polynomial> const & qs() const { return m_qs; }
sign_condition * sc(unsigned idx) const { return m_sign_conditions[idx]; }
unsigned num_roots() const { return m_prs.size(); }
array<int> const & taqrs() const { return m_taqrs; }
array<polynomial> const & prs() const { return m_prs; }
};
struct algebraic : public extension {
polynomial m_p;
mpbqi m_iso_interval;
sign_det * m_sign_det;
unsigned m_sc_idx;
bool m_depends_on_infinitesimals;
algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(nullptr), m_sc_idx(0), m_depends_on_infinitesimals(false) {}
polynomial const & p() const { return m_p; }
bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; }
sign_det * sdt() const { return m_sign_det; }
unsigned sc_idx() const { return m_sc_idx; }
unsigned num_roots_inside_interval() const { return m_sign_det == nullptr ? 1 : m_sign_det->num_roots(); }
mpbqi & iso_interval() { return m_iso_interval; }
};
struct transcendental : public extension {
symbol m_name;
symbol m_pp_name;
unsigned m_k;
mk_interval & m_proc;
transcendental(unsigned idx, symbol const & n, symbol const & pp_n, mk_interval & p):
extension(TRANSCENDENTAL, idx), m_name(n), m_pp_name(pp_n), m_k(0), m_proc(p) {}
void display(std::ostream & out, bool pp = false) const {
if (pp)
out << m_pp_name;
else
out << m_name;
}
};
struct infinitesimal : public extension {
symbol m_name;
symbol m_pp_name;
infinitesimal(unsigned idx, symbol const & n, symbol const & pp_n):extension(INFINITESIMAL, idx), m_name(n), m_pp_name(pp_n) {}
void display(std::ostream & out, bool pp = false) const {
if (pp) {
if (m_pp_name.is_numerical())
out << "ε<sub>" << m_pp_name.get_num() << "</sub>";
else
out << m_pp_name;
}
else {
if (m_name.is_numerical())
out << "eps!" << m_name.get_num();
else
out << m_name;
}
}
};
struct mk_pi_interval : public mk_interval {
void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) override {
im.pi(k, r);
}
};
struct mk_e_interval : public mk_interval {
void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) override {
im.e(k, r);
}
};
struct manager::imp {
typedef ref_vector<value, imp> value_ref_vector;
typedef ref_buffer<value, imp, REALCLOSURE_INI_BUFFER_SIZE> value_ref_buffer;
typedef obj_ref<value, imp> value_ref;
typedef _scoped_interval<mpqi_manager> scoped_mpqi;
typedef _scoped_interval<mpbqi_manager> scoped_mpbqi;
typedef sbuffer<int, REALCLOSURE_INI_BUFFER_SIZE> int_buffer;
typedef sbuffer<unsigned, REALCLOSURE_INI_BUFFER_SIZE> unsigned_buffer;
reslimit& m_limit;
small_object_allocator * m_allocator;
bool m_own_allocator;
unsynch_mpq_manager & m_qm;
mpz_matrix_manager m_mm;
mpbq_config::numeral_manager m_bqm;
mpqi_manager m_qim;
mpbqi_manager m_bqim;
ptr_vector<extension> m_extensions[3];
value * m_one;
mk_pi_interval m_mk_pi_interval;
value * m_pi;
mk_e_interval m_mk_e_interval;
value * m_e;
ptr_vector<value> m_to_restore;
ptr_vector<extension> m_ex_to_restore;
bool m_use_prem;
bool m_clean_denominators;
unsigned m_ini_precision;
unsigned m_max_precision;
unsigned m_inf_precision;
scoped_mpbq m_plus_inf_approx;
scoped_mpbq m_minus_inf_approx;
bool m_lazy_algebraic_normalization;
unsigned m_exec_depth;
bool m_in_aux_values;
struct scoped_polynomial_seq {
typedef ref_buffer<value, imp, REALCLOSURE_INI_SEQ_SIZE> value_seq;
value_seq m_seq_coeffs;
sbuffer<unsigned> m_begins;
sbuffer<unsigned> m_szs;
public:
scoped_polynomial_seq(imp & m):m_seq_coeffs(m) {}
~scoped_polynomial_seq() {
}
\brief Add a new polynomial to the sequence.
The contents of p is erased.
*/
void push(unsigned sz, value * const * p) {
m_begins.push_back(m_seq_coeffs.size());
m_szs.push_back(sz);
m_seq_coeffs.append(sz, p);
}
\brief Return the number of polynomials in the sequence.
*/
unsigned size() const { return m_szs.size(); }
\brief Return the vector of coefficients for the i-th polynomial in the sequence.
*/
value * const * coeffs(unsigned i) const {
return m_seq_coeffs.data() + m_begins[i];
}
\brief Return the size of the i-th polynomial in the sequence.
*/
unsigned size(unsigned i) const { return m_szs[i]; }
void reset() {
m_seq_coeffs.reset();
m_begins.reset();
m_szs.reset();
}
scoped_polynomial_seq & operator=(scoped_polynomial_seq & s) {
if (this == &s)
return *this;
reset();
m_seq_coeffs.append(s.m_seq_coeffs);
m_begins.append(s.m_begins);
m_szs.append(s.m_szs);
return *this;
}
};
struct scoped_sign_conditions {
imp & m_imp;
ptr_buffer<sign_condition, REALCLOSURE_INI_BUFFER_SIZE> m_scs;
scoped_sign_conditions(imp & m):m_imp(m) {}
~scoped_sign_conditions() {
m_imp.del_sign_conditions(m_scs.size(), m_scs.data());
}
sign_condition * & operator[](unsigned idx) { return m_scs[idx]; }
unsigned size() const { return m_scs.size(); }
bool empty() const { return m_scs.empty(); }
void push_back(sign_condition * sc) { m_scs.push_back(sc); }
void release() {
m_scs.reset();
}
void copy_from(scoped_sign_conditions & scs) {
SASSERT(this != &scs);
release();
m_scs.append(scs.m_scs.size(), scs.m_scs.data());
scs.release();
}
sign_condition * const * data() { return m_scs.data(); }
};
struct scoped_inc_depth {
imp & m_imp;
scoped_inc_depth(imp & m):m_imp(m) { m_imp.m_exec_depth++; }
~scoped_inc_depth() { m_imp.m_exec_depth--; }
};
#ifdef _TRACE
#define INC_DEPTH() scoped_inc_depth __inc(*this)
#else
#define INC_DEPTH() ((void) 0)
#endif
imp(reslimit& lim, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a):
m_limit(lim),
m_allocator(a == nullptr ? alloc(small_object_allocator, "realclosure") : a),
m_own_allocator(a == nullptr),
m_qm(qm),
m_mm(m_qm, *m_allocator),
m_bqm(m_qm),
m_qim(lim, m_qm),
m_bqim(lim, m_bqm),
m_plus_inf_approx(m_bqm),
m_minus_inf_approx(m_bqm) {
mpq one(1);
m_one = mk_rational(one);
inc_ref(m_one);
m_pi = nullptr;
m_e = nullptr;
m_exec_depth = 0;
m_in_aux_values = false;
updt_params(p);
}
~imp() {
restore_saved_intervals();
dec_ref(m_one);
dec_ref(m_pi);
dec_ref(m_e);
if (m_own_allocator)
dealloc(m_allocator);
}
unsynch_mpq_manager & qm() const { return m_qm; }
mpbq_config::numeral_manager & bqm() { return m_bqm; }
mpqi_manager & qim() { return m_qim; }
mpbqi_manager & bqim() { return m_bqim; }
mpbqi_manager const & bqim() const { return m_bqim; }
mpz_matrix_manager & mm() { return m_mm; }
small_object_allocator & allocator() { return *m_allocator; }
void checkpoint() {
if (!m_limit.inc())
throw exception(Z3_CANCELED_MSG);
}
value * one() const {
return m_one;
}
\brief Return the magnitude of the given interval.
The magnitude is an approximation of the size of the interval.
*/
int magnitude(mpbq const & l, mpbq const & u) {
SASSERT(bqm().ge(u, l));
scoped_mpbq w(bqm());
bqm().sub(u, l, w);
if (bqm().is_zero(w))
return INT_MIN;
SASSERT(bqm().is_pos(w));
return bqm().magnitude_ub(w);
}
\brief Return the magnitude of the given interval.
The magnitude is an approximation of the size of the interval.
*/
int magnitude(mpbqi const & i) {
if (i.lower_is_inf() || i.upper_is_inf())
return INT_MAX;
else
return magnitude(i.lower(), i.upper());
}
\brief Return the magnitude of the given interval.
The magnitude is an approximation of the size of the interval.
*/
int magnitude(mpq const & l, mpq const & u) {
SASSERT(qm().ge(u, l));
scoped_mpq w(qm());
qm().sub(u, l, w);
if (qm().is_zero(w))
return INT_MIN;
SASSERT(qm().is_pos(w));
return static_cast<int>(qm().log2(w.get().numerator())) + 1 - static_cast<int>(qm().log2(w.get().denominator()));
}
int magnitude(scoped_mpqi const & i) {
SASSERT(!i->m_lower_inf && !i->m_upper_inf);
return magnitude(i->m_lower, i->m_upper);
}
\brief Return true if the magnitude of the given interval is less than the parameter m_max_precision.
*/
bool too_small(mpbqi const & i) {
return magnitude(i) < -static_cast<int>(m_max_precision);
}
#define SMALL_UNSIGNED 1 << 16
static unsigned inc_precision(unsigned prec, unsigned inc) {
if (prec < SMALL_UNSIGNED)
return prec + inc;
else
return prec;
}
struct scoped_set_div_precision {
mpbq_config::numeral_manager & m_bqm;
unsigned m_old_precision;
scoped_set_div_precision(mpbq_config::numeral_manager & bqm, unsigned prec):m_bqm(bqm) {
m_old_precision = m_bqm.m_div_precision;
m_bqm.m_div_precision = prec;
}
~scoped_set_div_precision() {
m_bqm.m_div_precision = m_old_precision;
}
};
\brief c <- a/b with precision prec.
*/
void div(mpbqi const & a, mpbqi const & b, unsigned prec, mpbqi & c) {
SASSERT(!contains_zero(a));
SASSERT(!contains_zero(b));
scoped_set_div_precision set(bqm(), prec);
bqim().div(a, b, c);
SASSERT(!contains_zero(c));
}
\brief c <- a/b with precision prec.
*/
void div(mpbqi const & a, mpz const & b, unsigned prec, mpbqi & c) {
SASSERT(!contains_zero(a));
SASSERT(!qm().is_zero(b));
scoped_mpbqi bi(bqim());
set_interval(bi, b);
scoped_mpbqi r(bqim());
div(a, bi, prec, r);
swap(c, r);
}
\brief Save the current interval (i.e., approximation) of the given value or extension.
*/
template<typename T>
void save_interval(T * v, ptr_vector<T> & to_restore) {
if (v->m_old_interval != nullptr)
return;
to_restore.push_back(v);
inc_ref(v);
v->m_old_interval = new (allocator()) mpbqi();
set_interval(*(v->m_old_interval), v->m_interval);
}
void save_interval(value * v) {
save_interval(v, m_to_restore);
}
void save_interval(extension * x) {
save_interval(x, m_ex_to_restore);
}
\brief Save the current interval (i.e., approximation) of the given value IF it is too small.
*/
void save_interval_if_too_small(value * v, unsigned new_prec) {
if (new_prec > m_max_precision && !contains_zero(interval(v)))
save_interval(v);
}
\brief Save the current interval (i.e., approximation) of the given value IF it is too small.
*/
void save_interval_if_too_small(extension * x, unsigned new_prec) {
if (new_prec > m_max_precision && !contains_zero(x->m_interval))
save_interval(x);
}
\brief Restore the saved intervals (approximations) of RCF values and extensions
*/
template<typename T>
void restore_saved_intervals(ptr_vector<T> & to_restore) {
unsigned sz = to_restore.size();
for (unsigned i = 0; i < sz; i++) {
T * v = to_restore[i];
set_interval(v->m_interval, *(v->m_old_interval));
bqim().del(*(v->m_old_interval));
allocator().deallocate(sizeof(mpbqi), v->m_old_interval);
v->m_old_interval = nullptr;
dec_ref(v);
}
to_restore.reset();
}
void restore_saved_intervals() {
restore_saved_intervals(m_to_restore);
restore_saved_intervals(m_ex_to_restore);
}
void cleanup(extension::kind k) {
ptr_vector<extension> & exts = m_extensions[k];
while (!exts.empty() && exts.back() == 0) {
exts.pop_back();
}
}
unsigned next_transcendental_idx() {
cleanup(extension::TRANSCENDENTAL);
return m_extensions[extension::TRANSCENDENTAL].size();
}
unsigned next_infinitesimal_idx() {
cleanup(extension::INFINITESIMAL);
return m_extensions[extension::INFINITESIMAL].size();
}
unsigned next_algebraic_idx() {
cleanup(extension::ALGEBRAIC);
return m_extensions[extension::ALGEBRAIC].size();
}
void updt_params(params_ref const & _p) {
rcf_params p(_p);
m_use_prem = p.use_prem();
m_clean_denominators = p.clean_denominators();
m_ini_precision = p.initial_precision();
m_inf_precision = p.inf_precision();
m_max_precision = p.max_precision();
m_lazy_algebraic_normalization = p.lazy_algebraic_normalization();
bqm().power(mpbq(2), m_inf_precision, m_plus_inf_approx);
bqm().set(m_minus_inf_approx, m_plus_inf_approx);
bqm().neg(m_minus_inf_approx);
}
\brief Reset the given polynomial.
That is, after the call p is the 0 polynomial.
*/
void reset_p(polynomial & p) {
dec_ref(p.size(), p.data());
p.finalize(allocator());
}
void del_rational(rational_value * v) {
bqim().del(v->m_interval);
qm().del(v->m_value);
allocator().deallocate(sizeof(rational_value), v);
}
void del_rational_function(rational_function_value * v) {
bqim().del(v->m_interval);
reset_p(v->num());
reset_p(v->den());
dec_ref(v->ext());
allocator().deallocate(sizeof(rational_function_value), v);
}
void del_value(value * v) {
if (v->is_rational())
del_rational(static_cast<rational_value*>(v));
else
del_rational_function(static_cast<rational_function_value*>(v));
}
void finalize(array<polynomial> & ps) {
for (unsigned i = 0; i < ps.size(); i++)
reset_p(ps[i]);
ps.finalize(allocator());
}
void del_sign_condition(sign_condition * sc) {
allocator().deallocate(sizeof(sign_condition), sc);
}
void del_sign_conditions(unsigned sz, sign_condition * const * to_delete) {
ptr_buffer<sign_condition> all_to_delete;
for (unsigned i = 0; i < sz; i++) {
sign_condition * sc = to_delete[i];
while (sc && sc->m_mark == false) {
sc->m_mark = true;
all_to_delete.push_back(sc);
sc = sc->m_prev;
}
}
for (unsigned i = 0; i < all_to_delete.size(); i++) {
del_sign_condition(all_to_delete[i]);
}
}
void del_sign_det(sign_det * sd) {
mm().del(sd->M_s);
del_sign_conditions(sd->m_sign_conditions.size(), sd->m_sign_conditions.data());
sd->m_sign_conditions.finalize(allocator());
finalize(sd->m_prs);
sd->m_taqrs.finalize(allocator());
finalize(sd->m_qs);
allocator().deallocate(sizeof(sign_det), sd);
}
void inc_ref_sign_det(sign_det * sd) {
if (sd != nullptr)
sd->m_ref_count++;
}
void dec_ref_sign_det(sign_det * sd) {
if (sd != nullptr) {
sd->m_ref_count--;
if (sd->m_ref_count == 0) {
del_sign_det(sd);
}
}
}
void del_algebraic(algebraic * a) {
reset_p(a->m_p);
bqim().del(a->m_interval);
bqim().del(a->m_iso_interval);
dec_ref_sign_det(a->m_sign_det);
allocator().deallocate(sizeof(algebraic), a);
}
void del_transcendental(transcendental * t) {
bqim().del(t->m_interval);
allocator().deallocate(sizeof(transcendental), t);
}
void del_infinitesimal(infinitesimal * i) {
bqim().del(i->m_interval);
allocator().deallocate(sizeof(infinitesimal), i);
}
void inc_ref(extension * ext) {
SASSERT(ext != 0);
ext->m_ref_count++;
}
void dec_ref(extension * ext) {
SASSERT(m_extensions[ext->knd()][ext->idx()] == ext);
SASSERT(ext->m_ref_count > 0);
ext->m_ref_count--;
if (ext->m_ref_count == 0) {
m_extensions[ext->knd()][ext->idx()] = 0;
switch (ext->knd()) {
case extension::TRANSCENDENTAL: del_transcendental(static_cast<transcendental*>(ext)); break;
case extension::INFINITESIMAL: del_infinitesimal(static_cast<infinitesimal*>(ext)); break;
case extension::ALGEBRAIC: del_algebraic(static_cast<algebraic*>(ext)); break;
}
}
}
void inc_ref(value * v) {
if (v)
v->m_ref_count++;
}
void inc_ref(unsigned sz, value * const * p) {
for (unsigned i = 0; i < sz; i++)
inc_ref(p[i]);
}
void dec_ref(value * v) {
if (v) {
SASSERT(v->m_ref_count > 0);
v->m_ref_count--;
if (v->m_ref_count == 0)
del_value(v);
}
}
void dec_ref(unsigned sz, value * const * p) {
for (unsigned i = 0; i < sz; i++)
dec_ref(p[i]);
}
void del(numeral & a) {
dec_ref(a.m_value);
a.m_value = nullptr;
}
void del(numeral_vector & v) {
for (unsigned i = 0; i < v.size(); i++)
del(v[i]);
}
\brief Return true if the given interval is smaller than 1/2^k
*/
bool check_precision(mpbqi const & interval, unsigned k) {
if (interval.lower_is_inf() || interval.upper_is_inf())
return false;
scoped_mpbq w(bqm());
bqm().sub(interval.upper(), interval.lower(), w);
return bqm().lt_1div2k(w, k);
}
\brief Return true if v is zero.
*/
static bool is_zero(value * v) {
return v == nullptr;
}
\brief Return true if v is represented using a nonzero arbitrary precision rational value.
*/
static bool is_nz_rational(value * v) {
SASSERT(v != 0);
return v->is_rational();
}
\brief Return true if v is represented as rational value one.
*/
bool is_rational_one(value * v) const {
return !is_zero(v) && is_nz_rational(v) && qm().is_one(to_mpq(v));
}
\brief Return true if v is represented as rational value minus one.
*/
bool is_rational_minus_one(value * v) const {
return !is_zero(v) && is_nz_rational(v) && qm().is_minus_one(to_mpq(v));
}
\brief Return true if v is the value one;
*/
bool is_one(value * v) const {
return const_cast<imp*>(this)->compare(v, one()) == 0;
}
\brief Return true if p is the constant polynomial where the coefficient is
the rational value 1.
\remark This is NOT checking whether p is actually equal to 1.
That is, it is just checking the representation.
*/
bool is_rational_one(polynomial const & p) const {
return p.size() == 1 && is_rational_one(p[0]);
}
bool is_rational_one(value_ref_buffer const & p) const {
return p.size() == 1 && is_rational_one(p[0]);
}
bool is_denominator_one(rational_function_value * v) const {
if (v->ext()->is_algebraic()) {
SASSERT(v->den().size() == 0);
return true;
}
else {
return is_rational_one(v->den());
}
}
template<typename T>
bool is_one(polynomial const & p) const {
return p.size() == 1 && is_one(p[0]);
}
\brief Return true if v is a represented as a rational function of the set of field extensions.
*/
static bool is_rational_function(value * v) {
SASSERT(v != 0);
return !(v->is_rational());
}
static rational_value * to_nz_rational(value * v) {
SASSERT(is_nz_rational(v));
return static_cast<rational_value*>(v);
}
static rational_function_value * to_rational_function(value * v) {
SASSERT(!is_nz_rational(v));
return static_cast<rational_function_value*>(v);
}
static bool is_zero(numeral const & a) {
return is_zero(a.m_value);
}
static bool is_nz_rational(numeral const & a) {
SASSERT(!is_zero(a));
return is_nz_rational(a.m_value);
}
\brief Return true if v is not a shared value. That is, we can perform
destructive updates.
*/
static bool is_unique(value * v) {
SASSERT(v);
return v->m_ref_count <= 1;
}
static bool is_unique(numeral const & a) {
return is_unique(a.m_value);
}
static bool is_unique_nz_rational(value * v) {
return is_nz_rational(v) && is_unique(v);
}
static bool is_unique_nz_rational(numeral const & a) {
return is_unique_nz_rational(a.m_value);
}
static rational_value * to_nz_rational(numeral const & a) {
SASSERT(is_nz_rational(a));
return to_nz_rational(a.m_value);
}
static bool is_rational_function(numeral const & a) {
return is_rational_function(a.m_value);
}
static rational_function_value * to_rational_function(numeral const & a) {
SASSERT(is_rational_function(a));
return to_rational_function(a.m_value);
}
static mpq & to_mpq(value * v) {
SASSERT(is_nz_rational(v));
return to_nz_rational(v)->m_value;
}
static mpq & to_mpq(numeral const & a) {
SASSERT(is_nz_rational(a));
return to_nz_rational(a)->m_value;
}
static int compare_rank(value * a, value * b) {
SASSERT(a); SASSERT(b);
if (is_nz_rational(a))
return is_nz_rational(b) ? 0 : -1;
else if (is_nz_rational(b)) {
SASSERT(is_rational_function(a));
return 1;
}
else if (rank_eq(to_rational_function(a)->ext(), to_rational_function(b)->ext()))
return 0;
else
return rank_lt(to_rational_function(a)->ext(), to_rational_function(b)->ext()) ? -1 : 1;
}
static transcendental * to_transcendental(extension * ext) {
SASSERT(ext->is_transcendental());
return static_cast<transcendental*>(ext);
}
static infinitesimal * to_infinitesimal(extension * ext) {
SASSERT(ext->is_infinitesimal());
return static_cast<infinitesimal*>(ext);
}
static algebraic * to_algebraic(extension * ext) {
SASSERT(ext->is_algebraic());
return static_cast<algebraic*>(ext);
}
\brief Return True if the given extension depends on infinitesimal extensions.
If it doesn't, then it is definitely a real value.
If it does, then it may or may not be a real value.
Example: Assume eps is an infinitesimal, and pi is 3.14... .
Assume also that ext is the unique root between (3, 4) of the following polynomial:
x^2 - (pi + eps)*x + pi*ext
Thus, x is pi, but the system will return true, since its defining polynomial has infinitesimal
coefficients. In the future, we should be able to factor the polynomial
above as
(x - eps)*(x - pi)
and then detect that x is actually the root of (x - pi).
*/
bool depends_on_infinitesimals(extension * ext) {
switch (ext->knd()) {
case extension::TRANSCENDENTAL: return false;
case extension::INFINITESIMAL: return true;
case extension::ALGEBRAIC: return to_algebraic(ext)->depends_on_infinitesimals();
default:
UNREACHABLE();
return false;
}
}
\brief Return true if v is definitely a real value.
*/
bool depends_on_infinitesimals(value * v) const {
if (is_zero(v) || is_nz_rational(v))
return false;
else
return to_rational_function(v)->depends_on_infinitesimals();
}
bool depends_on_infinitesimals(unsigned sz, value * const * p) const {
for (unsigned i = 0; i < sz; i++)
if (depends_on_infinitesimals(p[i]))
return true;
return false;
}
\brief Set the polynomial p with the given coefficients as[0], ..., as[n-1]
*/
void set_p(polynomial & p, unsigned n, value * const * as) {
SASSERT(n > 0);
SASSERT(!is_zero(as[n - 1]));
reset_p(p);
p.set(allocator(), n, as);
inc_ref(n, as);
}
\brief Return true if a is an open interval.
*/
static bool is_open_interval(mpbqi const & a) {
return a.lower_is_inf() && a.upper_is_inf();
}
\brief Return true if the interval contains zero.
*/
bool contains_zero(mpbqi const & a) const {
return bqim().contains_zero(a);
}
\brief Set the lower bound of the given interval.
*/
void set_lower_core(mpbqi & a, mpbq const & k, bool open, bool inf) {
bqm().set(a.lower(), k);
a.set_lower_is_open(open);
a.set_lower_is_inf(inf);
}
\brief a.lower <- k
*/
void set_lower(mpbqi & a, mpbq const & k, bool open = true) {
set_lower_core(a, k, open, false);
}
\brief a.lower <- -oo
*/
void set_lower_inf(mpbqi & a) {
bqm().reset(a.lower());
a.set_lower_is_open(true);
a.set_lower_is_inf(true);
}
\brief a.lower <- 0
*/
void set_lower_zero(mpbqi & a) {
bqm().reset(a.lower());
a.set_lower_is_open(true);
a.set_lower_is_inf(false);
}
\brief Set the upper bound of the given interval.
*/
void set_upper_core(mpbqi & a, mpbq const & k, bool open, bool inf) {
bqm().set(a.upper(), k);
a.set_upper_is_open(open);
a.set_upper_is_inf(inf);
}
\brief a.upper <- k
*/
void set_upper(mpbqi & a, mpbq const & k, bool open = true) {
set_upper_core(a, k, open, false);
}
\brief a.upper <- oo
*/
void set_upper_inf(mpbqi & a) {
bqm().reset(a.upper());
a.set_upper_is_open(true);
a.set_upper_is_inf(true);
}
\brief a.upper <- 0
*/
void set_upper_zero(mpbqi & a) {
bqm().reset(a.upper());
a.set_upper_is_open(true);
a.set_upper_is_inf(false);
}
\brief a <- b
*/
void set_interval(mpbqi & a, mpbqi const & b) {
set_lower_core(a, b.lower(), b.lower_is_open(), b.lower_is_inf());
set_upper_core(a, b.upper(), b.upper_is_open(), b.upper_is_inf());
}
\brief a <- [b, b]
*/
void set_interval(mpbqi & a, mpbq const & b) {
set_lower_core(a, b, false, false);
set_upper_core(a, b, false, false);
}
\brief a <- [b, b]
*/
void set_interval(mpbqi & a, mpz const & b) {
scoped_mpbq _b(bqm());
bqm().set(_b, b);
set_lower_core(a, _b, false, false);
set_upper_core(a, _b, false, false);
}
sign_condition * mk_sign_condition(unsigned qidx, int sign, sign_condition * prev_sc) {
return new (allocator()) sign_condition(qidx, sign, prev_sc);
}
\brief Make a rational_function_value using the given extension, numerator and denominator.
This method does not set the interval. It remains (-oo, oo)
*/
rational_function_value * mk_rational_function_value_core(extension * ext, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den) {
rational_function_value * r = new (allocator()) rational_function_value(ext);
inc_ref(ext);
set_p(r->num(), num_sz, num);
if (ext->is_algebraic()) {
SASSERT(den_sz == 0 || (den_sz == 1 && is_rational_one(den[0])));
SASSERT(r->den().size() == 0);
}
else {
set_p(r->den(), den_sz, den);
}
r->set_depends_on_infinitesimals(depends_on_infinitesimals(ext) || depends_on_infinitesimals(num_sz, num) || depends_on_infinitesimals(den_sz, den));
return r;
}
rational_function_value * mk_rational_function_value_core(algebraic * ext, unsigned num_sz, value * const * num) {
return mk_rational_function_value_core(ext, num_sz, num, 0, nullptr);
}
\brief Create a value using the given extension.
*/
rational_function_value * mk_rational_function_value(extension * ext) {
value * num[2] = { nullptr, one() };
value * den[1] = { one() };
rational_function_value * v = mk_rational_function_value_core(ext, 2, num, 1, den);
set_interval(v->interval(), ext->interval());
return v;
}
\brief Create a new infinitesimal.
*/
void mk_infinitesimal(symbol const & n, symbol const & pp_n, numeral & r) {
unsigned idx = next_infinitesimal_idx();
infinitesimal * eps = new (allocator()) infinitesimal(idx, n, pp_n);
m_extensions[extension::INFINITESIMAL].push_back(eps);
set_lower(eps->interval(), mpbq(0));
set_upper(eps->interval(), mpbq(1, m_ini_precision));
set(r, mk_rational_function_value(eps));
SASSERT(sign(r) > 0);
SASSERT(depends_on_infinitesimals(r));
}
void mk_infinitesimal(char const * n, char const * pp_n, numeral & r) {
mk_infinitesimal(symbol(n), symbol(pp_n), r);
}
void mk_infinitesimal(numeral & r) {
mk_infinitesimal(symbol(next_infinitesimal_idx()+1), symbol(next_infinitesimal_idx()+1), r);
}
void refine_transcendental_interval(transcendental * t) {
scoped_mpqi i(qim());
t->m_k++;
t->m_proc(t->m_k, qim(), i);
int m = magnitude(i);
TRACE("rcf_transcendental",
tout << "refine_transcendental_interval rational: " << m << "\nrational interval: ";
qim().display(tout, i); tout << std::endl;);
unsigned k;
if (m >= 0)
k = m_ini_precision;
else
k = inc_precision(-m, 8);
scoped_mpbq l(bqm());
mpq_to_mpbqi(i->m_lower, t->interval(), k);
bqm().set(l, t->interval().lower());
mpq_to_mpbqi(i->m_upper, t->interval(), k);
bqm().set(t->interval().lower(), l);
}
void refine_transcendental_interval(transcendental * t, unsigned prec) {
while (!check_precision(t->interval(), prec)) {
TRACE("rcf_transcendental", tout << "refine_transcendental_interval: " << magnitude(t->interval()) << std::endl;);
checkpoint();
save_interval_if_too_small(t, prec);
refine_transcendental_interval(t);
}
}
void mk_transcendental(symbol const & n, symbol const & pp_n, mk_interval & proc, numeral & r) {
unsigned idx = next_transcendental_idx();
transcendental * t = new (allocator()) transcendental(idx, n, pp_n, proc);
m_extensions[extension::TRANSCENDENTAL].push_back(t);
while (contains_zero(t->interval())) {
checkpoint();
refine_transcendental_interval(t);
}
set(r, mk_rational_function_value(t));
SASSERT(!depends_on_infinitesimals(r));
}
void mk_transcendental(char const * p, char const * pp_n, mk_interval & proc, numeral & r) {
mk_transcendental(symbol(p), symbol(pp_n), proc, r);
}
void mk_transcendental(mk_interval & proc, numeral & r) {
mk_transcendental(symbol(next_transcendental_idx()+1), symbol(next_transcendental_idx()+1), proc, r);
}
void mk_pi(numeral & r) {
if (m_pi) {
set(r, m_pi);
}
else {
mk_transcendental(symbol("pi"), symbol("π"), m_mk_pi_interval, r);
m_pi = r.m_value;
inc_ref(m_pi);
}
}
void mk_e(numeral & r) {
if (m_e) {
set(r, m_e);
}
else {
mk_transcendental(symbol("e"), symbol("e"), m_mk_e_interval, r);
m_e = r.m_value;
inc_ref(m_e);
}
}
\brief r <- magnitude of the lower bound of |i|.
That is, 2^r <= |i|.lower()
Another way to view it is:
2^r is smaller than the absolute value of any element in the interval i.
Return true if succeeded, and false if i contains elements that are infinitely close to 0.
\pre !contains_zero(i)
*/
bool abs_lower_magnitude(mpbqi const & i, int & r) {
SASSERT(!contains_zero(i));
if (bqim().is_P(i)) {
if (bqm().is_zero(i.lower()))
return false;
r = bqm().magnitude_lb(i.lower());
return true;
}
else {
SASSERT(bqim().is_N(i));
if (bqm().is_zero(i.upper()))
return false;
scoped_mpbq tmp(bqm());
tmp = i.upper();
bqm().neg(tmp);
r = bqm().magnitude_lb(tmp);
return true;
}
}
\brief r <- magnitude of the upper bound of |i|.
That is, |i|.upper <= 2^r
Another way to view it is:
2^r is bigger than the absolute value of any element in the interval i.
Return true if succeeded, and false if i is unbounded.
\pre !contains_zero(i)
*/
bool abs_upper_magnitude(mpbqi const & i, int & r) {
SASSERT(!contains_zero(i));
if (bqim().is_P(i)) {
if (i.upper_is_inf())
return false;
r = bqm().magnitude_ub(i.upper());
return true;
}
else {
SASSERT(bqim().is_N(i));
if (i.lower_is_inf())
return false;
scoped_mpbq tmp(bqm());
tmp = i.lower();
bqm().neg(tmp);
r = bqm().magnitude_ub(tmp);
return true;
}
}
\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 N s.t. 2*B <= 2^N
The computation is performed using the intervals associated with the coefficients of
the polynomial.
The procedure may fail if the interval for a_n is of the form (l, 0) or (0, u).
Similarly, the procedure will fail if one of the a_{n-k} has an interval of the form (l, oo) or (-oo, u).
Both cases can only happen if the values of the coefficients depend on infinitesimal values.
*/
bool pos_root_upper_bound(unsigned n, value * const * p, int & N) {
SASSERT(n > 1);
SASSERT(!is_zero(p[n-1]));
int lc_sign = sign(p[n-1]);
SASSERT(lc_sign != 0);
int lc_mag;
if (!abs_lower_magnitude(interval(p[n-1]), lc_mag))
return false;
N = -static_cast<int>(m_ini_precision);
for (unsigned k = 2; k <= n; k++) {
value * a = p[n - k];
if (!is_zero(a) && sign(a) != lc_sign) {
int a_mag;
if (!abs_upper_magnitude(interval(a), a_mag))
return false;
int C = (a_mag - lc_mag)/static_cast<int>(k) + 1 + 1 ;
if (C > N)
N = C;
}
}
return true;
}
\brief Auxiliary method for creating the intervals of the coefficients of the polynomials p(-x)
without actually creating p(-x).
'a' is the interval of the i-th coefficient of a polynomial
a_n * x^n + ... + a_0
*/
void neg_root_adjust(mpbqi const & a, unsigned i, mpbqi & r) {
if (i % 2 == 0)
bqim().neg(a, r);
else
bqim().set(r, a);
}
\brief Find negative root lower bound using Knuth's approach.
This is similar to pos_root_upper_bound. In principle, we can use
the same algorithm. We just have to adjust the coefficients by using
the transformation p(-x).
*/
bool neg_root_lower_bound(unsigned n, value * const * as, int & N) {
SASSERT(n > 1);
SASSERT(!is_zero(as[n-1]));
scoped_mpbqi aux(bqim());
neg_root_adjust(interval(as[n-1]), n-1, aux);
int lc_sign = bqim().is_P(aux) ? 1 : -1;
int lc_mag;
if (!abs_lower_magnitude(aux, lc_mag))
return false;
N = -static_cast<int>(m_ini_precision);
for (unsigned k = 2; k <= n; k++) {
value * a = as[n - k];
if (!is_zero(a)) {
neg_root_adjust(interval(as[n-k]), n-k, aux);
int a_sign = bqim().is_P(aux) ? 1 : -1;
if (a_sign != lc_sign) {
int a_mag;
if (!abs_upper_magnitude(aux, a_mag))
return false;
int C = (a_mag - lc_mag)/static_cast<int>(k) + 1 + 1 ;
if (C > N)
N = C;
}
}
}
return true;
}
\brief q <- x^{n-1}*p(1/x)
Given p(x) a_{n-1} * x^{n-1} + ... + a_0, this method stores
a_0 * x^{n-1} + ... + a_{n-1} into q.
*/
void reverse(unsigned n, value * const * p, value_ref_buffer & q) {
unsigned i = n;
while (i > 0) {
--i;
q.push_back(p[i]);
}
}
\brief To compute the lower bound for positive roots we computer the upper bound for the polynomial q(x) = x^{n-1}*p(1/x).
Assume U is an upper bound for roots of q(x), i.e., (r > 0 and q(r) = 0) implies r < U.
Note that if r is a root for q(x), then 1/r is a root for p(x) and 1/U is a lower bound for positive roots of p(x).
The polynomial q(x) is just p(x) "reversed".
*/
bool pos_root_lower_bound(unsigned n, value * const * p, int & N) {
value_ref_buffer q(*this);
reverse(n, p, q);
if (pos_root_upper_bound(n, q.data(), N)) {
N = -N;
return true;
}
else {
return false;
}
}
\brief See comment on pos_root_lower_bound.
*/
bool neg_root_upper_bound(unsigned n, value * const * p, int & N) {
value_ref_buffer q(*this);
reverse(n, p, q);
if (neg_root_lower_bound(n, q.data(), N)) {
N = -N;
return true;
}
else {
return false;
}
}
\brief Store in ds all (non-constant) derivatives of p.
\post d.size() == n-2
*/
void mk_derivatives(unsigned n, value * const * p, scoped_polynomial_seq & ds) {
SASSERT(n >= 3);
SASSERT(!is_zero(p[0]));
SASSERT(!is_zero(p[n-1]));
value_ref_buffer p_prime(*this);
derivative(n, p, p_prime);
ds.push(p_prime.size(), p_prime.data());
SASSERT(n >= 3);
for (unsigned i = 0; i < n - 2; i++) {
SASSERT(ds.size() > 0);
unsigned prev = ds.size() - 1;
n = ds.size(prev);
p = ds.coeffs(prev);
derivative(n, p, p_prime);
ds.push(p_prime.size(), p_prime.data());
}
}
\brief Auxiliary function for count_signs_at_zeros. (See comments at count_signs_at_zeros).
- taq_p_q contains TaQ(p, q; interval)
*/
void count_signs_at_zeros_core(
int taq_p_q,
unsigned p_sz, value * const * p,
unsigned q_sz, value * const * q,
mpbqi const & interval,
int num_roots,
int & q_eq_0, int & q_gt_0, int & q_lt_0,
value_ref_buffer & q2) {
if (taq_p_q == num_roots) {
q_eq_0 = 0;
q_gt_0 = num_roots;
q_lt_0 = 0;
}
else if (taq_p_q == -num_roots) {
q_eq_0 = 0;
q_gt_0 = 0;
q_lt_0 = num_roots;
}
if (taq_p_q == num_roots - 1) {
q_eq_0 = 1;
q_gt_0 = num_roots - 1;
q_lt_0 = 0;
}
else if (taq_p_q == -(num_roots - 1)) {
q_eq_0 = 1;
q_gt_0 = 0;
q_lt_0 = num_roots - 1;
}
else {
mul(q_sz, q, q_sz, q, q2);
int taq_p_q2 = TaQ(p_sz, p, q2.size(), q2.data(), interval);
SASSERT(0 <= taq_p_q2 && taq_p_q2 <= num_roots);
SASSERT((taq_p_q2 + taq_p_q) % 2 == 0);
SASSERT((taq_p_q2 - taq_p_q) % 2 == 0);
q_eq_0 = num_roots - taq_p_q2;
q_gt_0 = (taq_p_q2 + taq_p_q)/2;
q_lt_0 = (taq_p_q2 - taq_p_q)/2;
}
SASSERT(q_eq_0 + q_gt_0 + q_lt_0 == num_roots);
}
\brief Given polynomials p and q, and an interval, compute the number of
roots of p in the interval such that:
- q is zero
- q is positive
- q is negative
\pre num_roots is the number of roots of p in the given interval.
\remark num_roots == q_eq_0 + q_gt_0 + q_lt_0
*/
void count_signs_at_zeros(
unsigned p_sz, value * const * p,
unsigned q_sz, value * const * q,
mpbqi const & interval,
int num_roots,
int & q_eq_0, int & q_gt_0, int & q_lt_0,
value_ref_buffer & q2) {
TRACE("rcf_count_signs",
tout << "p: "; display_poly(tout, p_sz, p); tout << "\n";
tout << "q: "; display_poly(tout, q_sz, q); tout << "\n";);
SASSERT(num_roots > 0);
int taq_p_q = TaQ(p_sz, p, q_sz, q, interval);
count_signs_at_zeros_core(taq_p_q, p_sz, p, q_sz, q, interval, num_roots, q_eq_0, q_gt_0, q_lt_0, q2);
}
\brief Expand the set of Tarski Queries used in the sign determination algorithm.
taqrs contains the results of TaQ(p, prs[i]; interval)
We have that taqrs.size() == prs.size()
We produce a new_taqrs and new_prs
For each pr in new_prs we have
pr in new_prs, TaQ(p, pr; interval) in new_taqrs
pr*q in new_prs, TaQ(p, pr*q; interval) in new_taqrs
if q2_sz != 0, we also have
pr*q^2 in new_prs, TaQ(p, pr*q^2; interval) in new_taqrs
*/
void expand_taqrs(
int_buffer const & taqrs,
scoped_polynomial_seq const & prs,
unsigned p_sz, value * const * p,
unsigned q_sz, value * const * q,
bool use_q2, unsigned q2_sz, value * const * q2,
mpbqi const & interval,
int_buffer & new_taqrs,
scoped_polynomial_seq & new_prs
) {
SASSERT(taqrs.size() == prs.size());
new_taqrs.reset(); new_prs.reset();
for (unsigned i = 0; i < taqrs.size(); i++) {
new_taqrs.push_back(taqrs[i]);
new_prs.push(prs.size(i), prs.coeffs(i));
value_ref_buffer prq(*this);
mul(prs.size(i), prs.coeffs(i), q_sz, q, prq);
new_taqrs.push_back(TaQ(p_sz, p, prq.size(), prq.data(), interval));
new_prs.push(prq.size(), prq.data());
if (use_q2) {
value_ref_buffer prq2(*this);
mul(prs.size(i), prs.coeffs(i), q2_sz, q2, prq2);
new_taqrs.push_back(TaQ(p_sz, p, prq2.size(), prq2.data(), interval));
new_prs.push(prq2.size(), prq2.data());
}
}
SASSERT(new_prs.size() == new_taqrs.size());
SASSERT(use_q2 || new_prs.size() == 2*prs.size());
SASSERT(!use_q2 || new_prs.size() == 3*prs.size());
}
\brief In the sign determination algorithm main loop, we keep processing polynomials q,
and checking whether they discriminate the roots of the target polynomial.
The vectors sc_cardinalities contains the cardinalities of the new realizable sign conditions.
That is, we started we a sequence of sign conditions
sc_1, ..., sc_n,
If q2_used is true, then we expanded this sequence as
sc1_1 and q == 0, sc_1 and q > 0, sc_1 and q < 0, ..., sc_n and q == 0, sc_n and q > 0, sc_n and q < 0
If q2_used is false, then we considered only two possible signs of q.
Thus, q is useful (i.e., it is a discriminator for the roots of p) IF
If !q2_used, then There is an i s.t. sc_cardinalities[2*i] > 0 && sc_cardinalities[2*i] > 0
If q2_used, then There is an i s.t. AtLeastTwo(sc_cardinalities[3*i] > 0, sc_cardinalities[3*i+1] > 0, sc_cardinalities[3*i+2] > 0)
*/
bool keep_new_sc_assignment(unsigned sz, int const * sc_cardinalities, bool q2_used) {
SASSERT(q2_used || sz % 2 == 0);
SASSERT(!q2_used || sz % 3 == 0);
if (q2_used) {
for (unsigned i = 0; i < sz; i += 3) {
unsigned c = 0;
if (sc_cardinalities[i] > 0) c++;
if (sc_cardinalities[i+1] > 0) c++;
if (sc_cardinalities[i+2] > 0) c++;
if (c >= 2)
return true;
}
}
else {
for (unsigned i = 0; i < sz; i += 2) {
if (sc_cardinalities[i] > 0 && sc_cardinalities[i+1] > 0)
return true;
}
}
return false;
}
\brief Store the polynomials in prs into the array of polynomials ps.
*/
void set_array_p(array<polynomial> & ps, scoped_polynomial_seq const & prs) {
unsigned sz = prs.size();
ps.set(allocator(), sz, polynomial());
for (unsigned i = 0; i < sz; i++) {
unsigned pi_sz = prs.size(i);
value * const * pi = prs.coeffs(i);
set_p(ps[i], pi_sz, pi);
}
}
\brief Create a "sign determination" data-structure for an algebraic extension.
The new object will assume the ownership of the elements stored in M and scs.
M and scs will be empty after this operation.
*/
sign_det * mk_sign_det(mpz_matrix & M_s, scoped_polynomial_seq const & prs, int_buffer const & taqrs, scoped_polynomial_seq const & qs, scoped_sign_conditions & scs) {
sign_det * r = new (allocator()) sign_det();
r->M_s.swap(M_s);
set_array_p(r->m_prs, prs);
r->m_taqrs.set(allocator(), taqrs.size(), taqrs.data());
set_array_p(r->m_qs, qs);
r->m_sign_conditions.set(allocator(), scs.size(), scs.data());
scs.release();
return r;
}
\brief Create a new algebraic extension
*/
algebraic * mk_algebraic(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, sign_det * sd, unsigned sc_idx) {
unsigned idx = next_algebraic_idx();
algebraic * r = new (allocator()) algebraic(idx);
m_extensions[extension::ALGEBRAIC].push_back(r);
set_p(r->m_p, p_sz, p);
set_interval(r->m_interval, interval);
set_interval(r->m_iso_interval, iso_interval);
r->m_sign_det = sd;
inc_ref_sign_det(sd);
r->m_sc_idx = sc_idx;
r->m_depends_on_infinitesimals = depends_on_infinitesimals(p_sz, p);
return r;
}
\brief Add a new root of p that is isolated by (interval, sd, sc_idx) to roots.
*/
void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, sign_det * sd, unsigned sc_idx, numeral_vector & roots) {
algebraic * a = mk_algebraic(p_sz, p, interval, iso_interval, sd, sc_idx);
numeral r;
set(r, mk_rational_function_value(a));
roots.push_back(r);
}
\brief Simpler version of add_root that does not use sign_det data-structure. That is,
interval contains only one root of p.
*/
void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, numeral_vector & roots) {
add_root(p_sz, p, interval, iso_interval, nullptr, UINT_MAX, roots);
}
\brief Create (the square) matrix for sign determination of q on the roots of p.
It builds matrix based on the number of root of p where
q is == 0, > 0 and < 0.
The resultant matrix is stored in M.
Return false if the sign of q is already determined, that is
only one of the q_eq_0, q_gt_0, q_lt_0 is greater than zero.
If the return value is true, then the resultant matrix M has size 2x2 or 3x3
- q_eq_0 > 0, q_gt_0 > 0, q_lt_0 == 0
M <- {{1, 1},
{0, 1}}
Meaning:
M . [ #(q == 0), #(q > 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t
[ ... ]^t represents a column matrix.
- q_eq_0 > 0, q_gt_0 == 0, q_lt_0 > 0
M <- {{1, 1},
{0, -1}}
Meaning:
M . [ #(q == 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t
- q_eq_0 == 0, q_gt_0 > 0, q_lt_0 > 0
M <- {{1, 1},
{1, -1}}
Meaning:
M . [ #(q > 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q) ]^t
- q_eq_0 > 0, q_gt_0 > 0, q_lt_0 > 0
M <- {{1, 1, 1},
{0, 1, -1},
{0, 1, 1}}
Meaning:
M . [ #(q == 0), #(q > 0), #(q < 0) ]^t == [ TaQ(p, 1), TaQ(p, q), TaQ(p, q^2) ]^t
*/
bool mk_sign_det_matrix(int q_eq_0, int q_gt_0, int q_lt_0, scoped_mpz_matrix & M) {
if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 == 0) {
mm().mk(2,2,M);
M.set(0,0, 1); M.set(0,1, 1);
M.set(1,0, 0); M.set(1,1, 1);
return true;
}
else if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 > 0) {
mm().mk(2,2,M);
M.set(0,0, 1); M.set(0,1, 1);
M.set(1,0, 0); M.set(1,1, -1);
return true;
}
else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 > 0) {
mm().mk(2,2,M);
M.set(0,0, 1); M.set(0,1, 1);
M.set(1,0, 1); M.set(1,1, -1);
return true;
}
else if (q_eq_0 > 0 && q_gt_0 > 0 && q_lt_0 > 0) {
mm().mk(3,3,M);
M.set(0,0, 1); M.set(0,1, 1); M.set(0,2, 1);
M.set(1,0, 0); M.set(1,1, 1); M.set(1,2, -1);
M.set(2,0, 0); M.set(2,1, 1); M.set(2,2, 1);
return true;
}
else {
return false;
}
}
\brief Isolate roots of p in the given interval using sign conditions to distinguish between them.
We need this method when the polynomial contains roots that are infinitesimally close to each other.
For example, given an infinitesimal eps, the polynomial (x - 1)(x - 1 - eps) == (1 + eps) + (- 2 - eps)*x + x^2
has two roots 1 and 1+eps, we can't isolate these roots using intervals with binary rational end points.
In this case, we use the signs of (some of the) derivatives in the roots.
By Thom's lemma, we know we can always use the signs of the derivatives to distinguish between different roots.
Remark: the polynomials do not need to be the derivatives of p. We use derivatives because a simple
sequential search can be used to find the set of polynomials that can be used to distinguish between
the different roots.
\pre num_roots is the number of roots in the given interval
*/
void sign_det_isolate_roots(unsigned p_sz, value * const * p, int num_roots, mpbqi const & interval, mpbqi const & iso_interval, numeral_vector & roots) {
SASSERT(num_roots >= 2);
scoped_polynomial_seq der_seq(*this);
mk_derivatives(p_sz, p, der_seq);
CASSERT("rcf_isolate_roots", TaQ_1(p_sz, p, interval) == num_roots);
scoped_mpz_matrix M_s(mm());
mm().mk(1, 1, M_s);
M_s.set(0, 0, 1);
scoped_polynomial_seq prs(*this);
value * one_p[] = { one() };
prs.push(1, one_p);
int_buffer taqrs;
taqrs.push_back(num_roots);
scoped_polynomial_seq qs(*this);
scoped_sign_conditions scs(*this);
scs.push_back(nullptr);
scoped_mpz_matrix new_M_s(mm());
int_buffer new_taqrs;
scoped_polynomial_seq new_prs(*this);
scoped_sign_conditions new_scs(*this);
int_buffer sc_cardinalities;
unsigned_buffer cols_to_keep;
unsigned_buffer new_row_idxs;
unsigned i = der_seq.size();
while (i > 0) {
--i;
checkpoint();
SASSERT(M_s.m() == M_s.n());
SASSERT(M_s.m() == taqrs.size());
SASSERT(M_s.m() == scs.size());
TRACE("rcf_sign_det",
tout << M_s;
for (unsigned j = 0; j < scs.size(); j++) {
display_sign_conditions(tout, scs[j]);
tout << " = " << taqrs[j] << "\n";
}
tout << "qs:\n";
for (unsigned j = 0; j < qs.size(); j++) {
display_poly(tout, qs.size(j), qs.coeffs(j)); tout << "\n";
});
unsigned q_sz = der_seq.size(i);
value * const * q = der_seq.coeffs(i);
int q_eq_0, q_gt_0, q_lt_0;
value_ref_buffer q2(*this);
count_signs_at_zeros(p_sz, p, q_sz, q, iso_interval, num_roots, q_eq_0, q_gt_0, q_lt_0, q2);
TRACE("rcf_sign_det",
tout << "q: "; display_poly(tout, q_sz, q); tout << "\n";
tout << "#(q == 0): " << q_eq_0 << ", #(q > 0): " << q_gt_0 << ", #(q < 0): " << q_lt_0 << "\n";);
scoped_mpz_matrix M(mm());
if (!mk_sign_det_matrix(q_eq_0, q_gt_0, q_lt_0, M)) {
continue;
}
bool use_q2 = M.n() == 3;
mm().tensor_product(M_s, M, new_M_s);
expand_taqrs(taqrs, prs, p_sz, p, q_sz, q, use_q2, q2.size(), q2.data(), iso_interval,
new_taqrs, new_prs);
SASSERT(new_M_s.n() == new_M_s.m());
SASSERT(new_M_s.m() == new_taqrs.size());
SASSERT(new_M_s.m() == new_prs.size());
sc_cardinalities.resize(new_taqrs.size(), 0);
VERIFY(mm().solve(new_M_s, sc_cardinalities.data(), new_taqrs.data()));
TRACE("rcf_sign_det", tout << "solution: "; for (unsigned i = 0; i < sc_cardinalities.size(); i++) { tout << sc_cardinalities[i] << " "; } tout << "\n";);
DEBUG_CODE(for (unsigned j = 0; j < sc_cardinalities.size(); j++) { SASSERT(0 <= sc_cardinalities[j] && sc_cardinalities[j] <= num_roots); });
if (!keep_new_sc_assignment(sc_cardinalities.size(), sc_cardinalities.data(), use_q2)) {
continue;
}
unsigned q_idx = qs.size();
qs.push(q_sz, q);
cols_to_keep.reset();
unsigned j = 0; unsigned k = 0;
unsigned step_sz = use_q2 ? 3 : 2;
bool all_one = true;
while (j < sc_cardinalities.size()) {
sign_condition * sc = scs[k];
k++;
for (unsigned s = 0; s < step_sz; s++) {
if (sc_cardinalities[j] > 0) {
new_scs.push_back(mk_sign_condition(q_idx, M.get_int(1, s), sc));
cols_to_keep.push_back(j);
}
if (sc_cardinalities[j] > 1)
all_one = false;
j++;
}
}
scs.copy_from(new_scs);
SASSERT(new_scs.empty());
mm().filter_cols(new_M_s, cols_to_keep.size(), cols_to_keep.data(), M_s);
SASSERT(M_s.n() == cols_to_keep.size());
new_row_idxs.resize(cols_to_keep.size(), 0);
unsigned new_num_rows = mm().linear_independent_rows(M_s, new_row_idxs.data(), M_s);
SASSERT(new_num_rows == cols_to_keep.size());
prs.reset();
taqrs.reset();
for (unsigned j = 0; j < new_num_rows; j++) {
unsigned rid = new_row_idxs[j];
prs.push(new_prs.size(rid), new_prs.coeffs(rid));
taqrs.push_back(new_taqrs[rid]);
}
if (all_one) {
break;
}
}
TRACE("rcf_sign_det",
tout << "Final state\n";
display_poly(tout, p_sz, p); tout << "\n";
tout << M_s;
for (unsigned j = 0; j < scs.size(); j++) {
display_sign_conditions(tout, scs[j]);
tout << " = " << taqrs[j] << "\n";
}
tout << "qs:\n";
for (unsigned j = 0; j < qs.size(); j++) {
display_poly(tout, qs.size(j), qs.coeffs(j)); tout << "\n";
}
tout << "prs:\n";
for (unsigned j = 0; j < prs.size(); j++) {
display_poly(tout, prs.size(j), prs.coeffs(j)); tout << "\n";
});
SASSERT(M_s.n() == M_s.m()); SASSERT(M_s.n() == static_cast<unsigned>(num_roots));
sign_det * sd = mk_sign_det(M_s, prs, taqrs, qs, scs);
for (unsigned idx = 0; idx < static_cast<unsigned>(num_roots); idx++) {
add_root(p_sz, p, interval, iso_interval, sd, idx, roots);
}
}
\brief Return true if p is a polynomial of the form a_{n-1}*x^{n-1} + a_0
*/
bool is_nz_binomial(unsigned n, value * const * p) {
SASSERT(n >= 2);
SASSERT(!is_zero(p[0]));
SASSERT(!is_zero(p[n-1]));
for (unsigned i = 1; i < n - 1; i++) {
if (!is_zero(p[i]))
return false;
}
return true;
}
\brief magnitude -> mpbq
*/
void magnitude_to_mpbq(int mag, bool sign, mpbq & r) {
if (mag < 0) {
bqm().set(r, mpbq(1, -mag));
}
else {
bqm().set(r, mpbq(2));
bqm().power(r, mag);
}
if (sign)
bqm().neg(r);
}
\brief Convert magnitudes for negative roots lower and upper bounds into an interval.
*/
void mk_neg_interval(bool has_neg_lower, int neg_lower_N, bool has_neg_upper, int neg_upper_N, mpbqi & r) {
scoped_mpbq aux(bqm());
if (!has_neg_lower) {
set_lower_inf(r);
}
else {
magnitude_to_mpbq(neg_lower_N, true, aux);
set_lower(r, aux);
}
if (!has_neg_upper) {
set_upper_zero(r);
}
else {
magnitude_to_mpbq(neg_upper_N, true, aux);
set_upper(r, aux);
}
}
\brief Convert magnitudes for negative roots lower and upper bounds into an interval.
*/
void mk_pos_interval(bool has_pos_lower, int pos_lower_N, bool has_pos_upper, int pos_upper_N, mpbqi & r) {
scoped_mpbq aux(bqm());
if (!has_pos_lower) {
set_lower_zero(r);
}
else {
magnitude_to_mpbq(pos_lower_N, false, aux);
set_lower(r, aux);
}
if (!has_pos_upper) {
set_upper_inf(r);
}
else {
magnitude_to_mpbq(pos_upper_N, false, aux);
set_upper(r, aux);
}
}
struct bisect_ctx {
unsigned m_p_sz;
value * const * m_p;
bool m_depends_on_infinitesimals;
scoped_polynomial_seq & m_sturm_seq;
numeral_vector & m_result_roots;
bisect_ctx(unsigned p_sz, value * const * p, bool dinf, scoped_polynomial_seq & seq, numeral_vector & roots):
m_p_sz(p_sz), m_p(p), m_depends_on_infinitesimals(dinf), m_sturm_seq(seq), m_result_roots(roots) {}
};
void bisect_isolate_roots(mpbqi & interval, mpbqi & iso_interval, int lower_sv, int upper_sv, bisect_ctx & ctx) {
SASSERT(lower_sv >= upper_sv);
int num_roots = lower_sv - upper_sv;
if (num_roots == 0) {
}
else if (num_roots == 1) {
if (eval_sign_at(ctx.m_p_sz, ctx.m_p, interval.upper()) == 0) {
numeral r;
set(r, mk_rational(interval.upper()));
ctx.m_result_roots.push_back(r);
}
else {
add_root(ctx.m_p_sz, ctx.m_p, interval, iso_interval, ctx.m_result_roots);
}
}
else if (ctx.m_depends_on_infinitesimals && check_precision(interval, m_max_precision)) {
sign_det_isolate_roots(ctx.m_p_sz, ctx.m_p, num_roots, interval, iso_interval, ctx.m_result_roots);
}
else {
scoped_mpbq mid(bqm());
bqm().add(interval.lower(), interval.upper(), mid);
bqm().div2(mid);
int mid_sv = sign_variations_at(ctx.m_sturm_seq, mid);
int num_left_roots = lower_sv - mid_sv;
int num_right_roots = mid_sv - upper_sv;
if (num_left_roots == 0) {
scoped_mpbqi right_interval(bqim());
set_lower(right_interval, mid);
set_upper(right_interval, interval.upper());
bisect_isolate_roots(right_interval, iso_interval, mid_sv, upper_sv, ctx);
}
else if (num_right_roots == 0) {
scoped_mpbqi left_interval(bqim());
set_lower(left_interval, interval.lower());
set_upper(left_interval, mid);
bisect_isolate_roots(left_interval, iso_interval, lower_sv, mid_sv, ctx);
}
else {
scoped_mpbqi left_interval(bqim());
scoped_mpbqi right_interval(bqim());
set_lower(left_interval, interval.lower());
set_upper(left_interval, mid);
set_lower(right_interval, mid);
set_upper(right_interval, interval.upper());
bisect_isolate_roots(left_interval, left_interval, lower_sv, mid_sv, ctx);
bisect_isolate_roots(right_interval, right_interval, mid_sv, upper_sv, ctx);
}
}
}
\brief Entry point for the root isolation procedure based on bisection.
*/
void bisect_isolate_roots(
unsigned p_sz, value * const * p, mpbqi & interval, mpbqi & iso_interval,
scoped_polynomial_seq & sturm_seq,
int lower_sv,
int upper_sv,
numeral_vector & roots) {
bool dinf = depends_on_infinitesimals(p_sz, p);
bisect_ctx ctx(p_sz, p, dinf, sturm_seq, roots);
bisect_isolate_roots(interval, iso_interval, lower_sv, upper_sv, ctx);
}
\brief Root isolation for polynomials that are
- nonlinear (degree > 2)
- zero is not a root
- square free
- nonconstant
*/
void nl_nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) {
SASSERT(n > 2);
SASSERT(!is_zero(p[0]));
SASSERT(!is_zero(p[n-1]));
int pos_lower_N, pos_upper_N, neg_lower_N, neg_upper_N;
bool has_neg_lower = neg_root_lower_bound(n, p, neg_lower_N);
bool has_neg_upper = neg_root_upper_bound(n, p, neg_upper_N);
bool has_pos_lower = pos_root_lower_bound(n, p, pos_lower_N);
bool has_pos_upper = pos_root_upper_bound(n, p, pos_upper_N);
TRACE("rcf_isolate",
display_poly(tout, n, p); tout << "\n";
if (has_neg_lower) tout << "-2^" << neg_lower_N; else tout << "-oo";
tout << " < neg-roots < ";
if (has_neg_upper) tout << "-2^" << neg_upper_N; else tout << "0";
tout << "\n";
if (has_pos_lower) tout << "2^" << pos_lower_N; else tout << "0";
tout << " < pos-roots < ";
if (has_pos_upper) tout << "2^" << pos_upper_N; else tout << "oo";
tout << "\n";);
scoped_polynomial_seq seq(*this);
sturm_seq(n, p, seq);
int num_sv_minus_inf = sign_variations_at_minus_inf(seq);
int num_sv_zero = sign_variations_at_zero(seq);
int num_sv_plus_inf = sign_variations_at_plus_inf(seq);
int num_neg_roots = num_sv_minus_inf - num_sv_zero;
int num_pos_roots = num_sv_zero - num_sv_plus_inf;
TRACE("rcf_isolate",
tout << "num_neg_roots: " << num_neg_roots << "\n";
tout << "num_pos_roots: " << num_pos_roots << "\n";);
scoped_mpbqi pos_interval(bqim());
scoped_mpbqi neg_interval(bqim());
mk_neg_interval(has_neg_lower, neg_lower_N, has_neg_upper, neg_upper_N, neg_interval);
mk_pos_interval(has_pos_lower, pos_lower_N, has_pos_upper, pos_upper_N, pos_interval);
scoped_mpbqi minf_zero(bqim());
set_lower_inf(minf_zero);
set_upper_zero(minf_zero);
scoped_mpbqi zero_inf(bqim());
set_lower_zero(zero_inf);
set_upper_inf(zero_inf);
if (num_neg_roots > 0) {
if (num_neg_roots == 1) {
add_root(n, p, neg_interval, minf_zero, nullptr, UINT_MAX, roots);
}
else {
if (has_neg_lower) {
bisect_isolate_roots(n, p, neg_interval, minf_zero, seq, num_sv_minus_inf, num_sv_zero, roots);
}
else {
sign_det_isolate_roots(n, p, num_neg_roots, minf_zero, minf_zero, roots);
}
}
}
if (num_pos_roots > 0) {
if (num_pos_roots == 1) {
add_root(n, p, pos_interval, zero_inf, nullptr, UINT_MAX, roots);
}
else {
if (has_pos_upper) {
bisect_isolate_roots(n, p, pos_interval, zero_inf, seq, num_sv_zero, num_sv_plus_inf, roots);
}
else {
sign_det_isolate_roots(n, p, num_pos_roots, zero_inf, zero_inf, roots);
}
}
}
}
\brief Root isolation for polynomials that are
- zero is not a root
- square free
- nonconstant
*/
void nz_sqf_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) {
SASSERT(n > 1);
SASSERT(!is_zero(p[0]));
SASSERT(!is_zero(p[n-1]));
if (n == 2) {
numeral r;
value_ref v(*this);
neg(p[0], v);
div(v, p[1], v);
set(r, v);
roots.push_back(r);
}
else {
nl_nz_sqf_isolate_roots(n, p, roots);
}
}
\brief Root isolation for polynomials where 0 is not a root, and the denominators have been cleaned
when m_clean_denominators == true
*/
void nz_cd_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) {
SASSERT(n > 0);
SASSERT(!is_zero(p[0]));
SASSERT(!is_zero(p[n-1]));
SASSERT(!m_clean_denominators || has_clean_denominators(n, p));
if (n == 1) {
return;
}
value_ref_buffer sqf(*this);
square_free(n, p, sqf);
nz_sqf_isolate_roots(sqf.size(), sqf.data(), roots);
}
\brief Root isolation for polynomials where 0 is not a root.
*/
void nz_isolate_roots(unsigned n, value * const * p, numeral_vector & roots) {
TRACE("rcf_isolate",
tout << "nz_isolate_roots\n";
display_poly(tout, n, p); tout << "\n";);
if (m_clean_denominators) {
value_ref d(*this);
value_ref_buffer norm_p(*this);
clean_denominators(n, p, norm_p, d);
nz_cd_isolate_roots(norm_p.size(), norm_p.data(), roots);
}
else {
nz_cd_isolate_roots(n, p, roots);
}
}
\brief Root isolation entry point.
*/
void isolate_roots(unsigned n, numeral const * p, numeral_vector & roots) {
TRACE("rcf_isolate_bug", tout << "isolate_roots: "; for (unsigned i = 0; i < n; i++) { display(tout, p[i]); tout << " "; } tout << "\n";);
SASSERT(n > 0);
SASSERT(!is_zero(p[n-1]));
if (n == 1) {
return;
}
unsigned i = 0;
for (; i < n; i++) {
if (!is_zero(p[i]))
break;
}
SASSERT(i < n);
SASSERT(!is_zero(p[i]));
ptr_buffer<value> nz_p;
for (; i < n; i++)
nz_p.push_back(p[i].m_value);
nz_isolate_roots(nz_p.size(), nz_p.data(), roots);
if (nz_p.size() < n) {
roots.push_back(numeral());
}
}
void reset(numeral & a) {
del(a);
SASSERT(is_zero(a));
}
int sign(value * a) {
if (is_zero(a))
return 0;
else if (is_nz_rational(a)) {
return qm().is_pos(to_mpq(a)) ? 1 : -1;
}
else {
SASSERT(!contains_zero(a->interval()));
return bqim().is_P(a->interval()) ? 1 : -1;
}
}
int sign(numeral const & a) {
return sign(a.m_value);
}
\brief Return true the given rational function value is actually an integer.
\pre a is a rational function (algebraic) extension.
\remark If a is actually an integer, this method also updates its representation.
*/
bool is_algebraic_int(numeral const & a) {
SASSERT(is_rational_function(a));
SASSERT(to_rational_function(a)->ext()->is_algebraic());
return false;
}
\brief Return true if a is an integer
*/
bool is_int(numeral const & a) {
if (is_zero(a))
return true;
else if (is_nz_rational(a))
return qm().is_int(to_mpq(a));
else {
rational_function_value * rf = to_rational_function(a);
switch (rf->ext()->knd()) {
case extension::TRANSCENDENTAL: return false;
case extension::INFINITESIMAL: return false;
case extension::ALGEBRAIC: return is_algebraic_int(a);
default:
UNREACHABLE();
return false;
}
}
}
\brief Return true if a depends on infinitesimal extensions.
*/
bool depends_on_infinitesimals(numeral const & a) const {
return depends_on_infinitesimals(a.m_value);
}
static void swap(mpbqi & a, mpbqi & b) {
realclosure::swap(a, b);
}
\brief Store in interval an approximation of the rational number q with precision k.
interval has binary rational end-points and the width is <= 1/2^k
*/
void mpq_to_mpbqi(mpq const & q, mpbqi & interval, unsigned k) {
interval.set_lower_is_inf(false);
interval.set_upper_is_inf(false);
if (bqm().to_mpbq(q, interval.lower())) {
bqm().set(interval.upper(), interval.lower());
interval.set_lower_is_open(false);
interval.set_upper_is_open(false);
}
else {
bqm().set(interval.upper(), interval.lower());
bqm().mul2(interval.upper());
interval.set_lower_is_open(true);
interval.set_upper_is_open(true);
if (qm().is_neg(q)) {
::swap(interval.lower(), interval.upper());
}
while (contains_zero(interval) || !check_precision(interval, k) || bqm().is_zero(interval.lower()) || bqm().is_zero(interval.upper())) {
checkpoint();
bqm().refine_lower(q, interval.lower(), interval.upper());
bqm().refine_upper(q, interval.lower(), interval.upper());
}
}
}
void initialize_rational_value_interval(value * a) {
SASSERT(is_nz_rational(a));
mpq_to_mpbqi(to_mpq(a), a->m_interval, m_ini_precision);
}
mpbqi & interval(value * a) const {
SASSERT(a != 0);
if (contains_zero(a->m_interval)) {
SASSERT(is_nz_rational(a));
const_cast<imp*>(this)->initialize_rational_value_interval(a);
}
return a->m_interval;
}
rational_value * mk_rational() {
return new (allocator()) rational_value();
}
\brief Make a rational and swap its value with v
*/
rational_value * mk_rational_and_swap(mpq & v) {
SASSERT(!qm().is_zero(v));
rational_value * r = mk_rational();
::swap(r->m_value, v);
return r;
}
rational_value * mk_rational(mpq const & v) {
SASSERT(!qm().is_zero(v));
rational_value * r = mk_rational();
qm().set(r->m_value, v);
return r;
}
rational_value * mk_rational(mpz const & v) {
SASSERT(!qm().is_zero(v));
rational_value * r = mk_rational();
qm().set(r->m_value, v);
return r;
}
rational_value * mk_rational(mpbq const & v) {
SASSERT(!bqm().is_zero(v));
scoped_mpq v_q(qm());
::to_mpq(qm(), v, v_q);
return mk_rational(v_q);
}
void reset_interval(value * a) {
bqim().reset(a->m_interval);
}
template<typename T>
void update_mpq_value(value * a, T & v) {
SASSERT(is_nz_rational(a));
qm().set(to_mpq(a), v);
reset_interval(a);
}
template<typename T>
void update_mpq_value(numeral & a, T & v) {
update_mpq_value(a.m_value, v);
}
\brief a <- n
*/
void set(numeral & a, int n) {
if (n == 0) {
reset(a);
return;
}
del(a);
a.m_value = mk_rational();
inc_ref(a.m_value);
update_mpq_value(a, n);
}
\brief a <- n
*/
void set(numeral & a, mpz const & n) {
if (qm().is_zero(n)) {
reset(a);
return;
}
del(a);
a.m_value = mk_rational();
inc_ref(a.m_value);
update_mpq_value(a, n);
}
\brief a <- n
*/
void set(numeral & a, mpq const & n) {
if (qm().is_zero(n)) {
reset(a);
return;
}
del(a);
a.m_value = mk_rational();
inc_ref(a.m_value);
update_mpq_value(a, n);
}
\brief a <- n
*/
void set(numeral & a, numeral const & n) {
inc_ref(n.m_value);
dec_ref(a.m_value);
a.m_value = n.m_value;
}
\brief Remove 0s
*/
void adjust_size(value_ref_buffer & r) {
while (!r.empty() && r.back() == nullptr) {
r.pop_back();
}
}
\brief r <- p1 + p2
*/
void add(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) {
SASSERT(p1 != r.data());
SASSERT(p2 != r.data());
r.reset();
value_ref a_i(*this);
unsigned min = std::min(sz1, sz2);
unsigned i = 0;
for (; i < min; i++) {
add(p1[i], p2[i], a_i);
r.push_back(a_i);
}
for (; i < sz1; i++)
r.push_back(p1[i]);
for (; i < sz2; i++)
r.push_back(p2[i]);
SASSERT(r.size() == std::max(sz1, sz2));
adjust_size(r);
}
\brief r <- p + a
*/
void add(unsigned sz, value * const * p, value * a, value_ref_buffer & r) {
SASSERT(p != r.data());
SASSERT(sz > 0);
r.reset();
value_ref a_0(*this);
add(p[0], a, a_0);
r.push_back(a_0);
r.append(sz - 1, p + 1);
adjust_size(r);
}
\brief r <- p1 - p2
*/
void sub(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) {
SASSERT(p1 != r.data());
SASSERT(p2 != r.data());
r.reset();
value_ref a_i(*this);
unsigned min = std::min(sz1, sz2);
unsigned i = 0;
for (; i < min; i++) {
sub(p1[i], p2[i], a_i);
r.push_back(a_i);
}
for (; i < sz1; i++)
r.push_back(p1[i]);
for (; i < sz2; i++) {
neg(p2[i], a_i);
r.push_back(a_i);
}
SASSERT(r.size() == std::max(sz1, sz2));
adjust_size(r);
}
\brief r <- p - a
*/
void sub(unsigned sz, value * const * p, value * a, value_ref_buffer & r) {
SASSERT(p != r.data());
SASSERT(sz > 0);
r.reset();
value_ref a_0(*this);
sub(p[0], a, a_0);
r.push_back(a_0);
r.append(sz - 1, p + 1);
adjust_size(r);
}
\brief r <- a * p
*/
void mul(value * a, unsigned sz, value * const * p, value_ref_buffer & r) {
SASSERT(p != r.data());
r.reset();
if (a == nullptr)
return;
value_ref a_i(*this);
for (unsigned i = 0; i < sz; i++) {
mul(a, p[i], a_i);
r.push_back(a_i);
}
}
\brief r <- p1 * p2
*/
void mul(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) {
SASSERT(p1 != r.data());
SASSERT(p2 != r.data());
r.reset();
unsigned sz = sz1*sz2;
r.resize(sz);
if (sz1 < sz2) {
std::swap(sz1, sz2);
std::swap(p1, p2);
}
value_ref tmp(*this);
for (unsigned i = 0; i < sz1; i++) {
checkpoint();
if (p1[i] == nullptr)
continue;
for (unsigned j = 0; j < sz2; j++) {
mul(p1[i], p2[j], tmp);
add(r[i+j], tmp, tmp);
r.set(i+j, tmp);
}
}
adjust_size(r);
}
\brief p <- p/a
*/
void div(value_ref_buffer & p, value * a) {
SASSERT(!is_zero(a));
if (is_rational_one(a))
return;
value_ref a_i(*this);
unsigned sz = p.size();
for (unsigned i = 0; i < sz; i++) {
div(p[i], a, a_i);
p.set(i, a_i);
}
}
\brief q <- quotient(p1, p2); r <- rem(p1, p2)
*/
void div_rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2,
value_ref_buffer & q, value_ref_buffer & r) {
SASSERT(sz2 > 0);
if (sz2 == 1) {
q.reset(); q.append(sz1, p1);
div(q, *p2);
r.reset();
}
else {
q.reset();
r.reset(); r.append(sz1, p1);
if (sz1 > 1) {
if (sz1 >= sz2) {
q.resize(sz1 - sz2 + 1);
}
else {
SASSERT(q.empty());
}
value * b_n = p2[sz2-1];
SASSERT(!is_zero(b_n));
value_ref ratio(*this);
value_ref aux(*this);
while (true) {
checkpoint();
sz1 = r.size();
if (sz1 < sz2) {
adjust_size(q);
break;
}
unsigned m_n = sz1 - sz2;
div(r[sz1 - 1], b_n, ratio);
add(q[m_n], ratio, aux);
q.set(m_n, aux);
for (unsigned i = 0; i < sz2 - 1; i++) {
mul(ratio, p2[i], aux);
sub(r[i + m_n], aux, aux);
r.set(i + m_n, aux);
}
r.shrink(sz1 - 1);
adjust_size(r);
}
}
}
}
\brief q <- quotient(p1, p2)
*/
void div(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & q) {
value_ref_buffer r(*this);
div_rem(sz1, p1, sz2, p2, q, r);
}
\brief r <- p/a
*/
void div(unsigned sz, value * const * p, value * a, value_ref_buffer & r) {
r.reset();
value_ref a_i(*this);
for (unsigned i = 0; i < sz; i++) {
div(p[i], a, a_i);
r.push_back(a_i);
}
}
\brief r <- rem(p1, p2)
*/
void rem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) {
SASSERT(p1 != r.data());
SASSERT(p2 != r.data());
TRACE("rcf_rem",
tout << "rem\n";
display_poly(tout, sz1, p1); tout << "\n";
display_poly(tout, sz2, p2); tout << "\n";);
r.reset();
SASSERT(sz2 > 0);
if (sz2 == 1)
return;
r.append(sz1, p1);
if (sz1 <= 1)
return;
value * b_n = p2[sz2 - 1];
value_ref ratio(*this);
value_ref new_a(*this);
SASSERT(b_n != 0);
while (true) {
checkpoint();
sz1 = r.size();
if (sz1 < sz2) {
TRACE("rcf_rem", tout << "rem result\n"; display_poly(tout, r.size(), r.data()); tout << "\n";);
return;
}
unsigned m_n = sz1 - sz2;
div(r[sz1 - 1], b_n, ratio);
for (unsigned i = 0; i < sz2 - 1; i++) {
mul(ratio, p2[i], new_a);
sub(r[i + m_n], new_a, new_a);
r.set(i + m_n, new_a);
}
r.shrink(sz1 - 1);
adjust_size(r);
}
}
\brief r <- prem(p1, p2) Pseudo-remainder
We are working on a field, but it is useful to use the pseudo-remainder algorithm because
it does not create rational function values.
That is, if has_clean_denominators(p1) and has_clean_denominators(p2) then has_clean_denominators(r).
*/
void prem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, unsigned & d, value_ref_buffer & r) {
SASSERT(p1 != r.data());
SASSERT(p2 != r.data());
TRACE("rcf_prem",
tout << "prem\n";
display_poly(tout, sz1, p1); tout << "\n";
display_poly(tout, sz2, p2); tout << "\n";);
d = 0;
r.reset();
SASSERT(sz2 > 0);
if (sz2 == 1)
return;
r.append(sz1, p1);
if (sz1 <= 1)
return;
value * b_n = p2[sz2 - 1];
SASSERT(b_n != 0);
value_ref a_m(*this);
value_ref new_a(*this);
while (true) {
checkpoint();
sz1 = r.size();
if (sz1 < sz2) {
TRACE("rcf_prem", tout << "prem result\n"; display_poly(tout, r.size(), r.data()); tout << "\n";);
return;
}
unsigned m_n = sz1 - sz2;
d++;
a_m = r[sz1 - 1];
if (!is_rational_one(b_n)) {
for (unsigned i = 0; i < sz1 - 1; i++) {
mul(r[i], b_n, new_a);
r.set(i, new_a);
}
}
for (unsigned i = 0; i < sz2 - 1; i++) {
mul(a_m, p2[i], new_a);
sub(r[i + m_n], new_a, new_a);
r.set(i + m_n, new_a);
}
r.shrink(sz1 - 1);
adjust_size(r);
}
}
void prem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) {
unsigned d;
prem(sz1, p1, sz2, p2, d, r);
}
\brief r <- -p
*/
void neg(unsigned sz, value * const * p, value_ref_buffer & r) {
SASSERT(p != r.data());
r.reset();
value_ref a_i(*this);
for (unsigned i = 0; i < sz; i++) {
neg(p[i], a_i);
r.push_back(a_i);
}
}
\brief r <- -r
*/
void neg(value_ref_buffer & r) {
value_ref a_i(*this);
unsigned sz = r.size();
for (unsigned i = 0; i < sz; i++) {
neg(r[i], a_i);
r.set(i, a_i);
}
}
\brief p <- -p
*/
void neg(polynomial & p) {
value_ref a_i(*this);
unsigned sz = p.size();
for (unsigned i = 0; i < sz; i++) {
neg(p[i], a_i);
inc_ref(a_i.get());
dec_ref(p[i]);
p[i] = a_i.get();
}
}
\brief r <- srem(p1, p2)
Signed remainder
*/
void srem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) {
SASSERT(p1 != r.data());
SASSERT(p2 != r.data());
rem(sz1, p1, sz2, p2, r);
neg(r);
}
\brief r <- sprem(p1, p2)
Signed pseudo remainder
*/
void sprem(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) {
SASSERT(p1 != r.data());
SASSERT(p2 != r.data());
unsigned d;
prem(sz1, p1, sz2, p2, d, r);
if (d % 2 == 0 || (sz2 > 0 && sign(p2[sz2-1]) > 0))
neg(r);
}
\brief Values a and b are said to be "structurally" equal if:
- a and b are 0.
- a and b are rationals and compare(a, b) == 0
- a and b are rational function values p_a(x)/q_a(x) and p_b(y)/q_b(y) where x and y are field extensions, and
* x == y (pointer equality, i.e., they are the same field extension object).
* Every coefficient of p_a is structurally equal to every coefficient of p_b
* Every coefficient of q_a is structurally equal to every coefficient of q_b
Clearly structural equality implies equality, but the reverse is not true.
*/
bool struct_eq(value * a, value * b) const {
if (a == b)
return true;
else if (a == nullptr || b == nullptr)
return false;
else if (is_nz_rational(a) && is_nz_rational(b))
return qm().eq(to_mpq(a), to_mpq(b));
else if (is_nz_rational(a) || is_nz_rational(b))
return false;
else {
SASSERT(is_rational_function(a));
SASSERT(is_rational_function(b));
rational_function_value * rf_a = to_rational_function(a);
rational_function_value * rf_b = to_rational_function(b);
if (rf_a->ext() != rf_b->ext())
return false;
return struct_eq(rf_a->num(), rf_b->num()) && struct_eq(rf_a->den(), rf_b->den());
}
}
Auxiliary method for
bool struct_eq(value * a, value * b)
*/
bool struct_eq(unsigned sz_a, value * const * p_a, unsigned sz_b, value * const * p_b) const {
if (sz_a != sz_b)
return false;
for (unsigned i = 0; i < sz_a; i++) {
if (!struct_eq(p_a[i], p_b[i]))
return false;
}
return true;
}
Auxiliary method for
bool struct_eq(value * a, value * b)
*/
bool struct_eq(polynomial const & p_a, polynomial const & p_b) const {
return struct_eq(p_a.size(), p_a.data(), p_b.size(), p_b.data());
}
\brief We say 'a' has "clean" denominators if
- a is 0
- a is a rational_value that is an integer
- a is a rational_function_value of the form p_a(x)/1 where the coefficients of p_a also have clean denominators.
*/
bool has_clean_denominators(value * a) const {
if (a == nullptr)
return true;
else if (is_nz_rational(a))
return qm().is_int(to_mpq(a));
else {
rational_function_value * rf_a = to_rational_function(a);
return is_denominator_one(rf_a) && has_clean_denominators(rf_a->num());
}
}
\brief See comment at has_clean_denominators(value * a)
*/
bool has_clean_denominators(unsigned sz, value * const * p) const {
for (unsigned i = 0; i < sz; i++) {
if (!has_clean_denominators(p[i]))
return false;
}
return true;
}
\brief See comment at has_clean_denominators(value * a)
*/
bool has_clean_denominators(polynomial const & p) const {
return has_clean_denominators(p.size(), p.data());
}
\brief "Clean" the denominators of 'a'. That is, return p and q s.t.
a == p/q
and
has_clean_denominators(p) and has_clean_denominators(q)
*/
void clean_denominators_core(value * a, value_ref & p, value_ref & q) {
INC_DEPTH();
TRACE("rcf_clean", tout << "clean_denominators_core [" << m_exec_depth << "]\na: "; display(tout, a, false); tout << "\n";);
p.reset(); q.reset();
if (a == nullptr) {
p = a;
q = one();
}
else if (is_nz_rational(a)) {
p = mk_rational(to_mpq(a).numerator());
q = mk_rational(to_mpq(a).denominator());
}
else {
rational_function_value * rf_a = to_rational_function(a);
value_ref_buffer p_num(*this), p_den(*this);
value_ref d_num(*this), d_den(*this);
clean_denominators_core(rf_a->num(), p_num, d_num);
if (is_denominator_one(rf_a)) {
p_den.push_back(one());
d_den = one();
}
else {
clean_denominators_core(rf_a->den(), p_den, d_den);
}
value_ref x(*this);
x = mk_rational_function_value(rf_a->ext());
mk_polynomial_value(p_num.size(), p_num.data(), x, p);
mk_polynomial_value(p_den.size(), p_den.data(), x, q);
if (!struct_eq(d_den, d_num)) {
mul(p, d_den, p);
mul(q, d_num, q);
}
if (sign(q) < 0) {
neg(p, p);
neg(q, q);
}
}
}
\brief Clean the denominators of the polynomial p, it returns clean_p and d s.t.
p = clean_p/d
and has_clean_denominators(clean_p) && has_clean_denominators(d)
*/
void clean_denominators_core(unsigned p_sz, value * const * p, value_ref_buffer & norm_p, value_ref & d) {
SASSERT(p_sz >= 1);
value_ref_buffer nums(*this), dens(*this);
value_ref a_n(*this), a_d(*this);
bool all_one = true;
for (unsigned i = 0; i < p_sz; i++) {
if (p[i]) {
clean_denominators_core(p[i], a_n, a_d);
nums.push_back(a_n);
if (!is_rational_one(a_d))
all_one = false;
dens.push_back(a_d);
}
else {
nums.push_back(nullptr);
dens.push_back(nullptr);
}
}
if (all_one) {
norm_p = nums;
d = one();
}
else {
scoped_mpq lcm_z(qm());
bool found_z = false;
SASSERT(nums.size() == p_sz);
SASSERT(dens.size() == p_sz);
for (unsigned i = 0; i < p_sz; i++) {
if (!dens[i])
continue;
if (is_nz_rational(dens[i])) {
mpq const & _d = to_mpq(dens[i]);
SASSERT(qm().is_int(_d));
if (!found_z) {
found_z = true;
qm().set(lcm_z, _d);
}
else {
qm().lcm(lcm_z, _d, lcm_z);
}
}
}
value_ref lcm(*this);
if (found_z) {
lcm = mk_rational(lcm_z);
}
else {
lcm = one();
}
norm_p.reset();
d = lcm;
value_ref_buffer multipliers(*this);
value_ref m(*this);
for (unsigned i = 0; i < p_sz; i++) {
if (!nums[i]) {
norm_p.push_back(nullptr);
}
else {
SASSERT(dens[i]);
bool is_z;
if (!is_nz_rational(dens[i])) {
m = lcm;
is_z = false;
}
else {
scoped_mpq num_z(qm());
qm().div(lcm_z, to_mpq(dens[i]), num_z);
SASSERT(qm().is_int(num_z));
m = mk_rational_and_swap(num_z);
is_z = true;
}
bool found_lt_eq = false;
for (unsigned j = 0; j < p_sz; j++) {
TRACE("rcf_clean_bug", tout << "j: " << j << " "; display(tout, m, false); tout << "\n";);
if (!dens[j])
continue;
if (i != j && !is_nz_rational(dens[j])) {
if (struct_eq(dens[i], dens[j])) {
if (j < i)
found_lt_eq = true;
}
else {
mul(m, dens[j], m);
}
}
}
if (!is_z && !found_lt_eq) {
mul(dens[i], d, d);
}
mul(m, nums[i], m);
norm_p.push_back(m);
}
}
}
SASSERT(norm_p.size() == p_sz);
}
void clean_denominators_core(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) {
clean_denominators_core(p.size(), p.data(), norm_p, d);
}
void clean_denominators(value * a, value_ref & p, value_ref & q) {
if (has_clean_denominators(a)) {
p = a;
q = one();
}
else {
clean_denominators_core(a, p, q);
}
}
void clean_denominators(unsigned sz, value * const * p, value_ref_buffer & norm_p, value_ref & d) {
if (has_clean_denominators(sz, p)) {
norm_p.append(sz, p);
d = one();
}
else {
clean_denominators_core(sz, p, norm_p, d);
}
}
void clean_denominators(polynomial const & p, value_ref_buffer & norm_p, value_ref & d) {
clean_denominators(p.size(), p.data(), norm_p, d);
}
void clean_denominators(numeral const & a, numeral & p, numeral & q) {
value_ref _p(*this), _q(*this);
clean_denominators(a.m_value, _p, _q);
set(p, _p);
set(q, _q);
}
\brief If has_clean_denominators(a), then this method store the gcd of the integer coefficients in g.
If !has_clean_denominators(a) it returns false.
If g != 0, then it will compute the gcd of g and the coefficients in a.
*/
bool gcd_int_coeffs(value * a, mpz & g) {
if (a == nullptr) {
return false;
}
else if (is_nz_rational(a)) {
if (!qm().is_int(to_mpq(a)))
return false;
else if (qm().is_zero(g)) {
qm().set(g, to_mpq(a).numerator());
qm().abs(g);
}
else {
qm().gcd(g, to_mpq(a).numerator(), g);
}
return true;
}
else {
rational_function_value * rf_a = to_rational_function(a);
if (!is_denominator_one(rf_a))
return false;
else
return gcd_int_coeffs(rf_a->num(), g);
}
}
\brief See comment in gcd_int_coeffs(value * a, mpz & g)
*/
bool gcd_int_coeffs(unsigned p_sz, value * const * p, mpz & g) {
if (p_sz == 0) {
return false;
}
else {
for (unsigned i = 0; i < p_sz; i++) {
if (p[i]) {
if (!gcd_int_coeffs(p[i], g))
return false;
if (qm().is_one(g))
return true;
}
}
return true;
}
}
\brief See comment in gcd_int_coeffs(value * a, mpz & g)
*/
bool gcd_int_coeffs(polynomial const & p, mpz & g) {
return gcd_int_coeffs(p.size(), p.data(), g);
}
\brief Compute gcd_int_coeffs and divide p by it (if applicable).
*/
void normalize_int_coeffs(value_ref_buffer & p) {
scoped_mpz g(qm());
if (gcd_int_coeffs(p.size(), p.data(), g) && !qm().is_one(g)) {
SASSERT(qm().is_pos(g));
value_ref a(*this);
for (unsigned i = 0; i < p.size(); i++) {
if (p[i]) {
a = p[i];
p.set(i, nullptr);
exact_div_z(a, g);
p.set(i, a);
}
}
}
}
\brief a <- a/b where b > 0
Auxiliary function for normalize_int_coeffs.
It assumes has_clean_denominators(a), and that b divides all integer coefficients.
FUTURE: perform the operation using destructive updates when a is not shared.
*/
void exact_div_z(value_ref & a, mpz const & b) {
if (a == 0) {
return;
}
else if (is_nz_rational(a)) {
scoped_mpq r(qm());
SASSERT(qm().is_int(to_mpq(a)));
qm().div(to_mpq(a), b, r);
a = mk_rational_and_swap(r);
}
else {
rational_function_value * rf = to_rational_function(a);
SASSERT(is_denominator_one(rf));
value_ref_buffer new_ais(*this);
value_ref ai(*this);
polynomial const & p = rf->num();
for (unsigned i = 0; i < p.size(); i++) {
if (p[i]) {
ai = p[i];
exact_div_z(ai, b);
new_ais.push_back(ai);
}
else {
new_ais.push_back(nullptr);
}
}
rational_function_value * r = mk_rational_function_value_core(rf->ext(), new_ais.size(), new_ais.data(), 1, &m_one);
set_interval(r->m_interval, rf->m_interval);
a = r;
div(r->m_interval, b, m_ini_precision, r->m_interval);
}
}
bool is_monic(value_ref_buffer const & p) {
return !p.empty() && is_rational_one(p[p.size() - 1]);
}
bool is_monic(polynomial const & p) {
return !p.empty() && is_rational_one(p[p.size() - 1]);
}
\brief Force the leading coefficient of p to be 1.
*/
void mk_monic(value_ref_buffer & p) {
unsigned sz = p.size();
if (sz > 0) {
value_ref a_i(*this);
SASSERT(p[sz-1] != 0);
if (!is_rational_one(p[sz-1])) {
for (unsigned i = 0; i < sz - 1; i++) {
div(p[i], p[sz-1], a_i);
p.set(i, a_i);
}
p.set(sz-1, one());
}
}
}
\brief r <- gcd(p1, p2)
*/
void gcd(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) {
INC_DEPTH();
TRACE("rcf_gcd", tout << "GCD [" << m_exec_depth << "]\n";
display_poly(tout, sz1, p1); tout << "\n";
display_poly(tout, sz2, p2); tout << "\n";);
if (sz1 == 0) {
r.append(sz2, p2);
mk_monic(r);
}
else if (sz2 == 0) {
r.append(sz1, p1);
mk_monic(r);
}
else {
value_ref_buffer A(*this);
value_ref_buffer B(*this);
value_ref_buffer R(*this);
A.append(sz1, p1);
B.append(sz2, p2);
while (true) {
TRACE("rcf_gcd",
tout << "A: "; display_poly(tout, A.size(), A.data()); tout << "\n";
tout << "B: "; display_poly(tout, B.size(), B.data()); tout << "\n";);
if (B.empty()) {
mk_monic(A);
r = A;
TRACE("rcf_gcd",
tout << "gcd result: "; display_poly(tout, r.size(), r.data()); tout << "\n";);
return;
}
rem(A.size(), A.data(), B.size(), B.data(), R);
A = B;
B = R;
}
}
}
void flip_sign_if_lc_neg(value_ref_buffer & r) {
unsigned sz = r.size();
if (sz == 0)
return;
if (sign(r[sz - 1]) < 0)
neg(r);
}
void prem_gcd(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & r) {
INC_DEPTH();
TRACE("rcf_gcd", tout << "prem-GCD [" << m_exec_depth << "]\n";
display_poly(tout, sz1, p1); tout << "\n";
display_poly(tout, sz2, p2); tout << "\n";);
SASSERT(p1 != r.data());
SASSERT(p2 != r.data());
if (sz1 == 0) {
r.append(sz2, p2);
flip_sign_if_lc_neg(r);
}
else if (sz2 == 0) {
r.append(sz1, p1);
flip_sign_if_lc_neg(r);
}
else {
value_ref_buffer A(*this);
value_ref_buffer B(*this);
value_ref_buffer R(*this);
A.append(sz1, p1);
B.append(sz2, p2);
while (true) {
TRACE("rcf_gcd",
tout << "A: "; display_poly(tout, A.size(), A.data()); tout << "\n";
tout << "B: "; display_poly(tout, B.size(), B.data()); tout << "\n";);
if (B.empty()) {
normalize_int_coeffs(A);
flip_sign_if_lc_neg(A);
r = A;
TRACE("rcf_gcd",
tout << "gcd result: "; display_poly(tout, r.size(), r.data()); tout << "\n";);
return;
}
prem(A.size(), A.data(), B.size(), B.data(), R);
normalize_int_coeffs(R);
A = B;
B = R;
}
}
}
\brief r <- dp/dx
*/
void derivative(unsigned sz, value * const * p, value_ref_buffer & r) {
r.reset();
if (sz > 1) {
for (unsigned i = 1; i < sz; i++) {
mpq i_mpq(i);
value_ref a_i(*this);
a_i = mk_rational_and_swap(i_mpq);
mul(a_i, p[i], a_i);
r.push_back(a_i);
}
adjust_size(r);
}
}
\brief r <- squarefree(p)
Store in r the square free factors of p.
*/
void square_free(unsigned sz, value * const * p, value_ref_buffer & r) {
flet<bool> set(m_in_aux_values, true);
if (sz <= 1) {
r.append(sz, p);
}
else {
value_ref_buffer p_prime(*this);
value_ref_buffer g(*this);
derivative(sz, p, p_prime);
if (m_use_prem)
prem_gcd(sz, p, p_prime.size(), p_prime.data(), g);
else
gcd(sz, p, p_prime.size(), p_prime.data(), g);
if (g.size() <= 1) {
r.append(sz, p);
}
else {
div(sz, p, g.size(), g.data(), r);
if (m_use_prem)
normalize_int_coeffs(r);
}
}
}
\brief Keep expanding the Sturm sequence starting at seq
*/
void sturm_seq_core(scoped_polynomial_seq & seq) {
INC_DEPTH();
flet<bool> set(m_in_aux_values, true);
SASSERT(seq.size() >= 2);
TRACE("rcf_sturm_seq",
unsigned sz = seq.size();
tout << "sturm_seq_core [" << m_exec_depth << "]\n";
display_poly(tout, seq.size(sz-2), seq.coeffs(sz-2)); tout << "\n";
display_poly(tout, seq.size(sz-1), seq.coeffs(sz-1)); tout << "\n";);
value_ref_buffer r(*this);
while (true) {
unsigned sz = seq.size();
if (m_use_prem) {
sprem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r);
normalize_int_coeffs(r);
}
else {
srem(seq.size(sz-2), seq.coeffs(sz-2), seq.size(sz-1), seq.coeffs(sz-1), r);
}
TRACE("rcf_sturm_seq",
tout << "sturm_seq_core [" << m_exec_depth << "], new polynomial\n";
display_poly(tout, r.size(), r.data()); tout << "\n";);
if (r.empty())
return;
seq.push(r.size(), r.data());
}
}
\brief Store in seq the Sturm sequence for (p1; p2)
*/
void sturm_seq(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, scoped_polynomial_seq & seq) {
seq.reset();
seq.push(sz1, p1);
seq.push(sz2, p2);
sturm_seq_core(seq);
}
\brief Store in seq the Sturm sequence for (p; p')
*/
void sturm_seq(unsigned sz, value * const * p, scoped_polynomial_seq & seq) {
seq.reset();
value_ref_buffer p_prime(*this);
seq.push(sz, p);
derivative(sz, p, p_prime);
seq.push(p_prime.size(), p_prime.data());
sturm_seq_core(seq);
}
\brief Store in seq the Sturm sequence for (p1; p1' * p2)
*/
void sturm_tarski_seq(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, scoped_polynomial_seq & seq) {
seq.reset();
value_ref_buffer p1_prime(*this);
value_ref_buffer p1_prime_p2(*this);
seq.push(sz1, p1);
derivative(sz1, p1, p1_prime);
mul(p1_prime.size(), p1_prime.data(), sz2, p2, p1_prime_p2);
seq.push(p1_prime_p2.size(), p1_prime_p2.data());
sturm_seq_core(seq);
}
\brief Return the sign of p(0)
*/
int eval_sign_at_zero(unsigned n, value * const * p) {
if (n == 0)
return 0;
return sign(p[0]);
}
\brief Return the sign of p(oo)
*/
int eval_sign_at_plus_inf(unsigned n, value * const * p) {
if (n == 0)
return 0;
SASSERT(!is_zero(p[n-1]));
return sign(p[n-1]);
}
\brief Return the sign of p(-oo)
*/
int eval_sign_at_minus_inf(unsigned n, value * const * p) {
if (n == 0)
return 0;
SASSERT(!is_zero(p[n-1]));
unsigned degree = n - 1;
if (degree % 2 == 0)
return sign(p[n - 1]);
else
return -sign(p[n - 1]);
}
\brief Store in r an approximation (as an interval) for the interval p(b).
\pre n >= 2
*/
void eval_sign_at_approx(unsigned n, value * const * p, mpbq const & b, mpbqi & r) {
SASSERT(n >= 2);
SASSERT(n > 0);
SASSERT(p[n - 1] != 0);
scoped_mpbqi bi(bqim());
set_interval(bi, b);
bqim().mul(interval(p[n - 1]), bi, r);
unsigned i = n - 1;
while (i > 0) {
checkpoint();
--i;
if (p[i] != nullptr)
bqim().add(r, interval(p[i]), r);
if (i > 0)
bqim().mul(r, bi, r);
}
}
\brief We say a polynomial has "refinable" approximated coefficients if the intervals
approximating the coefficients do not have -oo or oo as lower/upper bounds.
*/
bool has_refineable_approx_coeffs(unsigned n, value * const * p) {
for (unsigned i = 0; i < n; i++) {
if (p[i] != nullptr) {
mpbqi & a_i = interval(p[i]);
if (a_i.lower_is_inf() || a_i.upper_is_inf())
return false;
}
}
return true;
}
\brief r <- p(b)
*/
void mk_polynomial_value(unsigned n, value * const * p, value * b, value_ref & r) {
SASSERT(n > 0);
if (n == 1 || b == nullptr) {
r = p[0];
}
else {
SASSERT(n >= 2);
mul(p[n - 1], b, r);
unsigned i = n - 1;
while (i > 0) {
--i;
if (p[i] != nullptr)
add(r, p[i], r);
if (i > 0)
mul(r, b, r);
}
}
}
\brief Evaluate the sign of p(b) by computing a value object.
*/
int expensive_eval_sign_at(unsigned n, value * const * p, mpbq const & b) {
flet<bool> set(m_in_aux_values, true);
SASSERT(n > 1);
SASSERT(p[n - 1] != 0);
scoped_mpz mpz_twok(qm());
qm().mul2k(mpz(1), b.k(), mpz_twok);
value_ref twok(*this), twok_i(*this);
twok = mk_rational(mpz_twok);
twok_i = twok;
value_ref c(*this);
c = mk_rational(b.numerator());
value_ref r(*this), ak(*this), rc(*this);
r = p[n-1];
unsigned i = n-1;
while (i > 0) {
--i;
if (is_zero(p[i])) {
mul(r, c, r);
}
else {
mul(p[i], twok_i, ak);
mul(r, c, rc);
add(ak, rc, r);
}
mul(twok_i, twok, twok_i);
}
return sign(r);
}
\brief Find the magnitude of the biggest interval use to approximate coefficients of p.
\pre has_refineable_approx_coeffs(n, p)
*/
int find_biggest_interval_magnitude(unsigned n, value * const * p) {
int r = INT_MIN;
for (unsigned i = 0; i < n; i++) {
if (p[i] != nullptr) {
mpbqi & a_i = interval(p[i]);
SASSERT(!a_i.lower_is_inf() && !a_i.upper_is_inf());
int m = magnitude(a_i);
if (m > r)
r = m;
}
}
return r;
}
\brief Return the sign of p(b)
*/
int eval_sign_at(unsigned n, value * const * p, mpbq const & b) {
if (n == 0)
return 0;
else if (n == 1)
return sign(p[0]);
else {
scoped_mpbqi r(bqim());
eval_sign_at_approx(n, p, b, r);
if (!contains_zero(r)) {
return bqim().is_P(r) ? 1 : -1;
}
else if (!has_refineable_approx_coeffs(n, p)) {
return expensive_eval_sign_at(n, p, b);
}
else {
int m = find_biggest_interval_magnitude(n, p);
unsigned prec;
if (m >= 0)
prec = 1;
else
prec = -m;
SASSERT(prec >= 1);
while (prec <= m_max_precision) {
checkpoint();
if (!refine_coeffs_interval(n, p, prec)) {
return expensive_eval_sign_at(n, p, b);
}
eval_sign_at_approx(n, p, b, r);
if (!contains_zero(r)) {
return bqim().is_P(r) ? 1 : -1;
}
prec++;
}
return expensive_eval_sign_at(n, p, b);
}
}
}
enum location {
ZERO,
MINUS_INF,
PLUS_INF,
MPBQ
};
\brief Compute the number of sign variations at position (loc, b) in the given polynomial sequence.
The position (loc, b) should be interpreted in the following way:
- (ZERO, *) -> number of sign variations at 0.
- (MINUS_INF, *) -> number of sign variations at -oo.
- (PLUS_INF, *) -> number of sign variations at oo.
- (MPBQ, b) -> number of sign variations at binary rational b.
*/
unsigned sign_variations_at_core(scoped_polynomial_seq const & seq, location loc, 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);
value * 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 sign_variations_at_minus_inf(scoped_polynomial_seq const & seq) {
mpbq dummy(0);
return sign_variations_at_core(seq, MINUS_INF, dummy);
}
unsigned sign_variations_at_plus_inf(scoped_polynomial_seq const & seq) {
mpbq dummy(0);
return sign_variations_at_core(seq, PLUS_INF, dummy);
}
unsigned sign_variations_at_zero(scoped_polynomial_seq const & seq) {
mpbq dummy(0);
return sign_variations_at_core(seq, ZERO, dummy);
}
unsigned sign_variations_at(scoped_polynomial_seq const & seq, mpbq const & b) {
return sign_variations_at_core(seq, MPBQ, b);
}
int sign_variations_at_lower(scoped_polynomial_seq & seq, mpbqi const & interval) {
if (interval.lower_is_inf())
return sign_variations_at_minus_inf(seq);
else if (bqm().is_zero(interval.lower()))
return sign_variations_at_zero(seq);
else
return sign_variations_at(seq, interval.lower());
}
int sign_variations_at_upper(scoped_polynomial_seq & seq, mpbqi const & interval) {
if (interval.upper_is_inf())
return sign_variations_at_plus_inf(seq);
else if (bqm().is_zero(interval.upper()))
return sign_variations_at_zero(seq);
else
return sign_variations_at(seq, interval.upper());
}
\brief Given a polynomial Sturm sequence seq for (P; P' * Q) and an interval (a, b], it returns
TaQ(Q, P; a, b) =
#{ x \in (a, b] | P(x) = 0 and Q(x) > 0 }
-
#{ x \in (a, b] | P(x) = 0 and Q(x) < 0 }
\remark This method ignores whether the interval end-points are closed or open.
*/
int TaQ(scoped_polynomial_seq & seq, mpbqi const & interval) {
return sign_variations_at_lower(seq, interval) - sign_variations_at_upper(seq, interval);
}
\brief Return TaQ(Q, P; a, b) =
#{ x \in (a, b] | P(x) = 0 and Q(x) > 0 }
-
#{ x \in (a, b] | P(x) = 0 and Q(x) < 0 }
\remark This method ignores whether the interval end-points are closed or open.
*/
int TaQ(unsigned p_sz, value * const * p, unsigned q_sz, value * const * q, mpbqi const & interval) {
INC_DEPTH();
TRACE("rcf_TaQ", tout << "TaQ [" << m_exec_depth << "]\n";
display_poly(tout, p_sz, p); tout << "\n";
display_poly(tout, q_sz, q); tout << "\n";);
scoped_polynomial_seq seq(*this);
sturm_tarski_seq(p_sz, p, q_sz, q, seq);
return TaQ(seq, interval);
}
\brief Return TaQ(1, P; a, b) =
#{ x \in (a, b] | P(x) = 0 }
\remark This method ignores whether the interval end-points are closed or open.
*/
int TaQ_1(unsigned p_sz, value * const * p, mpbqi const & interval) {
INC_DEPTH();
TRACE("rcf_TaQ", tout << "TaQ_1 [" << m_exec_depth << "]\n";
display_poly(tout, p_sz, p); tout << "\n";);
scoped_polynomial_seq seq(*this);
sturm_seq(p_sz, p, seq);
return TaQ(seq, interval);
}
void refine_rational_interval(rational_value * v, unsigned prec) {
mpbqi & i = interval(v);
if (!i.lower_is_open() && !i.upper_is_open()) {
SASSERT(bqm().eq(i.lower(), i.upper()));
return;
}
while (!check_precision(i, prec)) {
checkpoint();
bqm().refine_lower(to_mpq(v), i.lower(), i.upper());
bqm().refine_upper(to_mpq(v), i.lower(), i.upper());
}
}
\brief Refine the interval for each coefficient of in the polynomial p.
*/
bool refine_coeffs_interval(unsigned n, value * const * p, unsigned prec) {
for (unsigned i = 0; i < n; i++) {
if (p[i] != nullptr && !refine_interval(p[i], prec))
return false;
}
return true;
}
\brief Refine the interval for each coefficient of in the polynomial p.
*/
bool refine_coeffs_interval(polynomial const & p, unsigned prec) {
return refine_coeffs_interval(p.size(), p.data(), prec);
}
\brief Store in r the interval p(v).
*/
void polynomial_interval(polynomial const & p, mpbqi const & v, mpbqi & r) {
unsigned sz = p.size();
if (sz == 1) {
bqim().set(r, interval(p[0]));
}
else {
SASSERT(sz > 0);
SASSERT(p[sz - 1] != 0);
bqim().mul(interval(p[sz-1]), v, r);
unsigned i = sz - 1;
while (i > 0) {
--i;
if (p[i] != 0)
bqim().add(r, interval(p[i]), r);
if (i > 0)
bqim().mul(r, v, r);
}
}
}
\brief Update the interval of v by using the intervals of
extension and coefficients of the rational function.
*/
void update_rf_interval(rational_function_value * v, unsigned prec) {
if (is_denominator_one(v)) {
polynomial_interval(v->num(), v->ext()->interval(), v->interval());
}
else {
scoped_mpbqi num_i(bqim()), den_i(bqim());
polynomial_interval(v->num(), v->ext()->interval(), num_i);
polynomial_interval(v->den(), v->ext()->interval(), den_i);
if (!contains_zero(num_i) && !contains_zero(den_i)) {
div(num_i, den_i, inc_precision(prec, 2), v->interval());
}
}
}
void refine_transcendental_interval(rational_function_value * v, unsigned prec) {
SASSERT(v->ext()->is_transcendental());
polynomial const & n = v->num();
polynomial const & d = v->den();
unsigned _prec = prec;
while (true) {
VERIFY(refine_coeffs_interval(n, _prec));
VERIFY(refine_coeffs_interval(d, _prec));
refine_transcendental_interval(to_transcendental(v->ext()), _prec);
update_rf_interval(v, prec);
TRACE("rcf_transcendental", tout << "after update_rf_interval: " << magnitude(v->interval()) << " ";
bqim().display(tout, v->interval()); tout << std::endl;);
if (check_precision(v->interval(), prec))
return;
_prec++;
}
}
bool refine_infinitesimal_interval(rational_function_value * v, unsigned prec) {
SASSERT(v->ext()->is_infinitesimal());
polynomial const & numerator = v->num();
polynomial const & denominator = v->den();
unsigned num_idx = first_non_zero(numerator);
unsigned den_idx = first_non_zero(denominator);
if (num_idx == 0 && den_idx == 0) {
unsigned _prec = prec;
while (true) {
refine_interval(numerator[num_idx], _prec);
refine_interval(denominator[num_idx], _prec);
mpbqi const & num_i = interval(numerator[num_idx]);
mpbqi const & den_i = interval(denominator[num_idx]);
SASSERT(!contains_zero(num_i));
SASSERT(!contains_zero(den_i));
if (is_open_interval(num_i) && is_open_interval(den_i)) {
div(num_i, den_i, inc_precision(prec, 2), v->interval());
}
else {
scoped_mpbqi new_num_i(bqim());
scoped_mpbqi new_den_i(bqim());
mpbq tiny_value(1, _prec*2);
if (numerator.size() > 1)
add_infinitesimal(num_i, sign_of_first_non_zero(numerator, 1) > 0, tiny_value, new_num_i);
else
bqim().set(new_num_i, num_i);
if (denominator.size() > 1)
add_infinitesimal(den_i, sign_of_first_non_zero(denominator, 1) > 0, tiny_value, new_den_i);
else
bqim().set(new_den_i, den_i);
div(new_num_i, new_den_i, inc_precision(prec, 2), v->interval());
}
if (check_precision(v->interval(), prec))
return true;
_prec++;
}
}
else {
SASSERT(num_idx == 0 || den_idx == 0);
int s = sign(numerator[num_idx]) * sign(denominator[den_idx]);
SASSERT(s != 0);
if (num_idx == 0) {
SASSERT(den_idx > 0);
return false;
}
else {
SASSERT(num_idx > 0);
SASSERT(den_idx == 0);
if (s == 1) {
set_lower(v->interval(), mpbq(0));
set_upper(v->interval(), mpbq(1, prec));
}
else {
set_lower(v->interval(), mpbq(-1, prec));
set_upper(v->interval(), mpbq(0));
}
return true;
}
}
}
bool refine_algebraic_interval(algebraic * a, unsigned prec) {
save_interval_if_too_small(a, prec);
if (a->sdt() != nullptr) {
return false;
}
else {
mpbqi & a_i = a->interval();
if (a_i.lower_is_inf() || a_i.upper_is_inf()) {
return false;
}
else {
mpbqi & a_i = a->interval();
SASSERT(!a_i.lower_is_inf() && !a_i.upper_is_inf());
int lower_sign = INT_MIN;
while (!check_precision(a_i, prec)) {
checkpoint();
SASSERT(!bqm().eq(a_i.lower(), a_i.upper()));
scoped_mpbq m(bqm());
bqm().add(a_i.lower(), a_i.upper(), m);
bqm().div2(m);
int mid_sign = eval_sign_at(a->p().size(), a->p().data(), m);
if (mid_sign == 0) {
set_lower(a_i, m, false);
set_upper(a_i, m, false);
return true;
}
else {
SASSERT(mid_sign == 1 || mid_sign == -1);
if (lower_sign == INT_MIN) {
lower_sign = eval_sign_at(a->p().size(), a->p().data(), a_i.lower());
}
SASSERT(lower_sign == 1 || lower_sign == -1);
if (mid_sign == lower_sign) {
set_lower(a_i, m);
}
else {
set_upper(a_i, m);
}
}
}
return true;
}
}
}
bool refine_algebraic_interval(rational_function_value * v, unsigned prec) {
SASSERT(v->ext()->is_algebraic());
polynomial const & n = v->num();
SASSERT(is_denominator_one(v));
unsigned _prec = prec;
while (true) {
if (!refine_coeffs_interval(n, _prec) ||
!refine_algebraic_interval(to_algebraic(v->ext()), _prec))
return false;
update_rf_interval(v, prec);
TRACE("rcf_algebraic", tout << "after update_rf_interval: " << magnitude(v->interval()) << " "; bqim().display(tout, v->interval()); tout << std::endl;);
if (check_precision(v->interval(), prec))
return true;
_prec++;
}
}
\brief Refine the interval of v to the desired precision (1/2^prec).
Return false in case of failure. A failure can only happen if v depends on infinitesimal values.
*/
bool refine_interval(value * v, unsigned prec) {
checkpoint();
SASSERT(!is_zero(v));
int m = magnitude(interval(v));
if (m == INT_MIN || (m < 0 && static_cast<unsigned>(-m) > prec))
return true;
save_interval_if_too_small(v, prec);
if (is_nz_rational(v)) {
refine_rational_interval(to_nz_rational(v), prec);
return true;
}
else {
rational_function_value * rf = to_rational_function(v);
if (rf->ext()->is_transcendental()) {
refine_transcendental_interval(rf, prec);
return true;
}
else if (rf->ext()->is_infinitesimal())
return refine_infinitesimal_interval(rf, prec);
else
return refine_algebraic_interval(rf, prec);
}
}
\brief Return the position of the first non-zero coefficient of p.
*/
static unsigned first_non_zero(polynomial const & p) {
unsigned sz = p.size();
for (unsigned i = 0; i < sz; i++) {
if (p[i] != 0)
return i;
}
UNREACHABLE();
return UINT_MAX;
}
\brief Return the sign of the first non zero coefficient starting at position start_idx
*/
int sign_of_first_non_zero(polynomial const & p, unsigned start_idx) {
unsigned sz = p.size();
SASSERT(start_idx < sz);
for (unsigned i = start_idx; i < sz; i++) {
if (p[i] != 0)
return sign(p[i]);
}
UNREACHABLE();
return 0;
}
out <- in + infinitesimal (if plus_eps == true)
out <- in - infinitesimal (if plus_eps == false)
We use the following rules for performing the assignment
If plus_eps == True
If lower(in) == v (closed or open), then lower(out) == v and open
If upper(in) == v and open, then upper(out) == v and open
If upper(in) == v and closed, then upper(out) == new_v and open
where new_v is v + tiny_value / 2^k, where k is the smallest natural such that sign(new_v) == sign(v)
If plus_eps == False
If lower(in) == v and open, then lower(out) == v and open
If lower(in) == v and closed, then lower(out) == new_v and open
If upper(in) == v (closed or open), then upper(out) == v and open
where new_v is v - tiny_value / 2^k, where k is the smallest natural such that sign(new_v) == sign(v)
*/
void add_infinitesimal(mpbqi const & in, bool plus_eps, mpbq const & tiny_value, mpbqi & out) {
set_interval(out, in);
out.set_lower_is_open(true);
out.set_upper_is_open(true);
if (plus_eps) {
if (!in.upper_is_open()) {
scoped_mpbq tval(bqm());
tval = tiny_value;
while (true) {
bqm().add(in.upper(), tval, out.upper());
if (bqm().is_pos(in.upper()) == bqm().is_pos(out.upper()))
return;
bqm().div2(tval);
checkpoint();
}
}
}
else {
if (!in.lower_is_open()) {
scoped_mpbq tval(bqm());
tval = tiny_value;
while (true) {
bqm().sub(in.lower(), tval, out.lower());
if (bqm().is_pos(in.lower()) == bqm().is_pos(out.lower()))
return;
bqm().div2(tval);
checkpoint();
}
}
}
}
\brief Determine the sign of an element of Q(trans_0, ..., trans_n)
*/
void determine_transcendental_sign(rational_function_value * v) {
SASSERT(v->ext()->is_transcendental());
int m = magnitude(v->interval());
unsigned prec = 1;
if (m < 0)
prec = static_cast<unsigned>(-m) + 1;
while (contains_zero(v->interval())) {
refine_transcendental_interval(v, prec);
prec++;
}
}
\brief Determine the sign of an element of Q(trans_0, ..., trans_n, eps_0, ..., eps_m)
*/
void determine_infinitesimal_sign(rational_function_value * v) {
SASSERT(v->ext()->is_infinitesimal());
polynomial const & numerator = v->num();
polynomial const & denominator = v->den();
unsigned num_idx = first_non_zero(numerator);
unsigned den_idx = first_non_zero(denominator);
if (num_idx == 0 && den_idx == 0) {
mpbqi const & num_i = interval(numerator[num_idx]);
mpbqi const & den_i = interval(denominator[num_idx]);
SASSERT(!contains_zero(num_i));
SASSERT(!contains_zero(den_i));
if (is_open_interval(num_i) && is_open_interval(den_i)) {
div(num_i, den_i, m_ini_precision, v->interval());
}
else {
scoped_mpbqi new_num_i(bqim());
scoped_mpbqi new_den_i(bqim());
mpbq tiny_value(1, m_ini_precision);
if (numerator.size() > 1)
add_infinitesimal(num_i, sign_of_first_non_zero(numerator, 1) > 0, tiny_value, new_num_i);
else
bqim().set(new_num_i, num_i);
if (denominator.size() > 1)
add_infinitesimal(den_i, sign_of_first_non_zero(denominator, 1) > 0, tiny_value, new_den_i);
else
bqim().set(new_den_i, den_i);
div(new_num_i, new_den_i, m_ini_precision, v->interval());
}
}
else {
SASSERT(num_idx == 0 || den_idx == 0);
int s = sign(numerator[num_idx]) * sign(denominator[den_idx]);
SASSERT(s != 0);
if (num_idx == 0) {
SASSERT(den_idx > 0);
if (s == 1) {
set_lower(v->interval(), m_plus_inf_approx);
set_upper_inf(v->interval());
}
else {
set_lower_inf(v->interval());
set_upper(v->interval(), m_minus_inf_approx);
}
}
else {
SASSERT(num_idx > 0);
SASSERT(den_idx == 0);
if (s == 1) {
set_lower(v->interval(), mpbq(0));
set_upper(v->interval(), mpbq(1, m_ini_precision));
}
else {
set_lower(v->interval(), mpbq(-1, m_ini_precision));
set_upper(v->interval(), mpbq(0));
}
}
}
SASSERT(!contains_zero(v->interval()));
}
\brief Return true if x and q depend on infinitesimal values.
That is, q(x) does not depend on infinitesimal values.
*/
bool depends_on_infinitesimals(polynomial const & q, algebraic * x) {
return x->depends_on_infinitesimals() || depends_on_infinitesimals(q.size(), q.data());
}
\brief This method is invoked when we know that q(x) is not zero and q(x) does not depend on infinitesimal values.
The procedure will keep refining the intervals associated with x and coefficients of q until
the interval of q(x) is of the form
(l > 0, u)
OR
(l, u < 0)
*/
void refine_until_sign_determined(polynomial const & q, algebraic * x, mpbqi & r) {
SASSERT(!depends_on_infinitesimals(q, x));
int m = magnitude(r);
unsigned prec;
if (m >= 0)
prec = m_ini_precision;
else
prec = -m;
while (true) {
checkpoint();
VERIFY(refine_coeffs_interval(q, prec));
VERIFY(refine_algebraic_interval(x, prec));
polynomial_interval(q, x->interval(), r);
SASSERT(!r.lower_is_inf());
SASSERT(!r.upper_is_inf());
if (!contains_zero(r) && !bqm().is_zero(r.lower()) && !bqm().is_zero(r.upper()))
return;
prec++;
}
}
\brief If q(x) != 0, return true and store in r an interval that contains the value q(x), but does not contain 0.
If q(x) == 0, return false
*/
bool expensive_algebraic_poly_interval(polynomial const & q, algebraic * x, mpbqi & r) {
polynomial_interval(q, x->interval(), r);
if (!contains_zero(r)) {
if (!depends_on_infinitesimals(q, x) && (bqm().is_zero(r.lower()) || bqm().is_zero(r.upper()))) {
refine_until_sign_determined(q, x, r);
}
return true;
}
int num_roots = x->num_roots_inside_interval();
SASSERT(x->sdt() != 0 || num_roots == 1);
polynomial const & p = x->p();
int taq_p_q = TaQ(p.size(), p.data(), q.size(), q.data(), x->iso_interval());
if (num_roots == 1 && taq_p_q == 0)
return false;
if (taq_p_q == num_roots) {
if (!depends_on_infinitesimals(q, x))
refine_until_sign_determined(q, x, r);
else
set_lower_zero(r);
SASSERT(!contains_zero(r));
return true;
}
else if (taq_p_q == -num_roots) {
if (!depends_on_infinitesimals(q, x))
refine_until_sign_determined(q, x, r);
else
set_upper_zero(r);
SASSERT(!contains_zero(r));
return true;
}
else {
SASSERT(num_roots > 1);
SASSERT(x->sdt() != 0);
int q_eq_0, q_gt_0, q_lt_0;
value_ref_buffer q2(*this);
count_signs_at_zeros_core(taq_p_q, p.size(), p.data(), q.size(), q.data(), x->iso_interval(), num_roots, q_eq_0, q_gt_0, q_lt_0, q2);
if (q_eq_0 > 0 && q_gt_0 == 0 && q_lt_0 == 0) {
return false;
}
else if (q_eq_0 == 0 && q_gt_0 > 0 && q_lt_0 == 0) {
set_lower_zero(r);
return true;
}
else if (q_eq_0 == 0 && q_gt_0 == 0 && q_lt_0 > 0) {
set_upper_zero(r);
return true;
}
else {
sign_det & sdt = *(x->sdt());
scoped_mpz_matrix M(mm());
VERIFY(mk_sign_det_matrix(q_eq_0, q_gt_0, q_lt_0, M));
bool use_q2 = M.n() == 3;
scoped_mpz_matrix new_M_s(mm());
mm().tensor_product(sdt.M_s, M, new_M_s);
array<polynomial> const & prs = sdt.prs();
array<int> const & taqrs = sdt.taqrs();
SASSERT(prs.size() == taqrs.size());
int_buffer new_taqrs;
value_ref_buffer prq(*this);
for (unsigned i = 0; i < taqrs.size(); i++) {
new_taqrs.push_back(taqrs[i]);
mul(prs[i].size(), prs[i].data(), q.size(), q.data(), prq);
new_taqrs.push_back(TaQ(p.size(), p.data(), prq.size(), prq.data(), x->iso_interval()));
if (use_q2) {
mul(prs[i].size(), prs[i].data(), q2.size(), q2.data(), prq);
new_taqrs.push_back(TaQ(p.size(), p.data(), prq.size(), prq.data(), x->iso_interval()));
}
}
int_buffer sc_cardinalities;
sc_cardinalities.resize(new_taqrs.size(), 0);
VERIFY(mm().solve(new_M_s, sc_cardinalities.data(), new_taqrs.data()));
DEBUG_CODE({
for (unsigned i = 0; i < sc_cardinalities.size(); i++) {
SASSERT(sc_cardinalities[i] == 0 || sc_cardinalities[i] == 1);
}
if (!use_q2) {
for (unsigned i = 0; i < taqrs.size(); i++) {
SASSERT((sc_cardinalities[2*i] == 1) + (sc_cardinalities[2*i + 1] == 1) == 1);
}
}
else {
for (unsigned i = 0; i < taqrs.size(); i++) {
SASSERT((sc_cardinalities[3*i] == 1) + (sc_cardinalities[3*i + 1] == 1) + (sc_cardinalities[3*i + 2] == 1) == 1);
}
}
});
unsigned sc_idx = x->sc_idx();
if (use_q2) {
if (sc_cardinalities[3*sc_idx] == 1) {
return false;
}
else if (sc_cardinalities[3*sc_idx + 1] == 1) {
set_lower_zero(r);
return true;
}
else {
SASSERT(sc_cardinalities[3*sc_idx + 2] == 1);
set_upper_zero(r);
return true;
}
}
else {
if (q_eq_0 == 0) {
if (sc_cardinalities[2*sc_idx] == 1) {
set_lower_zero(r);
return true;
}
else {
SASSERT(sc_cardinalities[2*sc_idx + 1] == 1);
set_upper_zero(r);
return true;
}
}
else if (q_gt_0 == 0) {
if (sc_cardinalities[2*sc_idx] == 1) {
return false;
}
else {
SASSERT(sc_cardinalities[2*sc_idx + 1] == 1);
set_upper_zero(r);
return true;
}
}
else {
SASSERT(q_lt_0 == 0);
if (sc_cardinalities[2*sc_idx] == 1) {
return false;
}
else {
SASSERT(sc_cardinalities[2*sc_idx + 1] == 1);
set_lower_zero(r);
return true;
}
}
}
}
}
}
bool expensive_determine_algebraic_sign(rational_function_value * v) {
SASSERT(contains_zero(v->interval()));
SASSERT(v->ext()->is_algebraic());
TRACE("rcf_algebraic_sign",
tout << "expensive_determine_algebraic_sign\n"; display(tout, v, false);
tout << "\ninterval: "; bqim().display(tout, v->interval()); tout << "\n";);
algebraic * x = to_algebraic(v->ext());
scoped_mpbqi num_interval(bqim());
SASSERT(is_denominator_one(v));
if (!expensive_algebraic_poly_interval(v->num(), x, num_interval))
return false;
SASSERT(!contains_zero(num_interval));
set_interval(v->interval(), num_interval);
SASSERT(!contains_zero(v->interval()));
return true;
}
\brief Determine the sign of an rational function value
p(x)/q(x) when x is an algebraic extension.
*/
bool determine_algebraic_sign(rational_function_value * v) {
SASSERT(v->ext()->is_algebraic());
mpbqi & interval = v->interval();
if (interval.lower_is_inf() || interval.upper_is_inf()) {
return expensive_determine_algebraic_sign(v);
}
else {
int m = magnitude(v->interval());
unsigned prec = 1;
if (m < 0)
prec = static_cast<unsigned>(-m) + 1;
while (contains_zero(v->interval())) {
if (!refine_algebraic_interval(v, prec))
return expensive_determine_algebraic_sign(v);
prec++;
if (prec > m_max_precision)
return expensive_determine_algebraic_sign(v);
}
SASSERT(!contains_zero(v->interval()));
return true;
}
}
\brief Determine the sign of the new rational function value.
The idea is to keep refining the interval until interval of v does not contain 0.
After a couple of steps we switch to expensive sign determination procedure.
Return false if v is actually zero.
*/
bool determine_sign(rational_function_value * v) {
if (!contains_zero(v->interval()))
return true;
bool r;
switch (v->ext()->knd()) {
case extension::TRANSCENDENTAL: determine_transcendental_sign(v); r = true; break;
case extension::INFINITESIMAL: determine_infinitesimal_sign(v); r = true; break;
case extension::ALGEBRAIC: r = determine_algebraic_sign(v); break;
default:
UNREACHABLE();
r = false;
}
TRACE("rcf_determine_sign_bug",
tout << "result: " << r << "\n";
display_compact(tout, v); tout << "\n";
tout << "sign: " << sign(v) << "\n";);
return r;
}
bool determine_sign(value_ref & r) {
SASSERT(is_rational_function(r.get()));
return determine_sign(to_rational_function(r.get()));
}
\brief Compute polynomials new_p1 and new_p2 s.t.
- p1/p2 == new_p1/new_p2, AND
- new_p2 is a Monic polynomial, AND
- gcd(new_p1, new_p2) == 1
*/
void normalize_fraction(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2, value_ref_buffer & new_p1, value_ref_buffer & new_p2) {
INC_DEPTH();
TRACE("rcf_arith", tout << "normalize [" << m_exec_depth << "]\n";
display_poly(tout, sz1, p1); tout << "\n";
display_poly(tout, sz2, p2); tout << "\n";);
SASSERT(sz1 > 0 && sz2 > 0);
if (sz2 == 1) {
div(sz1, p1, p2[0], new_p1);
new_p2.reset(); new_p2.push_back(one());
}
else {
value * lc = p2[sz2 - 1];
if (is_rational_one(lc)) {
normalize_num_monic_den(sz1, p1, sz2, p2, new_p1, new_p2);
}
else {
value_ref_buffer tmp1(*this);
value_ref_buffer tmp2(*this);
div(sz1, p1, lc, tmp1);
div(sz2, p2, lc, tmp2);
normalize_num_monic_den(tmp1.size(), tmp1.data(), tmp2.size(), tmp2.data(), new_p1, new_p2);
}
}
TRACE("normalize_fraction_bug",
display_poly(tout, sz1, p1); tout << "\n";
display_poly(tout, sz2, p2); tout << "\n";
tout << "====>\n";
display_poly(tout, new_p1.size(), new_p1.data()); tout << "\n";
display_poly(tout, new_p2.size(), new_p2.data()); tout << "\n";);
}
\brief Auxiliary function for normalize_fraction.
It produces new_p1 and new_p2 s.t.
new_p1/new_p2 == p1/p2
gcd(new_p1, new_p2) == 1
Assumptions:
\pre p2 is monic
\pre sz2 > 1
*/
void normalize_num_monic_den(unsigned sz1, value * const * p1, unsigned sz2, value * const * p2,
value_ref_buffer & new_p1, value_ref_buffer & new_p2) {
SASSERT(sz2 > 1);
SASSERT(is_rational_one(p2[sz2-1]));
value_ref_buffer g(*this);
gcd(sz1, p1, sz2, p2, g);
SASSERT(is_monic(g));
if (is_rational_one(g)) {
new_p1.append(sz1, p1);
new_p2.append(sz2, p2);
}
else {
div(sz1, p1, g.size(), g.data(), new_p1);
div(sz2, p2, g.size(), g.data(), new_p2);
SASSERT(is_monic(new_p2));
}
}
\brief Simplify p1(x) using x's defining polynomial.
By definition of polynomial division, we have:
new_p1(x) == quotient(p1,p)(x) * p(x) + rem(p1,p)(x)
Since p(x) == 0, we have that
new_p1(x) = rem(p1,p)(x)
*/
void normalize_algebraic(algebraic * x, unsigned sz1, value * const * p1, value_ref_buffer & new_p1) {
polynomial const & p = x->p();
if (!m_lazy_algebraic_normalization || !m_in_aux_values || is_monic(p)) {
rem(sz1, p1, p.size(), p.data(), new_p1);
}
else {
new_p1.reset();
new_p1.append(sz1, p1);
}
}
\brief Create a new value using the a->ext(), and the given numerator and denominator.
Use interval(a) + interval(b) as an initial approximation for the interval of the result, and invoke determine_sign()
*/
void mk_add_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den, value_ref & r) {
SASSERT(num_sz > 0);
if (num_sz == 1 && den_sz <= 1) {
SASSERT(den_sz == 0 || is_rational_one(den[0]));
r = num[0];
}
else {
scoped_mpbqi ri(bqim());
bqim().add(interval(a), interval(b), ri);
r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den);
swap(r->interval(), ri);
if (determine_sign(r)) {
SASSERT(!contains_zero(r->interval()));
}
else {
r = nullptr;
}
}
}
\brief Add a value of 'a' the form n/1 with b where rank(a) > rank(b)
*/
void add_p_v(rational_function_value * a, value * b, value_ref & r) {
SASSERT(is_denominator_one(a));
SASSERT(compare_rank(a, b) > 0);
polynomial const & an = a->num();
polynomial const & one = a->den();
SASSERT(an.size() > 1);
value_ref_buffer new_num(*this);
add(an.size(), an.data(), b, new_num);
SASSERT(new_num.size() == an.size());
mk_add_value(a, b, new_num.size(), new_num.data(), one.size(), one.data(), r);
}
\brief Add a value 'a' of the form n/d with b where rank(a) > rank(b)
*/
void add_rf_v(rational_function_value * a, value * b, value_ref & r) {
value_ref_buffer b_ad(*this);
value_ref_buffer num(*this);
polynomial const & an = a->num();
if (is_denominator_one(a)) {
add_p_v(a, b, r);
}
else {
SASSERT(!a->ext()->is_algebraic());
polynomial const & ad = a->den();
mul(b, ad.size(), ad.data(), b_ad);
add(an.size(), an.data(), b_ad.size(), b_ad.data(), num);
if (num.empty())
r = nullptr;
else {
value_ref_buffer new_num(*this);
value_ref_buffer new_den(*this);
normalize_fraction(num.size(), num.data(), ad.size(), ad.data(), new_num, new_den);
SASSERT(!new_num.empty());
mk_add_value(a, b, new_num.size(), new_num.data(), new_den.size(), new_den.data(), r);
}
}
}
\brief Add values 'a' and 'b' of the form n/1 and rank(a) == rank(b)
*/
void add_p_p(rational_function_value * a, rational_function_value * b, value_ref & r) {
SASSERT(is_denominator_one(a));
SASSERT(is_denominator_one(b));
SASSERT(compare_rank(a, b) == 0);
polynomial const & an = a->num();
polynomial const & one = a->den();
polynomial const & bn = b->num();
value_ref_buffer new_num(*this);
add(an.size(), an.data(), bn.size(), bn.data(), new_num);
if (new_num.empty())
r = nullptr;
else {
mk_add_value(a, b, new_num.size(), new_num.data(), one.size(), one.data(), r);
}
}
\brief Add values 'a' and 'b' of the form n/d and rank(a) == rank(b)
*/
void add_rf_rf(rational_function_value * a, rational_function_value * b, value_ref & r) {
SASSERT(compare_rank(a, b) == 0);
polynomial const & an = a->num();
polynomial const & bn = b->num();
if (is_denominator_one(a) && is_denominator_one(b)) {
add_p_p(a, b, r);
}
else {
SASSERT(!a->ext()->is_algebraic());
polynomial const & ad = a->den();
polynomial const & bd = b->den();
value_ref_buffer an_bd(*this);
value_ref_buffer bn_ad(*this);
mul(an.size(), an.data(), bd.size(), bd.data(), an_bd);
mul(bn.size(), bn.data(), ad.size(), ad.data(), bn_ad);
value_ref_buffer num(*this);
add(an_bd.size(), an_bd.data(), bn_ad.size(), bn_ad.data(), num);
if (num.empty()) {
r = nullptr;
}
else {
value_ref_buffer den(*this);
mul(ad.size(), ad.data(), bd.size(), bd.data(), den);
value_ref_buffer new_num(*this);
value_ref_buffer new_den(*this);
normalize_fraction(num.size(), num.data(), den.size(), den.data(), new_num, new_den);
SASSERT(!new_num.empty());
mk_add_value(a, b, new_num.size(), new_num.data(), new_den.size(), new_den.data(), r);
}
}
}
void add(value * a, value * b, value_ref & r) {
if (a == nullptr) {
r = b;
}
else if (b == nullptr) {
r = a;
}
else if (is_nz_rational(a) && is_nz_rational(b)) {
scoped_mpq v(qm());
qm().add(to_mpq(a), to_mpq(b), v);
if (qm().is_zero(v))
r = nullptr;
else
r = mk_rational_and_swap(v);
}
else {
INC_DEPTH();
TRACE("rcf_arith", tout << "add [" << m_exec_depth << "]\n";
display(tout, a, false); tout << "\n";
display(tout, b, false); tout << "\n";);
switch (compare_rank(a, b)) {
case -1: add_rf_v(to_rational_function(b), a, r); break;
case 0: add_rf_rf(to_rational_function(a), to_rational_function(b), r); break;
case 1: add_rf_v(to_rational_function(a), b, r); break;
default: UNREACHABLE();
}
}
}
void sub(value * a, value * b, value_ref & r) {
if (a == nullptr) {
neg(b, r);
}
else if (b == nullptr) {
r = a;
}
else if (is_nz_rational(a) && is_nz_rational(b)) {
scoped_mpq v(qm());
qm().sub(to_mpq(a), to_mpq(b), v);
if (qm().is_zero(v))
r = nullptr;
else
r = mk_rational_and_swap(v);
}
else {
value_ref neg_b(*this);
neg(b, neg_b);
switch (compare_rank(a, neg_b)) {
case -1: add_rf_v(to_rational_function(neg_b), a, r); break;
case 0: add_rf_rf(to_rational_function(a), to_rational_function(neg_b), r); break;
case 1: add_rf_v(to_rational_function(a), neg_b, r); break;
default: UNREACHABLE();
}
}
}
void neg_rf(rational_function_value * a, value_ref & r) {
polynomial const & an = a->num();
polynomial const & ad = a->den();
value_ref_buffer new_num(*this);
neg(an.size(), an.data(), new_num);
scoped_mpbqi ri(bqim());
bqim().neg(interval(a), ri);
r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.data(), ad.size(), ad.data());
swap(r->interval(), ri);
SASSERT(!contains_zero(r->interval()));
}
void neg(value * a, value_ref & r) {
if (a == nullptr) {
r = nullptr;
}
else if (is_nz_rational(a)) {
scoped_mpq v(qm());
qm().set(v, to_mpq(a));
qm().neg(v);
r = mk_rational_and_swap(v);
}
else {
neg_rf(to_rational_function(a), r);
}
}
\brief Create a new value using the a->ext(), and the given numerator and denominator.
Use interval(a) * interval(b) as an initial approximation for the interval of the result, and invoke determine_sign()
*/
void mk_mul_value(rational_function_value * a, value * b, unsigned num_sz, value * const * num, unsigned den_sz, value * const * den, value_ref & r) {
SASSERT(num_sz > 0);
if (num_sz == 1 && den_sz <= 1) {
SASSERT(den_sz == 0 || is_rational_one(den[0]));
r = num[0];
}
else {
scoped_mpbqi ri(bqim());
bqim().mul(interval(a), interval(b), ri);
r = mk_rational_function_value_core(a->ext(), num_sz, num, den_sz, den);
swap(ri, r->interval());
if (determine_sign(r)) {
SASSERT(!contains_zero(r->interval()));
}
else {
r = nullptr;
}
}
}
\brief Multiply a value of 'a' the form n/1 with b where rank(a) > rank(b)
*/
void mul_p_v(rational_function_value * a, value * b, value_ref & r) {
SASSERT(is_denominator_one(a));
SASSERT(b != 0);
SASSERT(compare_rank(a, b) > 0);
polynomial const & an = a->num();
polynomial const & one = a->den();
SASSERT(an.size() > 1);
value_ref_buffer new_num(*this);
mul(b, an.size(), an.data(), new_num);
SASSERT(new_num.size() == an.size());
mk_mul_value(a, b, new_num.size(), new_num.data(), one.size(), one.data(), r);
}
\brief Multiply a value 'a' of the form n/d with b where rank(a) > rank(b)
*/
void mul_rf_v(rational_function_value * a, value * b, value_ref & r) {
polynomial const & an = a->num();
if (is_denominator_one(a)) {
mul_p_v(a, b, r);
}
else {
SASSERT(!a->ext()->is_algebraic());
polynomial const & ad = a->den();
value_ref_buffer num(*this);
mul(b, an.size(), an.data(), num);
SASSERT(num.size() == an.size());
value_ref_buffer new_num(*this);
value_ref_buffer new_den(*this);
normalize_fraction(num.size(), num.data(), ad.size(), ad.data(), new_num, new_den);
SASSERT(!new_num.empty());
mk_mul_value(a, b, new_num.size(), new_num.data(), new_den.size(), new_den.data(), r);
}
}
\brief Multiply values 'a' and 'b' of the form n/1 and rank(a) == rank(b)
*/
void mul_p_p(rational_function_value * a, rational_function_value * b, value_ref & r) {
SASSERT(is_denominator_one(a));
SASSERT(is_denominator_one(b));
SASSERT(compare_rank(a, b) == 0);
polynomial const & an = a->num();
polynomial const & one = a->den();
polynomial const & bn = b->num();
value_ref_buffer new_num(*this);
mul(an.size(), an.data(), bn.size(), bn.data(), new_num);
SASSERT(!new_num.empty());
extension * x = a->ext();
if (x->is_algebraic()) {
value_ref_buffer new_num2(*this);
normalize_algebraic(to_algebraic(x), new_num.size(), new_num.data(), new_num2);
SASSERT(!new_num.empty());
mk_mul_value(a, b, new_num2.size(), new_num2.data(), one.size(), one.data(), r);
}
else {
mk_mul_value(a, b, new_num.size(), new_num.data(), one.size(), one.data(), r);
}
}
\brief Multiply values 'a' and 'b' of the form n/d and rank(a) == rank(b)
*/
void mul_rf_rf(rational_function_value * a, rational_function_value * b, value_ref & r) {
SASSERT(compare_rank(a, b) == 0);
polynomial const & an = a->num();
polynomial const & bn = b->num();
if (is_denominator_one(a) && is_denominator_one(b)) {
mul_p_p(a, b, r);
}
else {
SASSERT(!a->ext()->is_algebraic());
polynomial const & ad = a->den();
polynomial const & bd = b->den();
value_ref_buffer num(*this);
value_ref_buffer den(*this);
mul(an.size(), an.data(), bn.size(), bn.data(), num);
mul(ad.size(), ad.data(), bd.size(), bd.data(), den);
SASSERT(!num.empty()); SASSERT(!den.empty());
value_ref_buffer new_num(*this);
value_ref_buffer new_den(*this);
normalize_fraction(num.size(), num.data(), den.size(), den.data(), new_num, new_den);
SASSERT(!new_num.empty());
mk_mul_value(a, b, new_num.size(), new_num.data(), new_den.size(), new_den.data(), r);
}
}
void mul(value * a, value * b, value_ref & r) {
if (a == nullptr || b == nullptr) {
r = nullptr;
}
else if (is_rational_one(a)) {
r = b;
}
else if (is_rational_one(b)) {
r = a;
}
else if (is_rational_minus_one(a)) {
neg(b, r);
}
else if (is_rational_minus_one(b)) {
neg(a, r);
}
else if (is_nz_rational(a) && is_nz_rational(b)) {
scoped_mpq v(qm());
qm().mul(to_mpq(a), to_mpq(b), v);
r = mk_rational_and_swap(v);
}
else {
INC_DEPTH();
TRACE("rcf_arith", tout << "mul [" << m_exec_depth << "]\n";
display(tout, a, false); tout << "\n";
display(tout, b, false); tout << "\n";);
switch (compare_rank(a, b)) {
case -1: mul_rf_v(to_rational_function(b), a, r); break;
case 0: mul_rf_rf(to_rational_function(a), to_rational_function(b), r); break;
case 1: mul_rf_v(to_rational_function(a), b, r); break;
default: UNREACHABLE();
}
}
}
void div(value * a, value * b, value_ref & r) {
if (a == nullptr) {
r = nullptr;
}
else if (b == nullptr) {
throw exception("division by zero");
}
else if (is_rational_one(b)) {
r = a;
}
else if (is_rational_one(a)) {
inv(b, r);
}
else if (is_rational_minus_one(b)) {
neg(a, r);
}
else if (is_nz_rational(a) && is_nz_rational(b)) {
scoped_mpq v(qm());
qm().div(to_mpq(a), to_mpq(b), v);
r = mk_rational_and_swap(v);
}
else {
value_ref inv_b(*this);
inv(b, inv_b);
switch (compare_rank(a, inv_b)) {
case -1: mul_rf_v(to_rational_function(inv_b), a, r); break;
case 0: mul_rf_rf(to_rational_function(a), to_rational_function(inv_b), r); break;
case 1: mul_rf_v(to_rational_function(a), inv_b, r); break;
default: UNREACHABLE();
}
}
}
\brief Invert 1/q(alpha) given that p(alpha) = 0. That is, we find h s.t.
q(alpha) * h(alpha) = 1
The procedure succeeds (and returns true) if the GCD(q, p) = 1.
If the GCD(q, p) != 1, then it returns false, and store the GCD in g.
The following procedure is essentially a special case of the extended polynomial GCD algorithm.
*/
bool inv_algebraic(unsigned q_sz, value * const * q, unsigned p_sz, value * const * p, value_ref_buffer & g, value_ref_buffer & h) {
TRACE("inv_algebraic",
tout << "q: "; display_poly(tout, q_sz, q); tout << "\n";
tout << "p: "; display_poly(tout, p_sz, p); tout << "\n";);
SASSERT(q_sz > 0);
SASSERT(q_sz < p_sz);
value_ref_buffer Q(*this);
Q.append(q_sz, q);
value_ref_buffer R(*this);
R.push_back(one());
value_ref_buffer Quo(*this), Rem(*this), aux(*this);
while (true) {
TRACE("inv_algebraic",
tout << "Q: "; display_poly(tout, Q.size(), Q.data()); tout << "\n";
tout << "R: "; display_poly(tout, R.size(), R.data()); tout << "\n";);
if (Q.size() == 1) {
div(R.size(), R.data(), Q[0], h);
TRACE("inv_algebraic", tout << "h: "; display_poly(tout, h.size(), h.data()); tout << "\n";);
g.reset(); g.push_back(one());
return true;
}
else {
div_rem(p_sz, p, Q.size(), Q.data(), Quo, Rem);
if (Rem.empty()) {
g = Q;
mk_monic(g);
return false;
}
else {
neg(Rem.size(), Rem.data(), Q);
mul(R.size(), R.data(), Quo.size(), Quo.data(), aux);
rem(aux.size(), aux.data(), p_sz, p, R);
SASSERT(R.size() < p_sz);
}
}
}
}
\brief r <- 1/a specialized version when a->ext() is algebraic.
It avoids the use of rational functions.
*/
void inv_algebraic(rational_function_value * a, value_ref & r) {
SASSERT(a->ext()->is_algebraic());
SASSERT(is_denominator_one(a));
scoped_mpbqi ri(bqim());
bqim().inv(interval(a), ri);
algebraic * alpha = to_algebraic(a->ext());
polynomial const & q = a->num();
polynomial const & p = alpha->p();
value_ref_buffer norm_q(*this);
rem(q.size(), q.data(), p.size(), p.data(), norm_q);
SASSERT(norm_q.size() < p.size());
value_ref_buffer new_num(*this), g(*this);
if (inv_algebraic(norm_q.size(), norm_q.data(), p.size(), p.data(), g, new_num)) {
if (new_num.size() == 1) {
r = new_num[0];
}
else {
r = mk_rational_function_value_core(alpha, new_num.size(), new_num.data());
swap(r->interval(), ri);
SASSERT(!contains_zero(r->interval()));
}
}
else {
value_ref_buffer new_p(*this);
div(p.size(), p.data(), g.size(), g.data(), new_p);
if (m_clean_denominators) {
value_ref_buffer tmp(*this);
value_ref d(*this);
clean_denominators(new_p.size(), new_p.data(), tmp, d);
new_p = tmp;
}
SASSERT(new_p.size() >= 2);
if (new_p.size() == 2) {
value_ref alpha_val(*this);
alpha_val = new_p[0];
neg(alpha_val, alpha_val);
div(alpha_val, new_p[1], alpha_val);
value_ref new_a(*this);
mk_polynomial_value(q.size(), q.data(), alpha_val, new_a);
inv(new_a, r);
}
else if (alpha->sdt() == nullptr) {
reset_p(alpha->m_p);
set_p(alpha->m_p, new_p.size(), new_p.data());
inv_algebraic(a, r);
}
else {
SASSERT(new_p.size() > 2);
numeral_vector roots;
nl_nz_sqf_isolate_roots(new_p.size(), new_p.data(), roots);
SASSERT(roots.size() > 0);
algebraic * new_alpha;
if (roots.size() == 1) {
new_alpha = to_algebraic(to_rational_function(roots[0].m_value)->ext());
}
else {
value_ref alpha_val(*this);
alpha_val = mk_rational_function_value(alpha);
unsigned i = 0;
for (i = 0; i < roots.size(); i++) {
if (compare(alpha_val, roots[i].m_value) == 0) {
break;
}
}
new_alpha = to_algebraic(to_rational_function(roots[i].m_value)->ext());
}
SASSERT(new_alpha->p().size() == new_p.size());
reset_p(alpha->m_p);
set_p(alpha->m_p, new_alpha->m_p.size(), new_alpha->m_p.data());
inc_ref_sign_det(new_alpha->m_sign_det);
dec_ref_sign_det(alpha->m_sign_det);
alpha->m_sign_det = new_alpha->m_sign_det;
set_interval(alpha->m_iso_interval, new_alpha->m_iso_interval);
alpha->m_sc_idx = new_alpha->m_sc_idx;
alpha->m_depends_on_infinitesimals = new_alpha->m_depends_on_infinitesimals;
inv_algebraic(a, r);
}
}
}
void inv_rf(rational_function_value * a, value_ref & r) {
if (a->ext()->is_algebraic()) {
inv_algebraic(a, r);
}
else {
SASSERT(!a->ext()->is_algebraic());
polynomial const & an = a->num();
polynomial const & ad = a->den();
scoped_mpbqi ri(bqim());
bqim().inv(interval(a), ri);
value_ref_buffer new_num(*this);
value_ref_buffer new_den(*this);
normalize_fraction(ad.size(), ad.data(), an.size(), an.data(), new_num, new_den);
r = mk_rational_function_value_core(a->ext(), new_num.size(), new_num.data(), new_den.size(), new_den.data());
swap(r->interval(), ri);
SASSERT(!contains_zero(r->interval()));
}
}
void inv(value * a, value_ref & r) {
if (a == nullptr) {
throw exception("division by zero");
}
if (is_nz_rational(a)) {
scoped_mpq v(qm());
qm().inv(to_mpq(a), v);
r = mk_rational_and_swap(v);
}
else {
inv_rf(to_rational_function(a), r);
}
}
void set(numeral & n, value * v) {
inc_ref(v);
dec_ref(n.m_value);
n.m_value = v;
}
void set(numeral & n, value_ref const & v) {
set(n, v.get());
}
void neg(numeral & a) {
value_ref r(*this);
neg(a.m_value, r);
set(a, r);
}
void neg(numeral const & a, numeral & b) {
value_ref r(*this);
neg(a.m_value, r);
set(b, r);
}
void inv(numeral & a) {
value_ref r(*this);
inv(a.m_value, r);
set(a, r);
}
void inv(numeral const & a, numeral & b) {
value_ref r(*this);
inv(a.m_value, r);
set(b, r);
}
void add(numeral const & a, numeral const & b, numeral & c) {
value_ref r(*this);
add(a.m_value, b.m_value, r);
set(c, r);
}
void sub(numeral const & a, numeral const & b, numeral & c) {
value_ref r(*this);
sub(a.m_value, b.m_value, r);
set(c, r);
}
void mul(numeral const & a, numeral const & b, numeral & c) {
value_ref r(*this);
mul(a.m_value, b.m_value, r);
set(c, r);
}
void div(numeral const & a, numeral const & b, numeral & c) {
value_ref r(*this);
div(a.m_value, b.m_value, r);
set(c, r);
}
\brief a <- b^{1/k}
*/
void root(numeral const & a, unsigned k, numeral & b) {
if (k == 0)
throw exception("0-th root is indeterminate");
if (k == 1 || is_zero(a)) {
set(b, a);
return;
}
if (sign(a) < 0 && k % 2 == 0)
throw exception("even root of negative number");
value_ref_buffer p(*this);
value_ref neg_a(*this);
neg(a.m_value, neg_a);
p.push_back(neg_a);
for (unsigned i = 0; i < k - 1; i++)
p.push_back(nullptr);
p.push_back(one());
numeral_vector roots;
nz_isolate_roots(p.size(), p.data(), roots);
SASSERT(roots.size() == 1 || roots.size() == 2);
if (roots.size() == 1 || sign(roots[0].m_value) > 0) {
set(b, roots[0]);
}
else {
SASSERT(roots.size() == 2);
SASSERT(sign(roots[1].m_value) > 0);
set(b, roots[1]);
}
del(roots);
}
\brief a <- b^k
*/
void power(numeral const & a, unsigned k, numeral & b) {
unsigned mask = 1;
value_ref power(*this);
value_ref _b(*this);
power = a.m_value;
_b = one();
while (mask <= k) {
checkpoint();
if (mask & k)
mul(_b, power, _b);
mul(power, power, power);
mask = mask << 1;
}
set(b, _b);
}
int compare(value * a, value * b) {
if (a == nullptr)
return -sign(b);
else if (b == nullptr)
return sign(a);
else if (is_nz_rational(a) && is_nz_rational(b)) {
if (qm().eq(to_mpq(a), to_mpq(b)))
return 0;
else
return qm().lt(to_mpq(a), to_mpq(b)) ? -1 : 1;
}
else {
if (bqim().before(interval(a), interval(b)))
return -1;
else if (bqim().before(interval(b), interval(a)))
return 1;
else {
value_ref diff(*this);
sub(a, b, diff);
return sign(diff);
}
}
}
int compare(numeral const & a, numeral const & b) {
return compare(a.m_value, b.m_value);
}
struct collect_algebraic_refs {
char_vector m_visited;
ptr_vector<algebraic> m_found;
void mark(extension * ext) {
if (ext->is_algebraic()) {
m_visited.reserve(ext->idx() + 1, false);
if (!m_visited[ext->idx()]) {
m_visited[ext->idx()] = true;
algebraic * a = to_algebraic(ext);
m_found.push_back(a);
mark(a->p());
}
}
}
void mark(polynomial const & p) {
for (unsigned i = 0; i < p.size(); i++) {
mark(p[i]);
}
}
void mark(value * v) {
if (v == nullptr || is_nz_rational(v))
return;
rational_function_value * rf = to_rational_function(v);
mark(rf->ext());
mark(rf->num());
mark(rf->den());
}
};
static unsigned num_nz_coeffs(polynomial const & p) {
unsigned r = 0;
for (unsigned i = 0; i < p.size(); i++) {
if (p[i])
r++;
}
return r;
}
bool use_parenthesis(value * v) const {
if (is_zero(v) || is_nz_rational(v))
return false;
rational_function_value * rf = to_rational_function(v);
return num_nz_coeffs(rf->num()) > 1 || !is_denominator_one(rf);
}
template<typename DisplayVar>
void display_polynomial(std::ostream & out, unsigned sz, value * const * p, DisplayVar const & display_var, bool compact, bool pp) const {
if (sz == 0) {
out << "0";
return;
}
unsigned i = sz;
bool first = true;
while (i > 0) {
--i;
if (p[i] == nullptr)
continue;
if (first)
first = false;
else
out << " + ";
if (i == 0)
display(out, p[i], compact, pp);
else {
if (!is_rational_one(p[i])) {
if (use_parenthesis(p[i])) {
out << "(";
display(out, p[i], compact, pp);
out << ")";
if (pp)
out << " ";
else
out << "*";
}
else {
display(out, p[i], compact, pp);
if (pp)
out << " ";
else
out << "*";
}
}
display_var(out, compact, pp);
if (i > 1) {
if (pp)
out << "<sup>" << i << "</sup>";
else
out << "^" << i;
}
}
}
}
template<typename DisplayVar>
void display_polynomial(std::ostream & out, polynomial const & p, DisplayVar const & display_var, bool compact, bool pp) const {
display_polynomial(out, p.size(), p.data(), display_var, compact, pp);
}
struct display_free_var_proc {
void operator()(std::ostream & out, bool compact, bool pp) const {
out << "x";
}
};
struct display_ext_proc {
imp const & m;
extension * m_ref;
display_ext_proc(imp const & _m, extension * r):m(_m), m_ref(r) {}
void operator()(std::ostream & out, bool compact, bool pp) const {
m.display_ext(out, m_ref, compact, pp);
}
};
void display_polynomial_expr(std::ostream & out, polynomial const & p, extension * ext, bool compact, bool pp) const {
display_polynomial(out, p, display_ext_proc(*this, ext), compact, pp);
}
static void display_poly_sign(std::ostream & out, int s) {
if (s < 0)
out << " < 0";
else if (s == 0)
out << " = 0";
else
out << " > 0";
}
void display_sign_conditions(std::ostream & out, sign_condition * sc) const {
bool first = true;
out << "{";
while (sc) {
if (first)
first = false;
else
out << ", ";
out << "q(" << sc->qidx() << ")";
display_poly_sign(out, sc->sign());
sc = sc->prev();
}
out << "}";
}
void display_sign_conditions(std::ostream & out, sign_condition * sc, array<polynomial> const & qs, bool compact, bool pp) const {
bool first = true;
out << "{";
while (sc) {
if (first)
first = false;
else
out << ", ";
display_polynomial(out, qs[sc->qidx()], display_free_var_proc(), compact, pp);
display_poly_sign(out, sc->sign());
sc = sc->prev();
}
out << "}";
}
void display_interval(std::ostream & out, mpbqi const & i, bool pp) const {
if (pp)
bqim().display_pp(out, i);
else
bqim().display(out, i);
}
void display_algebraic_def(std::ostream & out, algebraic * a, bool compact, bool pp) const {
out << "root(";
display_polynomial(out, a->p(), display_free_var_proc(), compact, pp);
out << ", ";
display_interval(out, a->iso_interval(), pp);
out << ", ";
if (a->sdt() != nullptr)
display_sign_conditions(out, a->sdt()->sc(a->sc_idx()), a->sdt()->qs(), compact, pp);
else
out << "{}";
out << ")";
}
void display_poly(std::ostream & out, unsigned n, value * const * p) const {
collect_algebraic_refs c;
for (unsigned i = 0; i < n; i++)
c.mark(p[i]);
display_polynomial(out, n, p, display_free_var_proc(), true, false);
std::sort(c.m_found.begin(), c.m_found.end(), rank_lt_proc());
for (unsigned i = 0; i < c.m_found.size(); i++) {
algebraic * ext = c.m_found[i];
out << "\n r!" << ext->idx() << " := ";
display_algebraic_def(out, ext, true, false);
}
}
void display_ext(std::ostream & out, extension * r, bool compact, bool pp) const {
switch (r->knd()) {
case extension::TRANSCENDENTAL: to_transcendental(r)->display(out, pp); break;
case extension::INFINITESIMAL: to_infinitesimal(r)->display(out, pp); break;
case extension::ALGEBRAIC:
if (compact) {
if (pp)
out << "α<sub>" << r->idx() << "</sub>";
else
out << "r!" << r->idx();
}
else {
display_algebraic_def(out, to_algebraic(r), compact, pp);
}
}
}
void display(std::ostream & out, value * v, bool compact, bool pp=false) const {
if (v == nullptr)
out << "0";
else if (is_nz_rational(v))
qm().display(out, to_mpq(v));
else {
rational_function_value * rf = to_rational_function(v);
if (is_denominator_one(rf)) {
display_polynomial_expr(out, rf->num(), rf->ext(), compact, pp);
}
else if (is_rational_one(rf->num())) {
out << "1/(";
display_polynomial_expr(out, rf->den(), rf->ext(), compact, pp);
out << ")";
}
else {
out << "(";
display_polynomial_expr(out, rf->num(), rf->ext(), compact, pp);
out << ")/(";
display_polynomial_expr(out, rf->den(), rf->ext(), compact, pp);
out << ")";
}
}
}
void display_compact(std::ostream & out, value * a, bool pp=false) const {
collect_algebraic_refs c;
c.mark(a);
if (c.m_found.empty()) {
display(out, a, true, pp);
}
else {
std::sort(c.m_found.begin(), c.m_found.end(), rank_lt_proc());
out << "[";
display(out, a, true, pp);
for (unsigned i = 0; i < c.m_found.size(); i++) {
algebraic * ext = c.m_found[i];
if (pp)
out << "; α<sub>" << ext->idx() << "</sub> := ";
else
out << "; r!" << ext->idx() << " := ";
display_algebraic_def(out, ext, true, pp);
}
out << "]";
}
}
void display(std::ostream & out, numeral const & a, bool compact=false, bool pp=false) const {
if (compact)
display_compact(out, a.m_value, pp);
else
display(out, a.m_value, false, pp);
}
void display_non_rational_in_decimal(std::ostream & out, numeral const & a, unsigned precision) {
SASSERT(!is_zero(a));
SASSERT(!is_nz_rational(a));
mpbqi const & i = interval(a.m_value);
if (refine_interval(a.m_value, precision*4)) {
if (bqm().is_int(i.lower()))
bqm().display_decimal(out, i.upper(), precision);
else
bqm().display_decimal(out, i.lower(), precision);
}
else {
if (sign(a.m_value) > 0)
out << "?";
else
out << "-?";
}
}
void display_decimal(std::ostream & out, numeral const & a, unsigned precision) const {
if (is_zero(a)) {
out << "0";
}
else if (is_nz_rational(a)) {
qm().display_decimal(out, to_mpq(a), precision);
}
else {
const_cast<imp*>(this)->display_non_rational_in_decimal(out, a, precision);
}
}
void display_interval(std::ostream & out, numeral const & a) const {
if (is_zero(a))
out << "[0, 0]";
else
display_interval(out, interval(a.m_value), false);
}
};
class save_interval_ctx {
manager::imp * m;
public:
save_interval_ctx(manager const * _this):m(_this->m_imp) { SASSERT (m); }
~save_interval_ctx() { m->restore_saved_intervals(); }
};
manager::manager(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) {
m_imp = alloc(imp, lim, m, p, a);
}
manager::~manager() {
dealloc(m_imp);
}
void manager::get_param_descrs(param_descrs & r) {
rcf_params::collect_param_descrs(r);
}
void manager::updt_params(params_ref const & p) {
m_imp->updt_params(p);
}
unsynch_mpq_manager & manager::qm() const {
return m_imp->m_qm;
}
void manager::del(numeral & a) {
m_imp->del(a);
}
void manager::mk_infinitesimal(char const * n, char const * pp_n, numeral & r) {
m_imp->mk_infinitesimal(n, pp_n, r);
}
void manager::mk_infinitesimal(numeral & r) {
m_imp->mk_infinitesimal(r);
}
void manager::mk_transcendental(char const * n, char const * pp_n, mk_interval & proc, numeral & r) {
m_imp->mk_transcendental(n, pp_n, proc, r);
}
void manager::mk_transcendental(mk_interval & proc, numeral & r) {
m_imp->mk_transcendental(proc, r);
}
void manager::mk_pi(numeral & r) {
m_imp->mk_pi(r);
}
void manager::mk_e(numeral & r) {
m_imp->mk_e(r);
}
void manager::isolate_roots(unsigned n, numeral const * as, numeral_vector & roots) {
save_interval_ctx ctx(this);
m_imp->isolate_roots(n, as, roots);
}
void manager::reset(numeral & a) {
m_imp->reset(a);
}
int manager::sign(numeral const & a) {
save_interval_ctx ctx(this);
return m_imp->sign(a);
}
bool manager::is_zero(numeral const & a) {
return sign(a) == 0;
}
bool manager::is_pos(numeral const & a) {
return sign(a) > 0;
}
bool manager::is_neg(numeral const & a) {
return sign(a) < 0;
}
bool manager::is_int(numeral const & a) {
return m_imp->is_int(a);
}
bool manager::depends_on_infinitesimals(numeral const & a) {
return m_imp->depends_on_infinitesimals(a);
}
void manager::set(numeral & a, int n) {
m_imp->set(a, n);
}
void manager::set(numeral & a, mpz const & n) {
m_imp->set(a, n);
}
void manager::set(numeral & a, mpq const & n) {
m_imp->set(a, n);
}
void manager::set(numeral & a, numeral const & n) {
m_imp->set(a, n);
}
void manager::swap(numeral & a, numeral & b) {
std::swap(a.m_value, b.m_value);
}
void manager::root(numeral const & a, unsigned k, numeral & b) {
save_interval_ctx ctx(this);
m_imp->root(a, k, b);
}
void manager::power(numeral const & a, unsigned k, numeral & b) {
save_interval_ctx ctx(this);
m_imp->power(a, k, b);
}
void manager::add(numeral const & a, numeral const & b, numeral & c) {
save_interval_ctx ctx(this);
m_imp->add(a, b, c);
}
void manager::add(numeral const & a, mpz const & b, numeral & c) {
scoped_numeral _b(*this);
set(_b, b);
add(a, _b, c);
}
void manager::sub(numeral const & a, numeral const & b, numeral & c) {
save_interval_ctx ctx(this);
m_imp->sub(a, b, c);
}
void manager::mul(numeral const & a, numeral const & b, numeral & c) {
save_interval_ctx ctx(this);
m_imp->mul(a, b, c);
}
void manager::neg(numeral & a) {
save_interval_ctx ctx(this);
m_imp->neg(a);
}
void manager::neg(numeral const & a, numeral & b) {
save_interval_ctx ctx(this);
m_imp->neg(a, b);
}
void manager::inv(numeral & a) {
save_interval_ctx ctx(this);
m_imp->inv(a);
}
void manager::inv(numeral const & a, numeral & b) {
save_interval_ctx ctx(this);
m_imp->inv(a, b);
}
void manager::div(numeral const & a, numeral const & b, numeral & c) {
save_interval_ctx ctx(this);
m_imp->div(a, b, c);
}
int manager::compare(numeral const & a, numeral const & b) {
save_interval_ctx ctx(this);
return m_imp->compare(a, b);
}
bool manager::eq(numeral const & a, numeral const & b) {
return compare(a, b) == 0;
}
bool manager::eq(numeral const & a, mpq const & b) {
scoped_numeral _b(*this);
set(_b, b);
return eq(a, _b);
}
bool manager::eq(numeral const & a, mpz const & b) {
scoped_numeral _b(*this);
set(_b, b);
return eq(a, _b);
}
bool manager::lt(numeral const & a, numeral const & b) {
return compare(a, b) < 0;
}
bool manager::lt(numeral const & a, mpq const & b) {
scoped_numeral _b(*this);
set(_b, b);
return lt(a, _b);
}
bool manager::lt(numeral const & a, mpz const & b) {
scoped_numeral _b(*this);
set(_b, b);
return lt(a, _b);
}
bool manager::gt(numeral const & a, mpq const & b) {
scoped_numeral _b(*this);
set(_b, b);
return gt(a, _b);
}
bool manager::gt(numeral const & a, mpz const & b) {
scoped_numeral _b(*this);
set(_b, b);
return gt(a, _b);
}
void manager::display(std::ostream & out, numeral const & a, bool compact, bool pp) const {
save_interval_ctx ctx(this);
m_imp->display(out, a, compact, pp);
}
void manager::display_decimal(std::ostream & out, numeral const & a, unsigned precision) const {
save_interval_ctx ctx(this);
m_imp->display_decimal(out, a, precision);
}
void manager::display_interval(std::ostream & out, numeral const & a) const {
save_interval_ctx ctx(this);
m_imp->display_interval(out, a);
}
void manager::clean_denominators(numeral const & a, numeral & p, numeral & q) {
save_interval_ctx ctx(this);
m_imp->clean_denominators(a, p, q);
}
};
void pp(realclosure::manager::imp * imp, realclosure::polynomial const & p, realclosure::extension * ext) {
imp->display_polynomial_expr(std::cout, p, ext, false, false);
std::cout << std::endl;
}
void pp(realclosure::manager::imp * imp, realclosure::value * v) {
imp->display(std::cout, v, false);
std::cout << std::endl;
}
void pp(realclosure::manager::imp * imp, unsigned sz, realclosure::value * const * p) {
for (unsigned i = 0; i < sz; i++)
pp(imp, p[i]);
}
void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref_buffer const & p) {
for (unsigned i = 0; i < p.size(); i++)
pp(imp, p[i]);
}
void pp(realclosure::manager::imp * imp, realclosure::manager::imp::value_ref const & v) {
pp(imp, v.get());
}
void pp(realclosure::manager::imp * imp, realclosure::mpbqi const & i) {
imp->bqim().display(std::cout, i);
std::cout << std::endl;
}
void pp(realclosure::manager::imp * imp, realclosure::manager::imp::scoped_mpqi const & i) {
imp->qim().display(std::cout, i);
std::cout << std::endl;
}
void pp(realclosure::manager::imp * imp, mpbq const & n) {
imp->bqm().display(std::cout, n);
std::cout << std::endl;
}
void pp(realclosure::manager::imp * imp, mpq const & n) {
imp->qm().display(std::cout, n);
std::cout << std::endl;
}
void pp(realclosure::manager::imp * imp, realclosure::extension * x) {
imp->display_ext(std::cout, x, false, false);
std::cout << std::endl;
}