3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 17:15:31 +00:00

Reorganizing source code. Created util dir

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2012-10-20 10:19:38 -07:00
parent 630ba0c675
commit 2c464d413d
153 changed files with 0 additions and 0 deletions

61
src/util/approx_nat.cpp Normal file
View file

@ -0,0 +1,61 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
approx_nat.cpp
Abstract:
Approximated natural numbers. It performs operations on the set [0, ..., 2^{n-2}, huge].
Where huge represents all numbers greater than 2^{n-2}.
Author:
Leonardo (leonardo) 2008-01-11
Notes:
--*/
#include"approx_nat.h"
approx_nat::approx_nat(unsigned val) {
m_value = val > m_limit ? UINT_MAX : val;
}
approx_nat & approx_nat::operator=(unsigned val) {
m_value = val > m_limit ? UINT_MAX : val;
return *this;
}
approx_nat & approx_nat::operator+=(unsigned w) {
if (is_huge())
return *this;
if (w > m_limit) {
m_value = UINT_MAX;
return *this;
}
m_value += w;
if (m_value > m_limit)
m_value = UINT_MAX;
return *this;
}
approx_nat & approx_nat::operator*=(unsigned w) {
if (is_huge())
return *this;
unsigned long long r = static_cast<unsigned long long>(m_value) * static_cast<unsigned long long>(w);
if (r > m_limit)
m_value = UINT_MAX;
else
m_value = static_cast<unsigned>(r);
return *this;
}
std::ostream & operator<<(std::ostream & target, approx_nat const & w) {
if (w.is_huge())
target << "[huge]";
else
target << w.get_value();
return target;
}

45
src/util/approx_nat.h Normal file
View file

@ -0,0 +1,45 @@
/*++
Copyright (c) 2007 Microsoft Corporation
Module Name:
approx_nat.h
Abstract:
Approximated natural numbers. It performs operations on the set [0, ..., 2^{n-2}, huge].
Where huge represents all numbers greater than 2^{n-2}.
Author:
Leonardo (leonardo) 2008-01-11
Notes:
--*/
#ifndef _APPROX_NAT_H_
#define _APPROX_NAT_H_
#include<iostream>
#include<limits.h>
class approx_nat {
unsigned m_value;
static const unsigned m_limit = UINT_MAX >> 2;
public:
approx_nat():m_value(0) {}
explicit approx_nat(unsigned val);
bool is_huge() const { return m_value == UINT_MAX; }
unsigned get_value() const { return m_value; }
approx_nat & operator=(unsigned w);
approx_nat & operator+=(unsigned w);
approx_nat & operator+=(approx_nat const & w) { return operator+=(w.m_value); }
approx_nat & operator*=(unsigned w);
approx_nat & operator*=(approx_nat const & w) { return operator*=(w.m_value); }
bool operator<(unsigned w) const { return !is_huge() && m_value < w; }
bool operator<(approx_nat const & w) const { return !is_huge() && !w.is_huge() && m_value < w.m_value; }
};
std::ostream & operator<<(std::ostream & target, approx_nat const & w);
#endif /* _APPROX_NAT_H_ */

52
src/util/approx_set.cpp Normal file
View file

@ -0,0 +1,52 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
approx_set.cpp
Abstract:
Approximated sets.
Author:
Leonardo de Moura (leonardo) 2007-03-02.
Revision History:
--*/
#include"approx_set.h"
void approx_set::display(std::ostream & out) const {
out << "{";
bool first = true;
unsigned long long s = m_set;
for (unsigned i = 0; i < approx_set_traits<unsigned long long>::capacity; i++) {
if ((s & 1) != 0) {
if (first) {
first = false;
}
else {
out << ", ";
}
out << i;
}
s = s >> 1;
}
out << "}";
}
unsigned approx_set::size() const {
unsigned long long tmp = m_set;
unsigned r = 0;
while (tmp > 0) {
if ((tmp & 1) != 0) {
r++;
}
tmp = tmp >> 1;
}
return r;
}

236
src/util/approx_set.h Normal file
View file

@ -0,0 +1,236 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
approx_set.h
Abstract:
Approximated sets.
Author:
Leonardo de Moura (leonardo) 2007-03-02.
Revision History:
--*/
#ifndef _APPROX_SET_H_
#define _APPROX_SET_H_
#include<iostream>
#include"debug.h"
template<typename T> class approx_set_traits;
template <> class approx_set_traits<unsigned long long> {
public:
static const unsigned capacity = 64;
static const unsigned long long zero = 0ull;
static const unsigned long long one = 1ull;
};
COMPILE_TIME_ASSERT(sizeof(unsigned long long) == 8);
template <> class approx_set_traits<unsigned> {
public:
static const unsigned capacity = 32;
static const unsigned zero = 0;
static const unsigned one = 1;
};
COMPILE_TIME_ASSERT(sizeof(unsigned) == 4);
template<typename T, typename T2U_Proc, typename R=unsigned long long>
class approx_set_tpl : private T2U_Proc {
protected:
R m_set;
unsigned e2u(T const & e) const { return T2U_Proc::operator()(e); }
R u2s(unsigned u) const { return (approx_set_traits<R>::one << (u & (approx_set_traits<R>::capacity - 1))); }
R e2s(T const & e) const { return u2s(e2u(e)); }
static approx_set_tpl r2s(R const & s) { approx_set_tpl r; r.m_set = s; return r; }
public:
approx_set_tpl():
m_set(approx_set_traits<R>::zero) {
}
explicit approx_set_tpl(T const & e):
m_set(e2s(e)) {
}
approx_set_tpl(unsigned sz, T const * es):
m_set(approx_set_traits<R>::zero) {
for (unsigned i = 0; i < sz; i++)
insert(es[i]);
}
approx_set_tpl(approx_set_tpl const & s):
m_set(s.m_set) {
}
void insert(T const & e) {
m_set |= e2s(e);
}
bool may_contain(T const & e) const {
return (m_set & e2s(e)) != approx_set_traits<R>::zero;
}
bool must_not_contain(T const & e) const {
return !may_contain(e);
}
friend inline approx_set_tpl mk_union(approx_set_tpl const & s1, approx_set_tpl const & s2) {
return r2s(s1.m_set | s2.m_set);
}
friend inline approx_set_tpl mk_intersection(approx_set_tpl const & s1, approx_set_tpl const & s2) {
return r2s(s1.m_set & s2.m_set);
}
void operator|=(approx_set_tpl const & other) {
m_set |= other.m_set;
}
void operator&=(approx_set_tpl const & other) {
m_set &= other.m_set;
}
void operator-=(approx_set_tpl const & other) {
m_set &= ~(other.m_set);
}
bool empty() const {
return m_set == approx_set_traits<R>::zero;
}
friend inline bool empty(approx_set_tpl const & s) {
return s.empty();
}
bool must_not_subset(approx_set_tpl const & s2) const {
return (m_set & ~(s2.m_set)) != approx_set_traits<R>::zero;
}
friend inline bool must_not_subset(approx_set_tpl const & s1, approx_set_tpl const & s2) {
return s1.must_not_subset(s2);
}
bool must_not_subsume(approx_set_tpl const & s2) const {
return must_not_subset(s2);
}
friend inline bool must_not_subsume(approx_set_tpl const & s1, approx_set_tpl const & s2) {
return s1.must_not_subset(s2);
}
friend inline bool must_not_eq(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.m_set != s2.m_set; }
friend inline bool may_eq(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.m_set == s2.m_set; }
/**
\brief Return if s1 and s2 are the same approximated set.
*/
bool equiv(approx_set_tpl const & s2) const { return m_set == s2.m_set; }
friend inline bool equiv(approx_set_tpl const & s1, approx_set_tpl const & s2) { return s1.m_set == s2.m_set; }
/**
\brief Return true if the approximation of s1 is a subset of the approximation of s2.
*/
friend inline bool approx_subset(approx_set_tpl const & s1, approx_set_tpl const & s2) {
return s2.equiv(mk_union(s1, s2));
}
void reset() {
m_set = approx_set_traits<R>::zero;
}
bool empty_intersection(approx_set_tpl const & other) const {
return mk_intersection(*this, other).empty();
}
};
struct u2u { unsigned operator()(unsigned u) const { return u; } };
typedef approx_set_tpl<unsigned, u2u> u_approx_set;
#define APPROX_SET_CAPACITY (approx_set_traits<unsigned long long>::capacity)
class approx_set : public u_approx_set {
public:
approx_set():u_approx_set() {}
approx_set(unsigned e):u_approx_set(e) {}
class iterator {
unsigned long long m_set;
unsigned m_val;
void move_to_next() {
// TODO: this code can be optimized in platforms with special
// instructions to count leading (trailing) zeros in a word.
while (m_set > 0) {
if ((m_set & 1ull) != 0) {
return;
}
m_val ++;
m_set = m_set >> 1;
}
}
public:
iterator(unsigned long long s):
m_set(s),
m_val(0) {
move_to_next();
}
unsigned operator*() const {
return m_val;
}
iterator & operator++() {
m_val++;
m_set = m_set >> 1;
move_to_next();
return *this;
}
iterator operator++(int) {
iterator tmp = *this;
++*this;
return tmp;
}
bool operator==(iterator const & it) const {
return m_set == it.m_set;
}
bool operator!=(iterator const & it) const {
return m_set != it.m_set;
}
};
iterator begin() const {
return iterator(m_set);
}
static iterator end() {
return iterator(0);
}
void display(std::ostream & out) const;
unsigned size() const;
// for backward compatibility
friend inline bool operator==(approx_set const & s1, approx_set const & s2) { return may_eq(s1, s2); }
};
inline std::ostream & operator<<(std::ostream & out, approx_set const & s) {
s.display(out);
return out;
}
#endif /* _APPROX_SET_H_ */

205
src/util/array.h Normal file
View file

@ -0,0 +1,205 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
array.h
Abstract:
Fixed size arrays
Author:
Leonardo de Moura (leonardo) 2011-01-26.
Revision History:
--*/
#ifndef __ARRAY_H_
#define __ARRAY_H_
template<typename T, bool CallDestructors=true>
class array {
public:
// Return the space needed to store an array of size sz.
static size_t space(size_t sz) { return sizeof(T)*sz + sizeof(size_t); }
private:
#define ARRAY_SIZE_IDX -1
T * m_data;
void destroy_elements() {
iterator it = begin();
iterator e = end();
for (; it != e; ++it) {
it->~T();
}
}
char * raw_ptr() const { return reinterpret_cast<char*>(reinterpret_cast<size_t*>(m_data) - 1); }
array & operator=(array const & source);
void set_data(void * mem, size_t sz) {
size_t * _mem = static_cast<size_t*>(mem);
*_mem = sz;
_mem ++;
m_data = reinterpret_cast<T*>(_mem);
}
template<typename Allocator>
void allocate(Allocator & a, size_t sz) {
size_t * mem = reinterpret_cast<size_t*>(a.allocate(space(sz)));
set_data(mem, sz);
}
void init() {
iterator it = begin();
iterator e = end();
for (; it != e; ++it) {
new (it) T();
}
}
void init(T const * vs) {
iterator it = begin();
iterator e = end();
for (; it != e; ++it, ++vs) {
new (it) T(*vs);
}
}
public:
typedef T data;
typedef T * iterator;
typedef const T * const_iterator;
array():m_data(0) {}
/**
\brief Store the array in the given chunk of memory (mem).
This chunck should be big enough to store space(sz) bytes.
*/
array(void * mem, size_t sz, T const * vs) {
DEBUG_CODE(m_data = 0;);
set(mem, sz, vs);
}
// WARNING: the memory allocated will not be automatically freed.
array(void * mem, size_t sz, bool init_mem) {
DEBUG_CODE(m_data = 0;);
set_data(mem, sz);
if (init_mem)
init();
}
// WARNING: the memory allocated will not be automatically freed.
template<typename Allocator>
array(Allocator & a, size_t sz, T const * vs) {
DEBUG_CODE(m_data = 0;);
set(a, sz, vs);
}
// WARNING: the memory allocated will not be automatically freed.
template<typename Allocator>
array(Allocator & a, size_t sz, bool init_mem) {
DEBUG_CODE(m_data = 0;);
allocate(a, sz);
if (init_mem)
init();
}
// WARNING: this does not free the memory used to store the array.
// You must free it yourself, or use finalize.
~array() {
if (m_data && CallDestructors)
destroy_elements();
}
// Free the memory used to store the array.
template<typename Allocator>
void finalize(Allocator & a) {
if (m_data) {
if (CallDestructors)
destroy_elements();
a.deallocate(size(), raw_ptr);
m_data = 0;
}
}
void set(void * mem, size_t sz, T const * vs) {
SASSERT(m_data == 0);
set_data(mem, sz);
init(vs);
}
template<typename Allocator>
void set(Allocator & a, size_t sz, T const * vs) {
SASSERT(m_data == 0);
allocate(a, sz);
init(sz, vs);
}
size_t size() const {
if (m_data == 0) {
return 0;
}
return reinterpret_cast<size_t *>(m_data)[SIZE_IDX];
}
bool empty() const { return m_data == 0; }
T & operator[](size_t idx) {
SASSERT(idx < size());
return m_data[idx];
}
T const & operator[](size_t idx) const {
SASSERT(idx < size());
return m_data[idx];
}
iterator begin() {
return m_data;
}
iterator end() {
return m_data + size();
}
const_iterator begin() const {
return m_data;
}
const_iterator end() const {
return m_data + size();
}
T * c_ptr() { return m_data; }
};
template<typename T>
class ptr_array : public array<T *, false> {
public:
ptr_array() {}
ptr_array(void * mem, size_t sz, T * const * vs):array<T*, false>(mem, sz, vs) {}
template<typename Allocator>
ptr_array(Allocator & a, size_t sz, T * const * vs):array<T*, false>(a, sz, vs) {}
ptr_array(void * mem, size_t sz, bool init_mem):array<T*, false>(mem, sz, init_mem) {}
template<typename Allocator>
ptr_array(Allocator & a, size_t sz, bool init_mem):array<T*, false>(a, sz, init_mem) {}
};
template<typename T>
class sarray : public array<T, false> {
public:
sarray() {}
sarray(void * mem, size_t sz, T const * vs):array<T, false>(mem, sz, vs) {}
template<typename Allocator>
sarray(Allocator & a, size_t sz, T const * vs):array<T, false>(a, sz, vs) {}
sarray(void * mem, size_t sz, bool init_mem):array<T, false>(mem, sz, init_mem) {}
template<typename Allocator>
sarray(Allocator & a, size_t sz, bool init_mem):array<T, false>(a, sz, init_mem) {}
};
#endif

162
src/util/array_map.h Normal file
View file

@ -0,0 +1,162 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
array_map.h
Abstract:
A mapping for keys that can be mapped to unsigned integers.
Author:
Leonardo de Moura (leonardo) 2008-01-03.
Revision History:
--*/
#ifndef _ARRAY_MAP_H_
#define _ARRAY_MAP_H_
#include"vector.h"
#include"optional.h"
/**
\brief Implements a mapping from Key to Data.
Plugin must provide the following functions:
- void ins_eh(Key const & k, Data const & d);
- void del_eh(Key const & k, Data const & d);
- unsigned to_int(Key const & k);
*/
template<typename Key, typename Data, typename Plugin, bool CallDestructors=true>
class array_map {
struct entry {
Key m_key;
Data m_data;
unsigned m_timestamp;
entry(Key const & k, Data const & d, unsigned t): m_key(k), m_data(d), m_timestamp(t) {}
};
unsigned m_timestamp;
unsigned m_garbage;
unsigned m_non_garbage;
static const unsigned m_gc_threshold = 10000;
vector<optional<entry>, CallDestructors > m_map;
Plugin m_plugin;
bool is_current(optional<entry> const& e) const {
return e->m_timestamp == m_timestamp;
}
optional<entry> const & get_core(Key const & k) const {
unsigned id = m_plugin.to_int(k);
if (id < m_map.size()) {
optional<entry> const & e = m_map[id];
if (e && is_current(e)) {
return e;
}
}
return optional<entry>::undef();
}
void really_flush() {
typename vector<optional<entry> >::iterator it = m_map.begin();
typename vector<optional<entry> >::iterator end = m_map.end();
for (; it != end; ++it) {
optional<entry> & e = *it;
if (e) {
m_plugin.del_eh(e->m_key, e->m_data);
e.set_invalid();
}
}
m_garbage = 0;
m_non_garbage = 0;
}
public:
array_map(Plugin const & p = Plugin()):m_timestamp(0), m_garbage(0), m_non_garbage(0), m_plugin(p) {}
~array_map() { really_flush(); }
bool contains(Key const & k) const {
return get_core(k);
}
Data const & get(Key const & k) const {
optional<entry> const & e = get_core(k);
SASSERT(e);
return e->m_data;
}
void reset() {
if (m_timestamp < UINT_MAX) {
m_timestamp++;
}
else {
really_flush();
m_timestamp = 0;
}
}
void insert(Key const & k, Data const & d) {
unsigned id = m_plugin.to_int(k);
if (id >= m_map.size()) {
m_map.resize(id + 1, optional<entry>::undef());
}
m_plugin.ins_eh(k, d);
optional<entry> & e = m_map[id];
if (e) {
if (!is_current(e)) {
--m_garbage;
++m_non_garbage;
}
m_plugin.del_eh(e->m_key, e->m_data);
}
else {
++m_non_garbage;
}
e = entry(k, d, m_timestamp);
}
void erase(Key const & k) {
unsigned id = m_plugin.to_int(k);
if (id < m_map.size()) {
optional<entry> & e = m_map[id];
if (e) {
m_plugin.del_eh(e->m_key, e->m_data);
if (is_current(e)) {
SASSERT(m_non_garbage > 0);
--m_non_garbage;
}
else {
SASSERT(m_garbage > 0);
--m_garbage;
}
e.set_invalid();
}
}
}
void flush() {
m_garbage += m_non_garbage;
m_non_garbage = 0;
if (m_garbage > m_gc_threshold) {
really_flush();
}
else {
reset();
}
}
void finalize() {
really_flush();
}
};
#endif /* _ARRAY_MAP_H_ */

View file

@ -0,0 +1,114 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
backtrackable_set.h
Abstract:
Quick hack for support backtrackable sets.
Author:
Leonardo de Moura (leonardo) 2011-01-08.
Revision History:
--*/
#ifndef _BACKTRACKABLE_SET_H_
#define _BACKTRACKABLE_SET_H_
#include"vector.h"
template<typename T>
struct default_eh {
void operator()(T const & e, bool ins) {}
};
// quick hack for having backtrackable sets.
//
// EV is a big hack, it should be used with care.
//
template<typename Set, typename T, typename EV=default_eh<T> >
class backtrackable_set : private EV {
enum trail_kind { DEL, INS };
typedef std::pair<trail_kind, T> trail_obj;
Set m_set;
svector<trail_obj> m_trail;
svector<unsigned> m_scopes;
public:
typedef typename Set::iterator iterator;
backtrackable_set(EV const & ev = EV()):
EV(ev) {
}
void insert(T const & e) {
if (m_scopes.empty()) {
m_set.insert(e);
}
else if (!m_set.contains(e)) {
m_set.insert(e);
m_trail.push_back(std::make_pair(INS, e));
}
}
void erase(T const & e) {
if (m_scopes.empty()) {
m_set.insert(e);
}
else if (m_set.contains(e)) {
m_set.erase(e);
m_trail.push_back(std::make_pair(DEL, e));
}
}
bool contains(T const & e) const {
return m_set.contains(e);
}
bool empty() const {
return m_set.empty();
}
void push_scope() {
m_scopes.push_back(m_trail.size());
}
void pop_scope() {
unsigned old_sz = m_scopes.back();
m_scopes.pop_back();
SASSERT(old_sz <= m_trail.size());
while (m_trail.size() > old_sz) {
trail_obj & t = m_trail.back();
if (t.first == INS) {
this->operator()(t.second, true);
m_set.erase(t.second);
}
else {
SASSERT(t.first == DEL);
this->operator()(t.second, false);
m_set.insert(t.second);
}
m_trail.pop_back();
}
}
void reset() {
m_scopes.reset();
m_trail.reset();
m_set.reset();
}
iterator begin() const {
return m_set.begin();
}
iterator end() const {
return m_set.end();
}
};
#endif

363
src/util/basic_interval.h Normal file
View file

@ -0,0 +1,363 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
basic_interval.h
Abstract:
Basic interval arithmetic template for precise numerals: mpz, mpq, mpbq.
Only basic support is provided.
There is no support for:
- minus and plus infinity bounds.
- mixed open/closed intervals such as (2, 3]
The main customer of this package is the algebraic_number module.
Author:
Leonardo de Moura (leonardo) 2012-12-04.
Revision History:
--*/
#ifndef _BASIC_INTERVAL_H_
#define _BASIC_INTERVAL_H_
template<typename numeral_manager, bool closed>
class basic_interval_manager {
public:
typedef typename numeral_manager::numeral bound;
class interval {
friend class basic_interval_manager;
bound m_lower;
bound m_upper;
public:
interval() {}
bound const & lower() const { return m_lower; }
bound const & upper() const { return m_upper; }
bound & lower() { return m_lower; }
bound & upper() { return m_upper; }
};
class scoped_interval {
basic_interval_manager & m_manager;
interval m_interval;
public:
scoped_interval(basic_interval_manager & m):m_manager(m) {}
~scoped_interval() { m_manager.del(m_interval); }
basic_interval_manager & m() const { return m_manager; }
operator interval const &() const { return m_interval; }
operator interval&() { return m_interval; }
interval const & get() const { return m_interval; }
interval & get() { return m_interval; }
void reset() { m().reset(m_interval); }
void swap(scoped_interval & a) { m().swap(m_interval, a.m_interval); }
void swap(interval & a) { m().swap(m_interval, a); }
bound const & lower() const { return m_interval.lower(); }
bound const & upper() const { return m_interval.upper(); }
bound & lower() { return m_interval.lower(); }
bound & upper() { return m_interval.upper(); }
friend std::ostream & operator<<(std::ostream & out, scoped_interval const & a) {
a.m().display(out, a.get());
return out;
}
};
protected:
numeral_manager & m_manager;
bound m_mul_curr;
bound m_mul_max;
bound m_mul_min;
public:
typedef interval numeral; // allow intervals to be used by algorithms parameterized by numeral_manager
basic_interval_manager(numeral_manager & m):
m_manager(m) {
}
~basic_interval_manager() {
m().del(m_mul_curr);
m().del(m_mul_max);
m().del(m_mul_min);
}
numeral_manager & m() const { return m_manager; }
/**
\brief Delete interval
*/
void del(interval & a) {
m().del(a.m_lower);
m().del(a.m_upper);
}
/**
\brief Delete and reset lower and upper bounds to 0
*/
void reset(interval & a) {
m().reset(a.m_lower);
m().reset(a.m_upper);
}
bound const & lower(interval const & a) {
return a.lower();
}
bound const & upper(interval const & a) {
return a.upper();
}
/**
\brief a <- (lower, upper)
*/
void set(interval & a, bound const & lower, bound const & upper) {
SASSERT(m().le(lower, upper));
m().set(a.m_lower, lower);
m().set(a.m_upper, upper);
}
/**
\brief a <- b
*/
void set(interval & a, interval const & b) {
set(a, b.m_lower, b.m_upper);
}
/**
\brief a <- (n, n)
Manager must be configured for closed intervals.
*/
void set(interval & a, bound const & n) {
m().set(a.m_lower, n);
m().set(a.m_upper, n);
}
void set_lower(interval & a, bound const & n) {
SASSERT(m().le(n, a.m_upper));
m().set(a.m_lower, n);
}
void set_upper(interval & a, bound const & n) {
SASSERT(m().le(a.m_lower, n));
m().set(a.m_upper, n);
}
void swap(interval & a, interval & b) {
m().swap(a.m_lower, b.m_lower);
m().swap(a.m_upper, b.m_upper);
}
/**
\brief a <- -a
*/
void neg(interval & a) {
m().neg(a.m_lower);
m().neg(a.m_upper);
m().swap(a.m_lower, a.m_upper);
}
/**
\brief Return true if a does not contain any value. We can
only have empty intervals if the manager is configured to used
open intervals.
*/
bool is_empty(interval const & a) {
return !closed && m().eq(a.m_lower, a.m_upper);
}
/**
\brief Return true if all values in the given interval are positive.
*/
bool is_pos(interval const & a) { return (closed && m().is_pos(a.m_lower)) || (!closed && m().is_nonneg(a.m_lower)); }
/**
\brief Return true if all values in the given interval are negative.
*/
bool is_neg(interval const & a) { return (closed && m().is_neg(a.m_upper)) || (!closed && m().is_nonpos(a.m_upper)); }
/**
\brief Return true if 0 is in the interval.
*/
bool contains_zero(interval const & a) {
return
(closed && m().is_nonpos(a.m_lower) && m().is_nonneg(a.m_upper)) ||
(!closed && m().is_neg(a.m_lower) && m().is_pos(a.m_upper));
}
/**
\brief Return true if all values in interval a are in interval b.
*/
bool is_subset(interval const & a, interval const & b) {
return m().le(b.m_lower, a.m_lower) && m().le(a.m_upper, b.m_upper);
}
/**
\brief Return true if there is no value v s.t. v \in a and v \in b.
*/
bool disjoint(interval const & a, interval const & b) {
return
(closed && (m().lt(a.m_upper, b.m_lower) || m().lt(b.m_upper, a.m_lower))) ||
(!closed && (m().le(a.m_upper, b.m_upper) || m().le(b.m_upper, a.m_lower)));
}
/**
\brief Return true if all elements in a are smaller than all elements in b.
*/
bool precedes(interval const & a, interval const & b) {
return
(closed && (m().lt(a.m_upper, b.m_lower))) ||
(!closed && (m().le(a.m_upper, b.m_lower)));
}
/**
\brief Return true if all elements in a are smaller than b.
*/
bool precedes(interval const & a, bound const & b) {
return
(closed && (m().lt(a.m_upper, b))) ||
(!closed && (m().le(a.m_upper, b)));
}
/**
\brief Return true if a is smaller than all elements in b.
*/
bool precedes(bound const & a, interval const & b) {
return
(closed && (m().lt(a, b.m_lower))) ||
(!closed && (m().le(a, b.m_lower)));
}
/**
\brief a <- 1/a
\pre a.m_lower and m_upper must not be 0.
\pre bound must be a field.
*/
void inv(interval & a) {
SASSERT(numeral_manager::field());
SASSERT(!contains_zero(a));
SASSERT(!m().is_zero(a.m_lower) && !m().is_zero(a.m_upper));
m().inv(a.m_lower);
m().inv(a.m_upper);
m().swap(a.m_lower, a.m_upper);
}
/**
\brief c <- a + b
*/
void add(interval const & a, interval const & b, interval & c) {
m().add(a.m_lower, b.m_lower, c.m_lower);
m().add(a.m_upper, b.m_upper, c.m_upper);
}
/**
\brief c <- a - b
*/
void sub(interval const & a, interval const & b, interval & c) {
m().sub(a.m_lower, b.m_lower, c.m_lower);
m().sub(a.m_upper, b.m_upper, c.m_upper);
}
private:
/**
\brief Init the value of m_mul_max and m_mul_min using m_mul_curr
*/
void init_mul_max_min() {
m().set(m_mul_min, m_mul_curr);
m().swap(m_mul_max, m_mul_curr);
}
/**
\brief Update the value of m_mul_max and m_mul_min using m_mul_curr
*/
void update_mul_max_min() {
if (m().lt(m_mul_curr, m_mul_min))
m().set(m_mul_min, m_mul_curr);
if (m().gt(m_mul_curr, m_mul_max))
m().swap(m_mul_max, m_mul_curr);
}
public:
/**
\brief c <- a * b
*/
void mul(interval const & a, interval const & b, interval & c) {
m().mul(a.m_lower, b.m_lower, m_mul_curr);
init_mul_max_min();
m().mul(a.m_lower, b.m_upper, m_mul_curr);
update_mul_max_min();
m().mul(a.m_upper, b.m_lower, m_mul_curr);
update_mul_max_min();
m().mul(a.m_upper, b.m_upper, m_mul_curr);
update_mul_max_min();
m().swap(c.m_lower, m_mul_min);
m().swap(c.m_upper, m_mul_max);
}
/**
\brief c <- a/b
\pre b m_lower and m_upper must not be 0
\pre bound must be a field.
*/
void div(interval const & a, interval const & b, interval & c) {
SASSERT(numeral_manager::field());
SASSERT(!contains_zero(b));
SASSERT(!m().is_zero(b.m_lower) && !m().is_zero(b.m_upper));
m().div(a.m_lower, b.m_lower, m_mul_curr);
init_mul_max_min();
m().div(a.m_lower, b.m_upper, m_mul_curr);
update_mul_max_min();
m().div(a.m_upper, b.m_lower, m_mul_curr);
update_mul_max_min();
m().div(a.m_upper, b.m_upper, m_mul_curr);
update_mul_max_min();
m().swap(c.m_lower, m_mul_min);
m().swap(c.m_upper, m_mul_max);
}
/**
\brief c <- a^n
*/
void power(interval const & a, unsigned n, interval & c) {
// Let a be of the form (l, u)
if (n % 2 == 1) {
// n is odd
// c <- (l^n, u^n)
m().power(a.m_lower, n, c.m_lower);
m().power(a.m_upper, n, c.m_upper);
}
else {
SASSERT(n % 2 == 0);
m().power(a.m_lower, n, c.m_lower);
m().power(a.m_upper, n, c.m_upper);
if (m().is_nonneg(a.m_lower)) {
// n is even and l >= 0
// c <- (l^n, u^n)
return;
}
if (m().is_neg(a.m_upper)) {
// n is even and u < 0
// c <- (u^n, l^n)
m().swap(c.m_lower, c.m_upper);
return;
}
// c <- (0, max(l^n, u^n))
if (m().gt(c.m_lower, c.m_upper))
m().swap(c.m_lower, c.m_upper);
m().reset(c.m_lower);
}
}
void display(std::ostream & out, interval const & a) {
out << (closed ? "[" : "(") << m().to_string(a.m_lower) << ", " << m().to_string(a.m_upper) << (closed ? "]" : ")");
}
};
#endif

389
src/util/bit_util.cpp Normal file
View file

@ -0,0 +1,389 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
bit_util.cpp
Abstract:
Bit hacking utilities.
Author:
Leonardo de Moura (leonardo) 2012-09-11.
Revision History:
--*/
#include"bit_util.h"
#include"util.h"
#include"debug.h"
/**
\brief (Debugging version) Return the position of the most significant (set) bit of a
nonzero unsigned integer.
*/
#ifdef Z3DEBUG
unsigned slow_msb_pos(unsigned v) {
SASSERT(v != 0);
unsigned r = 0;
while (v != 1) {
v = v >> 1;
r++;
}
return r;
}
#endif
/**
\brief Return the position of the most significant (set) bit of a
nonzero unsigned integer.
*/
unsigned msb_pos(unsigned v) {
SASSERT(v != 0);
#ifdef Z3DEBUG
unsigned expected = slow_msb_pos(v);
#endif
unsigned r, shift;
r = (v > 0xFFFF) << 4;
v >>= r;
shift = (v > 0xFF) << 3;
v >>= shift;
r |= shift;
shift = (v > 0xF) << 2;
v >>= shift;
r |= shift;
shift = (v > 0x3) << 1;
v >>= shift;
r |= shift;
r |= (v >> 1);
SASSERT(r == expected);
return r;
}
/**
\brief Return the number of leading zeros bits in a nonzero unsigned integer.
*/
unsigned nlz_core(unsigned x) {
SASSERT(x != 0);
return 31 - msb_pos(x);
}
/**
\brief Return the number of leading zero bits in data (a number of sz words).
*/
unsigned nlz(unsigned sz, unsigned const * data) {
unsigned r = 0;
unsigned i = sz;
while (i > 0) {
--i;
unsigned d = data[i];
if (d == 0)
r += 32;
else
return r + nlz_core(d);
}
return r;
}
/**
\brief Return the number of trailing zeros in a nonzero unsigned number.
*/
unsigned ntz_core(unsigned x) {
SASSERT(x != 0);
float f = static_cast<float>(x & static_cast<unsigned>(-static_cast<int>(x)));
return (*reinterpret_cast<unsigned *>(&f) >> 23) - 0x7f;
}
/**
\brief Return the number of trailing zero bits in data (a number of sz words).
*/
unsigned ntz(unsigned sz, unsigned const * data) {
unsigned r = 0;
for (unsigned i = 0; i < sz; i++) {
unsigned d = data[i];
if (d == 0)
r += 32;
else
return r + ntz_core(d);
}
return r;
}
/**
\brief dst <- src
Trucate if src_sz > dst_sz.
Fill range [src_sz, dst_sz) of dst with zeros if dst_sz > src_sz.
*/
void copy(unsigned src_sz, unsigned const * src,
unsigned dst_sz, unsigned * dst) {
if (dst_sz >= src_sz) {
unsigned i;
for (i = 0; i < src_sz; i++)
dst[i] = src[i];
for (; i < dst_sz; i++)
dst[i] = 0;
}
else {
SASSERT(dst_sz < src_sz);
for (unsigned i = 0; i < dst_sz; i++)
dst[i] = src[i];
}
}
/**
\brief Return true if all words of data are zero.
*/
bool is_zero(unsigned sz, unsigned const * data) {
for (unsigned i = 0; i < sz; i++)
if (data[i])
return false;
return true;
}
/**
\brief Set all words of data to zero.
*/
void reset(unsigned sz, unsigned * data) {
for (unsigned i = 0; i < sz; i++)
data[i] = 0;
}
/**
\brief dst <- src << k
Store in dst the result of shifting src k bits to the left.
The result is truncated by dst_sz.
\pre src_sz != 0
\pre dst_sz != 0
*/
void shl(unsigned src_sz, unsigned const * src, unsigned k,
unsigned dst_sz, unsigned * dst) {
SASSERT(src_sz != 0);
SASSERT(dst_sz != 0);
SASSERT(k != 0);
unsigned word_shift = k / (8 * sizeof(unsigned));
unsigned bit_shift = k % (8 * sizeof(unsigned));
if (word_shift > 0) {
unsigned j = src_sz;
unsigned i = src_sz + word_shift;
if (i > dst_sz) {
if (j >= i - dst_sz)
j -= (i - dst_sz);
else
j = 0;
i = dst_sz;
}
else if (i < dst_sz) {
for (unsigned r = i; r < dst_sz; r++)
dst[r] = 0;
}
while (j > 0) {
--j; --i;
dst[i] = src[j];
}
while (i > 0) {
--i;
dst[i] = 0;
}
if (bit_shift > 0) {
unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift;
unsigned prev = 0;
for (unsigned i = word_shift; i < dst_sz; i++) {
unsigned new_prev = (dst[i] >> comp_shift);
dst[i] <<= bit_shift;
dst[i] |= prev;
prev = new_prev;
}
}
}
else {
unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift;
unsigned prev = 0;
if (src_sz > dst_sz)
src_sz = dst_sz;
for (unsigned i = 0; i < src_sz; i++) {
unsigned new_prev = (src[i] >> comp_shift);
dst[i] = src[i];
dst[i] <<= bit_shift;
dst[i] |= prev;
prev = new_prev;
}
if (dst_sz > src_sz) {
dst[src_sz] = prev;
for (unsigned i = src_sz+1; i < dst_sz; i++)
dst[i] = 0;
}
}
}
/**
\brief dst <- src >> k
Store in dst the result of shifting src k bits to the right.
\pre dst must have size sz.
\pre src_sz != 0
\pre dst_sz != 0
*/
void shr(unsigned sz, unsigned const * src, unsigned k, unsigned * dst) {
unsigned digit_shift = k / (8 * sizeof(unsigned));
if (digit_shift >= sz) {
reset(sz, dst);
return;
}
unsigned bit_shift = k % (8 * sizeof(unsigned));
unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift;
unsigned new_sz = sz - digit_shift;
if (new_sz < sz) {
unsigned i = 0;
unsigned j = digit_shift;
if (bit_shift != 0) {
for (; i < new_sz - 1; i++, j++) {
dst[i] = src[j];
dst[i] >>= bit_shift;
dst[i] |= (src[j+1] << comp_shift);
}
dst[i] = src[j];
dst[i] >>= bit_shift;
}
else {
for (; i < new_sz; i++, j++) {
dst[i] = src[j];
}
}
for (unsigned i = new_sz; i < sz; i++)
dst[i] = 0;
}
else {
SASSERT(new_sz == sz);
SASSERT(bit_shift != 0);
unsigned i = 0;
for (; i < new_sz - 1; i++) {
dst[i] = src[i];
dst[i] >>= bit_shift;
dst[i] |= (src[i+1] << comp_shift);
}
dst[i] = src[i];
dst[i] >>= bit_shift;
}
}
void shr(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst) {
unsigned digit_shift = k / (8 * sizeof(unsigned));
if (digit_shift >= src_sz) {
reset(dst_sz, dst);
return;
}
unsigned bit_shift = k % (8 * sizeof(unsigned));
unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift;
unsigned new_sz = src_sz - digit_shift;
if (digit_shift > 0) {
unsigned i = 0;
unsigned j = digit_shift;
if (bit_shift != 0) {
unsigned sz = new_sz;
if (new_sz > dst_sz)
sz = dst_sz;
for (; i < sz - 1; i++, j++) {
dst[i] = src[j];
dst[i] >>= bit_shift;
dst[i] |= (src[j+1] << comp_shift);
}
dst[i] = src[j];
dst[i] >>= bit_shift;
if (new_sz > dst_sz)
dst[i] |= (src[j+1] << comp_shift);
}
else {
if (new_sz > dst_sz)
new_sz = dst_sz;
for (; i < new_sz; i++, j++) {
dst[i] = src[j];
}
}
}
else {
SASSERT(new_sz == src_sz);
SASSERT(bit_shift != 0);
unsigned sz = new_sz;
if (new_sz > dst_sz)
sz = dst_sz;
unsigned i = 0;
for (; i < sz - 1; i++) {
dst[i] = src[i];
dst[i] >>= bit_shift;
dst[i] |= (src[i+1] << comp_shift);
}
dst[i] = src[i];
dst[i] >>= bit_shift;
if (new_sz > dst_sz)
dst[i] |= (src[i+1] << comp_shift);
}
for (unsigned i = new_sz; i < dst_sz; i++)
dst[i] = 0;
}
/**
\brief Return true if one of the first k bits of src is not zero.
*/
bool has_one_at_first_k_bits(unsigned sz, unsigned const * data, unsigned k) {
SASSERT(sz != 0);
unsigned word_sz = k / (8 * sizeof(unsigned));
if (word_sz > sz)
word_sz = sz;
for (unsigned i = 0; i < word_sz; i++) {
if (data[i] != 0)
return true;
}
if (word_sz < sz) {
unsigned bit_sz = k % (8 * sizeof(unsigned));
unsigned mask = (1 << bit_sz) - 1;
return (data[word_sz] & mask) != 0;
}
return false;
}
bool inc(unsigned sz, unsigned * data) {
for (unsigned i = 0; i < sz; i++) {
data[i]++;
if (data[i] != 0)
return true; // no overflow
}
return false; // overflow
}
bool dec(unsigned sz, unsigned * data) {
for (unsigned i = 0; i < sz; i++) {
data[i]--;
if (data[i] != UINT_MAX)
return true; // no underflow
}
return false; // underflow
}
bool lt(unsigned sz, unsigned * data1, unsigned * data2) {
unsigned i = sz;
while (i > 0) {
--i;
if (data1[i] < data2[i])
return true;
if (data1[i] > data2[i])
return false;
}
return false;
}
bool add(unsigned sz, unsigned const * a, unsigned const * b, unsigned * c) {
unsigned k = 0;
for (unsigned j = 0; j < sz; j++) {
unsigned r = a[j] + b[j];
bool c1 = r < a[j];
c[j] = r + k;
bool c2 = c[j] < r;
k = c1 | c2;
}
return k == 0;
}

132
src/util/bit_util.h Normal file
View file

@ -0,0 +1,132 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
bit_util.h
Abstract:
Bit hacking utilities.
Author:
Leonardo de Moura (leonardo) 2012-09-11.
Revision History:
--*/
#ifndef _BIT_UTIL_H_
#define _BIT_UTIL_H_
/**
\brief Return the position of the most significant (set) bit of a
nonzero unsigned integer.
*/
unsigned msb_pos(unsigned v);
/**
\brief Return the number of leading zeros bits in a nonzero unsigned integer.
*/
unsigned nlz_core(unsigned x);
/**
\brief Return the number of leading zero bits in data (a number of sz words).
*/
unsigned nlz(unsigned sz, unsigned const * data);
/**
\brief Return the number of trailing zeros in a nonzero unsigned number.
*/
unsigned ntz_core(unsigned x);
/**
\brief Return the number of trailing zero bits in data (a number of sz words).
*/
unsigned ntz(unsigned sz, unsigned const * data);
/**
\brief dst <- src
Trucate if src_sz > dst_sz.
Fill range [src_sz, dst_sz) of dst with zeros if dst_sz > src_sz.
*/
void copy(unsigned src_sz, unsigned const * src, unsigned dst_sz, unsigned * dst);
/**
\brief Return true if all words of data are zero.
*/
bool is_zero(unsigned sz, unsigned const * data);
/**
\brief Set all words of data to zero.
*/
void reset(unsigned sz, unsigned * data);
/**
\brief dst <- src << k
Store in dst the result of shifting src k bits to the left.
The result is truncated by dst_sz.
\pre src_sz != 0
\pre dst_sz != 0
*/
void shl(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst);
/**
\brief dst <- src >> k
Store in dst the result of shifting src k bits to the right.
\pre dst must have size sz.
\pre src_sz != 0
\pre dst_sz != 0
*/
void shr(unsigned sz, unsigned const * src, unsigned k, unsigned * dst);
/**
\brief dst <- src >> k
Store in dst the result of shifting src k bits to the right.
Trucate if src_sz > dst_sz.
Fill range [src_sz, dst_sz) of dst with zeros if dst_sz > src_sz.
\pre src_sz != 0
\pre dst_sz != 0
*/
void shr(unsigned src_sz, unsigned const * src, unsigned k, unsigned dst_sz, unsigned * dst);
/**
\brief Return true if one of the first k bits of src is not zero.
*/
bool has_one_at_first_k_bits(unsigned sz, unsigned const * data, unsigned k);
/**
\brief data <- data + 1
Return true if no overflow occurred.
*/
bool inc(unsigned sz, unsigned * data);
/**
\brief data <- data - 1
Return true if no underflow occurred.
*/
bool dec(unsigned sz, unsigned * data);
/**
\brief Return true if data1 < data2.
Both must have the same size.
*/
bool lt(unsigned sz, unsigned * data1, unsigned * data2);
/**
\brief Store in c the a+b. This procedure assumes that a,b,c are vectors of size sz.
Return false if a+b overflows.
*/
bool add(unsigned sz, unsigned const * a, unsigned const * b, unsigned * c);
#endif

220
src/util/bit_vector.cpp Normal file
View file

@ -0,0 +1,220 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bitvector.cpp
Abstract:
Simple bitvector implementation
Author:
Leonardo de Moura (leonardo) 2006-10-03.
Revision History:
--*/
#include"bit_vector.h"
#include"trace.h"
#define DEFAULT_CAPACITY 2
void bit_vector::expand_to(unsigned new_capacity) {
unsigned * new_data = alloc_svect(unsigned, new_capacity);
memset(new_data, 0, new_capacity * sizeof(unsigned));
if (m_capacity > 0) {
memcpy(new_data, m_data, m_capacity * sizeof(unsigned));
dealloc_svect(m_data);
}
m_data = new_data;
m_capacity = new_capacity;
}
void bit_vector::resize(unsigned new_size, bool val) {
if (new_size <= m_num_bits) {
m_num_bits = new_size;
return;
}
TRACE("bit_vector", tout << "expanding: " << new_size << " capacity: " << m_capacity << " num words: "
<< num_words(new_size) << "\n";);
if (num_words(new_size) > m_capacity) {
expand_to((num_words(new_size) * 3 + 1) >> 1);
}
unsigned bwidx = m_num_bits/32;
unsigned ewidx = num_words(new_size);
unsigned * begin = m_data + bwidx;
unsigned pos = m_num_bits % 32;
unsigned mask = (1 << pos) - 1;
int cval;
if (val) {
*begin |= ~mask;
cval = ~0;
}
else {
*begin &= mask;
cval = 0;
}
TRACE("bit_vector",
tout << "num_bits: " << m_num_bits << "\n";
tout << "bwidx: " << bwidx << "\n";
tout << "ewidx: " << ewidx << "\n";
tout << "pos: " << pos << "\n";
tout << "mask: " << std::hex << mask << "\n" << std::dec;
tout << "cval: " << cval << "\n";);
if (bwidx < ewidx) {
memset(begin + 1, cval, (ewidx - bwidx - 1) * sizeof(unsigned));
}
m_num_bits = new_size;
}
void bit_vector::shift_right(unsigned k) {
if (k == 0)
return;
unsigned new_num_bits = m_num_bits + k;
unsigned old_num_words = num_words(m_num_bits);
unsigned new_num_words = num_words(new_num_bits);
resize(m_num_bits + k, false);
unsigned bit_shift = k % (8 * sizeof(unsigned));
unsigned word_shift = k / (8 * sizeof(unsigned));
if (word_shift > 0) {
unsigned j = old_num_words;
unsigned i = old_num_words + word_shift;
while (j > 0) {
--j; --i;
m_data[i] = m_data[j];
}
while (i > 0) {
--i;
m_data[i] = 0;
}
}
if (bit_shift > 0) {
DEBUG_CODE({
for (unsigned i = 0; i < word_shift; i++) {
SASSERT(m_data[i] == 0);
}
});
unsigned comp_shift = (8 * sizeof(unsigned)) - bit_shift;
unsigned prev = 0;
for (unsigned i = word_shift; i < new_num_words; i++) {
unsigned new_prev = (m_data[i] >> comp_shift);
m_data[i] <<= bit_shift;
m_data[i] |= prev;
prev = new_prev;
}
}
}
bool bit_vector::operator==(bit_vector const & source) {
if (m_num_bits != source.m_num_bits)
return false;
unsigned n = num_words();
if (n == 0)
return true;
unsigned i;
for (i = 0; i < n - 1; i++) {
if (m_data[i] != source.m_data[i])
return false;
}
unsigned bit_rest = source.m_num_bits % 32;
unsigned mask = (1 << bit_rest) - 1;
return (m_data[i] & mask) == (source.m_data[i] & mask);
}
bit_vector & bit_vector::operator|=(bit_vector const & source) {
if (size() < source.size())
resize(source.size(), false);
unsigned n1 = num_words();
unsigned n2 = source.num_words();
SASSERT(n2 <= n1);
unsigned bit_rest = source.m_num_bits % 32;
if (bit_rest == 0) {
unsigned i = 0;
for (i = 0; i < n2; i++)
m_data[i] |= source.m_data[i];
}
else {
unsigned i = 0;
for (i = 0; i < n2 - 1; i++)
m_data[i] |= source.m_data[i];
unsigned mask = (1 << bit_rest) - 1;
m_data[i] |= source.m_data[i] & mask;
}
return *this;
}
bit_vector & bit_vector::operator&=(bit_vector const & source) {
unsigned n1 = num_words();
unsigned n2 = source.num_words();
if (n1 == 0)
return *this;
if (n2 > n1) {
for (unsigned i = 0; i < n1; i++)
m_data[i] &= source.m_data[i];
}
else {
SASSERT(n2 <= n1);
unsigned bit_rest = source.m_num_bits % 32;
unsigned i = 0;
if (bit_rest == 0) {
for (i = 0; i < n2; i++)
m_data[i] &= source.m_data[i];
}
else {
for (i = 0; i < n2 - 1; i++)
m_data[i] &= source.m_data[i];
unsigned mask = (1 << bit_rest) - 1;
m_data[i] &= (source.m_data[i] & mask);
}
for (i = n2; i < n1; i++)
m_data[i] = 0;
}
return *this;
}
void bit_vector::display(std::ostream & out) const {
#if 1
unsigned i = m_num_bits;
while (i > 0) {
--i;
if (get(i))
out << "1";
else
out << "0";
}
#else
for (unsigned i = 0; i < m_num_bits; i++) {
if (get(i))
out << "1";
else
out << "0";
if ((i + 1) % 32 == 0) out << "\n";
}
#endif
}
void fr_bit_vector::reset() {
unsigned sz = size();
vector<unsigned>::const_iterator it = m_one_idxs.begin();
vector<unsigned>::const_iterator end = m_one_idxs.end();
for (; it != end; ++it) {
unsigned idx = *it;
if (idx < sz)
unset(idx);
}
m_one_idxs.reset();
}

234
src/util/bit_vector.h Normal file
View file

@ -0,0 +1,234 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
bit_vector.h
Abstract:
Simple bitvector implementation.
Author:
Leonardo de Moura (leonardo) 2006-10-03.
Revision History:
--*/
#ifndef _BIT_VECTOR_H_
#define _BIT_VECTOR_H_
#include<string.h>
#include"debug.h"
#include"vector.h"
#include"memory_manager.h"
COMPILE_TIME_ASSERT(sizeof(unsigned) == 4);
#define BV_DEFAULT_CAPACITY 2
class bit_vector {
unsigned m_num_bits;
unsigned m_capacity; //!< in words
unsigned * m_data;
static unsigned get_pos_mask(unsigned bit_idx) {
return 1 << (bit_idx % 32);
}
static unsigned num_words(unsigned num_bits) {
return (num_bits % 32) == 0 ? (num_bits / 32) : ((num_bits / 32) + 1);
}
void expand_to(unsigned new_capacity);
void expand() {
expand_to(m_capacity == 0 ? BV_DEFAULT_CAPACITY : ((m_capacity * 3 + 1) >> 1));
}
unsigned get_bit_word(unsigned bit_idx) const {
SASSERT(bit_idx < size());
return m_data[bit_idx / 32];
}
unsigned & get_bit_word(unsigned bit_idx) {
SASSERT(bit_idx < size());
return m_data[bit_idx / 32];
}
public:
bit_vector():
m_num_bits(0),
m_capacity(0),
m_data(0) {
}
bit_vector(bit_vector const & source):
m_num_bits(source.m_num_bits),
m_capacity(source.m_capacity),
m_data(alloc_svect(unsigned, source.m_capacity)) {
memcpy(m_data, source.m_data, source.m_capacity * sizeof(unsigned));
}
bit_vector(unsigned const * source, int num_bits):
m_num_bits(num_bits),
m_capacity(num_words(num_bits)),
m_data(alloc_svect(unsigned, m_capacity)) {
memcpy(m_data, source, m_capacity * sizeof(unsigned));
}
~bit_vector() {
if (m_data) {
dealloc_svect(m_data);
}
}
void reset() {
memset(m_data, 0, m_capacity * sizeof(unsigned));
m_num_bits = 0;
}
void swap(bit_vector & other) {
std::swap(m_data, other.m_data);
std::swap(m_num_bits, other.m_num_bits);
std::swap(m_capacity, other.m_capacity);
}
// Increase the size of the bit_vector by k 0-bits.
void shift_right(unsigned k);
void fill0() {
memset(m_data, 0, m_capacity * sizeof(unsigned));
}
unsigned size() const {
return m_num_bits;
}
bool empty() const {
return m_num_bits != 0;
}
unsigned num_words() const {
return num_words(m_num_bits);
}
unsigned get_word(unsigned word_idx) const {
return m_data[word_idx];
}
bool get(unsigned bit_idx) const {
SASSERT(bit_idx < size());
bool r = (get_bit_word(bit_idx) & get_pos_mask(bit_idx)) != 0;
return r;
}
void set(unsigned bit_idx) {
SASSERT(bit_idx < size());
get_bit_word(bit_idx) |= get_pos_mask(bit_idx);
}
void unset(unsigned bit_idx) {
SASSERT(bit_idx < size());
get_bit_word(bit_idx) &= ~get_pos_mask(bit_idx);
}
void set(unsigned bit_idx, bool val) {
SASSERT(bit_idx < size());
int _val = static_cast<int>(val);
get_bit_word(bit_idx) ^= (-_val ^ get_bit_word(bit_idx)) & get_pos_mask(bit_idx);
}
void push_back(bool val) {
unsigned idx = m_num_bits;
m_num_bits++;
if (num_words(m_num_bits) > m_capacity) {
expand();
}
set(idx, val);
}
void pop_back() {
SASSERT(m_num_bits > 0);
m_num_bits--;
}
bool back() const {
SASSERT(!empty());
bool r = get(m_num_bits - 1);
return r;
}
void shrink(unsigned new_size) {
SASSERT(new_size <= m_num_bits);
m_num_bits = new_size;
}
void resize(unsigned new_size, bool val = false);
void reserve(unsigned sz, bool val = false) {
if (sz > size())
resize(sz, val);
}
bool operator==(bit_vector const & other);
bool operator!=(bit_vector const & other) { return !operator==(other); }
bit_vector & operator=(bit_vector const & source) {
m_num_bits = source.m_num_bits;
if (m_capacity < source.m_capacity) {
dealloc_svect(m_data);
m_data = alloc_svect(unsigned, source.m_capacity);
m_capacity = source.m_capacity;
}
memcpy(m_data, source.m_data, source.m_capacity * sizeof(unsigned));
return *this;
}
bit_vector & operator|=(bit_vector const & source);
bit_vector & operator&=(bit_vector const & source);
void display(std::ostream & out) const;
};
inline std::ostream & operator<<(std::ostream & out, bit_vector const & b) {
b.display(out);
return out;
}
/**
\brief Bitvector class with fast reset.
This class should be used if the reset is frequently called.
*/
class fr_bit_vector : private bit_vector {
svector<unsigned> m_one_idxs;
public:
void reset();
void fill0() {
bit_vector::fill0();
m_one_idxs.reset();
}
void set(unsigned idx) {
m_one_idxs.push_back(idx);
bit_vector::set(idx);
}
void set(unsigned idx, bool val) {
if (val)
m_one_idxs.push_back(idx);
bit_vector::set(idx, val);
}
void push_back(bool val) {
if (val)
m_one_idxs.push_back(size());
bit_vector::push_back(val);
}
};
#endif /* _BIT_VECTOR_H_ */

256
src/util/buffer.h Normal file
View file

@ -0,0 +1,256 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
buffer.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2006-10-16.
Revision History:
--*/
#ifndef _BUFFER_H_
#define _BUFFER_H_
#include<string.h>
#include"memory_manager.h"
template<typename T, bool CallDestructors=true, unsigned INITIAL_SIZE=16>
class buffer {
protected:
T * m_buffer;
unsigned m_pos;
unsigned m_capacity;
char m_initial_buffer[INITIAL_SIZE * sizeof(T)];
void free_memory() {
if (m_buffer != reinterpret_cast<T*>(m_initial_buffer)) {
memory::deallocate(m_buffer);
}
}
void expand() {
unsigned new_capacity = m_capacity << 1;
T * new_buffer = reinterpret_cast<T*>(memory::allocate(sizeof(T) * new_capacity));
memcpy(new_buffer, m_buffer, m_pos * sizeof(T));
free_memory();
m_buffer = new_buffer;
m_capacity = new_capacity;
}
void destroy_elements() {
iterator it = begin();
iterator e = end();
for (; it != e; ++it) {
it->~T();
}
}
void destroy() {
if (CallDestructors) {
destroy_elements();
}
free_memory();
}
public:
typedef T data;
typedef T * iterator;
typedef const T * const_iterator;
buffer():
m_buffer(reinterpret_cast<T *>(m_initial_buffer)),
m_pos(0),
m_capacity(INITIAL_SIZE) {
}
buffer(const buffer & source):
m_buffer(reinterpret_cast<T *>(m_initial_buffer)),
m_pos(0),
m_capacity(INITIAL_SIZE) {
unsigned sz = source.size();
for(unsigned i = 0; i < sz; i++) {
push_back(source.m_buffer[i]);
}
}
buffer(unsigned sz, const T & elem):
m_buffer(reinterpret_cast<T *>(m_initial_buffer)),
m_pos(0),
m_capacity(INITIAL_SIZE) {
for (unsigned i = 0; i < sz; i++) {
push_back(elem);
}
SASSERT(size() == sz);
}
~buffer() {
destroy();
}
void reset() {
if (CallDestructors) {
destroy_elements();
}
m_pos = 0;
}
void finalize() {
destroy();
m_buffer = reinterpret_cast<T *>(m_initial_buffer);
m_pos = 0;
m_capacity = INITIAL_SIZE;
}
unsigned size() const {
return m_pos;
}
bool empty() const {
return m_pos == 0;
}
iterator begin() {
return m_buffer;
}
iterator end() {
return m_buffer + size();
}
void set_end(iterator it) {
m_pos = static_cast<unsigned>(it - m_buffer);
if (CallDestructors) {
iterator e = end();
for (; it != e; ++it) {
it->~T();
}
}
}
const_iterator begin() const {
return m_buffer;
}
const_iterator end() const {
return m_buffer + size();
}
void push_back(const T & elem) {
if (m_pos >= m_capacity)
expand();
new (m_buffer + m_pos) T(elem);
m_pos++;
}
void pop_back() {
if (CallDestructors) {
back().~T();
}
m_pos--;
}
const T & back() const {
SASSERT(!empty());
SASSERT(m_pos > 0);
return m_buffer[m_pos - 1];
}
T & back() {
SASSERT(!empty());
SASSERT(m_pos > 0);
return m_buffer[m_pos - 1];
}
T * c_ptr() const {
return m_buffer;
}
void append(unsigned n, T const * elems) {
for (unsigned i = 0; i < n; i++) {
push_back(elems[i]);
}
}
void append(const buffer& source) {
append(source.size(), source.c_ptr());
}
T & operator[](unsigned idx) {
SASSERT(idx < size());
return m_buffer[idx];
}
const T & operator[](unsigned idx) const {
SASSERT(idx < size());
return m_buffer[idx];
}
T & get(unsigned idx) {
SASSERT(idx < size());
return m_buffer[idx];
}
const T & get(unsigned idx) const {
SASSERT(idx < size());
return m_buffer[idx];
}
void set(unsigned idx, T const & val) {
SASSERT(idx < size());
m_buffer[idx] = val;
}
void resize(unsigned nsz, const T & elem=T()) {
unsigned sz = size();
if (nsz > sz) {
for (unsigned i = sz; i < nsz; i++) {
push_back(elem);
}
}
else if (nsz < sz) {
for (unsigned i = nsz; i < sz; i++) {
pop_back();
}
}
SASSERT(size() == nsz);
}
void shrink(unsigned nsz) {
unsigned sz = size();
SASSERT(nsz <= sz);
for (unsigned i = nsz; i < sz; i++)
pop_back();
SASSERT(size() == nsz);
}
private:
buffer& operator=(buffer const&);
};
template<typename T, unsigned INITIAL_SIZE=16>
class ptr_buffer : public buffer<T *, false, INITIAL_SIZE> {
public:
void append(unsigned n, T * const * elems) {
for (unsigned i = 0; i < n; i++) {
this->push_back(elems[i]);
}
}
};
template<typename T, unsigned INITIAL_SIZE=16>
class sbuffer : public buffer<T, false, INITIAL_SIZE> {
public:
sbuffer(): buffer<T, false, INITIAL_SIZE>() {}
sbuffer(unsigned sz, const T& elem) : buffer<T, false, INITIAL_SIZE>(sz,elem) {}
};
#endif /* _BUFFER_H_ */

37
src/util/cancel_eh.h Normal file
View file

@ -0,0 +1,37 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
cancel_eh.h
Abstract:
Template for implementing simple event handler that just invokes cancel method.
Author:
Leonardo de Moura (leonardo) 2011-04-27.
Revision History:
--*/
#ifndef _CANCEL_EH_H_
#define _CANCEL_EH_H_
#include"event_handler.h"
/**
\brief Generic event handler for invoking cancel method.
*/
template<typename T>
class cancel_eh : public event_handler {
T & m_obj;
public:
cancel_eh(T & o):m_obj(o) {}
virtual void operator()() {
m_obj.cancel();
}
};
#endif

672
src/util/chashtable.h Normal file
View file

@ -0,0 +1,672 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
chashtable.h
Abstract:
Hashtable with chaining.
The performance of the hashtable in hashtable.h deteriorates if
there is a huge number of deletions. In this case, the hashtable
starts to contain many cells marked as deleted, and insertion/deletion
start to suffer.
The hashtable defined in this class addresses this problem by using
chaining. Of course, there is the cost of storing the link to the next
cell.
Author:
Leonardo de Moura (leonardo) 2011-04-14.
Revision History:
--*/
#ifndef _CHASHTABLE_H_
#define _CHASHTABLE_H_
#include"memory_manager.h"
#include"debug.h"
#include"trace.h"
#ifdef Z3DEBUG
#include"hashtable.h"
#endif
#define CH_STATISTICS
#ifdef CH_STATISTICS
#define CHS_CODE(CODE) { CODE }
#else
#define CHS_CODE(CODE)
#endif
template<typename T, typename HashProc, typename EqProc>
class chashtable : private HashProc, private EqProc {
public:
static const unsigned default_init_slots = 8;
static const unsigned default_init_cellar = 2;
protected:
struct cell {
cell * m_next;
T m_data;
cell():m_next(reinterpret_cast<cell*>(1)) {}
bool is_free() const { return m_next == reinterpret_cast<cell*>(1); }
void mark_free() { m_next = reinterpret_cast<cell*>(1); }
};
cell * m_table; // array of cells.
unsigned m_capacity; // size of the array of cells.
unsigned m_init_slots;
unsigned m_init_cellar;
unsigned m_slots; // m_slots < m_capacity, and m_slots is a power of two, the cells [m_slots, m_capacity) are used for chaining.
unsigned m_used_slots; // m_used_slots <= m_slots (number of used slots).
unsigned m_size; // number of occupied cells.
#ifdef CH_STATISTICS
unsigned m_collisions;
#endif
cell * m_next_cell;
cell * m_free_cell;
unsigned get_hash(T const & d) const { return HashProc::operator()(d); }
bool equals(T const & e1, T const & e2) const { return EqProc::operator()(e1, e2); }
static cell * alloc_table(unsigned sz) {
return alloc_vect<cell>(sz);
}
void delete_table() {
dealloc_vect(m_table, m_capacity);
}
// Return the next free cell in the cellar, and the number of used slots
// Return 0 if the cellar is too small (unlikely but it might happen with a bad hash)
cell * copy_table(cell * source, unsigned source_slots, unsigned source_capacity,
cell * target, unsigned target_slots, unsigned target_capacity,
unsigned & used_slots) {
TRACE("chashtable", tout << "copy_table...\n";);
SASSERT(target_slots >= source_slots);
SASSERT(target_capacity >= source_capacity);
unsigned target_mask = target_slots - 1;
used_slots = 0;
cell * source_end = source + source_slots;
cell * target_cellar = target + target_slots;
cell * target_end = target + target_capacity;
for (cell * source_it = source; source_it != source_end; ++source_it) {
if (!source_it->is_free()) {
cell * list_it = source_it;
do {
unsigned h = get_hash(list_it->m_data);
unsigned idx = h & target_mask;
cell * target_it = target + idx;
SASSERT(target_it >= target);
SASSERT(target_it < target + target_slots);
if (target_it->is_free()) {
target_it->m_data = list_it->m_data;
target_it->m_next = 0;
used_slots++;
}
else {
SASSERT((get_hash(target_it->m_data) & target_mask) == idx);
if (target_cellar == target_end)
return 0; // the cellar is too small...
SASSERT(target_cellar >= target + target_slots);
SASSERT(target_cellar < target_end);
*target_cellar = *target_it;
target_it->m_data = list_it->m_data;
target_it->m_next = target_cellar;
target_cellar++;
}
SASSERT(!target_it->is_free());
list_it = list_it->m_next;
}
while (list_it != 0);
}
}
#if 0
TRACE("chashtable",
for (unsigned i = 0; i < source_capacity; i++) {
tout << i << ":[";
if (source[i].m_next == 0)
tout << "null";
else if (source[i].m_next == reinterpret_cast<cell*>(1))
tout << "X";
else
tout << (source[i].m_next - source);
tout << ", " << source[i].m_data << "]\n";
}
tout << "\n";
for (unsigned i = 0; i < target_capacity; i++) {
tout << i << ":[";
if (target[i].m_next == 0)
tout << "null";
else if (target[i].m_next == reinterpret_cast<cell*>(1))
tout << "X";
else
tout << (target[i].m_next - target);
tout << ", " << target[i].m_data << "]\n";
}
tout << "\n";);
#endif
return target_cellar;
}
void expand_table() {
unsigned curr_cellar = (m_capacity - m_slots);
unsigned new_slots = m_slots * 2;
unsigned new_cellar = curr_cellar * 2;
while (true) {
unsigned new_capacity = new_slots + new_cellar;
cell * new_table = alloc_table(new_capacity);
cell * next_cell = copy_table(m_table, m_slots, m_capacity,
new_table, new_slots, new_capacity,
m_used_slots);
if (next_cell != 0) {
delete_table();
m_table = new_table;
m_capacity = new_capacity;
m_slots = new_slots;
m_next_cell = next_cell;
m_free_cell = 0;
CASSERT("chashtable", check_invariant());
return;
}
dealloc_vect(new_table, new_capacity);
new_cellar *= 2;
}
}
bool has_free_cells() const {
return m_free_cell != 0 || m_next_cell < m_table + m_capacity;
}
cell * get_free_cell() {
if (m_free_cell != 0) {
cell * c = m_free_cell;
m_free_cell = c->m_next;
return c;
}
else {
cell * c = m_next_cell;
m_next_cell++;
return c;
}
}
void recycle_cell(cell * c) {
// c is in the cellar
SASSERT(c >= m_table + m_slots);
SASSERT(c < m_table + m_capacity);
c->m_next = m_free_cell;
m_free_cell = c;
}
void init(unsigned slots, unsigned cellar) {
m_capacity = slots + cellar;
m_table = alloc_table(m_capacity);
m_slots = slots;
m_used_slots = 0;
m_size = 0;
m_next_cell = m_table + slots;
m_free_cell = 0;
}
public:
chashtable(HashProc const & h = HashProc(),
EqProc const & e = EqProc(),
unsigned init_slots = default_init_slots,
unsigned init_cellar = default_init_cellar):
HashProc(h),
EqProc(e) {
SASSERT(is_power_of_two(init_slots));
SASSERT(init_cellar > 0);
m_init_slots = init_slots;
m_init_cellar = init_cellar;
init(m_init_slots, m_init_cellar);
CHS_CODE(m_collisions = 0;);
}
~chashtable() {
#if 0
cell * it = m_table;
cell * end = m_table + m_slots;
verbose_stream() << "[chashtable] free slots: ";
for (; it != end; ++it) {
if (it->is_free())
verbose_stream() << (it - m_table) << " ";
}
verbose_stream() << "\n";
#endif
delete_table();
}
void reset() {
if (m_size == 0)
return;
finalize();
}
void finalize() {
delete_table();
init(m_init_slots, m_init_cellar);
}
bool empty() const {
return m_size == 0;
}
unsigned size() const {
return m_size;
}
unsigned capacity() const {
return m_capacity;
}
unsigned used_slots() const {
return m_used_slots;
}
void insert(T const & d) {
if (!has_free_cells())
expand_table();
unsigned mask = m_slots - 1;
unsigned h = get_hash(d);
unsigned idx = h & mask;
cell * c = m_table + idx;
if (c->is_free()) {
m_size++;
m_used_slots++;
c->m_data = d;
c->m_next = 0;
CASSERT("chashtable_bug", check_invariant());
return;
}
else {
cell * it = c;
do {
if (equals(it->m_data, d)) {
// already there
it->m_data = d;
CASSERT("chashtable_bug", check_invariant());
return;
}
CHS_CODE(m_collisions++;);
it = it->m_next;
}
while (it != 0);
// d is not in the table.
m_size++;
cell * new_c = get_free_cell();
*new_c = *c;
c->m_data = d;
c->m_next = new_c;
CASSERT("chashtable_bug", check_invariant());
return;
}
}
T & insert_if_not_there(T const & d) {
if (!has_free_cells())
expand_table();
unsigned mask = m_slots - 1;
unsigned h = get_hash(d);
unsigned idx = h & mask;
cell * c = m_table + idx;
if (c->is_free()) {
m_size++;
m_used_slots++;
c->m_data = d;
c->m_next = 0;
CASSERT("chashtable_bug", check_invariant());
return c->m_data;
}
else {
cell * it = c;
do {
if (equals(it->m_data, d)) {
// already there
CASSERT("chashtable_bug", check_invariant());
return it->m_data;
}
CHS_CODE(m_collisions++;);
it = it->m_next;
}
while (it != 0);
// d is not in the table.
m_size++;
cell * new_c = get_free_cell();
*new_c = *c;
c->m_data = d;
c->m_next = new_c;
CASSERT("chashtable_bug", check_invariant());
return c->m_data;
}
}
bool insert_if_not_there2(T const & d) {
if (!has_free_cells())
expand_table();
unsigned mask = m_slots - 1;
unsigned h = get_hash(d);
unsigned idx = h & mask;
cell * c = m_table + idx;
if (c->is_free()) {
m_size++;
m_used_slots++;
c->m_data = d;
c->m_next = 0;
CASSERT("chashtable_bug", check_invariant());
return true;
}
else {
cell * it = c;
do {
if (equals(it->m_data, d)) {
// already there
CASSERT("chashtable_bug", check_invariant());
return false;
}
CHS_CODE(m_collisions++;);
it = it->m_next;
}
while (it != 0);
// d is not in the table.
m_size++;
cell * new_c = get_free_cell();
*new_c = *c;
c->m_data = d;
c->m_next = new_c;
CASSERT("chashtable_bug", check_invariant());
return true;
}
}
bool contains(T const & d) const {
unsigned mask = m_slots - 1;
unsigned h = get_hash(d);
unsigned idx = h & mask;
cell * c = m_table + idx;
if (c->is_free())
return false;
do {
if (equals(c->m_data, d)) {
return true;
}
CHS_CODE(const_cast<chashtable*>(this)->m_collisions++;);
c = c->m_next;
}
while (c != 0);
return false;
}
T * find_core(T const & d) const {
unsigned mask = m_slots - 1;
unsigned h = get_hash(d);
unsigned idx = h & mask;
cell * c = m_table + idx;
if (c->is_free())
return 0;
do {
if (equals(c->m_data, d)) {
return &(c->m_data);
}
CHS_CODE(const_cast<chashtable*>(this)->m_collisions++;);
c = c->m_next;
}
while (c != 0);
return 0;
}
bool find(T const & d, T & r) {
unsigned mask = m_slots - 1;
unsigned h = get_hash(d);
unsigned idx = h & mask;
cell * c = m_table + idx;
if (c->is_free())
return false;
do {
if (equals(c->m_data, d)) {
r = c->m_data;
return true;
}
CHS_CODE(const_cast<chashtable*>(this)->m_collisions++;);
c = c->m_next;
}
while (c != 0);
return false;
}
void erase(T const & d) {
unsigned mask = m_slots - 1;
unsigned h = get_hash(d);
unsigned idx = h & mask;
cell * c = m_table + idx;
if (c->is_free())
return;
cell * prev = 0;
do {
if (equals(c->m_data, d)) {
m_size--;
if (prev == 0) {
cell * next = c->m_next;
if (next == 0) {
m_used_slots--;
c->mark_free();
SASSERT(c->is_free());
}
else {
*c = *next;
recycle_cell(next);
}
}
else {
prev->m_next = c->m_next;
recycle_cell(c);
}
CASSERT("chashtable_bug", check_invariant());
return;
}
CHS_CODE(m_collisions++;);
prev = c;
c = c->m_next;
}
while (c != 0);
}
class iterator {
cell * m_it;
cell * m_end;
cell * m_list_it;
void move_to_used() {
while (m_it != m_end) {
if (!m_it->is_free()) {
m_list_it = m_it;
return;
}
m_it++;
}
m_list_it = 0;
}
public:
iterator(cell * start, cell * end): m_it(start), m_end(end) { move_to_used(); }
iterator():m_it(0), m_end(0), m_list_it(0) {}
T & operator*() {
return m_list_it->m_data;
}
T const & operator*() const {
return m_list_it->m_data;
}
T const * operator->() const { return &(operator*()); }
T * operator->() { return &(operator*()); }
iterator & operator++() {
m_list_it = m_list_it->m_next;
if (m_list_it == 0) {
m_it++;
move_to_used();
}
return *this;
}
iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
bool operator==(iterator const & it) const { return m_list_it == it.m_list_it; }
bool operator!=(iterator const & it) const { return m_list_it != it.m_list_it; }
};
iterator begin() const { return iterator(m_table, m_table + m_slots); }
iterator end() const { return iterator(); }
void swap(chashtable & other) {
std::swap(m_table, other.m_table);
std::swap(m_capacity, other.m_capacity);
std::swap(m_init_slots, other.m_init_slots);
std::swap(m_init_cellar, other.m_init_cellar);
std::swap(m_slots, other.m_slots);
std::swap(m_used_slots, other.m_used_slots);
std::swap(m_size, other.m_size);
#ifdef CH_STATISTICS
std::swap(m_collisions, other.m_collisions);
#endif
std::swap(m_next_cell, other.m_next_cell);
std::swap(m_free_cell, other.m_free_cell);
}
unsigned collisions() const {
#ifdef CH_STATISTICS
return m_collisions;
#else
return 0;
#endif
}
#ifdef Z3DEBUG
bool check_invariant() const {
ptr_addr_hashtable<cell> visited;
unsigned sz = 0;
cell * _end = m_table + m_slots;
for (cell * it = m_table; it != _end; ++it) {
if (!it->is_free()) {
cell * list_it = it;
while (list_it != 0) {
sz++;
SASSERT(!visited.contains(list_it));
visited.insert(list_it);
list_it = list_it->m_next;
}
}
}
SASSERT(m_size == sz);
return true;
}
#endif
};
template<typename Key, typename Value, typename HashProc, typename EqProc>
class cmap {
public:
struct key_value {
Key m_key;
Value m_value;
key_value() {}
key_value(Key const & k):m_key(k) {}
key_value(Key const & k, Value const & v):m_key(k), m_value(v) {}
};
protected:
struct key_value_hash_proc : private HashProc {
key_value_hash_proc(HashProc const & p):HashProc(p) {}
unsigned operator()(key_value const & d) const { return HashProc::operator()(d.m_key); }
};
struct key_value_eq_proc : private EqProc {
key_value_eq_proc(EqProc const & p):EqProc(p) {}
bool operator()(key_value const & d1, key_value const & d2) const { return EqProc::operator()(d1.m_key, d2.m_key); }
};
typedef chashtable<key_value, key_value_hash_proc, key_value_eq_proc> table;
table m_table;
public:
cmap(HashProc const & h = HashProc(),
EqProc const & e = EqProc(),
unsigned init_slots = table::default_init_slots,
unsigned init_cellar = table::default_init_cellar):
m_table(key_value_hash_proc(h),
key_value_eq_proc(e),
init_slots,
init_cellar) {
}
typedef typename table::iterator iterator;
void reset() {
m_table.reset();
}
void finalize() {
m_table.finalize();
}
bool empty() const {
return m_table.empty();
}
unsigned size() const {
return m_table.size();
}
unsigned capacity() const {
return m_table.capacity();
}
unsigned used_slots() const {
return m_table.used_slots();
}
unsigned collisions() const {
return m_table.collisions();
}
iterator begin() const {
return m_table.begin();
}
iterator end() const {
return m_table.end();
}
void insert(Key const & k, Value const & v) {
return m_table.insert(key_value(k, v));
}
key_value & insert_if_not_there(Key const & k, Value const & v) {
return m_table.insert_if_not_there(key_value(k, v));
}
bool contains(Key const & k) const {
return m_table.contains(key_value(k));
}
key_value * find_core(Key const & k) const {
return m_table.find_core(key_value(k));
}
bool find(Key const & k, Value & v) const {
key_value * e = m_table.find_core(key_value(k));
if (e == 0)
return false;
v = e->m_value;
return true;
}
void erase(Key const & k) {
m_table.erase(key_value(k));
}
};
#endif

45
src/util/critical_flet.h Normal file
View file

@ -0,0 +1,45 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
critical flet.cpp
Abstract:
Version of flet using "omp critical" directive.
Warning: it uses omp critical section "critical_flet"
Author:
Leonardo de Moura (leonardo) 2011-05-12
Revision History:
--*/
#ifndef _CRITICAL_FLET_H_
#define _CRITICAL_FLET_H_
template<typename T>
class critical_flet {
T & m_ref;
T m_old_value;
public:
critical_flet(T & ref, const T & new_value):
m_ref(ref),
m_old_value(ref) {
#pragma omp critical (critical_flet)
{
m_ref = new_value;
}
}
~critical_flet() {
#pragma omp critical (critical_flet)
{
m_ref = m_old_value;
}
}
};
#endif

110
src/util/debug.cpp Normal file
View file

@ -0,0 +1,110 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
debug.cpp
Abstract:
Basic debugging support.
Author:
Leonardo de Moura (leonardo) 2006-09-11.
Revision History:
--*/
#include<cstdio>
#ifndef _WINDOWS
#include<unistd.h>
#endif
#include<iostream>
#include"str_hashtable.h"
volatile bool g_enable_assertions = true;
void enable_assertions(bool f) {
g_enable_assertions = f;
}
bool assertions_enabled() {
return g_enable_assertions;
}
void notify_assertion_violation(const char * fileName, int line, const char * condition) {
std::cerr << "ASSERTION VIOLATION\n";
std::cerr << "File: " << fileName << "\n";
std::cerr << "Line: " << line << "\n";
std::cerr << condition << "\n";
}
str_hashtable* g_enabled_debug_tags = 0;
static void init_debug_table() {
if (!g_enabled_debug_tags) {
g_enabled_debug_tags = alloc(str_hashtable);
}
}
void finalize_debug() {
dealloc(g_enabled_debug_tags);
g_enabled_debug_tags = 0;
}
void enable_debug(const char * tag) {
init_debug_table();
g_enabled_debug_tags->insert(const_cast<char *>(tag));
}
void disable_debug(const char * tag) {
init_debug_table();
g_enabled_debug_tags->erase(const_cast<char *>(tag));
}
bool is_debug_enabled(const char * tag) {
init_debug_table();
return g_enabled_debug_tags->contains(const_cast<char *>(tag));
}
#ifndef _WINDOWS
void invoke_gdb() {
char buffer[1024];
int * x = 0;
for (;;) {
std::cerr << "(C)ontinue, (A)bort, (S)top, Invoke (G)DB\n";
char result;
std::cin >> result;
switch(result) {
case 'C':
case 'c':
return;
case 'A':
case 'a':
exit(1);
case 'S':
case 's':
// force seg fault...
*x = 0;
return;
case 'G':
case 'g':
sprintf(buffer, "gdb -nw /proc/%d/exe %d", getpid(), getpid());
std::cerr << "invoking GDB...\n";
if (system(buffer) == 0) {
std::cerr << "continuing the execution...\n";
}
else {
std::cerr << "error starting GDB...\n";
// forcing seg fault.
int * x = 0;
*x = 0;
}
return;
default:
std::cerr << "INVALID COMMAND\n";
}
}
}
#endif

99
src/util/debug.h Normal file
View file

@ -0,0 +1,99 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
debug.h
Abstract:
Basic debugging support.
Author:
Leonardo de Moura (leonardo) 2006-09-11.
Revision History:
--*/
#ifndef _DEBUG_H_
#define _DEBUG_H_
void enable_assertions(bool f);
bool assertions_enabled();
#if 0
#define _CRTDBG_MAP_ALLOC
#include<stdlib.h>
#include<new>
#include<crtdbg.h>
#endif
#include"error_codes.h"
#include"warning.h"
#ifdef Z3DEBUG
#define DEBUG_CODE(CODE) { CODE } ((void) 0)
#else
#define DEBUG_CODE(CODE) ((void) 0)
#endif
#ifdef _WINDOWS
#define INVOKE_DEBUGGER() __debugbreak()
#else
void invoke_gdb();
#define INVOKE_DEBUGGER() invoke_gdb()
#endif
void notify_assertion_violation(const char * file_name, int line, const char * condition);
void enable_debug(const char * tag);
void disable_debug(const char * tag);
bool is_debug_enabled(const char * tag);
#define SASSERT(COND) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); })
#define CASSERT(TAG, COND) DEBUG_CODE(if (assertions_enabled() && is_debug_enabled(TAG) && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); INVOKE_DEBUGGER(); })
#define XASSERT(COND, EXTRA_CODE) DEBUG_CODE(if (assertions_enabled() && !(COND)) { notify_assertion_violation(__FILE__, __LINE__, #COND); { EXTRA_CODE } INVOKE_DEBUGGER(); })
#define UNREACHABLE() DEBUG_CODE(notify_assertion_violation(__FILE__, __LINE__, "UNREACHABLE CODE WAS REACHED."); INVOKE_DEBUGGER();)
#define NOT_IMPLEMENTED_YET() { std::cerr << "NOT IMPLEMENTED YET!\n"; UNREACHABLE(); exit(ERR_NOT_IMPLEMENTED_YET); } ((void) 0)
#ifdef Z3DEBUG
#define VERIFY(_x_) if (!(_x_)) { \
std::cerr << "Failed to verify: " << #_x_ << "\n"; \
UNREACHABLE(); \
}
#else
#define VERIFY(_x_) (_x_)
#endif
#define MAKE_NAME2(LINE) zofty_ ## LINE
#define MAKE_NAME(LINE) MAKE_NAME2(LINE)
#define DBG_UNIQUE_NAME MAKE_NAME(__LINE__)
#define COMPILE_TIME_ASSERT(expr) extern char DBG_UNIQUE_NAME[expr]
template<class T>
class class_invariant
{
T* m_class;
char const* m_module;
public:
class_invariant(T* cls) : m_class(cls), m_module(0) {
SASSERT(cls->invariant());
}
class_invariant(T* cls, char const* module) : m_class(cls), m_module(module) {
CASSERT(module, cls->invariant());
}
~class_invariant() {
if (m_module) {
CASSERT(m_module, m_class->invariant());
}
else {
SASSERT(m_class->invariant());
}
}
private:
};
void finalize_debug();
#endif /* _DEBUG_H_ */

68
src/util/dec_ref_util.h Normal file
View file

@ -0,0 +1,68 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
map_util.h
Abstract:
Some goodies for managing reference counters
stored in maps.
Author:
Leonardo (leonardo) 2011-06-07
Notes:
--*/
#ifndef _MAP_UTIL_H_
#define _MAP_UTIL_H_
/**
\brief Decrement the reference counter of the keys and values stored in the map,
then reset the map.
*/
template<typename Mng, typename Map>
void dec_ref_key_values(Mng & m, Map & map) {
typename Map::iterator it = map.begin();
typename Map::iterator end = map.end();
for (; it != end; ++it) {
m.dec_ref(it->m_key);
m.dec_ref(it->m_value);
}
map.reset();
}
/**
\brief Decrement the reference counter of the keys stored in the map,
then reset the map.
*/
template<typename Mng, typename Map>
void dec_ref_keys(Mng & m, Map & map) {
typename Map::iterator it = map.begin();
typename Map::iterator end = map.end();
for (; it != end; ++it) {
m.dec_ref(it->m_key);
}
map.reset();
}
/**
\brief Decrement the reference counter of the values stored in the map,
then reset the map.
*/
template<typename Mng, typename Map>
void dec_ref_values(Mng & m, Map & map) {
typename Map::iterator it = map.begin();
typename Map::iterator end = map.end();
for (; it != end; ++it) {
m.dec_ref(it->m_value);
}
map.reset();
}
#endif

326
src/util/dependency.h Normal file
View file

@ -0,0 +1,326 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dependency.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-12-10.
Revision History:
--*/
#ifndef _DEPENDENCY_H_
#define _DEPENDENCY_H_
#include"vector.h"
#include"region.h"
template<typename C>
class dependency_manager {
public:
typedef typename C::value value;
typedef typename C::value_manager value_manager;
typedef typename C::allocator allocator;
class dependency {
unsigned m_ref_count:30;
unsigned m_mark:1;
unsigned m_leaf:1;
friend class dependency_manager;
dependency(bool leaf):
m_ref_count(0),
m_mark(false),
m_leaf(leaf) {
}
bool is_marked() const { return m_mark == 1; }
void mark() { m_mark = true; }
void unmark() { m_mark = false; }
public:
unsigned get_ref_count() const { return m_ref_count; }
bool is_leaf() const { return m_leaf == 1; }
};
private:
struct join : public dependency {
dependency * m_children[2];
join(dependency * d1, dependency * d2):
dependency(false) {
m_children[0] = d1;
m_children[1] = d2;
}
};
struct leaf : public dependency {
value m_value;
leaf(value const & v):
dependency(true),
m_value(v) {
}
};
static join * to_join(dependency * d) { SASSERT(!d->is_leaf()); return static_cast<join*>(d); }
static leaf * to_leaf(dependency * d) { SASSERT(d->is_leaf()); return static_cast<leaf*>(d); }
value_manager & m_vmanager;
allocator & m_allocator;
ptr_vector<dependency> m_todo;
void inc_ref(value const & v) {
if (C::ref_count)
m_vmanager.inc_ref(v);
}
void dec_ref(value const & v) {
if (C::ref_count)
m_vmanager.dec_ref(v);
}
void del(dependency * d) {
SASSERT(d);
m_todo.push_back(d);
while (!m_todo.empty()) {
d = m_todo.back();
m_todo.pop_back();
if (d->is_leaf()) {
dec_ref(to_leaf(d)->m_value);
to_leaf(d)->~leaf();
m_allocator.deallocate(sizeof(leaf), to_leaf(d));
}
else {
for (unsigned i = 0; i < 2; i++) {
dependency * c = to_join(d)->m_children[i];
SASSERT(c->m_ref_count > 0);
c->m_ref_count--;
if (c->m_ref_count == 0)
m_todo.push_back(c);
}
to_join(d)->~join();
m_allocator.deallocate(sizeof(join), to_join(d));
}
}
}
void unmark_todo() {
typename ptr_vector<dependency>::iterator it = m_todo.begin();
typename ptr_vector<dependency>::iterator end = m_todo.end();
for (; it != end; ++it) {
(*it)->unmark();
}
m_todo.reset();
}
public:
dependency_manager(value_manager & m, allocator & a):
m_vmanager(m),
m_allocator(a) {
}
void inc_ref(dependency * d) {
if (d)
d->m_ref_count++;
}
void dec_ref(dependency * d) {
if (d) {
SASSERT(d->m_ref_count > 0);
d->m_ref_count--;
if (d->m_ref_count == 0)
del(d);
}
}
dependency * mk_empty() {
return 0;
}
dependency * mk_leaf(value const & v) {
void * mem = m_allocator.allocate(sizeof(leaf));
inc_ref(v);
return new (mem) leaf(v);
}
dependency * mk_join(dependency * d1, dependency * d2) {
if (d1 == 0) {
return d2;
}
else if (d2 == 0) {
return d1;
}
else if (d1 == d2) {
return d1;
}
else {
void * mem = m_allocator.allocate(sizeof(join));
inc_ref(d1); inc_ref(d2);
return new (mem) join(d1, d2);
}
}
bool contains(dependency * d, value const & v) {
if (d) {
m_todo.reset();
d->mark();
m_todo.push_back(d);
unsigned qhead = 0;
while (qhead < m_todo.size()) {
dependency * d = m_todo[qhead];
qhead++;
if (d->is_leaf()) {
if (to_leaf(d)->m_value == v) {
unmark_todo();
return true;
}
}
else {
for (unsigned i = 0; i < 2; i++) {
dependency * child = to_join(d)->m_children[i];
if (!child->is_marked()) {
m_todo.push_back(child);
child->mark();
}
}
}
}
unmark_todo();
}
return false;
}
void linearize(dependency * d, vector<value, false> & vs) {
if (d) {
m_todo.reset();
d->mark();
m_todo.push_back(d);
unsigned qhead = 0;
while (qhead < m_todo.size()) {
dependency * d = m_todo[qhead];
qhead++;
if (d->is_leaf()) {
vs.push_back(to_leaf(d)->m_value);
}
else {
for (unsigned i = 0; i < 2; i++) {
dependency * child = to_join(d)->m_children[i];
if (!child->is_marked()) {
m_todo.push_back(child);
child->mark();
}
}
}
}
unmark_todo();
}
}
};
/**
\brief Version of the dependency_manager where
memory management is scoped (i.e., reference counting is ignored),
and push_scope/pop_scope are used instead.
Value must be a primitive type such as an integer or pointer.
*/
template<typename Value>
class scoped_dependency_manager {
class config {
public:
static const bool ref_count = true;
typedef Value value;
class value_manager {
public:
void inc_ref(value const & v) {
}
void dec_ref(value const & v) {
}
};
class allocator {
region m_region;
public:
void * allocate(size_t sz) {
return m_region.allocate(sz);
}
void deallocate(size_t sz, void * mem) {
}
void push_scope() {
m_region.push_scope();
}
void pop_scope(unsigned num) {
m_region.pop_scope(num);
}
void reset() {
m_region.reset();
}
};
};
typedef dependency_manager<config> dep_manager;
public:
typedef typename dep_manager::dependency dependency;
typedef Value value;
private:
typename config::value_manager m_vmanager;
typename config::allocator m_allocator;
dep_manager m_dep_manager;
public:
scoped_dependency_manager():
m_dep_manager(m_vmanager, m_allocator) {
}
dependency * mk_empty() {
return m_dep_manager.mk_empty();
}
dependency * mk_leaf(value const & v) {
return m_dep_manager.mk_leaf(v);
}
dependency * mk_join(dependency * d1, dependency * d2) {
return m_dep_manager.mk_join(d1, d2);
}
bool contains(dependency * d, value const & v) {
return m_dep_manager.contains(d, v);
}
void linearize(dependency * d, vector<value, false> & vs) {
return m_dep_manager.linearize(d, vs);
}
void reset() {
m_allocator.reset();
}
void push_scope() {
m_allocator.push_scope();
}
void pop_scope(unsigned num_scopes) {
m_allocator.pop_scope(num_scopes);
}
};
// Implement old dependency manager used by interval and groebner
typedef scoped_dependency_manager<void*> v_dependency_manager;
typedef scoped_dependency_manager<void*>::dependency v_dependency;
#endif /* _DEPENDENCY_H_ */

29
src/util/dictionary.h Normal file
View file

@ -0,0 +1,29 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
dictionary.h
Abstract:
Author:
Leonardo (leonardo) 2011-03-01
Notes:
--*/
#ifndef _DICTIONARY_H_
#define _DICTIONARY_H_
#include"map.h"
#include"symbol.h"
template<typename T>
class dictionary : public map<symbol, T, symbol_hash_proc, symbol_eq_proc> {
public:
dictionary() {}
};
#endif

137
src/util/dlist.h Normal file
View file

@ -0,0 +1,137 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
dlist.h
Abstract:
Templates for manipulating doubly linked lists.
Author:
Leonardo de Moura (leonardo) 2011-01-25.
Revision History:
--*/
#ifndef __DLIST_H_
#define __DLIST_H_
/**
Add element \c elem to the list headed by \c head.
NextProc and PrevProc must have the methods:
T * & operator()(T *);
T * & operator()(T *);
They should return the next and prev fields of the given object.
*/
template<typename T, typename NextProc, typename PrevProc>
void dlist_add(T * & head, T * elem, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) {
SASSERT(prev(elem) == 0);
SASSERT(next(elem) == 0);
if (head == 0) {
head = elem;
}
else {
next(elem) = head;
prev(head) = elem;
head = elem;
}
}
template<typename T, typename NextProc, typename PrevProc>
void dlist_del(T * & head, T * elem, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) {
T * & prev_elem = prev(elem);
T * & next_elem = next(elem);
if (head == elem) {
SASSERT(prev_elem == 0);
if (next_elem != 0)
prev(next_elem) = 0;
head = next_elem;
}
else {
SASSERT(prev_elem != 0);
next(prev_elem) = next_elem;
if (next_elem != 0)
prev(next_elem) = prev_elem;
}
prev_elem = 0;
next_elem = 0;
}
/**
\brief Remove the head of the list. Return the old head.
*/
template<typename T, typename NextProc, typename PrevProc>
T * dlist_pop(T * & head, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) {
if (head == 0) {
return 0;
}
else {
SASSERT(prev(head) == 0);
T * r = head;
head = next(head);
if (head)
prev(head) = 0;
next(r) = 0;
return r;
}
}
/**
\brief Insert new element after elem.
*/
template<typename T, typename NextProc, typename PrevProc>
void dlist_insert_after(T * elem, T * new_elem, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) {
SASSERT(elem);
SASSERT(new_elem);
T * & old_next_elem = next(elem);
prev(new_elem) = elem;
next(new_elem) = old_next_elem;
if (old_next_elem)
prev(old_next_elem) = new_elem;
// next(elem) = new_elem;
old_next_elem = new_elem;
}
template<typename T, typename NextProc>
bool dlist_contains(T * head, T const * elem, NextProc const & next = NextProc()) {
T * curr = head;
while (curr != 0) {
if (curr == elem)
return true;
curr = next(curr);
}
return false;
}
template<typename T, typename NextProc>
unsigned dlist_length(T * head, NextProc const & next = NextProc()) {
unsigned r = 0;
T * curr = head;
while (curr != 0) {
r++;
curr = next(curr);
}
return r;
}
template<typename T, typename NextProc, typename PrevProc>
bool dlist_check_invariant(T * head, NextProc const & next = NextProc(), PrevProc const & prev = PrevProc()) {
if (head == 0)
return true;
SASSERT(prev(head) == 0);
T * old = head;
T * curr = next(head);
while (curr != 0) {
SASSERT(prev(curr) == old);
old = curr;
curr = next(curr);
}
return true;
}
#endif

103
src/util/double_manager.h Normal file
View file

@ -0,0 +1,103 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
double_manager.h
Abstract:
Simulates mpq interface using doubles
Author:
Leonardo de Moura (leonardo) 2011-06-09.
Revision History:
--*/
#ifndef _DOUBLE_MANAGER_H_
#define _DOUBLE_MANAGER_H_
#include<cmath>
#include<string>
#include<sstream>
#include<iomanip>
#include"util.h"
#include"debug.h"
#include"hash.h"
#include"params.h"
/**
\brief Create an interface for manipulating double numbers compatible with the one for mpq.
*/
class double_manager {
double m_zero_tolerance;
public:
typedef double numeral;
static bool precise() { return false; }
double_manager(params_ref const & p = params_ref()) { updt_params(p); }
void updt_params(params_ref const & p) {
m_zero_tolerance = p.get_double(":zero-tolerance", 0.00000001);
}
static void reset(double & a) { a = 0.0; }
static void del(double & a) { /* do nothing */ }
static void add(double a, double b, double & c) { c = a + b; }
// d <- a + b*c
static void addmul(double a, double b, double c, double & d) { d = a + b*c; }
// d <- a - b*c
static void submul(double a, double b, double c, double & d) { d = a - b*c; }
static void sub(double a, double b, double & c) { c = a - b; }
static void mul(double a, double b, double & c) { c = a * b; }
static void div(double a, double b, double & c) { c = a / b; }
static void inv(double a, double & b) { b = 1 / a; }
static void inv(double & a) { a = 1 / a; }
static void neg(double & a) { a = -a; }
static void abs(double & a) { if (a < 0.0) neg(a); }
static void power(double a, unsigned p, double & b) {
SASSERT(p <= INT_MAX);
b = ::pow(a, static_cast<int>(p));
}
static void floor(double a, double & b) { b = ::floor(a); }
static void ceil(double a, double & b) { b = ::ceil(a); }
bool eq(double a, double b) const { return is_zero(a - b); }
bool neq(double a, double b) const { return !eq(a, b); }
static bool lt(double a, double b) { return a < b; }
static bool le(double a, double b) { return a <= b; }
static bool gt(double a, double b) { return a > b; }
static bool ge(double a, double b) { return a >= b; }
static void set(double & a, int n, int d) { a = static_cast<double>(n)/static_cast<double>(d); }
static void set(double & a, double val) { a = val; }
static void set(double & a, char const * val) { a = atof(val); }
static void set(double & a, int val) { a = static_cast<double>(val); }
static void set(double & a, unsigned val) { a = static_cast<double>(val); }
static void set(double & a, int64 val) { a = static_cast<double>(val); }
static void set(double & a, uint64 val) { a = static_cast<double>(val); }
static void swap(double & a, double & b) { std::swap(a, b); }
bool is_pos(double a) const { return a > m_zero_tolerance; }
bool is_neg(double a) const { return a < m_zero_tolerance; }
bool is_zero(double a) const { return -m_zero_tolerance <= a && a <= m_zero_tolerance; }
bool is_nonpos(double a) const { return !is_pos(a); }
bool is_nonneg(double a) const { return !is_neg(a); }
static bool is_one(double a) { return a == 1.0; }
static bool is_minus_one(double a) { return a == -1.0; }
static bool is_int(double a) { return a == ::floor(a); }
static std::string to_string(double a) {
std::ostringstream sstream;
sstream << std::setprecision(12) << a;
return sstream.str();
}
static unsigned hash(double a) {
return hash_ull(static_cast<uint64>(a));
}
};
COMPILE_TIME_ASSERT(sizeof(uint64) == sizeof(double));
#endif /* _DOUBLE_MANAGER_H_ */

28
src/util/event_handler.h Normal file
View file

@ -0,0 +1,28 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
event_handler.h
Abstract:
Abstract event handler.
Author:
Leonardo de Moura (leonardo) 2011-04-26.
Revision History:
--*/
#ifndef _EVENT_HANDLER_H_
#define _EVENT_HANDLER_H_
class event_handler {
public:
virtual ~event_handler() {}
virtual void operator()() = 0;
};
#endif

52
src/util/ext_gcd.h Normal file
View file

@ -0,0 +1,52 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
ext_gcd.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2007-06-09.
Revision History:
--*/
#ifndef _EXT_GCD_H_
#define _EXT_GCD_H_
template<typename numeral>
void extended_gcd(const numeral & in_a, const numeral & in_b, numeral & gcd, numeral & x, numeral & y) {
numeral a = in_a;
numeral b = in_b;
x = numeral(0);
y = numeral(1);
numeral lastx(1);
numeral lasty(0);
numeral tmp;
numeral quotient;
while (!b.is_zero()) {
tmp = b;
quotient = div(a, b);
b = mod(a, b);
a = tmp;
tmp = x;
x = lastx - (quotient * x);
lastx = tmp;
tmp = y;
y = lasty - (quotient * y);
lasty = tmp;
}
gcd = a;
x = lastx;
y = lasty;
}
#endif /* _EXT_GCD_H_ */

335
src/util/ext_numeral.h Normal file
View file

@ -0,0 +1,335 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
ext_numeral.h
Abstract:
Goodies for handling extended numerals such as R union { -oo, +oo }.
We can have extended sets of mpq, mpz, mpbq, mpf, etc.
Author:
Leonardo de Moura (leonardo) 2011-12-04.
Revision History:
--*/
#ifndef _EXT_NUMERAL_H_
#define _EXT_NUMERAL_H_
#include<iostream>
#include"debug.h"
enum ext_numeral_kind { EN_MINUS_INFINITY, EN_NUMERAL, EN_PLUS_INFINITY };
template<typename numeral_manager>
bool is_zero(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak) {
return ak == EN_NUMERAL && m.is_zero(a);
}
template<typename numeral_manager>
bool is_pos(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak) {
return ak == EN_PLUS_INFINITY || (ak == EN_NUMERAL && m.is_pos(a));
}
template<typename numeral_manager>
bool is_neg(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak) {
return ak == EN_MINUS_INFINITY || (ak == EN_NUMERAL && m.is_neg(a));
}
inline bool is_infinite(ext_numeral_kind ak) { return ak != EN_NUMERAL; }
template<typename numeral_manager>
void set(numeral_manager & m,
typename numeral_manager::numeral & a,
ext_numeral_kind & ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk) {
m.set(a, b);
ak = bk;
}
template<typename numeral_manager>
void reset(numeral_manager & m,
typename numeral_manager::numeral & a,
ext_numeral_kind & ak) {
m.reset(a);
ak = EN_NUMERAL;
}
template<typename numeral_manager>
void neg(numeral_manager & m,
typename numeral_manager::numeral & a,
ext_numeral_kind & ak) {
switch (ak) {
case EN_MINUS_INFINITY:
ak = EN_PLUS_INFINITY;
break;
case EN_NUMERAL:
m.neg(a);
break;
case EN_PLUS_INFINITY:
ak = EN_MINUS_INFINITY;
break;
}
}
template<typename numeral_manager>
void inv(numeral_manager & m,
typename numeral_manager::numeral & a,
ext_numeral_kind & ak) {
SASSERT(numeral_manager::field());
switch (ak) {
case EN_MINUS_INFINITY:
ak = EN_NUMERAL;
m.reset(a);
break;
case EN_NUMERAL:
SASSERT(!m.is_zero(a));
m.inv(a);
break;
case EN_PLUS_INFINITY:
ak = EN_NUMERAL;
m.reset(a);
break;
}
}
template<typename numeral_manager>
void add(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk,
typename numeral_manager::numeral & c,
ext_numeral_kind & ck) {
SASSERT(!(ak == EN_MINUS_INFINITY && bk == EN_PLUS_INFINITY)); // result is undefined
SASSERT(!(ak == EN_PLUS_INFINITY && bk == EN_MINUS_INFINITY)); // result is undefined
if (ak != EN_NUMERAL) {
m.reset(c);
ck = ak;
}
else if (bk != EN_NUMERAL) {
m.reset(c);
ck = bk;
}
else {
m.add(a, b, c);
ck = EN_NUMERAL;
}
}
template<typename numeral_manager>
void sub(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk,
typename numeral_manager::numeral & c,
ext_numeral_kind & ck) {
SASSERT(!(ak == EN_MINUS_INFINITY && bk == EN_MINUS_INFINITY)); // result is undefined
SASSERT(!(ak == EN_PLUS_INFINITY && bk == EN_PLUS_INFINITY)); // result is undefined
if (ak != EN_NUMERAL) {
SASSERT(bk != ak);
m.reset(c);
ck = ak;
}
else {
switch (bk) {
case EN_MINUS_INFINITY:
m.reset(c);
ck = EN_PLUS_INFINITY;
break;
case EN_NUMERAL:
m.sub(a, b, c);
ck = EN_NUMERAL;
break;
case EN_PLUS_INFINITY:
m.reset(c);
ck = EN_MINUS_INFINITY;
break;
}
}
}
template<typename numeral_manager>
void mul(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk,
typename numeral_manager::numeral & c,
ext_numeral_kind & ck) {
if (is_zero(m, a, ak) || is_zero(m, b, bk)) {
m.reset(c);
ck = EN_NUMERAL;
}
else if (is_infinite(ak) || is_infinite(bk)) {
if (is_pos(m, a, ak) == is_pos(m, b, bk))
ck = EN_PLUS_INFINITY;
else
ck = EN_MINUS_INFINITY;
m.reset(c);
}
else {
ck = EN_NUMERAL;
m.mul(a, b, c);
}
}
template<typename numeral_manager>
void div(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk,
typename numeral_manager::numeral & c,
ext_numeral_kind & ck) {
SASSERT(!is_zero(m, b, bk));
if (is_zero(m, a, ak)) {
SASSERT(!is_zero(m, b, bk));
m.reset(c);
ck = EN_NUMERAL;
}
else if (is_infinite(ak)) {
SASSERT(!is_infinite(bk));
if (is_pos(m, a, ak) == is_pos(m, b, bk))
ck = EN_PLUS_INFINITY;
else
ck = EN_MINUS_INFINITY;
m.reset(c);
}
else if (is_infinite(bk)) {
SASSERT(!is_infinite(ak));
m.reset(c);
ck = EN_NUMERAL;
}
else {
ck = EN_NUMERAL;
m.div(a, b, c);
}
}
template<typename numeral_manager>
void power(numeral_manager & m,
typename numeral_manager::numeral & a,
ext_numeral_kind & ak,
unsigned n) {
switch (ak) {
case EN_MINUS_INFINITY:
if (n % 2 == 0)
ak = EN_PLUS_INFINITY;
break;
case EN_NUMERAL:
m.power(a, n, a);
break;
case EN_PLUS_INFINITY:
break; // do nothing
}
}
/**
\brief Return true if (a,ak) == (b,bk).
*/
template<typename numeral_manager>
bool eq(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk) {
if (ak == EN_NUMERAL) {
return bk == EN_NUMERAL && m.eq(a, b);
}
else {
return ak == bk;
}
}
template<typename numeral_manager>
bool neq(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk) {
return !eq(m, a, ak, b, bk);
}
template<typename numeral_manager>
bool lt(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk) {
switch (ak) {
case EN_MINUS_INFINITY:
return bk != EN_MINUS_INFINITY;
case EN_NUMERAL:
switch (bk) {
case EN_MINUS_INFINITY:
return false;
case EN_NUMERAL:
return m.lt(a, b);
case EN_PLUS_INFINITY:
return true;
default:
UNREACHABLE();
return false;
}
case EN_PLUS_INFINITY:
return false;
default:
UNREACHABLE();
return false;
}
}
template<typename numeral_manager>
bool gt(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk) {
return lt(m, b, bk, a, ak);
}
template<typename numeral_manager>
bool le(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk) {
return !gt(m, a, ak, b, bk);
}
template<typename numeral_manager>
bool ge(numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak,
typename numeral_manager::numeral const & b,
ext_numeral_kind bk) {
return !lt(m, a, ak, b, bk);
}
template<typename numeral_manager>
void display(std::ostream & out,
numeral_manager & m,
typename numeral_manager::numeral const & a,
ext_numeral_kind ak) {
switch (ak) {
case EN_MINUS_INFINITY: out << "-oo"; break;
case EN_NUMERAL: m.display(out, a); break;
case EN_PLUS_INFINITY: out << "+oo"; break;
}
}
#endif

167
src/util/f2n.h Normal file
View file

@ -0,0 +1,167 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
f2n.h
Abstract:
Template for wrapping a float-like API as a numeral-like API.
The basic idea is to have the rounding mode as an implicit argument.
Author:
Leonardo de Moura (leonardo) 2012-07-30.
Revision History:
--*/
#ifndef __F2N_H_
#define __F2N_H_
#include"mpf.h"
template<typename fmanager>
class f2n {
public:
typedef typename fmanager::numeral numeral;
struct exception {};
private:
fmanager & m_manager;
mpf_rounding_mode m_mode;
unsigned m_ebits;
unsigned m_sbits;
numeral m_tmp1;
numeral m_one;
void check(numeral const & n) { if (!m().is_regular(n)) throw exception(); }
public:
static bool field() { return true; }
static bool precise() { return false; }
f2n(fmanager & m, unsigned ebits = 11, unsigned sbits = 53):m_manager(m), m_mode(MPF_ROUND_TOWARD_POSITIVE), m_ebits(ebits), m_sbits(sbits) {
m_manager.set(m_one, ebits, sbits, 1);
}
~f2n() {
m().del(m_tmp1);
m().del(m_one);
}
void set_rounding_mode(mpf_rounding_mode m) { m_mode = m; }
mpf_rounding_mode rounding_mode() const { return m_mode; }
void round_to_plus_inf() { m_mode = MPF_ROUND_TOWARD_POSITIVE; }
void round_to_minus_inf() { m_mode = MPF_ROUND_TOWARD_NEGATIVE; }
void set_rounding(bool to_plus_inf) { if (to_plus_inf) round_to_plus_inf(); else round_to_minus_inf(); }
unsigned ebits() const { return m_ebits; }
unsigned sbits() const { return m_sbits; }
fmanager & m() const { return m_manager; }
double to_double(numeral & x) const { return m().to_double(x); }
void del(numeral & x) { m().del(x); }
void abs(numeral & o) { m().abs(o); }
void abs(numeral const & x, numeral & o) { m().abs(x, o); }
void neg(numeral & o) { m().neg(o); }
void neg(numeral const & x, numeral & o) { m().neg(x, o); }
bool is_zero(numeral const & x) { return m().is_zero(x); }
bool is_neg(numeral const & x) { return m().is_neg(x) && !m().is_zero(x); /* it is not clear whether actual hardware returns true for is_neg(0-) */ }
bool is_pos(numeral const & x) { return m().is_pos(x) && !m().is_zero(x); }
bool is_nonneg(numeral const & x) { return !is_neg(x); }
bool is_nonpos(numeral const & x) { return !is_pos(x); }
void set(numeral & o, int value) { m().set(o, m_ebits, m_sbits, value); check(o); }
void set(numeral & o, int n, int d) { m().set(o, m_ebits, m_sbits, m_mode, n, d); check(o); }
void set(numeral & o, double x) { m().set(o, m_ebits, m_sbits, x); check(o); }
void set(numeral & o, unsigned value) { m().set(o, m_ebits, m_sbits, (double)value); check(o); }
void set(numeral & o, numeral const & x) { m().set(o, x); check(o); }
void set(numeral & o, mpq const & x) { m().set(o, m_ebits, m_sbits, m_mode, x); check(o); }
void reset(numeral & o) { m().reset(o, m_ebits, m_sbits); }
static void swap(numeral & x, numeral & y) { x.swap(y); }
void add(numeral const & x, numeral const & y, numeral & o) { m().add(m_mode, x, y, o); check(o); }
void sub(numeral const & x, numeral const & y, numeral & o) { m().sub(m_mode, x, y, o); check(o); }
void mul(numeral const & x, numeral const & y, numeral & o) { m().mul(m_mode, x, y, o); check(o); }
void div(numeral const & x, numeral const & y, numeral & o) { m().div(m_mode, x, y, o); check(o); }
void inv(numeral & o) { numeral a; set(a, 1); div(a, o, o); del(a); check(o); }
void inv(numeral const & x, numeral & o) { set(o, x); inv(o); }
void inc(numeral & x) { add(x, m_one, x); }
void dec(numeral & x) { sub(x, m_one, x); }
void power(numeral const & a, unsigned p, numeral & b) {
unsigned mask = 1;
numeral power;
set(power, a);
set(b, 1);
while (mask <= p) {
if (mask & p)
mul(b, power, b);
mul(power, power, power);
mask = mask << 1;
}
del(power);
check(b);
}
// Store the floor of a into b. Return true if a is an integer.
// Throws an exception if the result cannot be computed precisely.
void floor(numeral const & a, numeral & b) {
SASSERT(m().is_regular(a));
// Claim: If a is a regular float, then floor(a) is an integer that can be precisely represented.
// Justification: (for the case a is nonnegative)
// If 0 <= a > 2^sbits(), then a is an integer, and floor(a) == a
// If 0 <= a <= 2^sbits(), then floor(a) is representable since every integer less than 2^sbit
m().round_to_integral(MPF_ROUND_TOWARD_NEGATIVE, a, m_tmp1);
SASSERT(m().is_regular(m_tmp1));
if (m().le(m_tmp1, a)) {
m().set(b, m_tmp1);
}
else {
// the rounding mode doesn't matter for the following operation.
m().sub(MPF_ROUND_TOWARD_NEGATIVE, m_tmp1, m_one, b);
}
SASSERT(m().is_regular(b));
}
void ceil(numeral const & a, numeral & b) {
SASSERT(m().is_regular(a));
// See comment in floor
m().round_to_integral(MPF_ROUND_TOWARD_POSITIVE, a, m_tmp1);
SASSERT(m().is_regular(m_tmp1));
if (m().ge(m_tmp1, a)) {
m().set(b, m_tmp1);
}
else {
// the rounding mode doesn't matter for the following operation.
m().add(MPF_ROUND_TOWARD_NEGATIVE, m_tmp1, m_one, b);
}
SASSERT(m().is_regular(b));
}
unsigned prev_power_of_two(numeral const & a) { return m().prev_power_of_two(a); }
bool eq(numeral const & x, numeral const & y) { return m().eq(x, y); }
bool lt(numeral const & x, numeral const & y) { return m().lt(x, y); }
bool le(numeral const & x, numeral const & y) { return m().le(x, y); }
bool gt(numeral const & x, numeral const & y) { return m().gt(x, y); }
bool ge(numeral const & x, numeral const & y) { return m().ge(x, y); }
bool is_int(numeral const & x) { return m().is_int(x); }
bool is_one(numeral const & x) { return m().is_one(x); }
bool is_minus_one(numeral const & x) { numeral & _x = const_cast<numeral &>(x); m().neg(_x); bool r = m().is_one(_x); m().neg(_x); return r; }
std::string to_string(numeral const & a) { return m().to_string(a); }
std::string to_rational_string(numeral const & a) { return m().to_rational_string(a); }
void display(std::ostream & out, numeral const & a) { out << to_string(a); }
void display_decimal(std::ostream & out, numeral const & a, unsigned k) { m().display_decimal(out, a, k); }
void display_smt2(std::ostream & out, numeral const & a, bool decimal) { m().display_smt2(out, a, decimal); }
};
#endif

214
src/util/fvi.h Normal file
View file

@ -0,0 +1,214 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
fvi.h
Abstract:
Feature Vector Indexing.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _FVI_H_
#define _FVI_H_
#include"splay_tree_map.h"
#include"hashtable.h"
#include"vector.h"
/**
\brief A feature vector indexing for objects of type T *.
ToVector is a functor for converting T into a vector of natural numbers.
It should provide a method:
- void operator(T * d, unsigned * f);
This method should fill the vector f with the features of d.
Hash: functor for computing the hashcode of T *.
Eq : functor for comparing T pointers.
*/
template<typename T, typename ToVector, typename Hash, typename Eq=ptr_eq<T> >
class fvi : private ToVector {
public:
struct statistics {
unsigned m_size;
unsigned m_num_nodes;
unsigned m_num_leaves;
unsigned m_min_leaf_size;
unsigned m_avg_leaf_size;
unsigned m_max_leaf_size;
statistics() { reset(); }
void reset() {
m_size = m_num_nodes = m_num_leaves = m_avg_leaf_size = m_max_leaf_size = 0;
m_min_leaf_size = UINT_MAX;
}
};
private:
struct ucompare {
int operator()(unsigned i1, unsigned i2) const {
if (i1 < i2) return -1;
if (i1 > i2) return 1;
return 0;
}
};
struct node {
node() {}
virtual ~node() {}
virtual bool is_leaf() const = 0;
};
typedef splay_tree_map<unsigned, node *, ucompare> children;
struct non_leaf : public node {
children m_children;
non_leaf() {}
struct delete_children {
void operator()(unsigned k, node * n) const {
dealloc(n);
}
};
virtual ~non_leaf() {
delete_children visitor;
m_children.visit(visitor);
m_children.reset();
}
virtual bool is_leaf() const { return false; }
};
typedef ptr_hashtable<T, Hash, Eq> set;
struct leaf : public node {
set m_set;
leaf() {}
virtual ~leaf() {}
virtual bool is_leaf() const { return true; }
};
unsigned m_num_features;
svector<unsigned> m_tmp_buffer;
non_leaf * m_root;
struct stop {};
template<typename Visitor>
void visit_leaf(leaf * n, Visitor & v, bool le) const {
typename set::iterator it = n->m_set.begin();
typename set::iterator end = n->m_set.end();
for (; it != end; ++it)
if (!v(*it))
throw stop();
}
template<typename Visitor>
struct non_leaf_visitor {
fvi const & m_owner;
unsigned m_fidx;
Visitor & m_visitor;
bool m_le;
non_leaf_visitor(fvi const & o, unsigned fidx, Visitor & v, bool le):
m_owner(o), m_fidx(fidx), m_visitor(v), m_le(le) {}
void operator()(unsigned k, node * n) {
if (n->is_leaf())
m_owner.visit_leaf(static_cast<leaf*>(n), m_visitor, m_le);
else
m_owner.visit_non_leaf(static_cast<non_leaf*>(n), m_fidx + 1, m_visitor, m_le);
}
};
template<typename Visitor>
void visit_non_leaf(non_leaf * n, unsigned fidx, Visitor & v, bool le) const {
// Remark: this function is recursive, but there is no risk
// of stack overflow since the number of features is small.
non_leaf_visitor<Visitor> v2(*this, fidx, v, le);
if (le)
n->m_children.visit_le(v2, m_tmp_buffer[fidx]);
else
n->m_children.visit_ge(v2, m_tmp_buffer[fidx]);
}
#ifdef Z3DEBUG
bool m_visiting;
#endif
void to_fvector(T * d) const {
fvi * _this = const_cast<fvi *>(this);
_this->ToVector::operator()(d, _this->m_tmp_buffer.c_ptr());
}
struct non_leaf_stat_visitor {
fvi const & m_owner;
statistics & m_stats;
non_leaf_stat_visitor(fvi const & o, statistics & st):m_owner(o), m_stats(st) {}
void operator()(unsigned k, node * n);
};
void stats(leaf * n, statistics & result) const;
void stats(non_leaf * n, statistics & result) const;
struct non_leaf_collect_visitor {
fvi const & m_owner;
ptr_vector<T> & m_elems;
non_leaf_collect_visitor(fvi const & o, ptr_vector<T> & elems):m_owner(o), m_elems(elems) {}
void operator()(unsigned k, node * n);
};
void collect(leaf * n, ptr_vector<T> & result) const;
void collect(non_leaf * n, ptr_vector<T> & result) const;
public:
fvi(unsigned num_features, ToVector const & t = ToVector());
~fvi() { reset(); dealloc(m_root); }
void insert(T * d);
bool contains(T * d) const;
void erase(T * d);
void reset();
/**
\brief Traverse the elements that have features smaller (greater) or equal than the one of the given element.
For each visited element the following method of v is executed:
- bool operator()(T * d)
If false is returned, the traversal is aborted.
\warning The object cannot be updated during the traversal.
*/
template<typename Visitor>
void visit(T * d, Visitor & v, bool le = true) const {
DEBUG_CODE(const_cast<fvi*>(this)->m_visiting = true;);
to_fvector(d);
try {
visit_non_leaf(m_root, 0, v, le);
}
catch (stop) {
}
DEBUG_CODE(const_cast<fvi*>(this)->m_visiting = false;);
}
void stats(statistics & result) const;
/**
\brief Copy to result the set of elements stored in the index.
*/
void collect(ptr_vector<T> & result) const;
};
#endif /* _FVI_H_ */

199
src/util/fvi_def.h Normal file
View file

@ -0,0 +1,199 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
fvi_def.h
Abstract:
Feature Vector Indexing.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _FVI_DEF_H_
#define _FVI_DEF_H_
#include"fvi.h"
#include"splay_tree_def.h"
#include"buffer.h"
template<typename T, typename ToVector, typename Hash, typename Eq>
fvi<T, ToVector, Hash, Eq>::fvi(unsigned num_features, ToVector const & t):
ToVector(t),
m_num_features(num_features),
m_root(0) {
m_tmp_buffer.resize(num_features, 0);
m_root = alloc(non_leaf);
SASSERT(num_features >= 2);
DEBUG_CODE(m_visiting = false;);
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::reset() {
SASSERT(!m_visiting);
dealloc(m_root);
m_root = alloc(non_leaf);
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::insert(T * d) {
SASSERT(!m_visiting);
to_fvector(d);
non_leaf * n = m_root;
unsigned i = 0;
for (; i < m_num_features - 1; i++) {
node * child = 0;
if (!n->m_children.find(m_tmp_buffer[i], child)) {
child = alloc(non_leaf);
n->m_children.insert(m_tmp_buffer[i], child);
}
SASSERT(child);
SASSERT(!child->is_leaf());
n = static_cast<non_leaf*>(child);
}
node * l = 0;
SASSERT(i == m_num_features - 1);
if (!n->m_children.find(m_tmp_buffer[i], l)) {
l = alloc(leaf);
n->m_children.insert(m_tmp_buffer[i], l);
}
SASSERT(l);
SASSERT(l->is_leaf());
static_cast<leaf*>(l)->m_set.insert(d);
}
template<typename T, typename ToVector, typename Hash, typename Eq>
bool fvi<T, ToVector, Hash, Eq>::contains(T * d) const {
to_fvector(d);
non_leaf * n = m_root;
node * child;
unsigned i = 0;
for (; i < m_num_features - 1; i++) {
if (!n->m_children.find(m_tmp_buffer[i], child))
return false;
SASSERT(child);
SASSERT(!child->is_leaf());
n = static_cast<non_leaf*>(child);
}
SASSERT(i == m_num_features - 1);
return
n->m_children.find(m_tmp_buffer[i], child) &&
static_cast<leaf*>(child)->m_set.contains(d);
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::erase(T * d) {
SASSERT(!m_visiting);
SASSERT(contains(d));
ptr_buffer<non_leaf> path;
to_fvector(d);
non_leaf * n = m_root;
node * child;
unsigned i = 0;
for (; i < m_num_features - 1; i++) {
path.push_back(n);
if (!n->m_children.find(m_tmp_buffer[i], child)) {
UNREACHABLE();
}
SASSERT(child);
SASSERT(!child->is_leaf());
n = static_cast<non_leaf*>(child);
}
path.push_back(n);
SASSERT(i == m_num_features - 1);
if (!n->m_children.find(m_tmp_buffer[i], child)) {
UNREACHABLE();
}
SASSERT(child);
SASSERT(child->is_leaf());
leaf * l = static_cast<leaf*>(child);
l->m_set.erase(d);
if (l->m_set.empty()) {
dealloc(l);
while (true) {
non_leaf * n = path.back();
n->m_children.erase(m_tmp_buffer[i]);
path.pop_back();
i--;
if (!n->m_children.empty() || n == m_root)
break;
dealloc(n);
}
}
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::non_leaf_stat_visitor::operator()(unsigned k, node * n) {
if (n->is_leaf())
m_owner.stats(static_cast<leaf*>(n), m_stats);
else
m_owner.stats(static_cast<non_leaf*>(n), m_stats);
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::stats(leaf * n, statistics & result) const {
unsigned sz = n->m_set.size();
result.m_size += sz;
if (sz > result.m_max_leaf_size)
result.m_max_leaf_size = sz;
if (sz < result.m_min_leaf_size)
result.m_min_leaf_size = sz;
result.m_num_leaves ++;
result.m_num_nodes ++;
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::stats(non_leaf * n, statistics & result) const {
result.m_num_nodes++;
// Remark: this function is recursive, but there is no risk
// of stack overflow since the number of features is small.
non_leaf_stat_visitor v(*this, result);
n->m_children.visit(v);
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::stats(statistics & result) const {
result.reset();
stats(m_root, result);
if (m_root->m_children.empty())
result.m_min_leaf_size = 0;
if (result.m_num_leaves > 0)
result.m_avg_leaf_size = result.m_size / result.m_num_leaves;
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::non_leaf_collect_visitor::operator()(unsigned k, node * n) {
if (n->is_leaf())
m_owner.collect(static_cast<leaf*>(n), m_elems);
else
m_owner.collect(static_cast<non_leaf*>(n), m_elems);
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::collect(leaf * n, ptr_vector<T> & result) const {
typename set::iterator it = n->m_set.begin();
typename set::iterator end = n->m_set.end();
for (; it != end; ++it)
result.push_back(*it);
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::collect(non_leaf * n, ptr_vector<T> & result) const {
// Remark: this function is recursive, but there is no risk
// of stack overflow since the number of features is small.
non_leaf_collect_visitor v(*this, result);
n->m_children.visit(v);
}
template<typename T, typename ToVector, typename Hash, typename Eq>
void fvi<T, ToVector, Hash, Eq>::collect(ptr_vector<T> & result) const {
collect(m_root, result);
}
#endif /* _FVI_DEF_H_ */

85
src/util/hash.cpp Normal file
View file

@ -0,0 +1,85 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
hash.cpp
Abstract:
Basic hash computation support.
Author:
Leonardo de Moura (leonardo) 2006-09-11.
Revision History:
--*/
#include"debug.h"
#include"hash.h"
// I'm using Bob Jenkin's hash function.
// http://burtleburtle.net/bob/hash/doobs.html
unsigned string_hash(const char * str, unsigned length, unsigned init_value) {
register unsigned a, b, c, len;
/* Set up the internal state */
len = length;
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
c = init_value; /* the previous hash value */
/*---------------------------------------- handle most of the key */
while (len >= 12) {
a += reinterpret_cast<const unsigned *>(str)[0];
b += reinterpret_cast<const unsigned *>(str)[1];
c += reinterpret_cast<const unsigned *>(str)[2];
mix(a,b,c);
str += 12; len -= 12;
}
/*------------------------------------- handle the last 11 bytes */
c += length;
switch(len) { /* all the case statements fall through */
case 11:
c+=((unsigned)str[10]<<24);
__fallthrough;
case 10:
c+=((unsigned)str[9]<<16);
__fallthrough;
case 9 :
c+=((unsigned)str[8]<<8);
__fallthrough;
/* the first byte of c is reserved for the length */
case 8 :
b+=((unsigned)str[7]<<24);
__fallthrough;
case 7 :
b+=((unsigned)str[6]<<16);
__fallthrough;
case 6 :
b+=((unsigned)str[5]<<8);
__fallthrough;
case 5 :
b+=str[4];
__fallthrough;
case 4 :
a+=((unsigned)str[3]<<24);
__fallthrough;
case 3 :
a+=((unsigned)str[2]<<16);
__fallthrough;
case 2 :
a+=((unsigned)str[1]<<8);
__fallthrough;
case 1 :
a+=str[0];
__fallthrough;
/* case 0: nothing left to add */
}
mix(a,b,c);
/*-------------------------------------------- report the result */
return c;
}

247
src/util/hash.h Normal file
View file

@ -0,0 +1,247 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
hash.h
Abstract:
Basic hash computation support.
Author:
Leonardo de Moura (leonardo) 2006-09-11.
Revision History:
--*/
#ifndef _HASH_H_
#define _HASH_H_
#include<algorithm>
#ifndef __fallthrough
#define __fallthrough
#endif
#define mix(a,b,c) \
{ \
a -= b; a -= c; a ^= (c>>13); \
b -= c; b -= a; b ^= (a<<8); \
c -= a; c -= b; c ^= (b>>13); \
a -= b; a -= c; a ^= (c>>12); \
b -= c; b -= a; b ^= (a<<16); \
c -= a; c -= b; c ^= (b>>5); \
a -= b; a -= c; a ^= (c>>3); \
b -= c; b -= a; b ^= (a<<10); \
c -= a; c -= b; c ^= (b>>15); \
}
inline unsigned hash_u(unsigned a) {
a = (a+0x7ed55d16) + (a<<12);
a = (a^0xc761c23c) ^ (a>>19);
a = (a+0x165667b1) + (a<<5);
a = (a+0xd3a2646c) ^ (a<<9);
a = (a+0xfd7046c5) + (a<<3);
a = (a^0xb55a4f09) ^ (a>>16);
return a;
}
inline unsigned hash_ull(unsigned long long a) {
a = (~a) + (a << 18);
a ^= (a >> 31);
a += (a << 2) + (a << 4);
a ^= (a >> 11);
a += (a << 6);
a ^= (a >> 22);
return static_cast<unsigned>(a);
}
inline unsigned combine_hash(unsigned h1, unsigned h2) {
h2 -= h1; h2 ^= (h1 << 8);
h1 -= h2; h2 ^= (h1 << 16);
h2 -= h1; h2 ^= (h1 << 10);
return h2;
}
inline unsigned hash_u_u(unsigned a, unsigned b) {
return combine_hash(hash_u(a), hash_u(b));
}
unsigned string_hash(const char * str, unsigned len, unsigned init_value);
template<typename Composite, typename GetKindHashProc, typename GetChildHashProc>
unsigned get_composite_hash(Composite app, unsigned n, GetKindHashProc const & khasher = GetKindHashProc(), GetChildHashProc const & chasher = GetChildHashProc()) {
unsigned a, b, c;
SASSERT(n > 0);
unsigned kind_hash = khasher(app);
a = b = 0x9e3779b9;
c = 11;
switch (n) {
case 1:
a += kind_hash;
b = chasher(app, 0);
mix(a, b, c);
return c;
case 2:
a += kind_hash;
b += chasher(app, 0);
c += chasher(app, 1);
mix(a, b, c);
return c;
case 3:
a += chasher(app, 0);
b += chasher(app, 1);
c += chasher(app, 2);
mix(a, b, c);
a += kind_hash;
mix(a, b, c);
return c;
default:
while (n >= 3) {
n--;
a += chasher(app, n);
n--;
b += chasher(app, n);
n--;
c += chasher(app, n);
mix(a, b, c);
}
a += kind_hash;
switch (n) {
case 2:
b += chasher(app, 1);
__fallthrough;
case 1:
c += chasher(app, 0);
}
mix(a, b, c);
return c;
}
}
template<typename Composite>
struct default_kind_hash_proc { unsigned operator()(Composite const & c) const { return 17; } };
struct int_hash {
typedef int data;
unsigned operator()(int x) const { return static_cast<unsigned>(x); }
};
struct unsigned_hash {
typedef unsigned data;
unsigned operator()(unsigned x) const { return x; }
};
struct size_t_hash {
typedef size_t data;
unsigned operator()(size_t x) const { return static_cast<unsigned>(x); }
};
struct bool_hash {
typedef bool data;
unsigned operator()(bool x) const { return static_cast<unsigned>(x); }
};
template<typename T>
struct obj_hash {
typedef T data;
unsigned operator()(const T & e) const {
return e.hash();
}
};
template<typename T>
struct obj_ptr_hash {
typedef T * data;
unsigned operator()(T * a) const {
return a->hash();
}
};
template<typename T1, typename T2>
struct obj_ptr_pair_hash {
typedef std::pair<T1*, T2*> data;
unsigned operator()(data const & d) const {
return combine_hash(d.first->hash(), d.second->hash());
}
};
template<typename T1, typename T2, typename T3>
struct triple {
T1 first;
T2 second;
T3 third;
triple(): first(T1()), second(T2()), third(T3()) {}
triple(T1 f, T2 s, T3 t): first(f), second(s), third(t) {}
bool operator==(triple const& other) const {
return
first == other.first &&
second == other.second &&
third == other.third;
}
};
template<typename Hash1, typename Hash2, typename Hash3>
struct triple_hash : private Hash1 {
Hash2 m_hash2;
Hash3 m_hash3;
typedef triple<typename Hash1::data, typename Hash2::data, typename Hash3::data> data;
triple_hash(Hash1 const & h1 = Hash1(), Hash2 const & h2 = Hash2(), Hash3 const & h3 = Hash3()):
Hash1(h1),
m_hash2(h2),
m_hash3(h3) {
}
unsigned operator()(std::pair<typename Hash1::data, typename Hash2::data> const & p) const {
return combine_hash(combine_hash(Hash1::operator()(p.first), m_hash2.operator()(p.second)), m_hash3.operator()(p.third));
}
};
template<typename T1, typename T2, typename T3>
struct obj_ptr_triple_hash {
typedef triple<T1*, T2*, T3*> data;
unsigned operator()(data const & d) const {
return combine_hash(combine_hash(d.first->hash(), d.second->hash()), d.third->hash());
}
};
template<typename Hash1, typename Hash2>
struct pair_hash : private Hash1 {
Hash2 m_hash2;
typedef std::pair<typename Hash1::data, typename Hash2::data> data;
pair_hash(Hash1 const & h1 = Hash1(), Hash2 const & h2 = Hash2()):
Hash1(h1),
m_hash2(h2) {
}
unsigned operator()(std::pair<typename Hash1::data, typename Hash2::data> const & p) const {
return combine_hash(Hash1::operator()(p.first), m_hash2.operator()(p.second));
}
};
template<typename T>
inline unsigned get_ptr_hash(T * ptr) {
return static_cast<unsigned>(reinterpret_cast<size_t>(ptr));
}
template<typename T>
struct ptr_hash {
typedef T * data;
unsigned operator()(T * ptr) const {
return get_ptr_hash(ptr);
}
};
inline unsigned mk_mix(unsigned a, unsigned b, unsigned c) {
mix(a, b, c);
return c;
}
#endif /* _HASH_H_ */

643
src/util/hashtable.h Normal file
View file

@ -0,0 +1,643 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
hashtable.h
Abstract:
Hashtable without buckets.
Author:
Leonardo de Moura (leonardo) 2006-09-11.
Revision History:
--*/
#ifndef _HASHTABLE_H_
#define _HASHTABLE_H_
#include"debug.h"
#include<ostream>
#include"util.h"
#include"limits.h"
#include"memory_manager.h"
#include"hash.h"
#define DEFAULT_HASHTABLE_INITIAL_CAPACITY 8
#define SMALL_TABLE_CAPACITY 64
// #define HASHTABLE_STATISTICS
#ifdef HASHTABLE_STATISTICS
#define HS_CODE(CODE) { CODE }
#else
#define HS_CODE(CODE)
#endif
typedef enum { HT_FREE,
HT_DELETED,
HT_USED } hash_entry_state;
template<typename T>
class default_hash_entry {
unsigned m_hash; //!< cached hash code
hash_entry_state m_state;
T m_data;
public:
typedef T data;
default_hash_entry():m_state(HT_FREE) {}
unsigned get_hash() const { return m_hash; }
bool is_free() const { return m_state == HT_FREE; }
bool is_deleted() const { return m_state == HT_DELETED; }
bool is_used() const { return m_state == HT_USED; }
T & get_data() { return m_data; }
const T & get_data() const { return m_data; }
void set_data(const T & d) { m_data = d; m_state = HT_USED; }
void set_hash(unsigned h) { m_hash = h; }
void mark_as_deleted() { m_state = HT_DELETED; }
void mark_as_free() { m_state = HT_FREE; }
};
/**
\brief Special entry for a hashtable of integers. This entry "steals" two values for representing HT_FREE and HT_DELETED.
*/
template<int Free, int Deleted>
class int_hash_entry {
unsigned m_hash; //!< cached hash code
int m_data;
public:
typedef int data;
int_hash_entry():m_data(Free) {}
unsigned get_hash() const { return m_hash; }
bool is_free() const { return m_data == Free; }
bool is_deleted() const { return m_data == Deleted; }
bool is_used() const { return m_data != Free && m_data != Deleted; }
int get_data() const { return m_data; }
int & get_data() { return m_data; }
void set_data(int d) { m_data = d; }
void set_hash(unsigned h) { m_hash = h; }
void mark_as_deleted() { m_data = Deleted; }
void mark_as_free() { m_data = Free; }
};
/**
\brief Special entry for a hashtable of pointers. This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED.
*/
template<typename T>
class ptr_hash_entry {
unsigned m_hash; //!< cached hash code
T * m_ptr;
public:
typedef T * data;
ptr_hash_entry():m_ptr(0) {}
unsigned get_hash() const { return m_hash; }
bool is_free() const { return m_ptr == 0; }
bool is_deleted() const { return m_ptr == reinterpret_cast<T *>(1); }
bool is_used() const { return m_ptr != reinterpret_cast<T *>(0) && m_ptr != reinterpret_cast<T *>(1); }
T * get_data() const { return m_ptr; }
T * & get_data() { return m_ptr; }
void set_data(T * d) { m_ptr = d; }
void set_hash(unsigned h) { m_hash = h; }
void mark_as_deleted() { m_ptr = reinterpret_cast<T *>(1); }
void mark_as_free() { m_ptr = 0; }
};
/**
\brief Special entry for a hashtable of pointers which uses the pointer itself as the hashcode.
This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED.
*/
template<typename T>
class ptr_addr_hash_entry : public ptr_hash_entry<T> {
T * m_ptr;
public:
typedef T * data;
ptr_addr_hash_entry():m_ptr(0) {}
unsigned get_hash() const { return get_ptr_hash(m_ptr); }
bool is_free() const { return m_ptr == 0; }
bool is_deleted() const { return m_ptr == reinterpret_cast<T *>(1); }
bool is_used() const { return m_ptr != reinterpret_cast<T *>(0) && m_ptr != reinterpret_cast<T *>(1); }
T * get_data() const { return m_ptr; }
T * & get_data() { return m_ptr; }
void set_data(T * d) { m_ptr = d; }
void set_hash(unsigned h) { SASSERT(h == get_ptr_hash(m_ptr)); /* do nothing */ }
void mark_as_deleted() { m_ptr = reinterpret_cast<T *>(1); }
void mark_as_free() { m_ptr = 0; }
};
template<typename Entry, typename HashProc, typename EqProc>
class core_hashtable : private HashProc, private EqProc {
protected:
Entry * m_table;
unsigned m_capacity;
unsigned m_size;
unsigned m_num_deleted;
#ifdef HASHTABLE_STATISTICS
unsigned long long m_st_collision;
#endif
Entry* alloc_table(unsigned size) {
Entry* entries = alloc_vect<Entry>(size);
return entries;
}
void delete_table() {
dealloc_vect(m_table, m_capacity);
m_table = 0;
}
public:
typedef typename Entry::data data;
typedef Entry entry;
protected:
unsigned get_hash(data const & e) const { return HashProc::operator()(e); }
bool equals(data const & e1, data const & e2) const { return EqProc::operator()(e1, e2); }
static void copy_table(entry * source, unsigned source_capacity, entry * target, unsigned target_capacity) {
SASSERT(target_capacity >= source_capacity);
unsigned target_mask = target_capacity - 1;
entry * source_end = source + source_capacity;
entry * target_end = target + target_capacity;
for (entry * source_curr = source; source_curr != source_end; ++source_curr) {
if (source_curr->is_used()) {
unsigned hash = source_curr->get_hash();
unsigned idx = hash & target_mask;
entry * target_begin = target + idx;
entry * target_curr = target_begin;
for (; target_curr != target_end; ++target_curr) {
SASSERT(!target_curr->is_deleted());
if (target_curr->is_free()) {
*target_curr = *source_curr;
goto end;
}
}
for (target_curr = target; target_curr != target_begin; ++target_curr) {
SASSERT(!target_curr->is_deleted());
if (target_curr->is_free()) {
*target_curr = *source_curr;
goto end;
}
}
UNREACHABLE();
end:
;
}
}
}
void expand_table() {
unsigned new_capacity = m_capacity << 1;
entry * new_table = alloc_table(new_capacity);
copy_table(m_table, m_capacity, new_table, new_capacity);
delete_table();
m_table = new_table;
m_capacity = new_capacity;
m_num_deleted = 0;
}
void remove_deleted_entries() {
if (memory::is_out_of_memory())
return;
entry * new_table = alloc_table(m_capacity);
copy_table(m_table, m_capacity, new_table, m_capacity);
delete_table();
m_table = new_table;
m_num_deleted = 0;
}
public:
core_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY,
HashProc const & h = HashProc(),
EqProc const & e = EqProc()):
HashProc(h),
EqProc(e) {
SASSERT(is_power_of_two(initial_capacity));
m_table = alloc_table(initial_capacity);
m_capacity = initial_capacity;
m_size = 0;
m_num_deleted = 0;
HS_CODE({
m_st_collision = 0;
});
}
core_hashtable(const core_hashtable & source):
HashProc(source),
EqProc(source) {
m_capacity = source.m_capacity;
m_table = alloc_table(m_capacity);
copy_table(source.m_table, m_capacity, m_table, m_capacity);
m_size = source.m_size;
m_num_deleted = 0;
HS_CODE({
m_st_collision = 0;
});
}
~core_hashtable() {
delete_table();
}
void swap(core_hashtable & source) {
std::swap(m_table, source.m_table);
std::swap(m_capacity, source.m_capacity);
std::swap(m_size, source.m_size);
std::swap(m_num_deleted, source.m_num_deleted);
HS_CODE({
std::swap(m_st_collision, source.m_st_collision);
});
}
void reset() {
if (m_size == 0 && m_num_deleted == 0)
return;
unsigned overhead = 0;
entry * curr = m_table;
entry * end = m_table + m_capacity;
for (; curr != end; ++curr) {
if (!curr->is_free())
curr->mark_as_free();
else
overhead++;
}
if (m_capacity > 16 && overhead << 2 > (m_capacity * 3)) {
delete_table();
SASSERT(m_capacity > 16);
SASSERT(is_power_of_two(m_capacity));
m_capacity = (m_capacity >> 1);
SASSERT(is_power_of_two(m_capacity));
m_table = alloc_table(m_capacity);
}
m_size = 0;
m_num_deleted = 0;
}
void finalize() {
if (m_capacity > SMALL_TABLE_CAPACITY) {
delete_table();
m_table = alloc_table(SMALL_TABLE_CAPACITY);
m_capacity = SMALL_TABLE_CAPACITY;
m_size = 0;
m_num_deleted = 0;
}
else {
reset();
}
}
class iterator {
entry * m_curr;
entry * m_end;
void move_to_used() {
while (m_curr != m_end && !m_curr->is_used()) {
m_curr++;
}
}
public:
iterator(entry * start, entry * end): m_curr(start), m_end(end) { move_to_used(); }
data & operator*() { return m_curr->get_data(); }
data const & operator*() const { return m_curr->get_data(); }
data const * operator->() const { return &(operator*()); }
data * operator->() { return &(operator*()); }
iterator & operator++() { ++m_curr; move_to_used(); return *this; }
iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
bool operator==(iterator const & it) const { return m_curr == it.m_curr; }
bool operator!=(iterator const & it) const { return m_curr != it.m_curr; }
};
bool empty() const { return m_size == 0; }
unsigned size() const { return m_size; }
unsigned capacity() const { return m_capacity; }
iterator begin() const { return iterator(m_table, m_table + m_capacity); }
iterator end() const { return iterator(m_table + m_capacity, m_table + m_capacity); }
#define INSERT_LOOP_BODY() { \
if (curr->is_used()) { \
if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \
curr->set_data(e); \
return; \
} \
HS_CODE(m_st_collision++;); \
} \
else if (curr->is_free()) { \
entry * new_entry; \
if (del_entry) { new_entry = del_entry; m_num_deleted--; } \
else { new_entry = curr; } \
new_entry->set_data(e); \
new_entry->set_hash(hash); \
m_size++; \
return; \
} \
else { \
SASSERT(curr->is_deleted()); \
del_entry = curr; \
HS_CODE(m_st_collision++;); \
} \
} ((void) 0)
void insert(data const & e) {
if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) {
// if ((m_size + m_num_deleted) * 2 > (m_capacity)) {
expand_table();
}
unsigned hash = get_hash(e);
unsigned mask = m_capacity - 1;
unsigned idx = hash & mask;
entry * begin = m_table + idx;
entry * end = m_table + m_capacity;
entry * curr = begin;
entry * del_entry = 0;
for (; curr != end; ++curr) {
INSERT_LOOP_BODY();
}
for (curr = m_table; curr != begin; ++curr) {
INSERT_LOOP_BODY();
}
UNREACHABLE();
}
#define INSERT_LOOP_CORE_BODY() { \
if (curr->is_used()) { \
if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \
et = curr; \
return false; \
} \
HS_CODE(m_st_collision++;); \
} \
else if (curr->is_free()) { \
entry * new_entry; \
if (del_entry) { new_entry = del_entry; m_num_deleted--; } \
else { new_entry = curr; } \
new_entry->set_data(e); \
new_entry->set_hash(hash); \
m_size++; \
et = new_entry; \
return true; \
} \
else { \
SASSERT(curr->is_deleted()); \
del_entry = curr; \
HS_CODE(m_st_collision++;); \
} \
} ((void) 0)
/**
\brief Insert the element e if it is not in the table.
Return true if it is a new element, and false otherwise.
Store the entry/slot of the table in et.
*/
bool insert_if_not_there_core(data const & e, entry * & et) {
if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) {
// if ((m_size + m_num_deleted) * 2 > (m_capacity)) {
expand_table();
}
unsigned hash = get_hash(e);
unsigned mask = m_capacity - 1;
unsigned idx = hash & mask;
entry * begin = m_table + idx;
entry * end = m_table + m_capacity;
entry * curr = begin;
entry * del_entry = 0;
for (; curr != end; ++curr) {
INSERT_LOOP_CORE_BODY();
}
for (curr = m_table; curr != begin; ++curr) {
INSERT_LOOP_CORE_BODY();
}
UNREACHABLE();
return 0;
}
/**
\brief Insert the element e if it is not in the table.
Return a reference to e or to an object identical to e
that was already in the table.
*/
data const & insert_if_not_there(data const & e) {
entry * et;
insert_if_not_there_core(e, et);
return et->get_data();
}
/**
\brief Insert the element e if it is not in the table.
Return the entry that contains e.
*/
entry * insert_if_not_there2(data const & e) {
entry * et;
insert_if_not_there_core(e, et);
return et;
}
#define FIND_LOOP_BODY() { \
if (curr->is_used()) { \
if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \
return curr; \
} \
HS_CODE(const_cast<core_hashtable*>(this)->m_st_collision++;); \
} \
else if (curr->is_free()) { \
return 0; \
} \
HS_CODE(const_cast<core_hashtable*>(this)->m_st_collision++;); \
} ((void) 0)
entry * find_core(data const & e) const {
unsigned hash = get_hash(e);
unsigned mask = m_capacity - 1;
unsigned idx = hash & mask;
entry * begin = m_table + idx;
entry * end = m_table + m_capacity;
entry * curr = begin;
for (; curr != end; ++curr) {
FIND_LOOP_BODY();
}
for (curr = m_table; curr != begin; ++curr) {
FIND_LOOP_BODY();
}
return 0;
}
bool find(data const & k, data & r) const {
entry * e = find_core(k);
if (e != 0) {
r = e->get_data();
return true;
}
return false;
}
bool contains(data const & e) const {
return find_core(e) != 0;
}
iterator find(data const & e) const {
entry * r = find_core(e);
if (r) {
return iterator(r, m_table + m_capacity);
}
else {
return end();
}
}
#define REMOVE_LOOP_BODY() { \
if (curr->is_used()) { \
if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \
goto end_remove; \
} \
HS_CODE(m_st_collision++;); \
} \
else if (curr->is_free()) { \
return; \
} \
HS_CODE(m_st_collision++;); \
} ((void) 0)
void remove(data const & e) {
unsigned hash = get_hash(e);
unsigned mask = m_capacity - 1;
unsigned idx = hash & mask;
entry * begin = m_table + idx;
entry * end = m_table + m_capacity;
entry * curr = begin;
for (; curr != end; ++curr) {
REMOVE_LOOP_BODY();
}
for (curr = m_table; curr != begin; ++curr) {
REMOVE_LOOP_BODY();
}
SASSERT(!contains(e));
return; // node is not in the table
end_remove:
entry * next = curr + 1;
if (next == end) {
next = m_table;
}
if (next->is_free()) {
curr->mark_as_free();
m_size--;
}
else {
curr->mark_as_deleted();
m_num_deleted++;
m_size--;
if (m_num_deleted > m_size && m_num_deleted > SMALL_TABLE_CAPACITY) {
remove_deleted_entries();
}
}
}
void erase(data const & e) { remove(e); }
void dump(std::ostream & out) {
entry * curr = m_table;
entry * end = m_table + m_capacity;
out << "[";
bool first = true;
for (; curr != end; ++curr) {
if (curr->is_used()) {
if (first) {
first = false;
}
else {
out << " ";
}
out << curr->get_data();
}
}
out << "]";
}
#ifdef Z3DEBUG
bool check_invariant() {
entry * curr = m_table;
entry * end = m_table + m_capacity;
unsigned num_deleted = 0;
unsigned num_used = 0;
for (; curr != end; ++curr) {
if (curr->is_deleted()) {
num_deleted ++;
}
if (curr->is_used()) {
num_used++;
}
}
SASSERT(num_deleted == m_num_deleted);
SASSERT(num_used == m_size);
return true;
}
#endif
#ifdef HASHTABLE_STATISTICS
unsigned long long get_num_collision() const { return m_st_collision; }
#else
unsigned long long get_num_collision() const { return 0; }
#endif
private:
core_hashtable& operator=(core_hashtable const&);
};
template<typename T, typename HashProc, typename EqProc>
class hashtable : public core_hashtable<default_hash_entry<T>, HashProc, EqProc> {
public:
hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY,
HashProc const & h = HashProc(),
EqProc const & e = EqProc()):
core_hashtable<default_hash_entry<T>, HashProc, EqProc>(initial_capacity, h, e) {}
};
template<typename T, typename HashProc, typename EqProc>
class ptr_hashtable : public core_hashtable<ptr_hash_entry<T>, HashProc, EqProc> {
public:
ptr_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY,
HashProc const & h = HashProc(),
EqProc const & e = EqProc()):
core_hashtable<ptr_hash_entry<T>, HashProc, EqProc>(initial_capacity, h, e) {}
};
/**
\brief Hashtable of pointers which use the pointer as the hash-code.
*/
template<typename T>
class ptr_addr_hashtable : public core_hashtable<ptr_addr_hash_entry<T>, ptr_hash<T>, ptr_eq<T> > {
public:
typedef typename core_hashtable<ptr_addr_hash_entry<T>, ptr_hash<T>, ptr_eq<T> >::iterator iterator;
ptr_addr_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY):
core_hashtable<ptr_addr_hash_entry<T>, ptr_hash<T>, ptr_eq<T> >(initial_capacity) {}
// Using iterators to traverse the elements of this kind of hashtable will produce non-determinism.
iterator begin() const {
UNREACHABLE();
}
iterator end() const {
UNREACHABLE();
}
};
/**
\brief Simple int_hashtable. The values INT_MIN and INT_MIN + 1 are used to mark
deleted and free slots. So, these values cannot be stored in the table. Use core_hashtable
template to avoid this limitation.
*/
template<typename HashProc, typename EqProc>
class int_hashtable : public core_hashtable<int_hash_entry<INT_MIN, INT_MIN + 1>, HashProc, EqProc> {
public:
int_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY,
HashProc const & h = HashProc(),
EqProc const & e = EqProc()):
core_hashtable<int_hash_entry<INT_MIN, INT_MIN + 1>, HashProc, EqProc>(initial_capacity, h, e) {}
};
#endif /* _HASHTABLE_H_ */

245
src/util/heap.h Normal file
View file

@ -0,0 +1,245 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
heap.h
Abstract:
A heap of integers.
Author:
Leonardo de Moura (leonardo) 2006-09-14.
Revision History:
--*/
#ifndef _HEAP_H_
#define _HEAP_H_
#include"vector.h"
#include"debug.h"
template<typename LT>
class heap : private LT {
int_vector m_values;
int_vector m_value2indices;
bool less_than(int v1, int v2) const {
return LT::operator()(v1, v2);
}
static int left(int i) {
return i << 1;
}
static int right(int i) {
return (i << 1) + 1;
}
static int parent(int i) {
return i >> 1;
}
#ifdef Z3DEBUG
// Return true if the value can be inserted in the heap. That is, the vector m_value2indices is big enough to store this value.
bool is_valid_value(int v) const {
SASSERT(v >= 0 && v < static_cast<int>(m_value2indices.size()));
return true;
}
bool check_invariant_core(int idx) const {
if (idx < static_cast<int>(m_values.size())) {
SASSERT(m_value2indices[m_values[idx]] == idx);
SASSERT(parent(idx) == 0 || !less_than(m_values[idx], m_values[parent(idx)]));
SASSERT(check_invariant_core(left(idx)));
SASSERT(check_invariant_core(right(idx)));
}
return true;
}
public:
bool check_invariant() const {
return check_invariant_core(1);
}
#endif
private:
void move_up(int idx) {
int val = m_values[idx];
while (true) {
int parent_idx = parent(idx);
if (parent_idx == 0 || !less_than(val, m_values[parent_idx])) {
break;
}
m_values[idx] = m_values[parent_idx];
m_value2indices[m_values[idx]] = idx;
idx = parent_idx;
}
m_values[idx] = val;
m_value2indices[val] = idx;
CASSERT("heap", check_invariant());
}
void move_down(int idx) {
int val = m_values[idx];
int sz = static_cast<int>(m_values.size());
while (true) {
int left_idx = left(idx);
if (left_idx >= sz) {
break;
}
int right_idx = right(idx);
int min_idx = right_idx < sz && less_than(m_values[right_idx], m_values[left_idx]) ? right_idx : left_idx;
SASSERT(parent(min_idx) == idx);
int min_value = m_values[min_idx];
if (!less_than(min_value, val)) {
break;
}
m_values[idx] = min_value;
m_value2indices[min_value] = idx;
idx = min_idx;
}
m_values[idx] = val;
m_value2indices[val] = idx;
CASSERT("heap", check_invariant());
}
public:
typedef int * iterator;
typedef const int * const_iterator;
heap(int s, const LT & lt = LT()):LT(lt) {
m_values.push_back(-1);
set_bounds(s);
}
bool empty() const {
return m_values.size() == 1;
}
bool contains(int val) const {
return val < static_cast<int>(m_value2indices.size()) && m_value2indices[val] != 0;
}
void reset() {
if (empty()) {
return;
}
memset(m_value2indices.begin(), 0, sizeof(int) * m_value2indices.size());
m_values.reset();
m_values.push_back(-1);
}
void clear() {
reset();
}
void set_bounds(int s) {
m_value2indices.resize(s, 0);
}
unsigned get_bounds() const {
return m_value2indices.size();
}
void reserve(int s) {
if (s > static_cast<int>(m_value2indices.size()))
set_bounds(s);
}
int min_value() const {
SASSERT(!empty());
return m_values[1];
}
int erase_min() {
SASSERT(!empty());
SASSERT(m_values.size() >= 2);
int result = m_values[1];
if (m_values.size() == 2) {
m_value2indices[result] = 0;
m_values.pop_back();
SASSERT(empty());
}
else {
int last_val = m_values.back();
m_values[1] = last_val;
m_value2indices[last_val] = 1;
m_value2indices[result] = 0;
m_values.pop_back();
move_down(1);
}
CASSERT("heap", check_invariant());
return result;
}
void erase(int val) {
SASSERT(contains(val));
int idx = m_value2indices[val];
if (idx == static_cast<int>(m_values.size()) - 1) {
m_value2indices[val] = 0;
m_values.pop_back();
}
else {
int last_val = m_values.back();
m_values[idx] = last_val;
m_value2indices[last_val] = idx;
m_value2indices[val] = 0;
m_values.pop_back();
int parent_idx = parent(idx);
if (parent_idx != 0 && less_than(last_val, m_values[parent(idx)])) {
move_up(idx);
}
else {
move_down(idx);
}
}
CASSERT("heap", check_invariant());
}
void decreased(int val) {
SASSERT(contains(val));
move_up(m_value2indices[val]);
}
void increased(int val) {
SASSERT(contains(val));
move_down(m_value2indices[val]);
}
void insert(int val) {
SASSERT(is_valid_value(val));
int idx = static_cast<int>(m_values.size());
m_value2indices[val] = idx;
m_values.push_back(val);
SASSERT(idx == static_cast<int>(m_values.size()) - 1);
move_up(idx);
}
iterator begin() {
return m_values.begin() + 1;
}
iterator end() {
return m_values.end();
}
const_iterator begin() const {
return m_values.begin() + 1;
}
const_iterator end() const {
return m_values.end();
}
void swap(heap & other) {
m_values.swap(other.m_values);
m_value2indices.swap(other.m_value2indices);
}
};
#endif /* _HEAP_H_ */

646
src/util/hwf.cpp Normal file
View file

@ -0,0 +1,646 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
hwf.cpp
Abstract:
Hardware Floating Point Numbers
Author:
Christoph Wintersteiger (cwinter) 2012-07-30.
Revision History:
--*/
#include<float.h>
#ifdef _WINDOWS
#pragma float_control( except, on ) // exception semantics; this does _not_ mean that exceptions are enabled (we want them off!)
#pragma float_control( precise, on ) // precise semantics (no guessing!)
#pragma fp_contract(off) // contractions off (`contraction' means x*y+z is turned into a fused-mul-add).
#pragma fenv_access(on) // fpu environment sensitivity (needed to be allowed to make FPU mode changes).
#else
#pragma STDC FENV_ACCESS ON
#include <math.h>
#include <fenv.h>
#endif
#ifndef _M_IA64
#define USE_INTRINSICS
#endif
#include<sstream>
#include"hwf.h"
// Note:
// Which FPU will be used is determined by compiler settings. On x64 it's always SSE2,
// on x86 we have to chose SSE2 by enabling /arch:SSE2 (otherwise the x87 FPU will be used).
// Christoph has decided that we don't want to use the x87; this makes everything a lot easier.
// For SSE2, it is best to use compiler intrinsics because this makes it completely
// clear to the compiler what instructions should be used. E.g., for sqrt(), the Windows compiler selects
// the x87 FPU, even when /arch:SSE2 is on.
// Luckily, these are kind of standardized, at least for Windows/Linux/OSX.
#include <emmintrin.h>
hwf_manager::hwf_manager() :
m_mpz_manager(m_mpq_manager)
{
#ifdef _WINDOWS
#if defined(_AMD64_) || defined(_M_IA64)
// Precision control is not supported on x64.
// See: http://msdn.microsoft.com/en-us/library/e9b52ceh(VS.110).aspx
// CMW: I think this is okay though, the compiler will chose the right instructions
// (the x64/SSE2 FPU has separate instructions for different precisions).
#else
// Setting the precision should only be required on the x87, but it won't hurt to do it anyways.
// _PC_53 means double precision (53 significand bits). For extended precision use _PC_64.
#ifndef USE_INTRINSICS
__control87_2(_PC_53, _MCW_PC, &x86_state, &sse2_state);
#endif
#endif
#else
// OSX/Linux: Nothing.
#endif
// We only set the precision of the FPU here in the constructor. At the moment, there are no
// other parts of the code that could overwrite this, and Windows takes care of context switches.
// CMW: I'm not sure what happens on CPUs with hyper-threading (since the FPU is shared).
// I have yet to discover whether Linux and OSX save the FPU state when switching context.
// As long as we stick to using the SSE2 FPU though, there shouldn't be any problems with respect
// to the precision (not sure about the rounding modes though).
}
hwf_manager::~hwf_manager()
{
}
#define RAW(X) (*reinterpret_cast<const uint64*>(&(X)))
#define DBL(X) (*reinterpret_cast<const double*>(&(X)))
void hwf_manager::set(hwf & o, int value) {
o.value = (double) value;
}
void hwf_manager::set(hwf & o, mpf_rounding_mode rm, int n, int d) {
set_rounding_mode(rm);
o.value = ((double) n)/((double) d);
}
void hwf_manager::set(hwf & o, double value) {
o.value = value;
}
void hwf_manager::set(hwf & o, float value) {
o.value = (double)value;
}
void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & value) {
set_rounding_mode(rm);
o.value = m_mpq_manager.get_double(value);
}
void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) {
// We expect [i].[f]P[e], where P means that the exponent is interpreted as 2^e instead of 10^e.
std::string v(value);
size_t e_pos = v.find('p');
if (e_pos == std::string::npos) e_pos = v.find('P');
std::string f, e;
f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v;
e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0";
TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;);
mpq q;
m_mpq_manager.set(q, f.c_str());
mpz ex;
m_mpz_manager.set(ex, e.c_str());
set(o, rm, q, ex);
TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;);
}
void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent) {
// Assumption: this represents significand * 2^exponent.
set_rounding_mode(rm);
mpq sig;
m_mpq_manager.set(sig, significand);
int64 exp = m_mpz_manager.get_int64(exponent);
if (m_mpq_manager.is_zero(significand))
o.value = 0.0;
else
{
while (m_mpq_manager.lt(sig, 1))
{
m_mpq_manager.mul(sig, 2, sig);
exp--;
}
hwf s; s.value = m_mpq_manager.get_double(sig);
uint64 r = (RAW(s.value) & 0x800FFFFFFFFFFFFFull) | ((exp + 1023) << 52);
o.value = DBL(r);
}
}
void hwf_manager::set(hwf & o, bool sign, uint64 significand, int exponent) {
// Assumption: this represents (sign * -1) * (significand/2^sbits) * 2^exponent.
SASSERT(significand <= 0x000FFFFFFFFFFFFFull);
SASSERT(-1022 <= exponent && exponent <= 1023);
uint64 raw = (sign?0x8000000000000000ull:0);
raw |= (((uint64)exponent) + 1023) << 52;
raw |= significand;
o.value = *reinterpret_cast<double*>(&raw);
}
void hwf_manager::set(hwf & o, hwf const & x) {
o.value = x.value;
}
void hwf_manager::abs(hwf & o) {
o.value = fabs(o.value);
}
void hwf_manager::abs(hwf const & x, hwf & o) {
o.value = fabs(x.value);
}
void hwf_manager::neg(hwf & o) {
o.value = -o.value;
}
void hwf_manager::neg(hwf const & x, hwf & o) {
o.value = -x.value;
}
bool hwf_manager::eq(hwf const & x, hwf const & y) {
return (x.value == y.value);
}
bool hwf_manager::lt(hwf const & x, hwf const & y) {
return (x.value < y.value);
}
bool hwf_manager::lte(hwf const & x, hwf const & y) {
return (x.value <= y.value);
}
bool hwf_manager::gt(hwf const & x, hwf const & y) {
return (x.value > y.value);
}
bool hwf_manager::gte(hwf const & x, hwf const & y) {
return (x.value >= y.value);
}
void hwf_manager::add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) {
set_rounding_mode(rm);
#ifdef USE_INTRINSICS
_mm_store_sd(&o.value, _mm_add_sd(_mm_set_sd(x.value), _mm_set_sd(y.value)));
#else
o.value = x.value + y.value;
#endif
}
void hwf_manager::sub(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) {
set_rounding_mode(rm);
#ifdef USE_INTRINSICS
_mm_store_sd(&o.value, _mm_sub_sd(_mm_set_sd(x.value), _mm_set_sd(y.value)));
#else
o.value = x.value - y.value;
#endif
}
#define DBL_SCALE 15360
void hwf_manager::mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) {
set_rounding_mode(rm);
#ifdef USE_INTRINSICS
_mm_store_sd(&o.value, _mm_mul_sd(_mm_set_sd(x.value), _mm_set_sd(y.value)));
#else
o.value = x.value * y.value;
#endif
#if 0
// On the x86 FPU (x87), we use custom assembly routines because
// the code generated for x*y and x/y suffers from the double
// rounding on underflow problem. The scaling trick is described
// in Roger Golliver: `Efficiently producing default orthogonal IEEE
// double results using extended IEEE hardware', see
// http://www.open-std.org/JTC1/SC22/JSG/docs/m3/docs/jsgn326.pdf
// CMW: Tthis is not really needed if we use only the SSE2 FPU,
// it shouldn't hurt the performance too much though.
static const int const1 = -DBL_SCALE;
static const int const2 = +DBL_SCALE;
double xv = x.value;
double yv = y.value;
double & ov = o.value;
__asm {
fild const1;
fld xv;
fscale;
fstp st(1);
fmul yv;
fild const2;
fxch st(1);
fscale;
fstp ov;
}
#endif
}
void hwf_manager::div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) {
set_rounding_mode(rm);
#ifdef USE_INTRINSICS
_mm_store_sd(&o.value, _mm_div_sd(_mm_set_sd(x.value), _mm_set_sd(y.value)));
#else
o.value = x.value / y.value;
#endif
#if 0
// see mul(...)
static const int const1 = -DBL_SCALE;
static const int const2 = +DBL_SCALE;
double xv = x.value;
double yv = y.value;
double & ov = o.value;
__asm {
fild const1;
fld xv;
fscale;
fstp st(1);
fdiv yv;
fild const2;
fxch st(1);
fscale;
fstp ov;
}
#endif
}
#ifdef _M_IA64
#pragma fp_contract(on)
#endif
void hwf_manager::fused_mul_add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o) {
// CMW: fused_mul_add is not available on most CPUs. As of 2012, only Itanium,
// Intel Sandybridge and AMD Bulldozers support that (via AVX).
#ifdef _M_IA64
// IA64 (Itanium) will do it, if contractions are on.
set_rounding_mode(rm);
o.value = x.value * y.value + z.value;
#else
// NOT_IMPLEMENTED_YET();
// Just a dummy for now:
hwf t;
mul(rm, x, y, t);
add(rm, t, z, o);
#endif
}
#ifdef _M_IA64
#pragma fp_contract(off)
#endif
void hwf_manager::sqrt(mpf_rounding_mode rm, hwf const & x, hwf & o) {
set_rounding_mode(rm);
#ifdef USE_INTRINSICS
_mm_store_sd(&o.value, _mm_sqrt_pd(_mm_set_sd(x.value)));
#else
o.value = ::sqrt(x.value);
#endif
}
void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o) {
set_rounding_mode(rm);
modf(x.value, &o.value);
// Note: on x64, this sometimes produces an SNAN instead of a QNAN?
}
void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) {
// The built-in fmod() works, except for the special numbers.
if (is_inf(x) && is_inf(y))
o.value = x.value/y.value; // NaN
else if (is_inf(y))
o.value = x.value;
else
o.value = fmod(x.value, y.value);
// Here is an x87 alternative if the above makes problems; this may also be faster.
#if 0
double xv = x.value;
double yv = y.value;
double & ov = o.value;
// This is from: http://webster.cs.ucr.edu/AoA/DOS/ch14/CH14-4.html#HEADING4-173
__asm {
fld yv
fld xv
L: fprem1
fstsw ax // Get condition bits in AX.
test ah, 100b // See if C2 is set.
jnz L // Repeat if not done yet.
fstp ov // Store remainder away.
fstp st(0) // Pop old y value.
}
#endif
}
void hwf_manager::maximum(hwf const & x, hwf const & y, hwf & o) {
#ifdef USE_INTRINSICS
_mm_store_sd(&o.value, _mm_max_sd(_mm_set_sd(x.value), _mm_set_sd(y.value)));
#else
// use __max ?
if (is_nan(x))
o.value = y.value;
else if (is_nan(y))
o.value = x.value;
else if (lt(x, y))
o.value = y.value;
else
o.value = x.value;
#endif
}
void hwf_manager::minimum(hwf const & x, hwf const & y, hwf & o) {
#ifdef USE_INTRINSICS
_mm_store_sd(&o.value, _mm_min_sd(_mm_set_sd(x.value), _mm_set_sd(y.value)));
#else
// use __min ?
if (is_nan(x) || is_nan(x))
o.value = y.value;
else if (is_nan(y))
o.value = x.value;
else if (lt(x, y))
o.value = x.value;
else
o.value = y.value;
#endif
}
std::string hwf_manager::to_string(hwf const & x) {
std::stringstream ss("");
ss << std::scientific << x.value;
return ss.str();
}
std::string hwf_manager::to_rational_string(hwf const & a) {
// temporary hack
unsynch_mpq_manager qm;
scoped_mpq q(qm);
to_rational(a, q);
return qm.to_string(q);
}
void hwf_manager::display_decimal(std::ostream & out, hwf const & a, unsigned k) {
// temporary hack
unsynch_mpq_manager qm;
scoped_mpq q(qm);
to_rational(a, q);
qm.display_decimal(out, q, k);
}
void hwf_manager::display_smt2(std::ostream & out, hwf const & a, bool decimal) {
// temporary hack
unsynch_mpq_manager qm;
scoped_mpq q(qm);
to_rational(a, q);
qm.display_smt2(out, q, decimal);
}
void hwf_manager::to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o) {
SASSERT(is_normal(x) || is_denormal(x) || is_zero(x));
scoped_mpz n(qm), d(qm);
if (is_normal(x))
qm.set(n, sig(x) | 0x0010000000000000ull);
else
qm.set(n, sig(x));
if (sgn(x))
qm.neg(n);
qm.set(d, 0x0010000000000000ull);
int e = exp(x);
if (e >= 0)
qm.mul2k(n, (unsigned)e);
else
qm.mul2k(d, (unsigned)-e);
qm.set(o, n, d);
}
bool hwf_manager::is_zero(hwf const & x) {
uint64 t = RAW(x.value) & 0x7FFFFFFFFFFFFFFFull;
return (t == 0x0ull);
// CMW: I tried, and these are slower:
// return (t != 0x0ull) ? false : true;
// return (x.value == 0.0 || x.value == -0.0); // [uses SSE2].
}
bool hwf_manager::is_neg(hwf const & x) {
// [Leo]: I added !is_nan(x)
return sgn(x) && !is_nan(x);
}
bool hwf_manager::is_pos(hwf const & x) {
return !sgn(x) && !is_nan(x);
}
bool hwf_manager::is_nzero(hwf const & x) {
return RAW(x.value) == 0x8000000000000000ull;
}
bool hwf_manager::is_pzero(hwf const & x) {
return RAW(x.value) == 0x0000000000000000ull;
}
bool hwf_manager::is_one(hwf const & x) {
return RAW(x.value) == 0x3FF0000000000000ull;
}
bool hwf_manager::is_nan(hwf const & x) {
bool r = ((RAW(x.value) & 0x7FF0000000000000ull) == 0x7FF0000000000000ull) &&
((RAW(x.value) & 0x000FFFFFFFFFFFFFull) != 0x0);
#ifdef _WINDOWS
SASSERT( !r || (_fpclass(x.value) == _FPCLASS_SNAN || _fpclass(x.value) == _FPCLASS_QNAN));
#endif
return r;
}
bool hwf_manager::is_inf(hwf const & x) {
bool r = ((RAW(x.value) & 0x7FF0000000000000ull) == 0x7FF0000000000000ull) &&
((RAW(x.value) & 0x000FFFFFFFFFFFFFull) == 0x0);
#ifdef _WINDOWS
SASSERT( !r || (_fpclass(x.value) == _FPCLASS_NINF || _fpclass(x.value) == _FPCLASS_PINF));
#endif
return r;
}
bool hwf_manager::is_pinf(hwf const & x) {
return !sgn(x) && is_inf(x);
}
bool hwf_manager::is_ninf(hwf const & x) {
return sgn(x) && is_inf(x);
}
bool hwf_manager::is_normal(hwf const & x) {
uint64 t = RAW(x.value) & 0x7FF0000000000000ull;
return (t != 0x0ull && t != 0x7FF0000000000000ull);
}
bool hwf_manager::is_denormal(hwf const & x) {
uint64 t = RAW(x.value);
return ((t & 0x7FF0000000000000ull) == 0x0 &&
(t & 0x000FFFFFFFFFFFFFull) != 0x0);
}
bool hwf_manager::is_regular(hwf const & x) {
// Everything that doesn't have the top-exponent is considered regular.
// Note that +-0.0 and denormal numbers have exponent==0; these are regular.
// All normal numbers are also regular. What remains is +-Inf and NaN, they are
// not regular and they are the only numbers that have exponent 7FF.
uint64 e = RAW(x.value) & 0x7FF0000000000000ull; // the exponent
return (e != 0x7FF0000000000000ull);
}
bool hwf_manager::is_int(hwf const & x) {
if (!is_normal(x))
return false;
const int e = exp(x);
if (e >= 52)
return true;
else if (e < 0)
return false;
else
{
uint64 t = sig(x);
unsigned shift = 52 - ((unsigned)e);
uint64 mask = (0x1ull << shift) - 1;
return (t & mask) == 0;
}
}
void hwf_manager::mk_nzero(hwf & o) {
uint64 raw = 0x8000000000000000ull;
o.value = DBL(raw);
}
void hwf_manager::mk_pzero(hwf & o) {
o.value = 0;
}
void hwf_manager::mk_zero(bool sign, hwf & o) {
if (sign)
mk_nzero(o);
else
mk_pzero(o);
}
void hwf_manager::mk_nan(hwf & o) {
uint64 raw = 0x7FF0000000000001ull;
o.value = DBL(raw);
}
void hwf_manager::mk_inf(bool sign, hwf & o) {
uint64 raw = (sign) ? 0xFFF0000000000000ull : 0x7FF0000000000000ull;
o.value = DBL(raw);
}
void hwf_manager::mk_pinf(hwf & o) {
uint64 raw = 0x7FF0000000000000ull;
o.value = DBL(raw);
}
void hwf_manager::mk_ninf(hwf & o) {
uint64 raw = 0xFFF0000000000000ull;
o.value = DBL(raw);
}
#ifdef _WINDOWS
#if defined(_AMD64_) || defined(_M_IA64)
#ifdef USE_INTRINSICS
#define SETRM(RM) _MM_SET_ROUNDING_MODE(RM)
#else
#define SETRM(RM) _controlfp_s(&sse2_state, RM, _MCW_RC);
#endif
#else
#ifdef USE_INTRINSICS
#define SETRM(RM) _MM_SET_ROUNDING_MODE(RM)
#else
#define SETRM(RM) __control87_2(RM, _MCW_RC, &x86_state, &sse2_state)
#endif
#endif
#else
#define SETRM(RM) fesetround(RM)
#endif
unsigned hwf_manager::prev_power_of_two(hwf const & a) {
SASSERT(!is_nan(a) && !is_pinf(a) && !is_ninf(a));
if (!is_pos(a))
return 0;
if (exp(a) <= -52)
return 0;
return 51 + exp(a);
}
void hwf_manager::set_rounding_mode(mpf_rounding_mode rm)
{
#ifdef _WINDOWS
switch (rm) {
case MPF_ROUND_NEAREST_TEVEN:
SETRM(_RC_NEAR);
break;
case MPF_ROUND_TOWARD_POSITIVE:
SETRM(_RC_UP);
break;
case MPF_ROUND_TOWARD_NEGATIVE:
SETRM(_RC_DOWN);
break;
case MPF_ROUND_TOWARD_ZERO:
SETRM(_RC_CHOP);
break;
case MPF_ROUND_NEAREST_TAWAY:
default:
UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware!
}
#else // OSX/Linux
switch (rm) {
case MPF_ROUND_NEAREST_TEVEN:
SETRM(FE_TONEAREST);
break;
case MPF_ROUND_TOWARD_POSITIVE:
SETRM(FE_UPWARD);
break;
case MPF_ROUND_TOWARD_NEGATIVE:
SETRM(FE_DOWNWARD);
break;
case MPF_ROUND_TOWARD_ZERO:
SETRM(FE_TOWARDZERO);
break;
case MPF_ROUND_NEAREST_TAWAY:
default:
UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware!
}
#endif
}

174
src/util/hwf.h Normal file
View file

@ -0,0 +1,174 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
hwf.h
Abstract:
Hardware Floating Point Numbers
Author:
Christoph Wintersteiger (cwinter) 2012-07-30.
Revision History:
--*/
#ifndef _HWF_H_
#define _HWF_H_
#include<string>
#include"mpz.h"
#include"mpq.h"
#include"mpf.h" // we use the same rounding modes as mpf's
class hwf {
friend class hwf_manager;
double value;
hwf & operator=(hwf const & other) { UNREACHABLE(); return *this; }
public:
hwf() {}
hwf(hwf const & other) { this->value = other.value; }
~hwf() {}
void swap(hwf & other) { double t = value; value = other.value; other.value = t; }
};
class hwf_manager {
unsynch_mpq_manager m_mpq_manager;
unsynch_mpz_manager & m_mpz_manager; // A mpq_manager is a mpz_manager, reusing it.
public:
typedef hwf numeral;
hwf_manager();
~hwf_manager();
void reset(hwf & o) { set(o, 0); }
void set(hwf & o, int value);
void set(hwf & o, mpf_rounding_mode rm, int n, int d);
void set(hwf & o, float value);
void set(hwf & o, double value);
void set(hwf & o, mpf_rounding_mode rm, mpq const & value);
void set(hwf & o, mpf_rounding_mode rm, char const * value);
void set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent);
void set(hwf & o, bool sign, uint64 significand, int exponent);
void set(hwf & o, hwf const & x);
// auxiliary methods to make the interface compatible with mpf
void reset(hwf & o, unsigned ebits, unsigned sbits) { set(o, 0); }
void set(hwf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value) { set(o, rm, value); }
void set(hwf & o, unsigned ebits, unsigned sbits, int value) { set(o, value); }
void set(hwf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, int n, int d) { set(o, rm, n, d); }
void set(hwf & o, unsigned ebits, unsigned sbits, float value) { set(o, value); }
void set(hwf & o, unsigned ebits, unsigned sbits, double value) { set(o, value); }
void del(hwf & x) {}
void abs(hwf & o);
void abs(hwf const & x, hwf & o);
void neg(hwf & o);
void neg(hwf const & x, hwf & o);
bool is_zero(hwf const & x);
bool is_neg(hwf const & x);
bool is_pos(hwf const & x);
bool is_nzero(hwf const & x);
bool is_pzero(hwf const & x);
bool is_one(hwf const & x);
// structural eq
bool eq_core(hwf const & x, hwf const & y);
bool eq(hwf const & x, hwf const & y);
bool lt(hwf const & x, hwf const & y);
bool lte(hwf const & x, hwf const & y);
bool le(hwf const & x, hwf const & y) { return lte(x, y); }
bool gt(hwf const & x, hwf const & y);
bool gte(hwf const & x, hwf const & y);
bool ge(hwf const & x, hwf const & y) { return gte(x, y); }
void add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o);
void sub(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o);
void mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o);
void div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o);
void fused_mul_add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o);
void sqrt(mpf_rounding_mode rm, hwf const & x, hwf & o);
void round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o);
void rem(hwf const & x, hwf const & y, hwf & o);
void maximum(hwf const & x, hwf const & y, hwf & o);
void minimum(hwf const & x, hwf const & y, hwf & o);
std::string to_string(hwf const & a);
std::string to_rational_string(hwf const & a);
void display_decimal(std::ostream & out, hwf const & a, unsigned k);
void display_smt2(std::ostream & out, hwf const & a, bool decimal);
double to_double(hwf const & x) { return x.value; }
float to_float(hwf const & x) { return (float) x.value; }
void to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o);
void to_rational(hwf const & x, scoped_mpq & o) { to_rational(x, o.m(), o); }
bool sgn(hwf const & x) const {
uint64 raw = *reinterpret_cast<uint64 const *>(&x.value);
return (raw & 0x8000000000000000ull) != 0;
}
const uint64 sig(hwf const & x) const {
return *reinterpret_cast<uint64 const *>(&x.value) & 0x000FFFFFFFFFFFFFull;
}
const int exp(hwf const & x) const {
return ((*reinterpret_cast<uint64 const *>(&x.value) & 0x7FF0000000000000ull) >> 52) - 1023;
}
bool is_nan(hwf const & x);
bool is_inf(hwf const & x);
bool is_pinf(hwf const & x);
bool is_ninf(hwf const & x);
bool is_normal(hwf const & x);
bool is_denormal(hwf const & x);
bool is_regular(hwf const & x);
bool is_int(hwf const & x);
void mk_zero(bool sign, hwf & o);
void mk_nzero(hwf & o);
void mk_pzero(hwf & o);
void mk_nan(hwf & o);
void mk_inf(bool sign, hwf & o);
void mk_pinf(hwf & o);
void mk_ninf(hwf & o);
unsigned hash(hwf const & a) { return hash_ull(*reinterpret_cast<const unsigned long long*>(&a.value)); }
inline void set_rounding_mode(mpf_rounding_mode rm);
/**
\brief Return the biggest k s.t. 2^k <= a.
\remark Return 0 if a is not positive.
*/
unsigned prev_power_of_two(hwf const & a);
protected:
#ifdef _WINDOWS
unsigned x86_state, sse2_state;
#endif
};
typedef _scoped_numeral<hwf_manager> scoped_hwf;
typedef _scoped_numeral_vector<hwf_manager> scoped_hwf_vector;
#endif

83
src/util/id_gen.h Normal file
View file

@ -0,0 +1,83 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
id_gen.h
Abstract:
Basic support for generating & recycling ids.
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _ID_GEN_H_
#define _ID_GEN_H_
#include"vector.h"
#include"util.h"
class id_gen {
unsigned m_next_id;
unsigned_vector m_free_ids;
public:
id_gen(unsigned start = 0):m_next_id(start) {}
unsigned mk() {
unsigned r;
if (m_free_ids.empty()) {
r = m_next_id;
m_next_id++;
}
else {
r = m_free_ids.back();
m_free_ids.pop_back();
}
return r;
}
void recycle(unsigned id) {
if (memory::is_out_of_memory())
return;
m_free_ids.push_back(id);
}
void reset(unsigned start = 0) {
m_next_id = start;
m_free_ids.reset();
}
void cleanup(unsigned start = 0) {
m_next_id = start;
m_free_ids.finalize();
}
/**
\brief Return N if the range of ids generated by this module is in the set [0..N)
*/
unsigned get_id_range() const { return m_next_id; }
/**
\brief Debugging support method: set m_next_id to the least value id' s.t. id' >= id and id' is not in m_free_ids.
This method is only used to create small repros that exposes bugs in Z3.
*/
unsigned set_next_id(unsigned id) {
m_next_id = id;
while (std::find(m_free_ids.begin(), m_free_ids.end(), m_next_id) != m_free_ids.end())
m_next_id++;
return m_next_id;
}
void display_free_ids(std::ostream & out) {
::display(out, m_free_ids.begin(), m_free_ids.end());
}
};
#endif /* _ID_GEN_H_ */

3690
src/util/imdd.cpp Normal file

File diff suppressed because it is too large Load diff

849
src/util/imdd.h Normal file
View file

@ -0,0 +1,849 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
imdd.h
Abstract:
Interval based Multiple-valued Decision Diagrams.
Author:
Leonardo de Moura (leonardo) 2010-10-13.
Revision History:
--*/
#ifndef _IMDD_H_
#define _IMDD_H_
#include"id_gen.h"
#include"hashtable.h"
#include"map.h"
#include"obj_hashtable.h"
#include"obj_pair_hashtable.h"
#include"buffer.h"
#include"interval_skip_list.h"
#include"region.h"
#include"obj_ref.h"
class imdd;
class imdd_manager;
/**
\brief Manager for skip-lists used to implement IMDD nodes.
*/
class sl_imdd_manager : public random_level_manager {
imdd_manager * m_manager; // real manager
small_object_allocator & m_alloc;
friend class imdd_manager;
public:
sl_imdd_manager(small_object_allocator & alloc):m_alloc(alloc) {}
void * allocate(size_t size) { return m_alloc.allocate(size); }
void deallocate(size_t size, void* p) { m_alloc.deallocate(size, p); }
void inc_ref_eh(imdd * v);
void dec_ref_eh(imdd * v);
};
#define IMDD_BUCKET_CAPACITY 128
#define IMDD_MAX_LEVEL 32
typedef interval_skip_list<unsigned_interval_skip_list_traits<imdd*,
default_eq<imdd*>,
IMDD_BUCKET_CAPACITY,
IMDD_MAX_LEVEL,
true, /* support ref-counting */
sl_imdd_manager> > imdd_children;
typedef interval_skip_list<unsigned_interval_skip_list_traits<unsigned,
default_eq<unsigned>,
IMDD_BUCKET_CAPACITY,
IMDD_MAX_LEVEL,
false,
sl_manager_base<unsigned> > > sl_interval_set;
/*
Notes:
- We use reference counting for garbage collecting IMDDs nodes.
- Each IMDD node has a "memoized" flag. If the flag is true, the we use hash-consing for this node.
- The children of a memoized node must be memoized.
- The children of a non-memoized node may be memoized.
- The "memoized" flag cannot be reset after it was set.
- The result of some operations may be cached. We only use caching for
operations processing memoized nodes.
- For non-memoized nodes, if m_ref_count <= 1, destructive updates may be performed by some operations.
- IMPORTANT: "memoized" flag == false doesn't imply m_ref_count <= 1.
*/
/**
\brief IMDDs
*/
class imdd {
protected:
friend class imdd_manager;
unsigned m_id; //!< Unique ID
unsigned m_ref_count;
unsigned m_arity:30;
unsigned m_memoized:1;
unsigned m_dead:1;
imdd_children m_children;
void inc_ref() {
m_ref_count ++;
}
void dec_ref() {
SASSERT(m_ref_count > 0);
m_ref_count --;
}
void mark_as_memoized(bool flag = true) {
SASSERT(is_memoized() != flag);
m_memoized = flag;
}
void mark_as_dead() { SASSERT(!m_dead); m_dead = true; }
void replace_children(sl_imdd_manager & m, sbuffer<imdd_children::entry> & new_children);
public:
imdd(sl_imdd_manager & m, unsigned id, unsigned arity):m_id(id), m_ref_count(0), m_arity(arity), m_memoized(false), m_dead(false), m_children(m) {}
unsigned get_id() const { return m_id; }
unsigned get_ref_count() const { return m_ref_count; }
bool is_memoized() const { return m_memoized; }
bool is_shared() const { return m_ref_count > 1; }
bool is_dead() const { return m_dead; }
unsigned get_arity() const { return m_arity; }
imdd_children::iterator begin_children() const { return m_children.begin(); }
imdd_children::iterator end_children() const { return m_children.end(); }
unsigned hc_hash() const; // hash code for hash-consing.
bool hc_equal(imdd const * other) const; // eq function for hash-consing
bool empty() const { return m_children.empty(); }
unsigned hash() const { return m_id; }
unsigned memory() const { return sizeof(imdd) + m_children.memory() - sizeof(imdd_children); }
};
// -----------------------------------
//
// IMDD hash-consing
//
// -----------------------------------
// this is the internal hashing functor for hash-consing IMDDs.
struct imdd_hash_proc {
unsigned operator()(imdd const * d) const { return d->hc_hash(); }
};
// This is the internal comparison functor for hash-consing IMDDs.
struct imdd_eq_proc {
bool operator()(imdd const * d1, imdd const * d2) const { return d1->hc_equal(d2); }
};
typedef ptr_hashtable<imdd, imdd_hash_proc, imdd_eq_proc> imdd_table;
typedef obj_hashtable<imdd> imdd_cache;
typedef obj_map<imdd, imdd*> imdd2imdd_cache;
typedef obj_pair_map<imdd, imdd, imdd*> imdd_pair2imdd_cache;
typedef obj_pair_map<imdd, imdd, bool> imdd_pair2bool_cache;
typedef obj_map<imdd, sl_interval_set*> imdd2intervals;
typedef std::pair<imdd*, unsigned> imdd_value_pair;
struct fi_cache_entry {
imdd * m_d;
unsigned m_lower;
unsigned m_upper;
unsigned m_hash;
unsigned m_num_result;
imdd_value_pair m_result[0];
void mk_hash() {
m_hash = hash_u_u(m_d->get_id(), hash_u_u(m_lower, m_upper));
}
fi_cache_entry(imdd * d, unsigned l, unsigned u):
m_d(d),
m_lower(l),
m_upper(u) {
mk_hash();
}
fi_cache_entry(imdd * d, unsigned l, unsigned u, unsigned num, imdd_value_pair result[]):
m_d(d),
m_lower(l),
m_upper(u),
m_num_result(num) {
mk_hash();
memcpy(m_result, result, sizeof(imdd_value_pair)*num);
}
unsigned hash() const {
return m_hash;
}
bool operator==(fi_cache_entry const & other) const {
return
m_d == other.m_d &&
m_lower == other.m_lower &&
m_upper == other.m_upper;
}
};
typedef obj_hashtable<fi_cache_entry> imdd_fi_cache;
typedef union {
imdd * m_d;
fi_cache_entry * m_entry;
} mk_fi_result;
struct filter_cache_entry {
imdd * m_d;
imdd * m_r;
unsigned m_hash;
unsigned m_ctx_size;
unsigned m_ctx[0]; // lower and upper bounds that are part of the context.
static unsigned get_obj_size(unsigned ctx_size) {
return sizeof(filter_cache_entry) + ctx_size * sizeof(unsigned);
}
void mk_hash() {
if (m_ctx_size > 0)
m_hash = string_hash(reinterpret_cast<char *>(m_ctx), m_ctx_size * sizeof(unsigned), m_d->get_id());
else
m_hash = m_d->get_id();
}
filter_cache_entry(imdd * d, imdd * r, unsigned ctx_size, unsigned * ctx):
m_d(d),
m_r(r),
m_ctx_size(ctx_size) {
memcpy(m_ctx, ctx, sizeof(unsigned)*m_ctx_size);
mk_hash();
}
unsigned hash() const {
return m_hash;
}
bool operator==(filter_cache_entry const & other) const {
if (m_d != other.m_d)
return false;
if (m_ctx_size != other.m_ctx_size)
return false;
for (unsigned i = 0; i < m_ctx_size; i++)
if (m_ctx[i] != other.m_ctx[i])
return false;
return true;
}
};
typedef obj_hashtable<filter_cache_entry> imdd_mk_filter_cache;
typedef obj_ref<imdd, imdd_manager> imdd_ref;
class imdd_manager {
typedef imdd_children::entry entry;
small_object_allocator m_alloc;
id_gen m_id_gen;
vector<imdd_table> m_tables; // we keep a table for each height.
sl_imdd_manager m_sl_manager;
unsigned m_simple_max_entries; //!< maximum number of entries in a "simple" node.
bool m_delay_dealloc;
ptr_vector<imdd> m_to_delete; //!< list of IMDDs marked as dead. These IMDDs may still be in cache tables.
// generic cache and todo-lists
ptr_vector<imdd> m_worklist;
imdd_cache m_visited;
void mark_as_dead(imdd * d);
void deallocate_imdd(imdd * d);
void delete_imdd(imdd * d);
class delay_dealloc;
friend class delay_dealloc;
class delay_dealloc {
imdd_manager & m_manager;
bool m_delay_dealloc_value;
unsigned m_to_delete_size;
public:
delay_dealloc(imdd_manager & m):
m_manager(m),
m_delay_dealloc_value(m_manager.m_delay_dealloc),
m_to_delete_size(m_manager.m_to_delete.size()) {
m_manager.m_delay_dealloc = true;
}
~delay_dealloc();
};
bool is_simple_node(imdd * d) const;
void add_child(imdd * d, unsigned lower, unsigned upper, imdd * child) {
d->m_children.insert(m_sl_manager, lower, upper, child);
}
void add_child(imdd * d, unsigned value, imdd * child) {
add_child(d, value, value, child);
}
void remove_child(imdd * d, unsigned lower, unsigned upper) {
d->m_children.remove(m_sl_manager, lower, upper);
}
imdd * copy_main(imdd * d);
imdd * insert_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res);
imdd * remove_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res);
imdd2imdd_cache m_mk_product_cache;
struct null2imdd_proc;
struct mk_product_proc;
friend struct mk_product_proc;
imdd * mk_product_core(imdd * d1, imdd * d2, bool destructive, bool memoize);
imdd * mk_product_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res);
imdd2imdd_cache m_add_facts_cache;
ptr_vector<imdd> m_add_facts_new_children;
void init_add_facts_new_children(unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res);
imdd * add_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res);
imdd * add_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res);
imdd2imdd_cache m_remove_facts_cache;
imdd * remove_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res);
imdd * remove_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res);
imdd2imdd_cache m_defrag_cache;
imdd * defrag_core(imdd * d);
imdd_pair2imdd_cache m_union_cache;
void push_back_entries(unsigned head, imdd_children::iterator & it, imdd_children::iterator & end,
imdd_children::push_back_proc & push_back, bool & children_memoized);
void push_back_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned limit,
imdd_children::push_back_proc & push_back, bool & children_memoized);
void move_head(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned new_head);
void copy_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned limit, sbuffer<entry> & result);
void reset_union_cache();
imdd * mk_union_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res);
imdd * mk_union_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res);
void mk_union_core_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res);
void mk_union_core(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res);
imdd_pair2bool_cache m_is_equal_cache;
bool is_equal_core(imdd * d1, imdd * d2);
imdd_pair2bool_cache m_subsumes_cache;
bool subsumes_core(imdd * d1, imdd * d2);
imdd2imdd_cache m_complement_cache;
imdd * mk_complement_core(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res);
imdd * mk_complement_main(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res);
imdd2imdd_cache m_filter_equal_cache;
imdd * mk_filter_equal_core(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res);
imdd * mk_filter_equal_main(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res);
// original
imdd2intervals m_imdd2interval_set;
ptr_vector<sl_interval_set> m_alloc_is;
typedef sl_manager_base<unsigned> sl_imanager;
void reset_fi_intervals(sl_imanager& m);
sl_interval_set const* init_fi_intervals(sl_imanager& m, imdd* d, unsigned var, unsigned num_found);
imdd2imdd_cache m_fi_top_cache;
imdd_fi_cache m_fi_bottom_cache;
unsigned m_fi_num_vars;
unsigned * m_fi_begin_vars;
unsigned * m_fi_end_vars;
region m_fi_entries;
bool is_fi_var(unsigned v) const { return std::find(m_fi_begin_vars, m_fi_end_vars, v) != m_fi_end_vars; }
fi_cache_entry * mk_fi_cache_entry(imdd * d, unsigned lower, unsigned upper, unsigned num_pairs, imdd_value_pair pairs[]);
mk_fi_result mk_filter_identical_core(imdd * d, unsigned offset, unsigned num_found, unsigned lower, unsigned upper,
bool destructive, bool memoize_res);
imdd * mk_filter_identical_main(imdd * d, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res);
// v2
obj_map<imdd, imdd*> m_filter_identical_cache;
void filter_identical_core2(imdd* d, unsigned num_vars, unsigned b, unsigned e, ptr_vector<imdd>& ch);
imdd* filter_identical_core2(imdd* d, unsigned var, unsigned num_vars, bool memoize_res);
void filter_identical_main2(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res);
void swap_in(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars);
void swap_out(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars);
// v3
struct interval {
unsigned m_lo;
unsigned m_hi;
interval(unsigned lo, unsigned hi): m_lo(lo), m_hi(hi) {}
};
struct interval_dd : public interval {
imdd* m_dd;
interval_dd(unsigned lo, unsigned hi, imdd* d): interval(lo, hi), m_dd(d) {}
};
template<typename I>
class id_map {
unsigned m_T;
unsigned_vector m_Ts;
svector<svector<I>*> m_vecs;
unsigned_vector m_alloc;
unsigned m_first_free;
void hard_reset() {
std::for_each(m_vecs.begin(), m_vecs.end(), delete_proc<svector<I> >());
m_alloc.reset();
m_first_free = 0;
m_vecs.reset();
m_Ts.reset();
m_T = 0;
}
void allocate_entry(unsigned id) {
if (m_vecs[id]) {
return;
}
while (m_first_free < m_alloc.size()) {
if (m_vecs[m_first_free] && m_Ts[m_first_free] < m_T) {
svector<I>* v = m_vecs[m_first_free];
m_vecs[m_first_free] = 0;
m_vecs[id] = v;
++m_first_free;
return;
}
++m_first_free;
}
m_vecs[id] = alloc(svector<I>);
m_alloc.push_back(id);
}
public:
id_map():m_T(0) {}
~id_map() { hard_reset(); }
void reset() { ++m_T; m_first_free = 0; if (m_T == UINT_MAX) hard_reset(); }
svector<I>& init(imdd* d) {
unsigned id = d->get_id();
if (id >= m_vecs.size()) {
m_vecs.resize(id+1);
m_Ts.resize(id+1);
}
if (m_Ts[id] < m_T) {
allocate_entry(id);
m_vecs[id]->reset();
m_Ts[id] = m_T;
}
return *m_vecs[id];
}
typedef svector<I> data;
struct iterator {
unsigned m_offset;
id_map const& m;
iterator(unsigned o, id_map const& m):m_offset(o),m(m) {}
data const & operator*() const { return *m.m_vecs[m_offset]; }
data const * operator->() const { return &(operator*()); }
data * operator->() { return &(operator*()); }
iterator & operator++() { ++m_offset; return move_to_used(); }
iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
bool operator==(iterator const & it) const { return m_offset == it.m_offset; }
bool operator!=(iterator const & it) const { return m_offset != it.m_offset; }
iterator & move_to_used() {
while (m_offset < m.m_vecs.size() &&
m.m_Ts[m_offset] < m.m_T) {
++m_offset;
}
return *this;
}
};
iterator begin() const { return iterator(0, *this).move_to_used(); }
iterator end() const { return iterator(m_vecs.size(), *this); }
};
typedef id_map<interval > filter_id_map;
typedef id_map<interval_dd > filter_idd_map;
filter_id_map m_nodes;
filter_idd_map m_nodes_dd;
svector<interval_dd> m_i_nodes_dd, m_i_nodes_dd_tmp;
svector<interval> m_i_nodes, m_i_nodes_tmp;
unsigned_vector m_offsets;
void filter_identical_main3(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res);
void filter_identical_main3(imdd * d, imdd_ref& r, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res);
imdd* filter_identical_loop3(imdd * d, unsigned v1, bool del1, unsigned v2, bool del2, bool memoize_res);
void refine_intervals(svector<interval>& i_nodes, svector<interval_dd> const& i_nodes_dd);
void merge_intervals(svector<interval>& dst, svector<interval> const& src);
imdd* filter_identical_mk_nodes(imdd* d, unsigned v, bool del1, bool del2, bool memoize_res);
void print_filter_idd(std::ostream& out, filter_idd_map const& m);
void print_interval_dd(std::ostream& out, svector<interval_dd> const& m);
unsigned m_proj_num_vars;
unsigned * m_proj_begin_vars;
unsigned * m_proj_end_vars;
imdd2imdd_cache m_proj_cache;
bool is_proj_var(unsigned v) const { return std::find(m_proj_begin_vars, m_proj_end_vars, v) != m_proj_end_vars; }
void mk_project_init(unsigned num_vars, unsigned * vars);
void mk_project_core(imdd * d, imdd_ref & r, unsigned var, unsigned num_found, bool memoize_res);
void mk_project_dupdt_core(imdd_ref & d, unsigned var, unsigned num_found, bool memoize_res);
imdd2imdd_cache m_swap_cache;
imdd * m_swap_new_child;
bool m_swap_granchildren_memoized;
imdd * mk_swap_new_child(unsigned lower, unsigned upper, imdd * child);
void mk_swap_acc1_dupdt(imdd_ref & d, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res);
void mk_swap_acc1(imdd * d, imdd_ref & r, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res);
void mk_swap_acc2(imdd_ref & r, unsigned lower1, unsigned upper1, unsigned lower2, unsigned upper2, imdd * grandchild, bool memoize_res);
void mk_swap_top_vars(imdd * d, imdd_ref & r, bool memoize_res);
imdd * mk_swap_memoize(imdd * d);
void mk_swap_core(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res);
void mk_swap_dupdt_core(imdd_ref & d, unsigned vidx, bool memoize_res);
imdd2imdd_cache m_add_bounded_var_cache;
imdd * add_bounded_var_core(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res);
imdd * add_bounded_var_main(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res);
friend struct distinct_proc;
imdd * mk_distinct_imdd(unsigned l1, unsigned u1, unsigned l2, unsigned u2, imdd * d, bool memoize_res = true);
imdd_mk_filter_cache m_filter_cache;
region m_filter_entries;
unsigned m_filter_num_vars;
unsigned * m_filter_begin_vars;
unsigned * m_filter_end_vars;
unsigned_vector m_filter_context;
bool is_filter_var(unsigned v) const { return std::find(m_filter_begin_vars, m_filter_end_vars, v) != m_filter_end_vars; }
filter_cache_entry * mk_filter_cache_entry(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r);
imdd * is_mk_filter_cached(imdd * d, unsigned ctx_sz, unsigned * ctx);
void cache_mk_filter(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r);
void init_mk_filter(unsigned arity, unsigned num_vars, unsigned * vars);
template<typename FilterProc>
void mk_filter_dupdt_core(imdd_ref & d, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res);
template<typename FilterProc>
void mk_filter_core(imdd * d, imdd_ref & r, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res);
/**
\brief Filter the elements of the given IMDD using the given filter.
The FilterProc template parameter is a filter for computing subsets of sets of the form:
[L_1, U_1] X [L_2, U_2] X ... X [L_n, U_n] X d (where d is an IMDD)
where n == num_vars
The subset of elements is returned as an IMDD.
FilterProc must have a method of the form:
void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res);
The size of the array lowers_uppers is 2*num_vars
The arity of the resultant IMDD must be num_vars + d->get_arity().
*/
template<typename FilterProc>
void mk_filter_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res = true);
template<typename FilterProc>
void mk_filter(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res = true);
imdd * mk_disequal_imdd(unsigned l1, unsigned u1, unsigned value, imdd * d, bool memoize_res);
friend struct disequal_proc;
public:
imdd_manager();
void inc_ref(imdd * d) {
if (d)
d->inc_ref();
}
void dec_ref(imdd * d) {
if (d) {
d->dec_ref();
if (d->get_ref_count() == 0)
delete_imdd(d);
}
}
unsigned get_num_nodes(imdd const * d) const;
// count number of keys (rows) in table as if table is uncompressed.
size_t get_num_rows(imdd const* d) const;
unsigned memory(imdd const * d) const;
private:
imdd * _mk_empty(unsigned arity);
public:
imdd * mk_empty(unsigned arity) {
imdd * r = _mk_empty(arity);
STRACE("imdd_trace", tout << "mk_empty(" << arity << ", 0x" << r << ");\n";);
return r;
}
private:
imdd * memoize(imdd * d);
public:
void memoize(imdd_ref const & d, imdd_ref & r) { r = memoize(d.get()); }
void memoize(imdd_ref & d) { d = memoize(d.get()); }
imdd * memoize_new_imdd_if(bool cond, imdd * r) {
if (cond && is_simple_node(r)) {
SASSERT(!r->is_shared());
imdd * can_r = memoize(r);
if (can_r != r) {
SASSERT(r->get_ref_count() == 0);
delete_imdd(r);
}
return can_r;
}
return r;
}
public:
void defrag(imdd_ref & d);
void unmemoize(imdd * d);
void unmemoize_rec(imdd * d);
void copy(imdd * d, imdd_ref & r) { r = copy_main(d); }
void insert_dupdt(imdd_ref & d, unsigned b, unsigned e, bool memoize_res = true) {
d = insert_main(d, b, e, true, memoize_res);
}
void insert(imdd * d, imdd_ref & r, unsigned b, unsigned e, bool memoize_res = true) {
r = insert_main(d, b, e, false, memoize_res);
}
void mk_product_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res = true) {
d1 = mk_product_main(d1.get(), d2, true, memoize_res);
}
void mk_product(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res = true) {
r = mk_product_main(d1, d2, false, memoize_res);
STRACE("imdd_trace", tout << "mk_product(0x" << d1 << ", 0x" << d2 << ", 0x" << r.get() << ", " << memoize_res << ");\n";);
}
void mk_union_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res = true) {
d1 = mk_union_main(d1.get(), d2, true, memoize_res);
}
void mk_union(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res = true) {
r = mk_union_main(d1, d2, false, memoize_res);
STRACE("imdd_trace", tout << "mk_union(0x" << d1 << ", 0x" << d2 << ", 0x" << r.get() << ", " << memoize_res << ");\n";);
}
void mk_complement_dupdt(imdd_ref & d, unsigned num, unsigned const * mins, unsigned const * maxs, bool memoize_res = true) {
d = mk_complement_main(d, num, mins, maxs, true, memoize_res);
}
void mk_complement(imdd * d, imdd_ref & r, unsigned num, unsigned const * mins, unsigned const * maxs, bool memoize_res = true) {
r = mk_complement_main(d, num, mins, maxs, false, memoize_res);
STRACE("imdd_trace", tout << "mk_complement(0x" << d << ", 0x" << r.get() << ", ";
for (unsigned i = 0; i < num; i++) tout << mins[i] << ", " << maxs[i] << ", ";
tout << memoize_res << ");\n";);
}
void mk_filter_equal_dupdt(imdd_ref & d, unsigned vidx, unsigned value, bool memoize_res = true) {
d = mk_filter_equal_main(d, vidx, value, true, memoize_res);
}
void mk_filter_equal(imdd * d, imdd_ref & r, unsigned vidx, unsigned value, bool memoize_res = true) {
r = mk_filter_equal_main(d, vidx, value, false, memoize_res);
STRACE("imdd_trace", tout << "mk_filter_equal(0x" << d << ", 0x" << r.get() << ", " << vidx << ", " << value << ", " << memoize_res << ");\n";);
}
void mk_filter_identical_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res = true) {
// d = mk_filter_identical_main(d, num_vars, vars, true, memoize_res);
filter_identical_main3(d, d, num_vars, vars, true, memoize_res);
}
void mk_filter_identical(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res = true) {
filter_identical_main3(d, r, num_vars, vars, false, memoize_res);
STRACE("imdd_trace", tout << "mk_filter_identical(0x" << d << ", 0x" << r.get() << ", ";
for (unsigned i = 0; i < num_vars; i++) tout << vars[i] << ", ";
tout << memoize_res << ");\n";);
}
void mk_project_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res = true);
void mk_project(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res = true);
// swap vidx and vidx+1
void mk_swap_dupdt(imdd_ref & d, unsigned vidx, bool memoize_res = true);
// swap vidx and vidx+1
void mk_swap(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res = true);
void add_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res = true) {
d = add_facts_main(d, num, lowers, uppers, true, memoize_res);
}
void add_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res = true) {
r = add_facts_main(d, num, lowers, uppers, false, memoize_res);
STRACE("imdd_trace", tout << "add_facts(0x" << d << ", 0x" << r.get() << ", ";
for (unsigned i = 0; i < num; i++) tout << lowers[i] << ", " << uppers[i] << ", ";
tout << memoize_res << ");\n";);
}
void add_fact_dupdt(imdd_ref & d, unsigned num, unsigned const * values, bool memoize_res = true) {
add_facts_dupdt(d, num, values, values, memoize_res);
}
void add_fact(imdd * d, imdd_ref & r, unsigned num, unsigned const * values, bool memoize_res = true) {
add_facts(d, r, num, values, values, memoize_res);
}
void add_bounded_var_dupdt(imdd_ref & d, unsigned before_vidx, unsigned lower, unsigned upper, bool memoize_res = true) {
d = add_bounded_var_main(d, before_vidx, lower, upper, true, memoize_res);
}
void add_bounded_var(imdd * d, imdd_ref & r, unsigned before_vidx, unsigned lower, unsigned upper, bool memoize_res = true) {
r = add_bounded_var_main(d, before_vidx, lower, upper, false, memoize_res);
}
void mk_filter_distinct_dupdt(imdd_ref & d, unsigned v1, unsigned v2, bool memoize_res = true);
void mk_filter_distinct(imdd * d, imdd_ref & r, unsigned v1, unsigned v2, bool memoize_res = true);
void mk_filter_disequal_dupdt(imdd_ref & d, unsigned var, unsigned value, bool memoize_res = true);
void mk_filter_disequal(imdd * d, imdd_ref & r, unsigned var, unsigned value, bool memoize_res = true);
void mk_join(imdd * d1, imdd * d2, imdd_ref & r, unsigned_vector const& vars1, unsigned_vector const& vars2, bool memoize_res = true);
void mk_join_project(imdd * d1, imdd * d2, imdd_ref & r,
unsigned_vector const& vars1, unsigned_vector const& vars2,
unsigned_vector const& proj_vars, bool memoize_res = true);
void mk_join_dupdt(imdd_ref & d1, imdd * d2, unsigned num_vars, unsigned const * vars1, unsigned const * vars2, bool memoize_res = true);
void remove_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers);
void remove_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers);
bool is_equal(imdd * d1, imdd * d2);
bool contains(imdd * d, unsigned num, unsigned const * values) const;
bool contains(imdd * d, unsigned v) const { return contains(d, 1, &v); }
bool contains(imdd * d, unsigned v1, unsigned v2) const { unsigned vs[2] = {v1, v2}; return contains(d, 2, vs); }
bool contains(imdd * d, unsigned v1, unsigned v2, unsigned v3) const { unsigned vs[3] = {v1,v2,v3}; return contains(d, 3, vs); }
bool subsumes(imdd * d1, imdd * d2);
bool is_subset(imdd * d1, imdd * d2) { return subsumes(d2, d1); }
private:
void display(std::ostream & out, imdd const * d, unsigned_vector & intervals, bool & first) const;
public:
void display(std::ostream & out, imdd const * d) const;
void display_ll(std::ostream & out, imdd const * d) const;
/**
\brief Execute proc (once) in each node in the IMDD rooted by d.
*/
template<typename Proc>
void for_each(imdd * d, Proc & proc) const {
// for_each is a generic procedure, we don't know what proc will actually do.
// So, it is not safe to reuse m_worklist and m_visited.
ptr_buffer<imdd,128> worklist;
imdd_cache visited;
worklist.push_back(d);
while (!worklist.empty()) {
d = worklist.back();
worklist.pop_back();
if (d->is_shared() && visited.contains(d))
continue;
if (d->is_shared())
visited.insert(d);
proc(d);
if (d->get_arity() > 1) {
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it)
worklist.push_back(it->val());
}
}
}
class iterator {
bool m_done;
svector<imdd_children::iterator> m_iterators;
svector<unsigned> m_element;
void begin_iterators(imdd const * curr, unsigned start_idx);
public:
iterator():m_done(true) {}
iterator(imdd_manager const & m, imdd const * d);
unsigned get_arity() const { return m_element.size(); }
unsigned * operator*() const { return m_element.c_ptr(); }
iterator & operator++();
bool operator==(iterator const & it) const;
bool operator!=(iterator const & it) const { return !operator==(it); }
};
friend class iterator;
iterator begin(imdd const * d) const { return iterator(*this, d); }
iterator end(imdd const * d) const { return iterator(); }
};
inline std::ostream & operator<<(std::ostream & out, imdd_ref const & r) {
r.get_manager().display(out, r.get());
return out;
}
struct mk_imdd_pp {
imdd * m_d;
imdd_manager & m_manager;
mk_imdd_pp(imdd * d, imdd_manager & m):m_d(d), m_manager(m) {}
};
inline mk_imdd_pp mk_pp(imdd * d, imdd_manager & m) {
return mk_imdd_pp(d, m);
}
inline std::ostream & operator<<(std::ostream & out, mk_imdd_pp const & pp) {
pp.m_manager.display(out, pp.m_d);
return out;
}
struct mk_imdd_ll_pp : public mk_imdd_pp {
mk_imdd_ll_pp(imdd * d, imdd_manager & m):mk_imdd_pp(d, m) {}
};
inline mk_imdd_ll_pp mk_ll_pp(imdd * d, imdd_manager & m) {
return mk_imdd_ll_pp(d, m);
}
inline std::ostream & operator<<(std::ostream & out, mk_imdd_ll_pp const & pp) {
pp.m_manager.display_ll(out, pp.m_d);
return out;
}
#endif /* _IMDD_H_ */

View file

@ -0,0 +1,41 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
inf_int_rational.cpp
Abstract:
Rational numbers with infenitesimals
Author:
Nikolaj Bjorner (nbjorner) 2006-12-05.
Revision History:
--*/
#include<sstream>
#include"inf_int_rational.h"
inf_int_rational inf_int_rational::m_zero(0);
inf_int_rational inf_int_rational::m_one(1);
inf_int_rational inf_int_rational::m_minus_one(-1);
std::string inf_int_rational::to_string() const {
if (m_second == 0) {
return m_first.to_string();
}
std::ostringstream s;
s << "(" << m_first.to_string();
if (m_second < 0) {
s << " -e*" << (-m_second) << ")";
}
else {
s << " +e*" << m_second << ")";
}
return s.str();
}

372
src/util/inf_int_rational.h Normal file
View file

@ -0,0 +1,372 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
inf_int_rational.h
Abstract:
Rational numbers with infenitesimals
Author:
Leonardo de Moura (leonardo) 2006-09-18.
Nikolaj Bjorner (nbjorner) 2006-10-24.
Revision History:
--*/
#ifndef _INF_INT_RATIONAL_H_
#define _INF_INT_RATIONAL_H_
#include<stdlib.h>
#include<string>
#include"debug.h"
#include"vector.h"
#include"rational.h"
class inf_int_rational {
static inf_int_rational m_zero;
static inf_int_rational m_one;
static inf_int_rational m_minus_one;
rational m_first;
int m_second;
public:
unsigned hash() const {
return m_first.hash() ^ (static_cast<unsigned>(m_second) + 1);
}
struct hash_proc { unsigned operator()(inf_int_rational const& r) const { return r.hash(); } };
struct eq_proc { bool operator()(inf_int_rational const& r1, inf_int_rational const& r2) const { return r1 == r2; } };
void swap(inf_int_rational & n) {
m_first.swap(n.m_first);
std::swap(m_second, n.m_second);
}
std::string to_string() const;
inf_int_rational():
m_first(rational()),
m_second(0)
{}
inf_int_rational(const inf_int_rational & r):
m_first(r.m_first),
m_second(r.m_second)
{}
explicit inf_int_rational(int n):
m_first(rational(n)),
m_second(0)
{}
explicit inf_int_rational(int n, int d):
m_first(rational(n,d)),
m_second(0)
{}
explicit inf_int_rational(rational const& r, bool pos_inf):
m_first(r),
m_second(pos_inf?1:-1)
{}
explicit inf_int_rational(rational const& r):
m_first(r),
m_second(0) {}
inf_int_rational(rational const& r, int i):
m_first(r),
m_second(i) {
}
~inf_int_rational() {}
/**
\brief Set inf_int_rational to 0.
*/
void reset() {
m_first.reset();
m_second = 0;
}
bool is_int() const {
return m_first.is_int() && m_second == 0;
}
bool is_int64() const {
return m_first.is_int64() && m_second == 0;
}
bool is_uint64() const {
return m_first.is_uint64() && m_second == 0;
}
bool is_rational() const { return m_second == 0; }
int64 get_int64() const {
SASSERT(is_int64());
return m_first.get_int64();
}
uint64 get_uint64() const {
SASSERT(is_uint64());
return m_first.get_uint64();
}
rational const& get_rational() const {
return m_first;
}
rational get_infinitesimal() const {
return rational(m_second);
}
rational const & get_first() const { return m_first; }
inf_int_rational & operator=(const inf_int_rational & r) {
m_first = r.m_first;
m_second = r.m_second;
return *this;
}
inf_int_rational & operator=(const rational & r) {
m_first = r;
m_second = 0;
return *this;
}
friend inline inf_int_rational numerator(const inf_int_rational & r) {
SASSERT(r.m_second == 0);
return inf_int_rational(numerator(r.m_first));
}
friend inline inf_int_rational denominator(const inf_int_rational & r) {
SASSERT(r.m_second == 0);
return inf_int_rational(denominator(r.m_first));
}
inf_int_rational & operator+=(const inf_int_rational & r) {
m_first += r.m_first;
m_second += r.m_second;
return *this;
}
inf_int_rational & operator-=(const inf_int_rational & r) {
m_first -= r.m_first;
m_second -= r.m_second;
return *this;
}
inf_int_rational & operator+=(const rational & r) {
m_first += r;
return *this;
}
inf_int_rational & operator-=(const rational & r) {
m_first -= r;
return *this;
}
inf_int_rational & operator++() {
++m_first;
return *this;
}
const inf_int_rational operator++(int) { inf_int_rational tmp(*this); ++(*this); return tmp; }
inf_int_rational & operator--() {
--m_first;
return *this;
}
const inf_int_rational operator--(int) { inf_int_rational tmp(*this); --(*this); return tmp; }
friend inline bool operator==(const inf_int_rational & r1, const inf_int_rational & r2) {
return r1.m_first == r2.m_first && r1.m_second == r2.m_second;
}
friend inline bool operator==(const rational & r1, const inf_int_rational & r2) {
return r1 == r2.m_first && r2.m_second == 0;
}
friend inline bool operator==(const inf_int_rational & r1, const rational & r2) {
return r1.m_first == r2 && r1.m_second == 0;
}
friend inline bool operator<(const inf_int_rational & r1, const inf_int_rational & r2) {
return
(r1.m_first < r2.m_first) ||
(r1.m_first == r2.m_first && r1.m_second < r2.m_second);
}
friend inline bool operator<(const rational & r1, const inf_int_rational & r2) {
return
(r1 < r2.m_first) ||
(r1 == r2.m_first && r2.m_second > 0);
}
friend inline bool operator<(const inf_int_rational & r1, const rational & r2) {
return
(r1.m_first < r2) ||
(r1.m_first == r2 && r1.m_second < 0);
}
void neg() {
m_first.neg();
m_second = -m_second;
}
bool is_zero() const {
return m_first.is_zero() && m_second == 0;
}
bool is_one() const {
return m_first.is_one() && m_second == 0;
}
bool is_minus_one() const {
return m_first.is_minus_one() && m_second == 0;
}
bool is_neg() const {
return
m_first.is_neg() ||
(m_first.is_zero() && m_second < 0);
}
bool is_pos() const {
return
m_first.is_pos() ||
(m_first.is_zero() && m_second > 0);
}
bool is_nonneg() const {
return
m_first.is_pos() ||
(m_first.is_zero() && m_second >= 0);
}
bool is_nonpos() const {
return
m_first.is_neg() ||
(m_first.is_zero() && m_second <= 0);
}
friend inline rational floor(const inf_int_rational & r) {
if (r.m_first.is_int()) {
if (r.m_second >= 0) {
return r.m_first;
}
return r.m_first - rational(1);
}
return floor(r.m_first);
}
friend inline rational ceil(const inf_int_rational & r) {
if (r.m_first.is_int()) {
if (r.m_second <= 0) {
return r.m_first;
}
return r.m_first + rational(1);
}
return ceil(r.m_first);
}
static const inf_int_rational & zero() {
return m_zero;
}
static const inf_int_rational & one() {
return m_one;
}
static const inf_int_rational & minus_one() {
return m_minus_one;
}
};
inline bool operator!=(const inf_int_rational & r1, const inf_int_rational & r2) {
return !operator==(r1, r2);
}
inline bool operator!=(const rational & r1, const inf_int_rational & r2) {
return !operator==(r1, r2);
}
inline bool operator!=(const inf_int_rational & r1, const rational & r2) {
return !operator==(r1, r2);
}
inline bool operator>(const inf_int_rational & r1, const inf_int_rational & r2) {
return operator<(r2, r1);
}
inline bool operator>(const inf_int_rational & r1, const rational & r2) {
return operator<(r2, r1);
}
inline bool operator>(const rational & r1, const inf_int_rational & r2) {
return operator<(r2, r1);
}
inline bool operator<=(const inf_int_rational & r1, const inf_int_rational & r2) {
return !operator>(r1, r2);
}
inline bool operator<=(const rational & r1, const inf_int_rational & r2) {
return !operator>(r1, r2);
}
inline bool operator<=(const inf_int_rational & r1, const rational & r2) {
return !operator>(r1, r2);
}
inline bool operator>=(const inf_int_rational & r1, const inf_int_rational & r2) {
return !operator<(r1, r2);
}
inline bool operator>=(const rational & r1, const inf_int_rational & r2) {
return !operator<(r1, r2);
}
inline bool operator>=(const inf_int_rational & r1, const rational & r2) {
return !operator<(r1, r2);
}
inline inf_int_rational operator+(const inf_int_rational & r1, const inf_int_rational & r2) {
return inf_int_rational(r1) += r2;
}
inline inf_int_rational operator-(const inf_int_rational & r1, const inf_int_rational & r2) {
return inf_int_rational(r1) -= r2;
}
inline inf_int_rational operator-(const inf_int_rational & r) {
inf_int_rational result(r);
result.neg();
return result;
}
inline std::ostream & operator<<(std::ostream & target, const inf_int_rational & r)
{
target << r.to_string();
return target;
}
inline inf_int_rational abs(const inf_int_rational & r) {
inf_int_rational result(r);
if (result.is_neg()) {
result.neg();
}
return result;
}
#endif /* _INF_INT_RATIONAL_H_ */

179
src/util/inf_rational.cpp Normal file
View file

@ -0,0 +1,179 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
inf_rational.cpp
Abstract:
Rational numbers with infenitesimals
Author:
Nikolaj Bjorner (nbjorner) 2006-12-05.
Revision History:
--*/
#include"inf_rational.h"
inf_rational inf_rational::m_zero(0);
inf_rational inf_rational::m_one(1);
inf_rational inf_rational::m_minus_one(-1);
inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2)
{
inf_rational result;
result.m_first = r1.m_first * r2.m_first;
result.m_second = (r1.m_first * r2.m_second) + (r1.m_second * r2.m_first);
if (r1.m_second.is_pos() && r2.m_second.is_neg()) {
--result.m_second;
}
else if (r1.m_second.is_neg() && r2.m_second.is_pos()) {
--result.m_second;
}
return result;
}
inf_rational sup_mult(inf_rational const& r1, inf_rational const& r2)
{
inf_rational result;
result.m_first = r1.m_first * r2.m_first;
result.m_second = (r1.m_first * r2.m_second) + (r1.m_second * r2.m_first);
if (r1.m_second.is_pos() && r2.m_second.is_pos()) {
++result.m_second;
}
else if (r1.m_second.is_neg() && r2.m_second.is_neg()) {
++result.m_second;
}
return result;
}
//
// Find rationals c, x, such that c + epsilon*x <= r1/r2
//
// let r1 = a + d_1
// let r2 = b + d_2
//
// suppose b != 0:
//
// r1/b <= r1/r2
// <=> { if b > 0, then r2 > 0, and cross multiplication does not change the sign }
// { if b < 0, then r2 < 0, and cross multiplication changes sign twice }
// r1 * r2 <= b * r1
// <=>
// r1 * (b + d_2) <= r1 * b
// <=>
// r1 * d_2 <= 0
//
// if r1 * d_2 > 0, then r1/(b + sign_of(r1)*1/2*|b|) <= r1/r2
//
// Not handled here:
// if b = 0, then d_2 != 0
// if r1 * d_2 = 0 then it's 0.
// if r1 * d_2 > 0, then result is +oo
// if r1 * d_2 < 0, then result is -oo
//
inf_rational inf_div(inf_rational const& r1, inf_rational const& r2)
{
SASSERT(!r2.m_first.is_zero());
inf_rational result;
if (r2.m_second.is_neg() && r1.is_neg()) {
result = r1 / (r2.m_first - (abs(r2.m_first)/rational(2)));
}
else if (r2.m_second.is_pos() && r1.is_pos()) {
result = r1 / (r2.m_first + (abs(r2.m_first)/rational(2)));
}
else {
result = r1 / r2.m_first;
}
return result;
}
inf_rational sup_div(inf_rational const& r1, inf_rational const& r2)
{
SASSERT(!r2.m_first.is_zero());
inf_rational result;
if (r2.m_second.is_pos() && r1.is_neg()) {
result = r1 / (r2.m_first + (abs(r2.m_first)/rational(2)));
}
else if (r2.m_second.is_neg() && r1.is_pos()) {
result = r1 / (r2.m_first - (abs(r2.m_first)/rational(2)));
}
else {
result = r1 / r2.m_first;
}
return result;
}
inf_rational inf_power(inf_rational const& r, unsigned n)
{
bool is_even = (0 == (n & 0x1));
inf_rational result;
if (n == 1) {
result = r;
}
else if ((r.m_second.is_zero()) ||
(r.m_first.is_pos() && r.m_second.is_pos()) ||
(r.m_first.is_neg() && r.m_second.is_neg() && is_even)) {
result.m_first = r.m_first.expt(n);
}
else if (is_even) {
// 0 will work.
}
else if (r.m_first.is_zero()) {
result.m_first = rational(-1);
}
else if (r.m_first.is_pos()) {
result.m_first = rational(r.m_first - r.m_first/rational(2)).expt(n);
}
else {
result.m_first = rational(r.m_first + r.m_first/rational(2)).expt(n);
}
return result;
}
inf_rational sup_power(inf_rational const& r, unsigned n)
{
bool is_even = (0 == (n & 0x1));
inf_rational result;
if (n == 1) {
result = r;
}
else if (r.m_second.is_zero() ||
(r.m_first.is_pos() && r.m_second.is_neg()) ||
(r.m_first.is_neg() && r.m_second.is_pos() && is_even)) {
result.m_first = r.m_first.expt(n);
}
else if (r.m_first.is_zero() || (n == 0)) {
result.m_first = rational(1);
}
else if (r.m_first.is_pos() || is_even) {
result.m_first = rational(r.m_first + r.m_first/rational(2)).expt(n);
}
else {
// r (r.m_first) is negative, n is odd.
result.m_first = rational(r.m_first - r.m_first/rational(2)).expt(n);
}
return result;
}
inf_rational inf_root(inf_rational const& r, unsigned n)
{
SASSERT(!r.is_neg());
// use 0
return inf_rational();
}
inf_rational sup_root(inf_rational const& r, unsigned n)
{
SASSERT(!r.is_neg());
// use r.
return r;
}

470
src/util/inf_rational.h Normal file
View file

@ -0,0 +1,470 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
inf_rational.h
Abstract:
Rational numbers with infenitesimals
Author:
Leonardo de Moura (leonardo) 2006-09-18.
Nikolaj Bjorner (nbjorner) 2006-10-24.
Revision History:
--*/
#ifndef _INF_RATIONAL_H_
#define _INF_RATIONAL_H_
#include<stdlib.h>
#include<string>
#include"debug.h"
#include"vector.h"
#include"rational.h"
class inf_rational {
static inf_rational m_zero;
static inf_rational m_one;
static inf_rational m_minus_one;
rational m_first;
rational m_second;
public:
unsigned hash() const {
return m_first.hash() ^ (m_second.hash()+1);
}
struct hash_proc { unsigned operator()(inf_rational const& r) const { return r.hash(); } };
struct eq_proc { bool operator()(inf_rational const& r1, inf_rational const& r2) const { return r1 == r2; } };
void swap(inf_rational & n) {
m_first.swap(n.m_first);
m_second.swap(n.m_second);
}
std::string to_string() const {
if (m_second.is_zero()) {
return m_first.to_string();
}
std::string s = "(";
s += m_first.to_string();
if (m_second.is_neg()) {
s += " -e*";
}
else {
s += " +e*";
}
s += abs(m_second).to_string();
s += ")";
return s;
}
inf_rational():
m_first(rational()),
m_second(rational())
{}
inf_rational(const inf_rational & r):
m_first(r.m_first),
m_second(r.m_second)
{}
explicit inf_rational(int n):
m_first(rational(n)),
m_second(rational())
{}
explicit inf_rational(int n, int d):
m_first(rational(n,d)),
m_second(rational())
{}
explicit inf_rational(rational const& r, bool pos_inf):
m_first(r),
m_second(pos_inf?rational(1):rational(-1))
{}
explicit inf_rational(rational const& r):
m_first(r)
{
m_second.reset();
}
inf_rational(rational const& r, rational const& i):
m_first(r),
m_second(i) {
}
~inf_rational() {}
/**
\brief Set inf_rational to 0.
*/
void reset() {
m_first.reset();
m_second.reset();
}
bool is_int() const {
return m_first.is_int() && m_second.is_zero();
}
bool is_int64() const {
return m_first.is_int64() && m_second.is_zero();
}
bool is_uint64() const {
return m_first.is_uint64() && m_second.is_zero();
}
bool is_rational() const { return m_second.is_zero(); }
int64 get_int64() const {
SASSERT(is_int64());
return m_first.get_int64();
}
uint64 get_uint64() const {
SASSERT(is_uint64());
return m_first.get_uint64();
}
rational const& get_rational() const {
return m_first;
}
rational const& get_infinitesimal() const {
return m_second;
}
rational const & get_first() const { return m_first; }
inf_rational & operator=(const inf_rational & r) {
m_first = r.m_first;
m_second = r.m_second;
return *this;
}
inf_rational & operator=(const rational & r) {
m_first = r;
m_second.reset();
return *this;
}
friend inline inf_rational numerator(const inf_rational & r) {
SASSERT(r.m_second.is_zero());
return inf_rational(numerator(r.m_first));
}
friend inline inf_rational denominator(const inf_rational & r) {
SASSERT(r.m_second.is_zero());
return inf_rational(denominator(r.m_first));
}
inf_rational & operator+=(const inf_rational & r) {
m_first += r.m_first;
m_second += r.m_second;
return *this;
}
inf_rational & operator-=(const inf_rational & r) {
m_first -= r.m_first;
m_second -= r.m_second;
return *this;
}
inf_rational & operator+=(const rational & r) {
m_first += r;
return *this;
}
inf_rational & operator-=(const rational & r) {
m_first -= r;
return *this;
}
inf_rational & operator*=(const rational & r1) {
m_first *= r1;
m_second *= r1;
return *this;
}
//
// These operations get us out of the realm of inf_rational:
// (r1 + e*k1)*(r2 + e*k2) = (r1*r2 + (r1*k2 + r2*k1)*e)
//
// inf_rational & operator*=(const inf_rational & r)
// inf_rational & operator/=(const inf_rational & r)
// inf_rational & operator%=(const inf_rational & r)
// friend inline inf_rational div(const inf_rational & r1, const inf_rational & r2)
// inf_rational expt(int n)
// instead, we define operators that approximate some of these operations from above and below.
friend inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2);
friend inf_rational sup_mult(inf_rational const& r1, inf_rational const& r2);
friend inf_rational inf_div(inf_rational const& r1, inf_rational const& r2);
friend inf_rational sup_div(inf_rational const& r1, inf_rational const& r2);
friend inf_rational inf_power(inf_rational const& r1, unsigned n);
friend inf_rational sup_power(inf_rational const& r1, unsigned n);
friend inf_rational inf_root(inf_rational const& r1, unsigned n);
friend inf_rational sup_root(inf_rational const& r1, unsigned n);
inf_rational & operator/=(const rational & r) {
m_first /= r;
m_second /= r;
return *this;
}
friend inline inf_rational operator*(const rational & r1, const inf_rational & r2);
friend inline inf_rational operator/(const inf_rational & r1, const rational & r2);
inf_rational & operator++() {
++m_first;
return *this;
}
const inf_rational operator++(int) { inf_rational tmp(*this); ++(*this); return tmp; }
inf_rational & operator--() {
--m_first;
return *this;
}
const inf_rational operator--(int) { inf_rational tmp(*this); --(*this); return tmp; }
friend inline bool operator==(const inf_rational & r1, const inf_rational & r2) {
return r1.m_first == r2.m_first && r1.m_second == r2.m_second;
}
friend inline bool operator==(const rational & r1, const inf_rational & r2) {
return r1 == r2.m_first && r2.m_second.is_zero();
}
friend inline bool operator==(const inf_rational & r1, const rational & r2) {
return r1.m_first == r2 && r1.m_second.is_zero();
}
friend inline bool operator<(const inf_rational & r1, const inf_rational & r2) {
return
(r1.m_first < r2.m_first) ||
(r1.m_first == r2.m_first && r1.m_second < r2.m_second);
}
friend inline bool operator<(const rational & r1, const inf_rational & r2) {
return
(r1 < r2.m_first) ||
(r1 == r2.m_first && r2.m_second.is_pos());
}
friend inline bool operator<(const inf_rational & r1, const rational & r2) {
return
(r1.m_first < r2) ||
(r1.m_first == r2 && r1.m_second.is_neg());
}
void neg() {
m_first.neg();
m_second.neg();
}
bool is_zero() const {
return m_first.is_zero() && m_second.is_zero();
}
bool is_one() const {
return m_first.is_one() && m_second.is_zero();
}
bool is_minus_one() const {
return m_first.is_minus_one() && m_second.is_zero();
}
bool is_neg() const {
return
m_first.is_neg() ||
(m_first.is_zero() && m_second.is_neg());
}
bool is_pos() const {
return
m_first.is_pos() ||
(m_first.is_zero() && m_second.is_pos());
}
bool is_nonneg() const {
return
m_first.is_pos() ||
(m_first.is_zero() && m_second.is_nonneg());
}
bool is_nonpos() const {
return
m_first.is_neg() ||
(m_first.is_zero() && m_second.is_nonpos());
}
friend inline rational floor(const inf_rational & r) {
if (r.m_first.is_int()) {
if (r.m_second.is_nonneg()) {
return r.m_first;
}
return r.m_first - rational(1);
}
return floor(r.m_first);
}
friend inline rational ceil(const inf_rational & r) {
if (r.m_first.is_int()) {
if (r.m_second.is_nonpos()) {
return r.m_first;
}
return r.m_first + rational(1);
}
return ceil(r.m_first);
}
static const inf_rational & zero() {
return m_zero;
}
static const inf_rational & one() {
return m_one;
}
static const inf_rational & minus_one() {
return m_minus_one;
}
// Perform: this += c * k
void addmul(const rational & c, const inf_rational & k) {
m_first.addmul(c, k.m_first);
m_second.addmul(c, k.m_second);
}
// Perform: this += c * k
void submul(const rational & c, const inf_rational & k) {
m_first.submul(c, k.m_first);
m_second.submul(c, k.m_second);
}
};
inline bool operator!=(const inf_rational & r1, const inf_rational & r2) {
return !operator==(r1, r2);
}
inline bool operator!=(const rational & r1, const inf_rational & r2) {
return !operator==(r1, r2);
}
inline bool operator!=(const inf_rational & r1, const rational & r2) {
return !operator==(r1, r2);
}
inline bool operator>(const inf_rational & r1, const inf_rational & r2) {
return operator<(r2, r1);
}
inline bool operator>(const inf_rational & r1, const rational & r2) {
return operator<(r2, r1);
}
inline bool operator>(const rational & r1, const inf_rational & r2) {
return operator<(r2, r1);
}
inline bool operator<=(const inf_rational & r1, const inf_rational & r2) {
return !operator>(r1, r2);
}
inline bool operator<=(const rational & r1, const inf_rational & r2) {
return !operator>(r1, r2);
}
inline bool operator<=(const inf_rational & r1, const rational & r2) {
return !operator>(r1, r2);
}
inline bool operator>=(const inf_rational & r1, const inf_rational & r2) {
return !operator<(r1, r2);
}
inline bool operator>=(const rational & r1, const inf_rational & r2) {
return !operator<(r1, r2);
}
inline bool operator>=(const inf_rational & r1, const rational & r2) {
return !operator<(r1, r2);
}
inline inf_rational operator+(const inf_rational & r1, const inf_rational & r2) {
return inf_rational(r1) += r2;
}
inline inf_rational operator-(const inf_rational & r1, const inf_rational & r2) {
return inf_rational(r1) -= r2;
}
inline inf_rational operator-(const inf_rational & r) {
inf_rational result(r);
result.neg();
return result;
}
inline inf_rational operator*(const rational & r1, const inf_rational & r2) {
inf_rational result(r2);
result.m_first *= r1;
result.m_second *= r1;
return result;
}
inline inf_rational operator/(const inf_rational & r1, const rational & r2) {
inf_rational result(r1);
result.m_first /= r2;
result.m_second /= r2;
return result;
}
#if 0
inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2);
inf_rational sup_mult(inf_rational const& r1, inf_rational const& r2);
inf_rational inf_div(inf_rational const& r1, inf_rational const& r2);
inf_rational sup_div(inf_rational const& r1, inf_rational const& r2);
inf_rational inf_power(inf_rational const& r1, unsigned n);
inf_rational sup_power(inf_rational const& r1, unsigned n);
inf_rational inf_root(inf_rational const& r1, unsigned n);
inf_rational sup_root(inf_rational const& r1, unsigned n);
#endif
//
// inline inf_rational operator/(const inf_rational & r1, const inf_rational & r2)
// inline inf_rational operator%(const inf_rational & r1, const inf_rational & r2)
// inf_rational gcd(const inf_rational & r1, const inf_rational & r2);
// inf_rational lcm(const inf_rational & r1, const inf_rational & r2);
inline std::ostream & operator<<(std::ostream & target, const inf_rational & r)
{
target << r.to_string();
return target;
}
inline inf_rational abs(const inf_rational & r) {
inf_rational result(r);
if (result.is_neg()) {
result.neg();
}
return result;
}
#endif /* _INF_RATIONAL_H_ */

View file

@ -0,0 +1,25 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
inf_s_integer.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2007-06-16.
Revision History:
--*/
#include"inf_s_integer.h"
inf_s_integer inf_s_integer::m_zero(0);
inf_s_integer inf_s_integer::m_one(1);
inf_s_integer inf_s_integer::m_minus_one(-1);

351
src/util/inf_s_integer.h Normal file
View file

@ -0,0 +1,351 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
inf_s_integer.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2007-06-11.
Revision History:
--*/
#ifndef _INF_S_INTEGER_H_
#define _INF_S_INTEGER_H_
#include"s_integer.h"
#include"rational.h"
class inf_s_integer {
static inf_s_integer m_zero;
static inf_s_integer m_one;
static inf_s_integer m_minus_one;
int m_first;
int m_second;
public:
unsigned hash() const {
return m_first ^ (m_second + 1);
}
struct hash_proc { unsigned operator()(inf_s_integer const& r) const { return r.hash(); } };
struct eq_proc { bool operator()(inf_s_integer const& r1, inf_s_integer const& r2) const { return r1 == r2; } };
void swap(inf_s_integer & n) {
std::swap(m_first, n.m_first);
std::swap(m_second, n.m_second);
}
std::string to_string() const;
inf_s_integer():m_first(0), m_second(0) {}
inf_s_integer(const inf_s_integer & r):m_first(r.m_first), m_second(r.m_second) {}
explicit inf_s_integer(int n):m_first(n), m_second(0) {}
explicit inf_s_integer(int n, int d): m_first(n), m_second(0) { SASSERT(d == 1); }
explicit inf_s_integer(s_integer const& r, bool pos_inf):m_first(r.get_int()), m_second(pos_inf ? 1 : -1) {}
explicit inf_s_integer(s_integer const& r):m_first(r.get_int()), m_second(0) {}
explicit inf_s_integer(rational const& r):m_first(static_cast<int>(r.get_int64())), m_second(0) {}
inf_s_integer(s_integer const& r, s_integer const& i):m_first(r.get_int()), m_second(i.get_int()) {}
void reset() { m_first = 0; m_second = 0; }
bool is_int() const { return m_second == 0; }
bool is_int64() const { return m_second == 0; }
bool is_uint64() const { return m_second == 0; }
bool is_rational() const { return m_second == 0; }
int64 get_int64() const { return m_first; }
uint64 get_uint64() const { return m_first; }
s_integer get_rational() const { return s_integer(m_first); }
s_integer get_infinitesimal() const { return s_integer(m_second); }
inf_s_integer & operator=(const inf_s_integer & r) {
m_first = r.m_first;
m_second = r.m_second;
return *this;
}
inf_s_integer & operator=(const rational & r) {
m_first = static_cast<int>(r.get_int64());
m_second = 0;
return *this;
}
inf_s_integer & operator=(const s_integer & r) {
m_first = r.get_int();
m_second = 0;
return *this;
}
friend inline inf_s_integer numerator(const inf_s_integer & r) {
SASSERT(r.m_second == 0);
return inf_s_integer(r.m_first);
}
friend inline inf_s_integer denominator(const inf_s_integer & r) {
SASSERT(r.m_second == 0);
return inf_s_integer(1);
}
inf_s_integer & operator+=(const inf_s_integer & r) {
m_first += r.m_first;
m_second += r.m_second;
return *this;
}
inf_s_integer & operator-=(const inf_s_integer & r) {
m_first -= r.m_first;
m_second -= r.m_second;
return *this;
}
inf_s_integer & operator+=(const s_integer & r) {
m_first += r.get_int();
return *this;
}
inf_s_integer & operator-=(const s_integer & r) {
m_first -= r.get_int();
return *this;
}
inf_s_integer & operator*=(const s_integer & r1) {
m_first *= r1.get_int();
m_second *= r1.get_int();
return *this;
}
// friend inf_s_integer inf_mult(inf_s_integer const& r1, inf_s_integer const& r2);
// friend inf_s_integer sup_mult(inf_s_integer const& r1, inf_s_integer const& r2);
// friend inf_s_integer inf_div(inf_s_integer const& r1, inf_s_integer const& r2);
// friend inf_s_integer sup_div(inf_s_integer const& r1, inf_s_integer const& r2);
// friend inf_s_integer inf_power(inf_s_integer const& r1, unsigned n);
// friend inf_s_integer sup_power(inf_s_integer const& r1, unsigned n);
// friend inf_s_integer inf_root(inf_s_integer const& r1, unsigned n);
// friend inf_s_integer sup_root(inf_s_integer const& r1, unsigned n);
inf_s_integer & operator/=(const s_integer & r) {
m_first /= r.get_int();
m_second /= r.get_int();
return *this;
}
friend inline inf_s_integer operator*(const s_integer & r1, const inf_s_integer & r2);
friend inline inf_s_integer operator/(const inf_s_integer & r1, const s_integer & r2);
inf_s_integer & operator++() {
++m_first;
return *this;
}
const inf_s_integer operator++(int) { inf_s_integer tmp(*this); ++(*this); return tmp; }
inf_s_integer & operator--() {
--m_first;
return *this;
}
const inf_s_integer operator--(int) { inf_s_integer tmp(*this); --(*this); return tmp; }
friend inline bool operator==(const inf_s_integer & r1, const inf_s_integer & r2) {
return r1.m_first == r2.m_first && r1.m_second == r2.m_second;
}
friend inline bool operator==(const s_integer & r1, const inf_s_integer & r2) {
return r1.get_int() == r2.m_first && r2.m_second == 0;
}
friend inline bool operator==(const inf_s_integer & r1, const s_integer & r2) {
return r1.m_first == r2.get_int() && r1.m_second == 0;
}
friend inline bool operator<(const inf_s_integer & r1, const inf_s_integer & r2) {
return
(r1.m_first < r2.m_first) ||
(r1.m_first == r2.m_first && r1.m_second < r2.m_second);
}
friend inline bool operator<(const s_integer & r1, const inf_s_integer & r2) {
return
(r1.get_int() < r2.m_first) ||
(r1.get_int() == r2.m_first && r2.m_second > 0);
}
friend inline bool operator<(const inf_s_integer & r1, const s_integer & r2) {
return
(r1.m_first < r2.get_int()) ||
(r1.m_first == r2.get_int() && r1.m_second < 0);
}
void neg() {
m_first = -m_first;
m_second = -m_second;
}
bool is_zero() const {
return m_first == 0 && m_second == 0;
}
bool is_one() const {
return m_first == 1 && m_second == 0;
}
bool is_minus_one() const {
return m_first == -1 && m_second == 0;
}
bool is_neg() const {
return m_first < 0 || (m_first == 0 && m_second < 0);
}
bool is_pos() const {
return m_first > 0 || (m_first == 0 && m_second > 0);
}
bool is_nonneg() const {
return m_first > 0 || (m_first == 0 && m_second >= 0);
}
bool is_nonpos() const {
return m_first < 0 || (m_first == 0 && m_second <= 0);
}
friend inline s_integer floor(const inf_s_integer & r) {
if (r.m_second >= 0) {
return s_integer(r.m_first);
}
return s_integer(r.m_first - 1);
}
friend inline s_integer ceil(const inf_s_integer & r) {
if (r.m_second <= 0) {
return s_integer(r.m_first);
}
return s_integer(r.m_first + 1);
}
static const inf_s_integer & zero() {
return m_zero;
}
static const inf_s_integer & one() {
return m_one;
}
static const inf_s_integer & minus_one() {
return m_minus_one;
}
// Perform: this += c * k
void addmul(const s_integer & c, const inf_s_integer & k) {
m_first += c.get_int() * k.m_first;
m_second += c.get_int() * k.m_second;
}
// Perform: this += c * k
void submul(const s_integer & c, const inf_s_integer & k) {
m_first -= c.get_int() * k.m_first;
m_second -= c.get_int() * k.m_second;
}
friend inline std::ostream & operator<<(std::ostream & target, const inf_s_integer & r) {
if (r.m_second == 0) {
target << r.m_first;
}
else if (r.m_second < 0) {
target << "(" << r.m_first << " -e*" << r.m_second << ")";
}
else {
target << "(" << r.m_first << " +e*" << r.m_second << ")";
}
return target;
}
};
inline bool operator!=(const inf_s_integer & r1, const inf_s_integer & r2) {
return !operator==(r1, r2);
}
inline bool operator!=(const s_integer & r1, const inf_s_integer & r2) {
return !operator==(r1, r2);
}
inline bool operator!=(const inf_s_integer & r1, const s_integer & r2) {
return !operator==(r1, r2);
}
inline bool operator>(const inf_s_integer & r1, const inf_s_integer & r2) {
return operator<(r2, r1);
}
inline bool operator>(const inf_s_integer & r1, const s_integer & r2) {
return operator<(r2, r1);
}
inline bool operator>(const s_integer & r1, const inf_s_integer & r2) {
return operator<(r2, r1);
}
inline bool operator<=(const inf_s_integer & r1, const inf_s_integer & r2) {
return !operator>(r1, r2);
}
inline bool operator<=(const s_integer & r1, const inf_s_integer & r2) {
return !operator>(r1, r2);
}
inline bool operator<=(const inf_s_integer & r1, const s_integer & r2) {
return !operator>(r1, r2);
}
inline bool operator>=(const inf_s_integer & r1, const inf_s_integer & r2) {
return !operator<(r1, r2);
}
inline bool operator>=(const s_integer & r1, const inf_s_integer & r2) {
return !operator<(r1, r2);
}
inline bool operator>=(const inf_s_integer & r1, const s_integer & r2) {
return !operator<(r1, r2);
}
inline inf_s_integer operator+(const inf_s_integer & r1, const inf_s_integer & r2) {
return inf_s_integer(r1) += r2;
}
inline inf_s_integer operator-(const inf_s_integer & r1, const inf_s_integer & r2) {
return inf_s_integer(r1) -= r2;
}
inline inf_s_integer operator-(const inf_s_integer & r) {
inf_s_integer result(r);
result.neg();
return result;
}
inline inf_s_integer operator*(const s_integer & r1, const inf_s_integer & r2) {
inf_s_integer result(r2);
result.m_first *= r1.get_int();
result.m_second *= r1.get_int();
return result;
}
inline inf_s_integer operator/(const inf_s_integer & r1, const s_integer & r2) {
inf_s_integer result(r1);
result.m_first /= r2.get_int();
result.m_second /= r2.get_int();
return result;
}
inline inf_s_integer abs(const inf_s_integer & r) {
inf_s_integer result(r);
if (result.is_neg()) {
result.neg();
}
return result;
}
#endif /* _INF_S_INTEGER_H_ */

1564
src/util/ini_file.cpp Normal file

File diff suppressed because it is too large Load diff

117
src/util/ini_file.h Normal file
View file

@ -0,0 +1,117 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
ini_file.h
Abstract:
Configuration file support.
Author:
Leonardo de Moura (leonardo) 2007-05-10.
Revision History:
--*/
#ifndef _INI_FILE_H_
#define _INI_FILE_H_
#include<iostream>
#include<limits.h>
#include<string>
#include"symbol.h"
#include"vector.h"
#include"ref.h"
#ifdef _EXTERNAL_RELEASE
#define PRIVATE_PARAMS(CODE) ((void) 0)
#else
#define PRIVATE_PARAMS(CODE) { CODE }
#endif
struct param_vector_imp;
struct ini_params_imp;
class ini_parser;
typedef std::pair<symbol, unsigned> symbol_nat_pair;
inline std::ostream & operator<<(std::ostream & out, symbol_nat_pair const & p) {
out << p.first << ":" << p.second;
return out;
}
/**
\brief Support for multiple values for a parameter. The values are used to configure
different cores.
*/
class param_vector {
unsigned m_ref_count;
param_vector_imp * m_imp;
friend struct ini_params_imp;
friend class ini_parser;
public:
// TODO: onwer is a big hack
param_vector(void * owner);
~param_vector();
void inc_ref();
void dec_ref();
void copy_params(void * curr_owner, unsigned idx);
unsigned size(void) const;
};
class set_get_param_exception {
std::string m_id;
std::string m_msg;
public:
set_get_param_exception(char const * id, char const * msg):m_id(id), m_msg(msg) {}
char const * get_param_id() const { return m_id.c_str(); }
char const * get_msg() const { return m_msg.c_str(); }
};
class ini_parser_exception {
unsigned m_pos;
std::string m_msg;
public:
ini_parser_exception(unsigned pos, char const * msg):m_pos(pos), m_msg(msg) {}
unsigned get_pos() const { return m_pos; }
char const * get_msg() const { return m_msg.c_str(); }
};
class ini_params {
ini_params_imp * m_imp;
public:
ini_params(bool abort_on_error = true);
~ini_params();
void freeze(bool f = true);
void register_bool_param(char const * param_name, bool & value, char const * descr = 0, bool is_mutable = false);
void register_unsigned_param(char const * param_name, unsigned min, unsigned max, unsigned & value, char const * descr = 0, bool is_mutable = false);
void register_unsigned_param(char const * param_name, unsigned & value, char const * descr = 0, bool is_mutable = false) {
register_unsigned_param(param_name, 0, INT_MAX, value, descr, is_mutable);
}
void register_int_param(char const * param_name, int min, int max, int & value, char const * descr = 0, bool is_mutable = false);
void register_int_param(char const * param_name, int & value, char const * descr = 0, bool is_mutable = false) {
register_int_param(param_name, INT_MIN, INT_MAX, value, descr, is_mutable);
}
void register_double_param(char const * param_name, double & value, char const * descr = 0,bool is_mutable = false);
void register_percentage_param(char const * param_name, double & value, char const * descr = 0, bool is_mutable = false);
void register_string_param(char const * param_name, std::string & value, char const * descr = 0, bool is_mutable = false);
void register_symbol_param(char const * param_name, symbol & value, char const * descr = 0, bool is_mutable = false);
void register_symbol_list_param(char const * param_name, svector<symbol> & value, char const * descr = 0, bool is_mutable = false);
void register_symbol_nat_list_param(char const * param_name, svector<symbol_nat_pair> & value, char const * descr = 0, bool is_mutable = false);
void register_param_vector(param_vector * pv);
void read_ini_file(std::istream & in);
void display_params(std::ostream & out) const;
void display_parameter_help(char const* param_id, std::ostream & out) const;
void set_param_value(char const * param_id, char const * param_value);
void set_param_mutable(char const * param_id);
bool get_param_value(char const * param_id, std::string& param_value);
void display_params_documentation(std::ostream & out) const;
};
#endif /* _INI_FILE_H_ */

View file

@ -0,0 +1,41 @@
#ifdef _WINDOWS
#include "windows.h"
#endif
#include "instruction_count.h"
#ifdef _WINDOWS
typedef BOOL (WINAPI *QTCP)(HANDLE, PULONG64);
static QTCP QTCP_proc;
BOOL WINAPI dummy_qtcp(HANDLE h, PULONG64 u)
{
*u = 0;
return 0;
}
inline void check_handler()
{
if (!QTCP_proc) {
QTCP_proc = (QTCP) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "QueryThreadCycleTime");
if (!QTCP_proc)
QTCP_proc = &dummy_qtcp;
}
}
#endif
void instruction_count::start() {
m_count = 0;
#ifdef _WINDOWS
check_handler();
QTCP_proc(GetCurrentThread(), &m_count);
#endif
}
double instruction_count::get_num_instructions() {
unsigned long long current = 0;
#ifdef _WINDOWS
check_handler();
QTCP_proc(GetCurrentThread(), &current);
#endif
return static_cast<double>(current - m_count);
}

View file

@ -0,0 +1,43 @@
/*++
Copyright (c) 2009 Microsoft Corporation
Module Name:
instruction_count.h
Abstract:
<abstract>
Author:
Nikolaj Bjorner (nbjorner) 2009-03-04.
Revision History:
--*/
#ifndef _INSTRUCTION_COUNT_H_
#define _INSTRUCTION_COUNT_H_
/**
\brief Wrapper for an instruction counter.
*/
class instruction_count {
unsigned long long m_count;
public:
instruction_count() : m_count(0) {}
~instruction_count() {}
void start();
double get_num_instructions();
bool is_instruction_maxed(double max_instr) {
return max_instr > 0.0 && get_num_instructions() > max_instr;
}
};
#endif /* _INSTRUcTION_COUNT_H_ */

353
src/util/interval.h Normal file
View file

@ -0,0 +1,353 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
interval.h
Abstract:
Goodies/Templates for interval arithmetic
Author:
Leonardo de Moura (leonardo) 2012-07-19.
Revision History:
--*/
#ifndef _INTERVAL_H_
#define _INTERVAL_H_
#include"mpq.h"
#include"ext_numeral.h"
/**
\brief Default configuration for interval manager.
It is used for documenting the required interface.
*/
class im_default_config {
unsynch_mpq_manager & m_manager;
public:
typedef unsynch_mpq_manager numeral_manager;
typedef mpq numeral;
// Every configuration object must provide an interval type.
// The actual fields are irrelevant, the interval manager
// accesses interval data using the following API.
struct interval {
numeral m_lower;
numeral m_upper;
unsigned m_lower_open:1;
unsigned m_upper_open:1;
unsigned m_lower_inf:1;
unsigned m_upper_inf:1;
};
// Should be NOOPs for precise numeral types.
// For imprecise types (e.g., floats) it should set the rounding mode.
void round_to_minus_inf() {}
void round_to_plus_inf() {}
void set_rounding(bool to_plus_inf) {}
// Getters
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.m_lower_open; }
bool upper_is_open(interval const & a) const { return a.m_upper_open; }
bool lower_is_inf(interval const & a) const { return a.m_lower_inf; }
bool upper_is_inf(interval const & a) const { return a.m_upper_inf; }
// Setters
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; }
// Reference to numeral manager
numeral_manager & m() const { return m_manager; }
im_default_config(numeral_manager & m):m_manager(m) {}
};
#define DEP_IN_LOWER1 1
#define DEP_IN_UPPER1 2
#define DEP_IN_LOWER2 4
#define DEP_IN_UPPER2 8
typedef short bound_deps;
inline bool dep_in_lower1(bound_deps d) { return (d & DEP_IN_LOWER1) != 0; }
inline bool dep_in_lower2(bound_deps d) { return (d & DEP_IN_LOWER2) != 0; }
inline bool dep_in_upper1(bound_deps d) { return (d & DEP_IN_UPPER1) != 0; }
inline bool dep_in_upper2(bound_deps d) { return (d & DEP_IN_UPPER2) != 0; }
inline bound_deps dep1_to_dep2(bound_deps d) {
SASSERT(!dep_in_lower2(d) && !dep_in_upper2(d));
bound_deps r = d << 2;
SASSERT(dep_in_lower1(d) == dep_in_lower2(r));
SASSERT(dep_in_upper1(d) == dep_in_upper2(r));
SASSERT(!dep_in_lower1(r) && !dep_in_upper1(r));
return r;
}
/**
\brief Interval dependencies for unary and binary operations on intervals.
It contains the dependencies for the output lower and upper bounds
for the resultant interval.
*/
struct interval_deps {
bound_deps m_lower_deps;
bound_deps m_upper_deps;
};
template<typename C>
class interval_manager {
typedef typename C::numeral_manager numeral_manager;
typedef typename numeral_manager::numeral numeral;
typedef typename C::interval interval;
C m_c;
numeral m_result_lower;
numeral m_result_upper;
numeral m_mul_ad;
numeral m_mul_bc;
numeral m_mul_ac;
numeral m_mul_bd;
numeral m_one;
numeral m_minus_one;
numeral m_inv_k;
unsigned m_pi_n;
interval m_pi_div_2;
interval m_pi;
interval m_3_pi_div_2;
interval m_2_pi;
volatile bool m_cancel;
void round_to_minus_inf() { m_c.round_to_minus_inf(); }
void round_to_plus_inf() { m_c.round_to_plus_inf(); }
void set_rounding(bool to_plus_inf) { m_c.set_rounding(to_plus_inf); }
ext_numeral_kind lower_kind(interval const & a) const { return m_c.lower_is_inf(a) ? EN_MINUS_INFINITY : EN_NUMERAL; }
ext_numeral_kind upper_kind(interval const & a) const { return m_c.upper_is_inf(a) ? EN_PLUS_INFINITY : EN_NUMERAL; }
void set_lower(interval & a, numeral const & n) { m_c.set_lower(a, n); }
void set_upper(interval & a, numeral const & n) { m_c.set_upper(a, n); }
void set_lower_is_open(interval & a, bool v) { m_c.set_lower_is_open(a, v); }
void set_upper_is_open(interval & a, bool v) { m_c.set_upper_is_open(a, v); }
void set_lower_is_inf(interval & a, bool v) { m_c.set_lower_is_inf(a, v); }
void set_upper_is_inf(interval & a, bool v) { m_c.set_upper_is_inf(a, v); }
void nth_root_slow(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi);
void A_div_x_n(numeral const & A, numeral const & x, unsigned n, bool to_plus_inf, numeral & r);
void rough_approx_nth_root(numeral const & a, unsigned n, numeral & o);
void approx_nth_root(numeral const & a, unsigned n, numeral const & p, numeral & o);
void nth_root_pos(numeral const & A, unsigned n, numeral const & p, numeral & lo, numeral & hi);
void nth_root(numeral const & a, unsigned n, numeral const & p, numeral & lo, numeral & hi);
void pi_series(int x, numeral & r, bool to_plus_inf);
void fact(unsigned n, numeral & o);
void sine_series(numeral const & a, unsigned k, bool upper, numeral & o);
void cosine_series(numeral const & a, unsigned k, bool upper, numeral & o);
void e_series(unsigned k, bool upper, numeral & o);
void div_mul(numeral const & k, interval const & a, interval & b, bool inv_k);
void checkpoint();
public:
interval_manager(C const & c);
~interval_manager();
void set_cancel(bool f) { m_cancel = f; }
numeral_manager & m() const { return m_c.m(); }
void del(interval & a);
numeral const & lower(interval const & a) const { return m_c.lower(a); }
numeral const & upper(interval const & a) const { return m_c.upper(a); }
numeral & lower(interval & a) { return m_c.lower(a); }
numeral & upper(interval & a) { return m_c.upper(a); }
bool lower_is_open(interval const & a) const { return m_c.lower_is_open(a); }
bool upper_is_open(interval const & a) const { return m_c.upper_is_open(a); }
bool lower_is_inf(interval const & a) const { return m_c.lower_is_inf(a); }
bool upper_is_inf(interval const & a) const { return m_c.upper_is_inf(a); }
bool lower_is_neg(interval const & a) const { return ::is_neg(m(), lower(a), lower_kind(a)); }
bool lower_is_pos(interval const & a) const { return ::is_pos(m(), lower(a), lower_kind(a)); }
bool lower_is_zero(interval const & a) const { return ::is_zero(m(), lower(a), lower_kind(a)); }
bool upper_is_neg(interval const & a) const { return ::is_neg(m(), upper(a), upper_kind(a)); }
bool upper_is_pos(interval const & a) const { return ::is_pos(m(), upper(a), upper_kind(a)); }
bool upper_is_zero(interval const & a) const { return ::is_zero(m(), upper(a), upper_kind(a)); }
bool is_P(interval const & n) const { return lower_is_pos(n) || lower_is_zero(n); }
bool is_P0(interval const & n) const { return lower_is_zero(n) && !lower_is_open(n); }
bool is_P1(interval const & n) const { return lower_is_pos(n) || (lower_is_zero(n) && lower_is_open(n)); }
bool is_N(interval const & n) const { return upper_is_neg(n) || upper_is_zero(n); }
bool is_N0(interval const & n) const { return upper_is_zero(n) && !upper_is_open(n); }
bool is_N1(interval const & n) const { return upper_is_neg(n) || (upper_is_zero(n) && upper_is_open(n)); }
bool is_M(interval const & n) const { return lower_is_neg(n) && upper_is_pos(n); }
bool is_zero(interval const & n) const { return lower_is_zero(n) && upper_is_zero(n); }
void set(interval & t, interval const & s);
bool eq(interval const & a, interval const & b) const;
/**
\brief Set lower bound to -oo.
*/
void reset_lower(interval & a);
/**
\brief Set upper bound to +oo.
*/
void reset_upper(interval & a);
/**
\brief Set interval to (-oo, oo)
*/
void reset(interval & a);
/**
\brief Return true if the given interval contains 0.
*/
bool contains_zero(interval const & n) const;
/**
\brief Return true if n contains v.
*/
bool contains(interval const & n, numeral const & v) const;
void display(std::ostream & out, interval const & n) const;
bool check_invariant(interval const & n) const;
/**
\brief b <- -a
*/
void neg(interval const & a, interval & b, interval_deps & b_deps);
void neg(interval const & a, interval & b);
void neg_jst(interval const & a, interval_deps & b_deps);
/**
\brief c <- a + b
*/
void add(interval const & a, interval const & b, interval & c, interval_deps & c_deps);
void add(interval const & a, interval const & b, interval & c);
void add_jst(interval const & a, interval const & b, interval_deps & c_deps);
/**
\brief c <- a - b
*/
void sub(interval const & a, interval const & b, interval & c, interval_deps & c_deps);
void sub(interval const & a, interval const & b, interval & c);
void sub_jst(interval const & a, interval const & b, interval_deps & c_deps);
/**
\brief b <- k * a
*/
void mul(numeral const & k, interval const & a, interval & b, interval_deps & b_deps);
void mul(numeral const & k, interval const & a, interval & b) { div_mul(k, a, b, false); }
void mul_jst(numeral const & k, interval const & a, interval_deps & b_deps);
/**
\brief b <- (n/d) * a
*/
void mul(int n, int d, interval const & a, interval & b);
/**
\brief b <- a/k
\remark For imprecise numerals, this is not equivalent to
m().inv(k)
mul(k, a, b)
That is, we must invert k rounding towards +oo or -oo depending whether we
are computing a lower or upper bound.
*/
void div(interval const & a, numeral const & k, interval & b, interval_deps & b_deps);
void div(interval const & a, numeral const & k, interval & b) { div_mul(k, a, b, true); }
void div_jst(interval const & a, numeral const & k, interval_deps & b_deps) { mul_jst(k, a, b_deps); }
/**
\brief c <- a * b
*/
void mul(interval const & a, interval const & b, interval & c, interval_deps & c_deps);
void mul(interval const & a, interval const & b, interval & c);
void mul_jst(interval const & a, interval const & b, interval_deps & c_deps);
/**
\brief b <- a^n
*/
void power(interval const & a, unsigned n, interval & b, interval_deps & b_deps);
void power(interval const & a, unsigned n, interval & b);
void power_jst(interval const & a, unsigned n, interval_deps & b_deps);
/**
\brief b <- a^(1/n) with precision p.
\pre if n is even, then a must not contain negative numbers.
*/
void nth_root(interval const & a, unsigned n, numeral const & p, interval & b, interval_deps & b_deps);
void nth_root(interval const & a, unsigned n, numeral const & p, interval & b);
void nth_root_jst(interval const & a, unsigned n, numeral const & p, interval_deps & b_deps);
/**
\brief Given an equation x^n = y and an interval for y, compute the solution set for x with precision p.
\pre if n is even, then !lower_is_neg(y)
*/
void xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x, interval_deps & x_deps);
void xn_eq_y(interval const & y, unsigned n, numeral const & p, interval & x);
void xn_eq_y_jst(interval const & y, unsigned n, numeral const & p, interval_deps & x_deps);
/**
\brief b <- 1/a
\pre !contains_zero(a)
*/
void inv(interval const & a, interval & b, interval_deps & b_deps);
void inv(interval const & a, interval & b);
void inv_jst(interval const & a, interval_deps & b_deps);
/**
\brief c <- a/b
\pre !contains_zero(b)
\pre &a == &c (that is, c should not be an alias for a)
*/
void div(interval const & a, interval const & b, interval & c, interval_deps & c_deps);
void div(interval const & a, interval const & b, interval & c);
void div_jst(interval const & a, interval const & b, interval_deps & c_deps);
/**
\brief Store in r an interval that contains the number pi.
The size of the interval is (1/15)*(1/16^n)
*/
void pi(unsigned n, interval & r);
/**
\brief Set the precision of the internal interval representing pi.
*/
void set_pi_prec(unsigned n);
/**
\brief Set the precision of the internal interval representing pi to a precision of at least n.
*/
void set_pi_at_least_prec(unsigned n);
void sine(numeral const & a, unsigned k, numeral & lo, numeral & hi);
void cosine(numeral const & a, unsigned k, numeral & lo, numeral & hi);
/**
\brief Store in r the Euler's constant e.
The size of the interval is 4/(k+1)!
*/
void e(unsigned k, interval & r);
};
#endif

1985
src/util/interval_def.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

36
src/util/lbool.cpp Normal file
View file

@ -0,0 +1,36 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
lbool.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-08.
Revision History:
--*/
#include"lbool.h"
std::ostream & operator<<(std::ostream & out, lbool b) {
switch(b) {
case l_false: return out << "l_false";
case l_true: return out << "l_true";
default: return out << "l_undef";
}
}
char const * to_sat_str(lbool l) {
switch (l) {
case l_false: return "unsatisfiable";
case l_true: return "satisfiable";
default: return "unknown";
}
}

42
src/util/lbool.h Normal file
View file

@ -0,0 +1,42 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
lbool.h
Abstract:
Lifted boolean
Author:
Leonardo de Moura (leonardo) 2008-02-08.
Revision History:
--*/
#ifndef _LBOOL_H_
#define _LBOOL_H_
#include"util.h"
typedef enum { l_false = -1, l_undef, l_true } lbool;
inline lbool operator~(lbool lb) {
return static_cast<lbool>(-static_cast<int>(lb));
}
inline lbool to_lbool(bool b) {
return static_cast<lbool>(static_cast<int>(b)*2-1);
}
std::ostream & operator<<(std::ostream & out, lbool b);
/**
\brief Convert l_true -> satisfiable, l_false -> unsatisfiable, and l_undef -> unknown.
*/
char const * to_sat_str(lbool l);
#endif /* _LBOOL_H_ */

96
src/util/list.h Normal file
View file

@ -0,0 +1,96 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
list.h
Abstract:
Simple list data structure. It is meant to be used with region allocators.
Author:
Leonardo de Moura (leonardo) 2007-07-10.
Revision History:
--*/
#ifndef _LIST_H_
#define _LIST_H_
#include"buffer.h"
#include"region.h"
template<typename T>
class list {
T m_head;
list * m_tail;
public:
list(T const & h, list * t = 0):
m_head(h),
m_tail(t) {
}
T const & head() const { return m_head; }
list * tail() const { return m_tail; }
T & head() { return m_head; }
list * & tail() { return m_tail; }
class iterator {
list const * m_curr;
public:
iterator(list const * c = 0):m_curr(c) {}
T const & operator*() const { return m_curr->head(); }
iterator & operator++() { m_curr = m_curr->tail(); return *this; }
iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
bool operator==(iterator const & it) { return m_curr == it.m_curr; }
bool operator!=(iterator const & it) { return m_curr != it.m_curr; }
};
typedef iterator const_iterator;
iterator begin() const { return iterator(this); }
iterator end() const { return iterator(0); }
};
/**
\brief Return the list length.
*/
template<typename T>
unsigned length(list<T> * l) {
unsigned r = 0;
while(l) {
l = l->tail();
r++;
}
return r;
}
/**
\brief Non destructive apppend operation. The new nodes are allocated
using the given region allocator.
*/
template<typename T>
list<T> * append(region & r, list<T> * l1, list<T> * l2) {
if (l2 == 0) {
return l1;
}
ptr_buffer<list<T> > buffer;
while (l1) {
buffer.push_back(l1);
l1 = l1->tail();
}
list<T> * result = l2;
typename ptr_buffer<list<T> >::const_iterator it = buffer.end();
typename ptr_buffer<list<T> >::const_iterator begin = buffer.begin();
while (it != begin) {
--it;
list<T> * curr = *it;
result = new (r) list<T>(curr->head(), result);
}
return result;
}
#endif /* _LIST_H_ */

30
src/util/machine.h Normal file
View file

@ -0,0 +1,30 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
machine.h
Abstract:
Machine/OS dependent configuration
Author:
Leonardo de Moura (leonardo) 2006-09-13.
Revision History:
--*/
#ifndef _MACHINE_H_
#define _MACHINE_H_
#ifdef _AMD64_
#define PTR_ALIGNMENT 3
#else
#define PTR_ALIGNMENT 2
#endif
#endif /* _MACHINE_H_ */

286
src/util/map.h Normal file
View file

@ -0,0 +1,286 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
map.h
Abstract:
Simple mapping based on the hashtable.
Author:
Leonardo de Moura (leonardo) 2006-10-14.
Revision History:
--*/
#ifndef _MAP_H_
#define _MAP_H_
#include"hashtable.h"
template<typename Key, typename Value>
struct _key_data {
Key m_key;
Value m_value;
_key_data() {
}
_key_data(Key const & k):
m_key(k) {
}
_key_data(Key const & k, Value const & v):
m_key(k),
m_value(v) {
}
};
template<typename Entry, typename HashProc, typename EqProc>
class table2map {
public:
typedef Entry entry;
typedef typename Entry::key key;
typedef typename Entry::value value;
typedef typename Entry::key_data key_data;
struct entry_hash_proc : private HashProc {
entry_hash_proc(HashProc const & p):
HashProc(p) {
}
unsigned operator()(key_data const & d) const {
return HashProc::operator()(d.m_key);
}
};
struct entry_eq_proc : private EqProc {
entry_eq_proc(EqProc const & p):
EqProc(p) {
}
bool operator()(key_data const & d1, key_data const & d2) const {
return EqProc::operator()(d1.m_key, d2.m_key);
}
};
typedef core_hashtable<entry, entry_hash_proc, entry_eq_proc> table;
table m_table;
public:
table2map(HashProc const & h = HashProc(), EqProc const & e = EqProc()):
m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, entry_hash_proc(h), entry_eq_proc(e)) {
}
typedef typename table::iterator iterator;
void reset() {
m_table.reset();
}
void finalize() {
m_table.finalize();
}
bool empty() const {
return m_table.empty();
}
unsigned size() const {
return m_table.size();
}
unsigned capacity() const {
return m_table.capacity();
}
iterator begin() const {
return m_table.begin();
}
iterator end() const {
return m_table.end();
}
void insert(key const & k, value const & v) {
m_table.insert(key_data(k, v));
}
key_data const & insert_if_not_there(key const & k, value const & v) {
return m_table.insert_if_not_there(key_data(k, v));
}
entry * insert_if_not_there2(key const & k, value const & v) {
return m_table.insert_if_not_there2(key_data(k, v));
}
entry * find_core(key const & k) const {
return m_table.find_core(key_data(k));
}
bool find(key const & k, value & v) const {
entry * e = find_core(k);
if (e) {
v = e->get_data().m_value;
}
return (0 != e);
}
value const& get(key const& k, value const& default_value) const {
entry* e = find_core(k);
if (e) {
return e->m_value;
}
else {
return default_value;
}
}
iterator find_iterator(key const & k) const {
return m_table.find(key_data(k));
}
value const & find(key const& k) const {
entry * e = find_core(k);
SASSERT(e);
return e->get_data().m_value;
}
value & find(key const& k) {
entry * e = find_core(k);
SASSERT(e);
return e->get_data().m_value;
}
value const& operator[](key const& k) const { return find(k); }
value& operator[](key const& k) { return find(k); }
bool contains(key const & k) const {
return find_core(k) != 0;
}
void remove(key const & k) {
m_table.remove(key_data(k));
}
void erase(key const & k) {
remove(k);
}
unsigned long long get_num_collision() const { return m_table.get_num_collision(); }
void swap(table2map & other) {
m_table.swap(other.m_table);
}
#ifdef Z3DEBUG
bool check_invariant() {
return m_table.check_invariant();
}
#endif
};
template<typename Key, typename Value>
class default_map_entry : public default_hash_entry<_key_data<Key, Value> > {
public:
typedef Key key;
typedef Value value;
typedef _key_data<Key, Value> key_data;
};
template<typename Key, typename Value, typename HashProc, typename EqProc>
class map : public table2map<default_map_entry<Key, Value>, HashProc, EqProc> {
public:
map(HashProc const & h = HashProc(), EqProc const & e = EqProc()):
table2map<default_map_entry<Key, Value>, HashProc, EqProc>(h, e) {
}
};
template<typename Key, typename Value>
struct _key_ptr_data {
Key * m_key;
Value m_value;
_key_ptr_data():
m_key(0) {
}
_key_ptr_data(Key * k):
m_key(k) {
}
_key_ptr_data(Key * k, Value const & v):
m_key(k),
m_value(v) {
}
};
template<typename Key, typename Value>
class ptr_map_entry {
public:
typedef _key_ptr_data<Key, Value> key_data;
typedef _key_ptr_data<Key, Value> data;
private:
unsigned m_hash; //!< cached hash code
data m_data;
public:
typedef Key * key;
typedef Value value;
unsigned get_hash() const { return m_hash; }
bool is_free() const { return m_data.m_key == 0; }
bool is_deleted() const { return m_data.m_key == reinterpret_cast<Key *>(1); }
bool is_used() const { return m_data.m_key != reinterpret_cast<Key *>(0) && m_data.m_key != reinterpret_cast<Key *>(1); }
key_data const & get_data() const { return m_data; }
key_data & get_data() { return m_data; }
void set_data(key_data const & d) { m_data = d; }
void set_hash(unsigned h) { m_hash = h; }
void mark_as_deleted() { m_data.m_key = reinterpret_cast<Key *>(1); }
void mark_as_free() { m_data.m_key = 0; }
};
template<typename Key, typename Value>
class ptr_addr_map_entry {
public:
typedef _key_ptr_data<Key, Value> key_data;
typedef _key_ptr_data<Key, Value> data;
private:
data m_data;
public:
typedef Key * key;
typedef Value value;
unsigned get_hash() const { return get_ptr_hash(m_data.m_key); }
bool is_free() const { return m_data.m_key == 0; }
bool is_deleted() const { return m_data.m_key == reinterpret_cast<Key *>(1); }
bool is_used() const { return m_data.m_key != reinterpret_cast<Key *>(0) && m_data.m_key != reinterpret_cast<Key *>(1); }
key_data const & get_data() const { return m_data; }
key_data & get_data() { return m_data; }
void set_data(key_data const & d) { m_data = d; }
void set_hash(unsigned h) { SASSERT(h == get_ptr_hash(m_data.m_key)); /* do nothing */ }
void mark_as_deleted() { m_data.m_key = reinterpret_cast<Key *>(1); }
void mark_as_free() { m_data.m_key = 0; }
};
template<typename Key, typename Value>
class ptr_addr_map : public table2map<ptr_addr_map_entry<Key, Value>, ptr_hash<Key>, ptr_eq<Key> > {
public:
};
struct u_hash { unsigned operator()(unsigned u) const { return u; } };
struct u_eq { bool operator()(unsigned u1, unsigned u2) const { return u1 == u2; } };
struct size_t_eq { bool operator()(size_t u1, size_t u2) const { return u1 == u2; } };
struct int_eq { bool operator()(int u1, int u2) const { return u1 == u2; } };
template<typename Value>
class u_map : public map<unsigned, Value, u_hash, u_eq> {};
template<typename Value>
class size_t_map : public map<size_t, Value, size_t_hash, size_t_eq> {};
#endif

56
src/util/mem_stat.cpp Normal file
View file

@ -0,0 +1,56 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
mem_stat.cpp
Abstract:
Memory usage statistics
Author:
Leonardo de Moura (leonardo) 2006-11-09.
Revision History:
--*/
#ifdef _WINDOWS
#include<windows.h>
#include<cstdio>
#include<psapi.h>
double get_max_heap_size() {
DWORD processID = GetCurrentProcessId();
HANDLE hProcess;
PROCESS_MEMORY_COUNTERS pmc;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID);
double result = -1.0;
if (NULL == hProcess) {
return -1.0;
}
if (GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc))) {
result = static_cast<double>(pmc.PeakWorkingSetSize) / static_cast<double>(1024*1024);
}
CloseHandle( hProcess );
return result;
}
#else
double get_max_heap_size() {
// not available in this platform
return -1.0;
}
#endif

25
src/util/mem_stat.h Normal file
View file

@ -0,0 +1,25 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
mem_stat.h
Abstract:
Memory usage statistics
Author:
Leonardo de Moura (leonardo) 2006-11-09.
Revision History:
--*/
#ifndef _MEM_STAT_H_
#define _MEM_STAT_H_
double get_max_heap_size();
#endif /* _MEM_STAT_H_ */

260
src/util/memory_manager.cpp Normal file
View file

@ -0,0 +1,260 @@
#include<stdlib.h>
#include"memory_manager.h"
#include"rational.h"
#include"prime_generator.h"
#include"debug.h"
// If PROFILE_MEMORY is defined, Z3 will display the amount of memory used, and the number of synchronization steps during finalization
// #define PROFILE_MEMORY
void initialize_symbols();
void finalize_symbols();
out_of_memory_error::out_of_memory_error():z3_error(ERR_MEMOUT) {
}
volatile bool g_memory_out_of_memory = false;
long long g_memory_alloc_size = 0;
long long g_memory_max_size = 0;
long long g_memory_max_used_size = 0;
long long g_memory_watermark = 0;
bool g_exit_when_out_of_memory = false;
char const * g_out_of_memory_msg = "ERROR: out of memory";
void memory::exit_when_out_of_memory(bool flag, char const * msg) {
g_exit_when_out_of_memory = flag;
if (flag && msg)
g_out_of_memory_msg = msg;
}
static void throw_out_of_memory() {
#pragma omp critical (z3_memory_manager)
{
if (!g_memory_out_of_memory) {
g_memory_out_of_memory = true;
}
}
if (g_exit_when_out_of_memory) {
std::cerr << g_out_of_memory_msg << "\n";
exit(ERR_MEMOUT);
}
else {
throw out_of_memory_error();
}
}
#ifdef PROFILE_MEMORY
unsigned g_synch_counter = 0;
class mem_usage_report {
public:
~mem_usage_report() {
std::cerr << "(memory :max " << g_memory_max_used_size
<< " :final " << g_memory_alloc_size
<< " :synch " << g_synch_counter << ")" << std::endl;
}
};
mem_usage_report g_info;
#endif
void memory::initialize(size_t max_size) {
g_memory_out_of_memory = false;
g_memory_max_size = max_size;
rational::initialize();
initialize_symbols();
}
bool memory::is_out_of_memory() {
bool r = false;
#pragma omp critical (z3_memory_manager)
{
r = g_memory_out_of_memory;
}
return r;
}
void memory::set_high_watermark(size_t watermak) {
// This method is only safe to invoke at initialization time, that is, before the threads are created.
g_memory_watermark = watermak;
}
bool memory::above_high_watermark() {
if (g_memory_watermark == 0)
return false;
bool r;
#pragma omp critical (z3_memory_manager)
{
r = g_memory_watermark < g_memory_alloc_size;
}
return r;
}
void memory::set_max_size(size_t max_size) {
// This method is only safe to invoke at initialization time, that is, before the threads are created.
g_memory_max_size = max_size;
}
static bool g_finalizing = false;
void memory::finalize() {
g_finalizing = true;
finalize_debug();
finalize_trace();
finalize_symbols();
rational::finalize();
prime_iterator::finalize();
}
unsigned long long memory::get_allocation_size() {
long long r;
#pragma omp critical (z3_memory_manager)
{
r = g_memory_alloc_size;
}
if (r < 0)
r = 0;
return r;
}
unsigned long long memory::get_max_used_memory() {
unsigned long long r;
#pragma omp critical (z3_memory_manager)
{
r = g_memory_max_used_size;
}
return r;
}
void memory::display_max_usage(std::ostream & os) {
unsigned long long mem = get_max_used_memory();
os << "max. heap size: "
<< static_cast<double>(mem)/static_cast<double>(1024*1024)
<< " Mbytes\n";
}
void memory::display_i_max_usage(std::ostream & os) {
unsigned long long mem = get_max_used_memory();
std::cout << "MEMORY "
<< static_cast<double>(mem)/static_cast<double>(1024*1024)
<< "\n";
}
void memory::deallocate(char const * file, int line, void * p) {
deallocate(p);
TRACE_CODE(if (!g_finalizing) TRACE("memory", tout << "dealloc " << std::hex << p << std::dec << " " << file << ":" << line << "\n";););
}
void * memory::allocate(char const* file, int line, char const* obj, size_t s) {
void * r = allocate(s);
TRACE("memory", tout << "alloc " << std::hex << r << std::dec << " " << file << ":" << line << " " << obj << " " << s << "\n";);
return r;
}
#if defined(_WINDOWS) || defined(_USE_THREAD_LOCAL)
// ==================================
// ==================================
// THREAD LOCAL VERSION
// ==================================
// ==================================
// We only integrate the local thread counters with the global one
// when the local counter > SYNCH_THRESHOLD
#define SYNCH_THRESHOLD 100000
#ifdef _WINDOWS
// Actually this is VS specific instead of Windows specific.
__declspec(thread) long long g_memory_thread_alloc_size = 0;
#else
// GCC style
__thread long long g_memory_thread_alloc_size = 0;
#endif
static void synchronize_counters(bool allocating) {
#ifdef PROFILE_MEMORY
g_synch_counter++;
#endif
bool out_of_mem = false;
#pragma omp critical (z3_memory_manager)
{
g_memory_alloc_size += g_memory_thread_alloc_size;
if (g_memory_alloc_size > g_memory_max_used_size)
g_memory_max_used_size = g_memory_alloc_size;
if (g_memory_max_size != 0 && g_memory_alloc_size > g_memory_max_size)
out_of_mem = true;
}
g_memory_thread_alloc_size = 0;
if (out_of_mem && allocating) {
throw_out_of_memory();
}
}
void memory::deallocate(void * p) {
size_t * sz_p = reinterpret_cast<size_t*>(p) - 1;
size_t sz = *sz_p;
void * real_p = reinterpret_cast<void*>(sz_p);
g_memory_thread_alloc_size -= sz;
free(real_p);
if (g_memory_thread_alloc_size < -SYNCH_THRESHOLD) {
synchronize_counters(false);
}
}
void * memory::allocate(size_t s) {
if (s == 0)
return 0;
s = s + sizeof(size_t); // we allocate an extra field!
void * r = malloc(s);
if (r == 0) {
throw_out_of_memory();
}
*(static_cast<size_t*>(r)) = s;
g_memory_thread_alloc_size += s;
if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) {
synchronize_counters(true);
}
return static_cast<size_t*>(r) + 1; // we return a pointer to the location after the extra field
}
#else
// ==================================
// ==================================
// NO THREAD LOCAL VERSION
// ==================================
// ==================================
// allocate & deallocate without using thread local storage
void memory::deallocate(void * p) {
size_t * sz_p = reinterpret_cast<size_t*>(p) - 1;
size_t sz = *sz_p;
void * real_p = reinterpret_cast<void*>(sz_p);
#pragma omp critical (z3_memory_manager)
{
g_memory_alloc_size -= sz;
}
free(real_p);
}
void * memory::allocate(size_t s) {
if (s == 0)
return 0;
s = s + sizeof(size_t); // we allocate an extra field!
bool out_of_mem = false;
#pragma omp critical (z3_memory_manager)
{
g_memory_alloc_size += s;
if (g_memory_alloc_size > g_memory_max_used_size)
g_memory_max_used_size = g_memory_alloc_size;
if (g_memory_max_size != 0 && g_memory_alloc_size > g_memory_max_size)
out_of_mem = true;
}
if (out_of_mem)
throw_out_of_memory();
void * r = malloc(s);
if (r == 0)
throw_out_of_memory();
*(static_cast<size_t*>(r)) = s;
return static_cast<size_t*>(r) + 1; // we return a pointer to the location after the extra field
}
#endif

105
src/util/memory_manager.h Normal file
View file

@ -0,0 +1,105 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
memory_manager.h
Abstract:
Custom memory layer.
Author:
Nikolaj Bjorner (nbjorner) 2007-07-24
Revision History:
--*/
#ifndef _MEMORY_H_
#define _MEMORY_H_
#include<cstdlib>
#include<ostream>
#include"z3_exception.h"
class out_of_memory_error : public z3_error {
public:
out_of_memory_error();
};
class memory {
public:
static bool is_out_of_memory();
static void initialize(size_t max_size);
static void set_high_watermark(size_t watermak);
static bool above_high_watermark();
static void set_max_size(size_t max_size);
static void finalize();
static void display_max_usage(std::ostream& os);
static void display_i_max_usage(std::ostream& os);
static void deallocate(void* p);
static void* allocate(size_t s);
static void deallocate(char const* file, int line, void* p);
static void* allocate(char const* file, int line, char const* obj, size_t s);
static unsigned long long get_allocation_size();
static unsigned long long get_max_used_memory();
// temporary hack to avoid out-of-memory crash in z3.exe
static void exit_when_out_of_memory(bool flag, char const * msg);
};
#if _DEBUG
#define alloc(T,...) new (memory::allocate(__FILE__,__LINE__,#T, sizeof(T))) T(__VA_ARGS__)
#define dealloc(_ptr_) deallocf(__FILE__,__LINE__,_ptr_)
template<typename T>
void deallocf(char const* file, int line, T * ptr) {
if (ptr == 0) return;
ptr->~T();
memory::deallocate(file, line, ptr);
}
#else
#define alloc(T,...) new (memory::allocate(sizeof(T))) T(__VA_ARGS__)
template<typename T>
void dealloc(T * ptr) {
if (ptr == 0) return;
ptr->~T();
memory::deallocate(ptr);
}
#endif
template<typename T>
T * alloc_vect(unsigned sz) {
T * r = static_cast<T*>(memory::allocate(sizeof(T) * sz));
T * curr = r;
for (unsigned i = 0; i < sz; i++, curr++)
new (curr) T();
return r;
}
template<typename T>
void dealloc_vect(T * ptr, unsigned sz) {
if (ptr == 0) return;
T * curr = ptr;
for (unsigned i = 0; i < sz; i++, curr++)
curr->~T();
memory::deallocate(ptr);
}
#define alloc_svect(T, sz) static_cast<T*>(memory::allocate(sizeof(T) * sz))
template<typename T>
void dealloc_svect(T * ptr) {
if (ptr == 0) return;
memory::deallocate(ptr);
}
#endif /* _MEMORY_H_ */

907
src/util/mpbq.cpp Normal file
View file

@ -0,0 +1,907 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
mpbq.cpp
Abstract:
Binary Rational Numbers
A binary rational is a number of the form a/2^k.
All integers are binary rationals.
Binary rational numbers can be implemented more efficiently than rationals.
Binary rationals form a Ring.
They are not closed under division.
In Z3, they are used to implement algebraic numbers.
The root isolation operations only use division by 2.
Author:
Leonardo de Moura (leonardo) 2011-11-24.
Revision History:
--*/
#include<sstream>
#include"mpbq.h"
#ifdef Z3DEBUG
#define MPBQ_DEBUG
#endif
rational to_rational(mpbq const & v) {
rational r(v.numerator());
rational twok;
twok = power(rational(2), v.k());
return r/twok;
}
mpbq_manager::mpbq_manager(unsynch_mpz_manager & m):
m_manager(m) {
}
mpbq_manager::~mpbq_manager() {
del(m_addmul_tmp);
m_manager.del(m_tmp);
m_manager.del(m_tmp2);
m_manager.del(m_select_int_tmp1);
m_manager.del(m_select_int_tmp2);
m_manager.del(m_select_small_tmp);
del(m_select_small_tmp1);
del(m_select_small_tmp2);
m_manager.del(m_div_tmp1);
m_manager.del(m_div_tmp2);
m_manager.del(m_div_tmp3);
}
void mpbq_manager::reset(mpbq_vector & v) {
unsigned sz = v.size();
for (unsigned i = 0; i < sz; i++)
reset(v[i]);
v.reset();
}
void mpbq_manager::normalize(mpbq & a) {
if (a.m_k == 0)
return;
if (m_manager.is_zero(a.m_num)) {
a.m_k = 0;
return;
}
#ifdef MPBQ_DEBUG
rational r = to_rational(a);
#endif
unsigned k = m_manager.power_of_two_multiple(a.m_num);
if (k > a.m_k)
k = a.m_k;
m_manager.machine_div2k(a.m_num, k);
a.m_k -= k;
#ifdef MPBQ_DEBUG
rational new_r = to_rational(a);
SASSERT(r == new_r);
#endif
}
int mpbq_manager::magnitude_lb(mpbq const & a) {
if (m_manager.is_zero(a.m_num))
return 0;
if (m_manager.is_pos(a.m_num))
return m_manager.log2(a.m_num) - a.m_k;
return m_manager.mlog2(a.m_num) - a.m_k + 1;
}
int mpbq_manager::magnitude_ub(mpbq const & a) {
if (m_manager.is_zero(a.m_num))
return 0;
if (m_manager.is_pos(a.m_num))
return m_manager.log2(a.m_num) - a.m_k + 1;
return m_manager.mlog2(a.m_num) - a.m_k;
}
void mpbq_manager::mul2(mpbq & a) {
if (a.m_k == 0)
m_manager.mul2k(a.m_num, 1);
else
a.m_k--;
}
void mpbq_manager::mul2k(mpbq & a, unsigned k) {
if (k == 0)
return;
if (a.m_k < k) {
m_manager.mul2k(a.m_num, k - a.m_k);
a.m_k = 0;
}
else {
SASSERT(a.m_k >= k);
a.m_k -= k;
}
}
void mpbq_manager::add(mpbq const & a, mpbq const & b, mpbq & r) {
#ifdef MPBQ_DEBUG
rational _a = to_rational(a);
rational _b = to_rational(b);
#endif
if (a.m_k == b.m_k) {
m_manager.add(a.m_num, b.m_num, r.m_num);
r.m_k = a.m_k;
}
else if (a.m_k < b.m_k) {
m_manager.mul2k(a.m_num, b.m_k - a.m_k, m_tmp);
m_manager.add(b.m_num, m_tmp, r.m_num);
r.m_k = b.m_k;
}
else {
SASSERT(a.m_k > b.m_k);
m_manager.mul2k(b.m_num, a.m_k - b.m_k, m_tmp);
m_manager.add(a.m_num, m_tmp, r.m_num);
r.m_k = a.m_k;
}
normalize(r);
#ifdef MPBQ_DEBUG
rational _r = to_rational(r);
SASSERT(_a + _b == _r);
#endif
}
void mpbq_manager::add(mpbq const & a, mpz const & b, mpbq & r) {
#ifdef MPBQ_DEBUG
rational _a = to_rational(a);
rational _b(b);
#endif
if (a.m_k == 0) {
m_manager.add(a.m_num, b, r.m_num);
r.m_k = a.m_k;
}
else {
m_manager.mul2k(b, a.m_k, m_tmp);
m_manager.add(a.m_num, m_tmp, r.m_num);
r.m_k = a.m_k;
}
normalize(r);
#ifdef MPBQ_DEBUG
rational _r = to_rational(r);
TRACE("mpbq_bug", tout << "add a: " << _a << ", b: " << _b << ", r: " << _r << ", expected: " << (_a + _b) << "\n";);
SASSERT(_a + _b == _r);
#endif
}
void mpbq_manager::sub(mpbq const & a, mpbq const & b, mpbq & r) {
#ifdef MPBQ_DEBUG
rational _a = to_rational(a);
rational _b = to_rational(b);
#endif
if (a.m_k == b.m_k) {
m_manager.sub(a.m_num, b.m_num, r.m_num);
r.m_k = a.m_k;
}
else if (a.m_k < b.m_k) {
m_manager.mul2k(a.m_num, b.m_k - a.m_k, m_tmp);
m_manager.sub(m_tmp, b.m_num, r.m_num);
r.m_k = b.m_k;
}
else {
SASSERT(a.m_k > b.m_k);
m_manager.mul2k(b.m_num, a.m_k - b.m_k, m_tmp);
m_manager.sub(a.m_num, m_tmp, r.m_num);
r.m_k = a.m_k;
}
normalize(r);
#ifdef MPBQ_DEBUG
rational _r = to_rational(r);
TRACE("mpbq_bug", tout << "sub a: " << _a << ", b: " << _b << ", r: " << _r << ", expected: " << (_a - _b) << "\n";);
SASSERT(_a - _b == _r);
#endif
}
void mpbq_manager::sub(mpbq const & a, mpz const & b, mpbq & r) {
#ifdef MPBQ_DEBUG
rational _a = to_rational(a);
rational _b(b);
#endif
if (a.m_k == 0) {
m_manager.sub(a.m_num, b, r.m_num);
r.m_k = a.m_k;
}
else {
m_manager.mul2k(b, a.m_k, m_tmp);
m_manager.sub(a.m_num, m_tmp, r.m_num);
r.m_k = a.m_k;
}
normalize(r);
#ifdef MPBQ_DEBUG
rational _r = to_rational(r);
SASSERT(_a - _b == _r);
#endif
}
void mpbq_manager::mul(mpbq const & a, mpbq const & b, mpbq & r) {
#ifdef MPBQ_DEBUG
rational _a = to_rational(a);
rational _b = to_rational(b);
#endif
m_manager.mul(a.m_num, b.m_num, r.m_num);
r.m_k = a.m_k + b.m_k;
if (a.m_k == 0 || b.m_k == 0) {
// if a.m_k and b.m_k are greater than 0, then there is no point in normalizing r.
normalize(r);
}
#ifdef MPBQ_DEBUG
rational _r = to_rational(r);
SASSERT(_a * _b == _r);
#endif
}
void mpbq_manager::mul(mpbq const & a, mpz const & b, mpbq & r) {
#ifdef MPBQ_DEBUG
rational _a = to_rational(a);
rational _b(b);
#endif
m_manager.mul(a.m_num, b, r.m_num);
r.m_k = a.m_k;
normalize(r);
#ifdef MPBQ_DEBUG
rational _r = to_rational(r);
SASSERT(_a * _b == _r);
#endif
}
void mpbq_manager::power(mpbq & a, unsigned k) {
SASSERT(static_cast<uint64>(k) * static_cast<uint64>(a.k()) <= static_cast<uint64>(UINT_MAX));
// We don't need to normalize because:
// If a.m_k == 0, then a is an integer, and the result be an integer
// If a.m_k > 0, then a.m_num must be odd, and the (a.m_num)^k will also be odd
a.m_k *= k;
m_manager.power(a.m_num, k, a.m_num);
}
bool mpbq_manager::root_lower(mpbq & a, unsigned n) {
bool r = m_manager.root(a.m_num, n);
if (!r)
m_manager.dec(a.m_num);
if (a.m_k % n == 0) {
a.m_k /= n;
normalize(a);
return r;
}
else if (m_manager.is_neg(a.m_num)) {
a.m_k /= n;
normalize(a);
return false;
}
else {
a.m_k /= n;
a.m_k++;
normalize(a);
return false;
}
}
bool mpbq_manager::root_upper(mpbq & a, unsigned n) {
bool r = m_manager.root(a.m_num, n);
if (a.m_k % n == 0) {
a.m_k /= n;
normalize(a);
return r;
}
else if (m_manager.is_neg(a.m_num)) {
a.m_k /= n;
a.m_k++;
normalize(a);
return false;
}
else {
a.m_k /= n;
normalize(a);
return false;
}
}
bool mpbq_manager::lt(mpbq const & a, mpbq const & b) {
// TODO: try the following trick when k1 != k2
// Given, a = n1/2^k1 b = n2/2^k2
// Suppose n1 > 0 and n2 > 0,
// Then, we have, n1 <= 2^{log2(n1) - k1} 2^{log2(n2) - 1 - k2} <= n2
// Thus, log2(n1) - k1 < log2(n2) - 1 - k2 implies a < b
// Similarly: log2(n2) - k2 < log2(n1) - 1 - k1 implies b < a
// That is we compare the "magnitude" of the numbers before performing mul2k
//
// If n1 < 0 and n2 < 0, a similar trick can be implemented using mlog2 instead log2.
//
// It seems the trick is not useful when n1 and n2 are small
// numbers, and k1 and k2 very small < 8. Since, no bignumber
// computation is needed for mul2k.
if (a.m_k == b.m_k) {
return m_manager.lt(a.m_num, b.m_num);
}
else if (a.m_k < b.m_k) {
m_manager.mul2k(a.m_num, b.m_k - a.m_k, m_tmp);
return m_manager.lt(m_tmp, b.m_num);
}
else {
SASSERT(a.m_k > b.m_k);
m_manager.mul2k(b.m_num, a.m_k - b.m_k, m_tmp);
return m_manager.lt(a.m_num, m_tmp);
}
}
bool mpbq_manager::lt_1div2k(mpbq const & a, unsigned k) {
if (m_manager.is_nonpos(a.m_num))
return true;
if (a.m_k <= k) {
// since a.m_num >= 1
return false;
}
else {
SASSERT(a.m_k > k);
m_manager.mul2k(mpz(1), a.m_k - k, m_tmp);
return m_manager.lt(a.m_num, m_tmp);
}
}
bool mpbq_manager::eq(mpbq const & a, mpq const & b) {
if (is_int(a) && m_manager.is_one(b.denominator()))
return m_manager.eq(a.m_num, b.numerator());
m_manager.mul2k(b.numerator(), a.m_k, m_tmp);
m_manager.mul(a.m_num, b.denominator(), m_tmp2);
return m_manager.eq(m_tmp, m_tmp2);
}
bool mpbq_manager::lt(mpbq const & a, mpq const & b) {
if (is_int(a) && m_manager.is_one(b.denominator()))
return m_manager.lt(a.m_num, b.numerator());
m_manager.mul(a.m_num, b.denominator(), m_tmp);
m_manager.mul2k(b.numerator(), a.m_k, m_tmp2);
return m_manager.lt(m_tmp, m_tmp2);
}
bool mpbq_manager::le(mpbq const & a, mpq const & b) {
if (is_int(a) && m_manager.is_one(b.denominator()))
return m_manager.le(a.m_num, b.numerator());
m_manager.mul(a.m_num, b.denominator(), m_tmp);
m_manager.mul2k(b.numerator(), a.m_k, m_tmp2);
return m_manager.le(m_tmp, m_tmp2);
}
bool mpbq_manager::lt(mpbq const & a, mpz const & b) {
if (is_int(a))
return m_manager.lt(a.m_num, b);
m_manager.mul2k(b, a.m_k, m_tmp);
return m_manager.lt(a.m_num, m_tmp);
}
bool mpbq_manager::le(mpbq const & a, mpz const & b) {
if (is_int(a))
return m_manager.le(a.m_num, b);
m_manager.mul2k(b, a.m_k, m_tmp);
return m_manager.le(a.m_num, m_tmp);
}
std::string mpbq_manager::to_string(mpbq const & a) {
std::ostringstream buffer;
buffer << m_manager.to_string(a.m_num);
if (a.m_k == 1)
buffer << "/2";
else if (a.m_k > 1)
buffer << "/2^" << a.m_k;
return buffer.str();
}
void mpbq_manager::display(std::ostream & out, mpbq const & a) {
out << m_manager.to_string(a.m_num);
if (a.m_k > 0)
out << "/2";
if (a.m_k > 1)
out << "^" << a.m_k;
}
void mpbq_manager::display_smt2(std::ostream & out, mpbq const & a, bool decimal) {
if (a.m_k == 0) {
m_manager.display_smt2(out, a.m_num, decimal);
}
else {
out << "(/ ";
m_manager.display_smt2(out, a.m_num, decimal);
out << " ";
out << "(^ 2";
if (decimal) out << ".0";
out << " " << a.m_k;
if (decimal) out << ".0";
out << "))";
}
}
void mpbq_manager::display_decimal(std::ostream & out, mpbq const & a, unsigned prec) {
if (is_int(a)) {
out << m_manager.to_string(a.m_num);
return;
}
mpz two(2);
mpz ten(10);
mpz two_k;
mpz n1, v1;
if (m_manager.is_neg(a.m_num))
out << "-";
m_manager.set(v1, a.m_num);
m_manager.abs(v1);
m_manager.power(two, a.m_k, two_k);
m_manager.rem(v1, two_k, n1);
m_manager.div(v1, two_k, v1);
SASSERT(!m_manager.is_zero(n1));
out << m_manager.to_string(v1);
out << ".";
for (unsigned i = 0; i < prec; i++) {
m_manager.mul(n1, ten, n1);
m_manager.div(n1, two_k, v1);
m_manager.rem(n1, two_k, n1);
out << m_manager.to_string(v1);
if (m_manager.is_zero(n1))
goto end;
}
out << "?";
end:
m_manager.del(n1);
m_manager.del(v1);
m_manager.del(two_k);
}
void mpbq_manager::display_decimal(std::ostream & out, mpbq const & a, mpbq const & b, unsigned prec) {
mpz two(2);
mpz ten(10);
mpz two_k1, two_k2;
mpz n1, v1, n2, v2;
if (m_manager.is_neg(a.m_num) != m_manager.is_neg(b.m_num)) {
out << "?";
return;
}
if (m_manager.is_neg(a.m_num))
out << "-";
m_manager.set(v1, a.m_num);
m_manager.abs(v1);
m_manager.set(v2, b.m_num);
m_manager.abs(v2);
m_manager.power(two, a.m_k, two_k1);
m_manager.power(two, b.m_k, two_k2);
m_manager.rem(v1, two_k1, n1);
m_manager.rem(v2, two_k2, n2);
m_manager.div(v1, two_k1, v1);
m_manager.div(v2, two_k2, v2);
if (!m_manager.eq(v1, v2)) {
out << "?";
goto end;
}
out << m_manager.to_string(v1);
if (m_manager.is_zero(n1) && m_manager.is_zero(n2))
goto end; // number is an integer
out << ".";
for (unsigned i = 0; i < prec; i++) {
m_manager.mul(n1, ten, n1);
m_manager.mul(n2, ten, n2);
m_manager.div(n1, two_k1, v1);
m_manager.div(n2, two_k2, v2);
if (m_manager.eq(v1, v2)) {
out << m_manager.to_string(v1);
}
else {
out << "?";
goto end;
}
m_manager.rem(n1, two_k1, n1);
m_manager.rem(n2, two_k2, n2);
if (m_manager.is_zero(n1) && m_manager.is_zero(n2))
goto end; // number is precise
}
out << "?";
end:
m_manager.del(n1);
m_manager.del(v1);
m_manager.del(n2);
m_manager.del(v2);
m_manager.del(two_k1);
m_manager.del(two_k2);
}
bool mpbq_manager::to_mpbq(mpq const & q, mpbq & bq) {
mpz const & n = q.numerator();
mpz const & d = q.denominator();
unsigned shift;
if (m_manager.is_one(d)) {
set(bq, n);
SASSERT(eq(bq, q));
return true;
}
else if (m_manager.is_power_of_two(d, shift)) {
SASSERT(shift>=1);
unsigned k = shift;
set(bq, n, k);
SASSERT(eq(bq, q));
return true;
}
else {
unsigned k = m_manager.log2(d);
set(bq, n, k+1);
return false;
}
}
void mpbq_manager::refine_upper(mpq const & q, mpbq & l, mpbq & u) {
SASSERT(lt(l, q) && gt(u, q));
SASSERT(!m_manager.is_power_of_two(q.denominator()));
// l < q < u
mpbq mid;
while (true) {
add(l, u, mid);
div2(mid);
if (gt(mid, q)) {
swap(u, mid);
del(mid);
SASSERT(lt(l, q) && gt(u, q));
return;
}
swap(l, mid);
}
}
void mpbq_manager::refine_lower(mpq const & q, mpbq & l, mpbq & u) {
SASSERT(lt(l, q) && gt(u, q));
SASSERT(!m_manager.is_power_of_two(q.denominator()));
// l < q < u
mpbq mid;
while (true) {
add(l, u, mid);
div2(mid);
if (lt(mid, q)) {
swap(l, mid);
del(mid);
SASSERT(lt(l, q) && gt(u, q));
return;
}
swap(u, mid);
}
}
// sectect integer in [lower, upper]
bool mpbq_manager::select_integer(mpbq const & lower, mpbq const & upper, mpz & r) {
if (is_int(lower)) {
m_manager.set(r, lower.m_num);
return true;
}
if (is_int(upper)) {
m_manager.set(r, upper.m_num);
return true;
}
mpz & ceil_lower = m_select_int_tmp1;
mpz & floor_upper = m_select_int_tmp2;
ceil(m_manager, lower, ceil_lower);
floor(m_manager, upper, floor_upper);
if (m_manager.le(ceil_lower, floor_upper)) {
m_manager.set(r, ceil_lower);
return true;
}
return false;
}
// select integer in (lower, upper]
bool mpbq_manager::select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpz & r) {
if (is_int(upper)) {
m_manager.set(r, upper.m_num);
return true;
}
mpz & ceil_lower = m_select_int_tmp1;
mpz & floor_upper = m_select_int_tmp2;
if (qm.is_int(lower)) {
m_manager.set(ceil_lower, lower.numerator());
m_manager.inc(ceil_lower);
}
else {
scoped_mpz tmp(qm);
qm.ceil(lower, tmp);
m_manager.set(ceil_lower, tmp);
}
floor(m_manager, upper, floor_upper);
if (m_manager.le(ceil_lower, floor_upper)) {
m_manager.set(r, ceil_lower);
return true;
}
return false;
}
// sectect integer in [lower, upper)
bool mpbq_manager::select_integer(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpz & r) {
if (is_int(lower)) {
m_manager.set(r, lower.m_num);
return true;
}
mpz & ceil_lower = m_select_int_tmp1;
mpz & floor_upper = m_select_int_tmp2;
ceil(m_manager, lower, ceil_lower);
if (qm.is_int(upper)) {
m_manager.set(floor_upper, upper.numerator());
m_manager.dec(floor_upper);
}
else {
scoped_mpz tmp(qm);
qm.floor(upper, tmp);
m_manager.set(floor_upper, tmp);
}
if (m_manager.le(ceil_lower, floor_upper)) {
m_manager.set(r, ceil_lower);
return true;
}
return false;
}
// sectect integer in (lower, upper)
bool mpbq_manager::select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpz & r) {
mpz & ceil_lower = m_select_int_tmp1;
mpz & floor_upper = m_select_int_tmp2;
if (qm.is_int(lower)) {
m_manager.set(ceil_lower, lower.numerator());
m_manager.inc(ceil_lower);
}
else {
scoped_mpz tmp(qm);
qm.ceil(lower, tmp);
m_manager.set(ceil_lower, tmp);
}
if (qm.is_int(upper)) {
m_manager.set(floor_upper, upper.numerator());
m_manager.dec(floor_upper);
}
else {
scoped_mpz tmp(qm);
qm.floor(upper, tmp);
m_manager.set(floor_upper, tmp);
}
if (m_manager.le(ceil_lower, floor_upper)) {
m_manager.set(r, ceil_lower);
return true;
}
return false;
}
#define LINEAR_SEARCH_THRESHOLD 8
void mpbq_manager::select_small_core(mpbq const & lower, mpbq const & upper, mpbq & r) {
SASSERT(le(lower, upper));
mpz & aux = m_select_small_tmp;
if (select_integer(lower, upper, aux)) {
set(r, aux);
return;
}
// At this point we know that k=0 does not work, since there is no integer
// in the interval [lower, upper]
unsigned min_k = 0;
unsigned max_k = std::min(lower.m_k, upper.m_k);
if (max_k <= LINEAR_SEARCH_THRESHOLD) {
unsigned k = 0;
mpbq & l2k = m_select_small_tmp1;
mpbq & u2k = m_select_small_tmp2;
set(l2k, lower);
set(u2k, upper);
while (true) {
k++;
mul2(l2k);
mul2(u2k);
if (select_integer(l2k, u2k, aux)) {
set(r, aux, k);
break;
}
}
}
else {
mpbq & l2k = m_select_small_tmp1;
mpbq & u2k = m_select_small_tmp2;
while (true) {
unsigned mid_k = min_k + (max_k - min_k)/2;
set(l2k, lower);
set(u2k, upper);
mul2k(l2k, mid_k);
mul2k(u2k, mid_k);
if (select_integer(l2k, u2k, aux))
max_k = mid_k;
else
min_k = mid_k + 1;
if (min_k == max_k) {
if (max_k == mid_k) {
set(r, aux, max_k);
}
else {
set(l2k, lower);
set(u2k, upper);
mul2k(l2k, max_k);
mul2k(u2k, max_k);
VERIFY(select_integer(l2k, u2k, aux));
set(r, aux, max_k);
}
break;
}
}
}
SASSERT(le(lower, r));
SASSERT(le(r, upper));
}
bool mpbq_manager::select_small(mpbq const & lower, mpbq const & upper, mpbq & r) {
if (gt(lower, upper))
return false;
select_small_core(lower, upper, r);
return true;
}
void mpbq_manager::select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpbq & r) {
TRACE("select_small", tout << "lower (q): " << qm.to_string(lower) << ", upper (bq): " << to_string(upper) << "\n";);
SASSERT(gt(upper, lower));
mpz & aux = m_select_small_tmp;
if (select_integer(qm, lower, upper, aux)) {
set(r, aux);
return;
}
// At this point we know that k=0 does not work, since there is no integer
// in the interval [lower, upper]
unsigned k = 0;
scoped_mpq l2k(qm);
mpq two(2);
mpbq & u2k = m_select_small_tmp2;
qm.set(l2k, lower);
set(u2k, upper);
while (true) {
k++;
qm.mul(l2k, two, l2k);
mul2(u2k);
if (select_integer(qm, l2k, u2k, aux)) {
set(r, aux, k);
break;
}
}
}
void mpbq_manager::select_small_core(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpbq & r) {
SASSERT(lt(lower, upper));
mpz & aux = m_select_small_tmp;
if (select_integer(qm, lower, upper, aux)) {
set(r, aux);
return;
}
// At this point we know that k=0 does not work, since there is no integer
// in the interval [lower, upper]
unsigned k = 0;
mpbq & l2k = m_select_small_tmp2;
scoped_mpq u2k(qm);
mpq two(2);
set(l2k, lower);
qm.set(u2k, upper);
while (true) {
k++;
mul2(l2k);
qm.mul(u2k, two, u2k);
if (select_integer(qm, l2k, u2k, aux)) {
set(r, aux, k);
break;
}
}
}
void mpbq_manager::select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpbq & r) {
SASSERT(qm.lt(lower, upper));
mpz & aux = m_select_small_tmp;
if (select_integer(qm, lower, upper, aux)) {
set(r, aux);
return;
}
// At this point we know that k=0 does not work, since there is no integer
// in the interval [lower, upper]
unsigned k = 0;
scoped_mpq l2k(qm);
scoped_mpq u2k(qm);
mpq two(2);
qm.set(l2k, lower);
qm.set(u2k, upper);
while (true) {
k++;
qm.mul(l2k, two, l2k);
qm.mul(u2k, two, u2k);
if (select_integer(qm, l2k, u2k, aux)) {
set(r, aux, k);
break;
}
}
}
void mpbq_manager::approx(mpbq & a, unsigned k, bool to_plus_inf) {
if (a.m_k <= k)
return;
#ifdef MPBQ_DEBUG
scoped_mpbq old_a(*this);
old_a = a;
#endif
bool sgn = m_manager.is_neg(a.m_num);
bool _inc = (sgn != to_plus_inf);
unsigned shift = a.m_k - k;
m_manager.abs(a.m_num);
m_manager.machine_div2k(a.m_num, shift);
if (_inc)
m_manager.inc(a.m_num);
if (sgn)
m_manager.neg(a.m_num);
a.m_k = k;
normalize(a);
#ifdef MPBQ_DEBUG
if (to_plus_inf) {
SASSERT(lt(old_a, a));
}
else {
SASSERT(lt(a, old_a));
}
#endif
}
void mpbq_manager::approx_div(mpbq const & a, mpbq const & b, mpbq & c, unsigned k, bool to_plus_inf) {
SASSERT(!is_zero(b));
unsigned k_prime;
if (m_manager.is_power_of_two(b.m_num, k_prime)) {
// The division is precise, so we ignore k and to_plus_inf
SASSERT(b.m_k == 0); // remark: b.m_num is odd when b.m_k > 0
m_manager.set(c.m_num, a.m_num);
if (a.m_k == 0) {
c.m_k = k_prime;
normalize(c);
}
else {
c.m_k = a.m_k + k_prime;
// there is not need to normalize since the least significant bit of a must be 1.
}
}
else if (m_manager.divides(b.m_num, a.m_num)) {
// result is also precise
m_manager.div(a.m_num, b.m_num, c.m_num);
if (a.m_k >= b.m_k) {
c.m_k = a.m_k - b.m_k;
}
else {
m_manager.mul2k(c.m_num, b.m_k - a.m_k);
c.m_k = 0;
}
normalize(c);
}
else {
bool sgn = is_neg(a) != is_neg(b);
mpz & abs_a = m_div_tmp1;
mpz & norm_a = m_div_tmp2;
mpz & abs_b = m_div_tmp3;
m_manager.set(abs_a, a.m_num);
m_manager.abs(abs_a);
m_manager.set(abs_b, b.m_num);
m_manager.abs(abs_b);
if (a.m_k > b.m_k) {
if (k >= a.m_k - b.m_k)
m_manager.mul2k(abs_a, k - (a.m_k - b.m_k), norm_a);
else
m_manager.machine_div2k(abs_a, (a.m_k - b.m_k) - k, norm_a);
}
else {
m_manager.mul2k(abs_a, k + b.m_k - a.m_k, norm_a);
}
c.m_k = k;
m_manager.div(norm_a, abs_b, c.m_num);
if (sgn != to_plus_inf)
m_manager.inc(c.m_num);
if (sgn)
m_manager.neg(c.m_num);
normalize(c);
}
}

362
src/util/mpbq.h Normal file
View file

@ -0,0 +1,362 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
mpbq.h
Abstract:
Binary Rational Numbers
A binary rational is a number of the form a/2^k.
All integers are binary rationals.
Binary rational numbers can be implemented more efficiently than rationals.
Binary rationals form a Ring.
They are not closed under division.
In Z3, they are used to implement algebraic numbers.
The root isolation operations only use division by 2.
Author:
Leonardo de Moura (leonardo) 2011-11-24.
Revision History:
--*/
#ifndef _MPBQ_H_
#define _MPBQ_H_
#include"mpq.h"
#include"rational.h"
#include"vector.h"
class mpbq {
mpz m_num;
unsigned m_k; // we don't need mpz here. 2^(2^32-1) is a huge number, we will not even be able to convert the mpbq into an mpq
friend class mpbq_manager;
public:
mpbq():m_num(0), m_k(0) {}
mpbq(int v):m_num(v), m_k(0) {}
mpbq(int v, unsigned k):m_num(v), m_k(k) {}
mpz const & numerator() const { return m_num; }
unsigned k() const { return m_k; }
void swap(mpbq & other) { m_num.swap(other.m_num); std::swap(m_k, other.m_k); }
};
inline void swap(mpbq & m1, mpbq & m2) { m1.swap(m2); }
typedef svector<mpbq> mpbq_vector;
class mpbq_manager {
unsynch_mpz_manager & m_manager;
mpz m_tmp;
mpz m_tmp2;
mpbq m_addmul_tmp;
mpz m_select_int_tmp1;
mpz m_select_int_tmp2;
mpz m_select_small_tmp;
mpbq m_select_small_tmp1;
mpbq m_select_small_tmp2;
mpz m_div_tmp1, m_div_tmp2, m_div_tmp3;
void normalize(mpbq & a);
bool select_integer(mpbq const & lower, mpbq const & upper, mpz & r);
bool select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpz & r);
bool select_integer(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpz & r);
bool select_integer(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpz & r);
public:
static bool precise() { return true; }
static bool field() { return false; }
typedef mpbq numeral;
mpbq_manager(unsynch_mpz_manager & m);
~mpbq_manager();
static void swap(mpbq & a, mpbq & b) { a.swap(b); }
void del(mpbq & a) { m_manager.del(a.m_num); }
void reset(mpbq & a) { m_manager.reset(a.m_num); a.m_k = 0; }
void reset(mpbq_vector & v);
void set(mpbq & a, int n) { m_manager.set(a.m_num, n); a.m_k = 0; }
void set(mpbq & a, unsigned n) { m_manager.set(a.m_num, n); a.m_k = 0; }
void set(mpbq & a, int n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); }
void set(mpbq & a, mpz const & n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); }
void set(mpbq & a, mpz const & n) { m_manager.set(a.m_num, n); a.m_k = 0; }
void set(mpbq & a, mpbq const & b) { m_manager.set(a.m_num, b.m_num); a.m_k = b.m_k; }
void set(mpbq & a, int64 n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); }
bool is_int(mpbq const & a) const { return a.m_k == 0; }
void get_numerator(mpbq const & a, mpz & n) { m_manager.set(n, a.m_num); }
unsigned get_denominator_power(mpbq const & a) { return a.m_k; }
bool is_zero(mpbq const & a) const { return m_manager.is_zero(a.m_num); }
bool is_nonzero(mpbq const & a) const { return !is_zero(a); }
bool is_one(mpbq const & a) const { return a.m_k == 0 && m_manager.is_one(a.m_num); }
bool is_pos(mpbq const & a) const { return m_manager.is_pos(a.m_num); }
bool is_neg(mpbq const & a) const { return m_manager.is_neg(a.m_num); }
bool is_nonpos(mpbq const & a) const { return m_manager.is_nonpos(a.m_num); }
bool is_nonneg(mpbq const & a) const { return m_manager.is_nonneg(a.m_num); }
void add(mpbq const & a, mpbq const & b, mpbq & r);
void add(mpbq const & a, mpz const & b, mpbq & r);
void sub(mpbq const & a, mpbq const & b, mpbq & r);
void sub(mpbq const & a, mpz const & b, mpbq & r);
void mul(mpbq const & a, mpbq const & b, mpbq & r);
void mul(mpbq const & a, mpz const & b, mpbq & r);
// r <- a + b*c
void addmul(mpbq const & a, mpbq const & b, mpbq const & c, mpbq & r) {
mul(b, c, m_addmul_tmp);
add(a, m_addmul_tmp, r);
}
void addmul(mpbq const & a, mpz const & b, mpbq const & c, mpbq & r) {
mul(c, b, m_addmul_tmp);
add(a, m_addmul_tmp, r);
}
void neg(mpbq & a) { m_manager.neg(a.m_num); }
// when dividing by 2, we only need to normalize if m_k was zero.
void div2(mpbq & a) { bool old_k_zero = (a.m_k == 0); a.m_k++; if (old_k_zero) normalize(a); }
void div2k(mpbq & a, unsigned k) { bool old_k_zero = (a.m_k == 0); a.m_k += k; if (old_k_zero) normalize(a); }
void mul2(mpbq & a);
void mul2k(mpbq & a, unsigned k);
void power(mpbq & a, unsigned k);
void power(mpbq const & a, unsigned k, mpbq & b) { set(b, a); power(b, k); }
/**
\brief Return true if a^{1/n} is a binary rational, and store the result in a.
Otherwise, return false and return an lower bound based on the integer root of the
numerator and denominator/n
*/
bool root_lower(mpbq & a, unsigned n);
bool root_lower(mpbq const & a, unsigned n, mpbq & r) { set(r, a); return root_lower(r, n); }
/**
\brief Return true if a^{1/n} is a binary rational, and store the result in a.
Otherwise, return false and return an upper bound based on the integer root of the
numerator and denominator/n
*/
bool root_upper(mpbq & a, unsigned n);
bool root_upper(mpbq const & a, unsigned n, mpbq & r) { set(r, a); return root_upper(r, n); }
bool eq(mpbq const & a, mpbq const & b) { return a.m_k == b.m_k && m_manager.eq(a.m_num, b.m_num); }
bool lt(mpbq const & a, mpbq const & b);
bool neq(mpbq const & a, mpbq const & b) { return !eq(a, b); }
bool gt(mpbq const & a, mpbq const & b) { return lt(b, a); }
bool ge(mpbq const & a, mpbq const & b) { return le(b, a); }
bool le(mpbq const & a, mpbq const & b) { return !gt(a, b); }
bool eq(mpbq const & a, mpq const & b);
bool lt(mpbq const & a, mpq const & b);
bool le(mpbq const & a, mpq const & b);
bool neq(mpbq const & a, mpq const & b) { return !eq(a, b); }
bool gt(mpbq const & a, mpq const & b) { return !le(a, b); }
bool ge(mpbq const & a, mpq const & b) { return !lt(a, b); }
bool eq(mpbq const & a, mpz const & b) { return m_manager.eq(a.m_num, b) && a.m_k == 0; }
bool lt(mpbq const & a, mpz const & b);
bool le(mpbq const & a, mpz const & b);
bool neq(mpbq const & a, mpz const & b) { return !eq(a, b); }
bool gt(mpbq const & a, mpz const & b) { return !le(a, b); }
bool ge(mpbq const & a, mpz const & b) { return !lt(a, b); }
/**
\brief Return the magnitude of a = b/2^k.
It is defined as:
a == 0 -> 0
a > 0 -> log2(b) - k Note that 2^{log2(b) - k} <= a <= 2^{log2(b) - k + 1}
a < 0 -> mlog2(b) - k + 1 Note that -2^{mlog2(b) - k + 1} <= a <= -2^{mlog2(b) - k}
Remark: mlog2(b) = log2(-b)
Examples:
5/2^3 log2(5) - 3 = -1
21/2^2 log2(21) - 2 = 2
-3/2^4 log2(3) - 4 + 1 = -2
*/
int magnitude_lb(mpbq const & a);
/**
\brief Similar to magnitude_lb
a == 0 -> 0
a > 0 -> log2(b) - k + 1 a <= 2^{log2(b) - k + 1}
a < 0 -> mlog2(b) - k a <= -2^{mlog2(b) - k}
*/
int magnitude_ub(mpbq const & a);
/**
\brief Return true if a < 1/2^k
*/
bool lt_1div2k(mpbq const & a, unsigned k);
std::string to_string(mpbq const & a);
/**
\brief Return true if q (= c/d) is a binary rational,
and store it in bq (as a binary rational).
Otherwise return false, and set bq to c/2^{k+1}
where k = log2(d)
*/
bool to_mpbq(mpq const & q, mpbq & bq);
/**
\brief Given a rational q which cannot be represented as a binary rational,
and an interval (l, u) s.t. l < q < u. This method stores in u, a u' s.t.
q < u' < u.
In the refinement process, the lower bound l may be also refined to l'
s.t. l < l' < q
*/
void refine_upper(mpq const & q, mpbq & l, mpbq & u);
/**
\brief Similar to refine_upper.
*/
void refine_lower(mpq const & q, mpbq & l, mpbq & u);
template<typename mpz_manager>
void floor(mpz_manager & m, mpbq const & a, mpz & f) {
if (is_int(a)) {
m.set(f, a.m_num);
return;
}
bool is_neg_num = is_neg(a);
m.machine_div2k(a.m_num, a.m_k, f);
if (is_neg_num)
m.sub(f, mpz(1), f);
}
template<typename mpz_manager>
void ceil(mpz_manager & m, mpbq const & a, mpz & c) {
if (is_int(a)) {
m.set(c, a.m_num);
return;
}
bool is_pos_num = is_pos(a);
m.machine_div2k(a.m_num, a.m_k, c);
if (is_pos_num)
m.add(c, mpz(1), c);
}
/**
\brief Select some number in the interval [lower, upper].
Return true if succeeded, and false if lower > upper.
This method tries to minimize the size (in bits) of r.
For example, it will select an integer in [lower, upper]
if the interval contains one.
*/
bool select_small(mpbq const & lower, mpbq const & upper, mpbq & r);
/**
\brief Similar to select_small, but assumes lower <= upper
*/
void select_small_core(mpbq const & lower, mpbq const & upper, mpbq & r);
// Select some number in the interval (lower, upper]
void select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpbq const & upper, mpbq & r);
// Select some number in the interval [lower, upper)
void select_small_core(unsynch_mpq_manager & qm, mpbq const & lower, mpq const & upper, mpbq & r);
// Select some number in the interval (lower, upper)
void select_small_core(unsynch_mpq_manager & qm, mpq const & lower, mpq const & upper, mpbq & r);
void display(std::ostream & out, mpbq const & a);
void display_decimal(std::ostream & out, mpbq const & a, unsigned prec = 8);
/**
\brief Display a in decimal while its digits match b digits.
This function is useful when a and b are representing an interval [a,b] which
contains an algebraic number
*/
void display_decimal(std::ostream & out, mpbq const & a, mpbq const & b, unsigned prec);
void display_smt2(std::ostream & out, mpbq const & a, bool decimal);
/**
\brief Approximate n as b/2^k' s.t. k' <= k.
if get_denominator_power(n) <= k, then n is not modified.
if get_denominator_power(n) > k, then
if to_plus_inf, old(n) < b/2^k'
otherwise, b/2^k' < old(n)
*/
void approx(mpbq & n, unsigned k, bool to_plus_inf);
/**
\brief Approximated division c <- a/b
The result is precise when:
1) b is a power of two
2) get_numerator(b) divides get_numerator(a)
When the result is not precise, |c - a/b| <= 1/2^k
Actually, we have that
to_plus_inf => c - a/b <= 1/2^k
not to_plus_inf => a/b - c <= 1/2^k
*/
void approx_div(mpbq const & a, mpbq const & b, mpbq & c, unsigned k=32, bool to_plus_inf=false);
};
/**
\brief Convert a binary rational into a rational
*/
template<typename mpq_manager>
void to_mpq(mpq_manager & m, mpbq const & source, mpq & target) {
mpq two(2);
m.power(two, source.k(), target);
m.inv(target);
m.mul(source.numerator(), target, target);
}
/**
\brief Convert a binary rational into a rational.
*/
rational to_rational(mpbq const & m);
typedef _scoped_numeral<mpbq_manager> scoped_mpbq;
typedef _scoped_numeral_vector<mpbq_manager> scoped_mpbq_vector;
#define MPBQ_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \
inline bool EXTERNAL(scoped_mpbq const & a, TYPE const & b) { \
mpbq_manager & m = a.m(); \
scoped_mpbq _b(m); \
m.set(_b, b); \
return m.INTERNAL(a, _b); \
}
#define MPBQ_MK_COMPARISON(EXTERNAL, INTERNAL) \
MPBQ_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \
MPBQ_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \
MPBQ_MK_COMPARISON(operator==, eq);
MPBQ_MK_COMPARISON(operator!=, neq);
MPBQ_MK_COMPARISON(operator<, lt);
MPBQ_MK_COMPARISON(operator<=, le);
MPBQ_MK_COMPARISON(operator>, gt);
MPBQ_MK_COMPARISON(operator>=, ge);
#undef MPBQ_MK_COMPARISON
#undef MPBQ_MK_COMPARISON_CORE
#define MPBQ_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \
inline scoped_mpbq EXTERNAL(scoped_mpbq const & a, TYPE const & b) { \
mpbq_manager & m = a.m(); \
scoped_mpbq _b(m); \
m.set(_b, b); \
scoped_mpbq r(m); \
m.INTERNAL(a, _b, r); \
return r; \
}
#define MPBQ_MK_BINARY(EXTERNAL, INTERNAL) \
MPBQ_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \
MPBQ_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \
MPBQ_MK_BINARY(operator+, add)
MPBQ_MK_BINARY(operator-, sub)
MPBQ_MK_BINARY(operator*, mul)
#undef MPBQ_MK_BINARY
#undef MPBQ_MK_BINARY_CORE
#endif

50
src/util/mpbqi.h Normal file
View file

@ -0,0 +1,50 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
mpbqi.h
Abstract:
Binary Rational Number Intervals
Author:
Leonardo de Moura (leonardo) 2012-01-04
Revision History:
--*/
#ifndef _MPBQI_H_
#define _MPBQI_H_
#include"mpbq.h"
#include"basic_interval.h"
class mpbqi_manager : public basic_interval_manager<mpbq_manager, false> {
typedef basic_interval_manager<mpbq_manager, false> super;
public:
mpbqi_manager(mpbq_manager & m):super(m) {}
void set(interval & a, interval const & b) { super::set(a, b); }
void set(interval & a, bound const & lower, bound const & upper) { super::set(a, lower, upper); }
void set(interval & a, bound const & n) { super::set(a, n); }
void set(interval & a, mpz const & n) {
m().set(a.lower(), n);
m().set(a.upper(), n);
}
void add(interval const & a, interval const & b, interval & c) { super::add(a, b, c); }
void add(interval const & a, mpz const & b, interval & c) {
m().add(a.lower(), b, c.lower());
m().add(a.upper(), b, c.upper());
}
};
typedef mpbqi_manager::interval mpbqi;
typedef mpbqi_manager::scoped_interval scoped_mpbqi;
#endif

1572
src/util/mpf.cpp Normal file

File diff suppressed because it is too large Load diff

278
src/util/mpf.h Normal file
View file

@ -0,0 +1,278 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
mpf.h
Abstract:
Multi Precision Floating Point Numbers
Author:
Christoph Wintersteiger (cwinter) 2011-12-01.
Revision History:
--*/
#ifndef _MPF_H_
#define _MPF_H_
#include<string>
#include"mpz.h"
#include"mpq.h"
#include"map.h"
#include"scoped_numeral.h"
#include"scoped_numeral_vector.h"
#include"hash.h"
typedef enum {
MPF_ROUND_NEAREST_TEVEN,
MPF_ROUND_NEAREST_TAWAY,
MPF_ROUND_TOWARD_POSITIVE,
MPF_ROUND_TOWARD_NEGATIVE,
MPF_ROUND_TOWARD_ZERO
} mpf_rounding_mode;
typedef int64 mpf_exp_t;
class mpf {
friend class mpf_manager;
friend class scoped_mpf;
unsigned ebits:15;
unsigned sbits:16;
unsigned sign:1; // counts as one sbit.
mpz significand;
mpf_exp_t exponent;
mpf & operator=(mpf const & other) { UNREACHABLE(); return *this; }
void set(unsigned ebits, unsigned sbits);
public:
mpf();
mpf(unsigned ebits, unsigned sbits);
mpf(mpf const & other);
~mpf();
unsigned get_ebits() const { return ebits; }
unsigned get_sbits() const { return sbits; }
void swap(mpf & other);
};
class mpf_manager {
unsynch_mpq_manager m_mpq_manager;
unsynch_mpz_manager & m_mpz_manager; // A mpq_manager is a mpz_manager, reusing it.
public:
typedef mpf numeral;
mpf_manager();
~mpf_manager();
void reset(mpf & o, unsigned ebits, unsigned sbits) { set(o, ebits, sbits, 0); }
void set(mpf & o, unsigned ebits, unsigned sbits, int value);
void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, int n, int d);
void set(mpf & o, unsigned ebits, unsigned sbits, float value);
void set(mpf & o, unsigned ebits, unsigned sbits, double value);
void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value);
void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, char const * value);
void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent);
void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, uint64 significand, int exponent);
void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpz const & significand, mpf_exp_t exponent);
void set(mpf & o, mpf const & x);
void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpf const & x);
void del(mpf & x) {
m_mpz_manager.del(x.significand);
}
void abs(mpf & o);
void abs(mpf const & x, mpf & o);
void neg(mpf & o);
void neg(mpf const & x, mpf & o);
bool is_zero(mpf const & x);
bool is_neg(mpf const & x);
bool is_pos(mpf const & x);
bool is_one(mpf const & x);
bool is_nzero(mpf const & x);
bool is_pzero(mpf const & x);
// structural eq
bool eq_core(mpf const & x, mpf const & y) {
return
x.ebits == y.ebits &&
x.sbits == y.sbits &&
x.sign == y.sign &&
m_mpz_manager.eq(x.significand, y.significand) &&
x.exponent == y.exponent;
}
bool eq(mpf const & x, mpf const & y);
bool lt(mpf const & x, mpf const & y);
bool lte(mpf const & x, mpf const & y);
bool le(mpf const & x, mpf const & y) { return lte(x, y); }
bool gt(mpf const & x, mpf const & y);
bool gte(mpf const & x, mpf const & y);
bool ge(mpf const & x, mpf const & y) { return gte(x, y); }
void add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o);
void sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o);
void mul(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o);
void div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o);
void fused_mul_add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf const &z, mpf & o);
void sqrt(mpf_rounding_mode rm, mpf const & x, mpf & o);
void round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o);
void rem(mpf const & x, mpf const & y, mpf & o);
void maximum(mpf const & x, mpf const & y, mpf & o);
void minimum(mpf const & x, mpf const & y, mpf & o);
std::string to_string(mpf const & a);
std::string to_rational_string(mpf const & a);
void display_decimal(std::ostream & out, mpf const & a, unsigned k);
void display_smt2(std::ostream & out, mpf const & a, bool decimal);
// Convert x into a mpq numeral. zm is the manager that owns o.
void to_rational(mpf const & x, unsynch_mpq_manager & qm, mpq & o);
void to_rational(mpf const & x, scoped_mpq & o) { to_rational(x, o.m(), o); }
double to_double(mpf const & x);
float to_float(mpf const & x);
bool sgn(mpf const & x) const { return x.sign; }
const mpz & sig(mpf const & x) const { return x.significand; }
const mpf_exp_t & exp(mpf const & x) const { return x.exponent; }
bool is_nan(mpf const & x);
bool is_inf(mpf const & x);
bool is_pinf(mpf const & x);
bool is_ninf(mpf const & x);
bool is_normal(mpf const & x);
bool is_denormal(mpf const & x);
bool is_regular(mpf const & x) { return x.sbits == 0 || is_normal(x) || is_denormal(x); }
bool is_int(mpf const & x);
void mk_zero(unsigned ebits, unsigned sbits, bool sign, mpf & o);
void mk_nzero(unsigned ebits, unsigned sbits, mpf & o);
void mk_pzero(unsigned ebits, unsigned sbits, mpf & o);
void mk_nan(unsigned ebits, unsigned sbits, mpf & o);
void mk_inf(unsigned ebits, unsigned sbits, bool sign, mpf & o);
void mk_pinf(unsigned ebits, unsigned sbits, mpf & o);
void mk_ninf(unsigned ebits, unsigned sbits, mpf & o);
std::string to_string_raw(mpf const & a);
unsynch_mpz_manager & mpz_manager(void) { return m_mpz_manager; }
unsynch_mpq_manager & mpq_manager(void) { return m_mpq_manager; }
unsigned hash(mpf const & a) {
return hash_u_u(m_mpz_manager.hash(a.significand),
m_mpz_manager.hash(hash_ull(a.exponent)));
}
void mk_max_value(unsigned ebits, unsigned sbits, bool sign, mpf & o);
mpf_exp_t mk_bot_exp(unsigned ebits);
mpf_exp_t mk_top_exp(unsigned ebits);
mpf_exp_t mk_max_exp(unsigned ebits);
mpf_exp_t mk_min_exp(unsigned ebits);
/**
\brief Return the biggest k s.t. 2^k <= a.
\remark Return 0 if a is not positive.
*/
unsigned prev_power_of_two(mpf const & a);
protected:
bool has_bot_exp(mpf const & x);
bool has_top_exp(mpf const & x);
void unpack(mpf & o, bool normalize);
void add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o, bool sub);
void round(mpf_rounding_mode rm, mpf & o);
void round_sqrt(mpf_rounding_mode rm, mpf & o);
void mk_round_inf(mpf_rounding_mode rm, mpf & o);
// Convert x into a mpz numeral. zm is the manager that owns o.
void to_mpz(mpf const & x, unsynch_mpz_manager & zm, mpz & o);
void to_mpz(mpf const & x, scoped_mpz & o) { to_mpz(x, o.m(), o); }
class powers2 {
unsynch_mpz_manager & m;
u_map<mpz*> m_p;
u_map<mpz*> m_pn;
u_map<mpz*> m_pm1;
u_map<mpz*> m_pm1n;
public:
powers2(unsynch_mpz_manager & m) : m(m) {}
~powers2() {
dispose(m_p);
dispose(m_pn);
dispose(m_pm1);
dispose(m_pm1n);
}
void dispose(u_map<mpz*> & map) {
for (u_map<mpz*>::iterator it = map.begin(); it != map.end(); it++) {
m.del(*it->m_value);
dealloc(it->m_value);
}
}
const mpz & operator()(unsigned n, bool negated = false) {
u_map<mpz*> & map = (negated) ? m_pn : m_p;
u_map<mpz*>::iterator it = map.find_iterator(n);
if (it != map.end())
return *it->m_value;
else {
mpz * new_obj = alloc(mpz);
map.insert(n, new_obj);
m.power(unsynch_mpz_manager::mk_z(2), n, *new_obj);
if (negated) m.neg(*new_obj);
return *new_obj;
}
}
const mpz & m1(unsigned n, bool negated=false) { // (2 ^ n) - 1
u_map<mpz*> & map = (negated) ? m_pm1n : m_pm1;
u_map<mpz*>::iterator it = map.find_iterator(n);
if (it != map.end())
return *it->m_value;
else {
mpz * new_obj = alloc(mpz);
map.insert(n, new_obj);
m.power(unsynch_mpz_manager::mk_z(2), n, *new_obj);
m.dec(*new_obj);
if (negated) m.neg(*new_obj);
return *new_obj;
}
}
};
public:
powers2 m_powers2;
};
class scoped_mpf : public _scoped_numeral<mpf_manager> {
friend class mpf_manager;
mpz & significand() { return get().significand; }
bool sign() const { return get().sign; }
mpf_exp_t exponent() const { return get().exponent; }
unsigned sbits() const { return get().sbits; }
void set(unsigned ebits, unsigned sbits) { get().set(ebits, sbits); }
public:
scoped_mpf(mpf_manager & m):_scoped_numeral<mpf_manager>(m) {}
scoped_mpf(scoped_mpf const & n):_scoped_numeral<mpf_manager>(n) {}
scoped_mpf(mpf_manager & m, unsigned ebits, unsigned sbits):_scoped_numeral<mpf_manager>(m) { set(ebits, sbits); }
};
typedef _scoped_numeral_vector<mpf_manager> scoped_mpf_vector;
#endif

1413
src/util/mpff.cpp Normal file

File diff suppressed because it is too large Load diff

479
src/util/mpff.h Normal file
View file

@ -0,0 +1,479 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
mpff.h
Abstract:
Multi precision fast floating point numbers.
The implementation is not compliant with the IEEE standard.
For an IEEE compliant implementation, see mpf.h
There are only two rounding modes: towards plus or minus inf.
Author:
Leonardo de Moura (leonardo) 2012-09-12
Revision History:
--*/
#ifndef _MPFF_H_
#define _MPFF_H_
#include"id_gen.h"
#include"util.h"
#include"vector.h"
#include"z3_exception.h"
#include"scoped_numeral.h"
#include"scoped_numeral_vector.h"
class mpff_manager;
class mpff {
friend class mpff_manager;
unsigned m_sign:1;
unsigned m_sig_idx:31; // position where the significand is stored in the mpff_manager.
int m_exponent;
public:
mpff():
m_sign(0),
m_sig_idx(0),
m_exponent(0) {
}
void swap(mpff & other) {
unsigned sign = m_sign; m_sign = other.m_sign; other.m_sign = sign;
unsigned sig_idx = m_sig_idx; m_sig_idx = other.m_sig_idx; other.m_sig_idx = sig_idx;
std::swap(m_exponent, other.m_exponent);
}
};
inline void swap(mpff & m1, mpff & m2) { m1.swap(m2); }
class mpz;
class mpq;
template<bool SYNCH> class mpz_manager;
template<bool SYNCH> class mpq_manager;
typedef mpz_manager<true> synch_mpz_manager;
typedef mpz_manager<false> unsynch_mpz_manager;
typedef mpq_manager<true> synch_mpq_manager;
typedef mpq_manager<false> unsynch_mpq_manager;
class mpff_manager {
// Some restrictions on mpff numbers
//
// - The exponent is always a machine integer. The main point is that 2^(2^31) is a huge number,
// we will not even be able to convert the mpff into mpq. Formulas that need this kind of huge number
// are usually out-of-reach for Z3.
//
// - The significand size is measured in words of 32-bit. The number of words is always even.
// This decision makes sure that the size (in bits) of mpff numbers is always a multiple of 64.
// Thus mpff objs can be easily packed in 64-bit machines.
//
// - The smallest mpff numeral has 128-bits total. mpff structure has always 64-bits.
// The minimal size for the significand is 64-bits.
//
// - All mpff numerals in a given manager use the same number of words for storing the significand.
// This is different from the mpf_manager where the same manager can be used to manipulate floating point numbers
// of different precision.
//
// - In the encoding used for mpff numbers, the most significand bit of the most significand word is always 1.
// The only exception is the number zero.
// For example, assuming we are using 64-bits for the significand, the number 1 is encoded as
// (sign = 0, significand = 0x800..0, exponent = -63)
// Note that, in this representation, the smallest positive integer is:
// (sign = 0, significand = 0x800..0, exponent = INT_MIN)
// instead of
// (sign = 0, significand = 0x000..1, exponent = INT_MIN)
//
// Remarks:
//
// - All values of type int, unsigned, int64 and uint64 can be precisely represented as mpff numerals.
//
// - Hardware float and double values (corresponding to rationals) can also be precisely represented as mpff numberals.
// That is, NaN, +oo and -oo are not supported by this module.
//
// - An exception (mpff_manager::exception) is thrown if overflow occurs. This can happen because the exponent is
// represented as a machine integer.
//
// - There are only two rounding modes: towards plus infinity and towards minus infinity.
// The rounding mode can be dynamically modified.
//
// - The mpff numerals are stored in a dynamic array.
// Type mpff is just an index (unsigned) into this array.
unsigned m_precision; //!< Number of words in the significand. Must be an even number.
unsigned m_precision_bits; //!< Number of bits in the significand. Must be 32*m_precision.
vector<unsigned> m_significands; //!< Array containing all significands.
unsigned m_capacity; //!< Number of significands that can be stored in m_significands.
bool m_to_plus_inf; //!< If True, then round to plus infinity, otherwise to minus infinity
id_gen m_id_gen;
static const unsigned MPFF_NUM_BUFFERS = 4;
svector<unsigned> m_buffers[MPFF_NUM_BUFFERS];
svector<unsigned> m_set_buffer;
mpff m_one;
unsigned * sig(mpff const & n) const { return m_significands.c_ptr() + (n.m_sig_idx * m_precision); }
void ensure_capacity(unsigned sig_idx) {
while (sig_idx >= m_capacity)
expand();
}
void expand();
void allocate_if_needed(mpff & n) {
if (n.m_sig_idx == 0)
allocate(n);
}
void allocate(mpff & n);
// copy n to buffer idx.
void to_buffer(unsigned idx, mpff const & n) const;
// copy n to buffer idx and add m_precision zeros.
void to_buffer_ext(unsigned idx, mpff const & n) const;
// copy (and shift by m_precision_bits) n to buffer idx
void to_buffer_shifting(unsigned idx, mpff const & n) const;
void inc_significand(unsigned * s, int64 & exp);
void inc_significand(mpff & a);
void dec_significand(mpff & a);
bool min_significand(mpff const & a) const;
void set_min_significand(mpff & a);
void set_max_significand(mpff & a);
void set_big_exponent(mpff & a, int64 e);
void set_exponent(mpff & a, int64 e) {
if (e > INT_MAX || e < INT_MIN)
set_big_exponent(a, e);
else
a.m_exponent = static_cast<int>(e);
}
template<bool SYNCH>
void set_core(mpff & n, mpz_manager<SYNCH> & m, mpz const & v);
template<bool SYNCH>
void set_core(mpff & n, mpq_manager<SYNCH> & m, mpq const & v);
template<bool SYNCH>
void to_mpz_core(mpff const & n, mpz_manager<SYNCH> & m, mpz & t);
template<bool SYNCH>
void to_mpq_core(mpff const & n, mpq_manager<SYNCH> & m, mpq & t);
template<bool SYNCH>
void significand_core(mpff const & n, mpz_manager<SYNCH> & m, mpz & r);
void add_sub(bool is_sub, mpff const & a, mpff const & b, mpff & c);
public:
typedef mpff numeral;
static bool precise() { return false; }
static bool field() { return true; }
class exception : public z3_exception {
virtual char const * msg() const { return "multi-precision floating point (mpff) exception"; }
};
class overflow_exception : public exception {
virtual char const * msg() const { return "multi-precision floating point (mpff) overflow"; }
};
class div0_exception : public exception {
virtual char const * msg() const { return "multi-precision floating point (mpff) division by zero"; }
};
mpff_manager(unsigned prec = 2, unsigned initial_capacity = 1024);
~mpff_manager();
void round_to_plus_inf() { m_to_plus_inf = true; }
void round_to_minus_inf() { m_to_plus_inf = false; }
void set_rounding(bool to_plus_inf) { m_to_plus_inf = to_plus_inf; }
bool rounding_to_plus_inf() const { return m_to_plus_inf; }
/**
\brief Return the exponent of n.
*/
static int exponent(mpff const & n) { return n.m_exponent; }
/**
\brief Update the exponent of n.
\remark It is a NOOP if n is zero.
*/
void set_exponent(mpff & n, int exp) { if (is_zero(n)) return; n.m_exponent = exp; SASSERT(check(n)); }
/**
\brief Return the significand as a mpz numeral.
*/
void significand(mpff const & n, unsynch_mpz_manager & m, mpz & r);
void significand(mpff const & n, synch_mpz_manager & m, mpz & r);
/**
\brief Return true if n is negative
*/
static bool sign(mpff const & n) { return is_neg(n); }
/**
\brief Set n to zero.
*/
void reset(mpff & n);
/**
\brief Return true if n is an integer.
*/
bool is_int(mpff const & n) const;
/**
\brief Return true if n is zero.
*/
static bool is_zero(mpff const & n) { return n.m_sig_idx == 0; }
/**
\brief Return true if n is positive.
*/
static bool is_pos(mpff const & n) { return n.m_sign == 0 && !is_zero(n); }
/**
\brief Return true if n is negative.
*/
static bool is_neg(mpff const & n) { return n.m_sign != 0; }
/**
\brief Return true if n is non positive.
*/
static bool is_nonpos(mpff const & n) { return !is_pos(n); }
/**
\brief Return true if n is non negative.
*/
static bool is_nonneg(mpff const & n) { return !is_neg(n); }
/**
\brief Return true if the absolute value of n is 1.
*/
bool is_abs_one(mpff const & n) const;
/**
\brief Return true if n is one.
*/
bool is_one(mpff const & n) const { return is_pos(n) && is_abs_one(n); }
/**
\brief Return true if n is minus one.
*/
bool is_minus_one(mpff const & n) const { return is_neg(n) && is_abs_one(n); }
/**
\brief Return true if n is two.
*/
bool is_two(mpff const & n) const;
/**
\brief Return true if \c a is the smallest representable negative number.
*/
bool is_minus_epsilon(mpff const & a) const;
/**
\brief Return true if \c a is the smallest representable positive number.
*/
bool is_plus_epsilon(mpff const & a) const;
/**
\brief Return true if \c a is an integer and fits in an int64 machine integer.
*/
bool is_int64(mpff const & a) const;
/**
\brief Return true if \c a is a non-negative integer and fits in an int64 machine integer.
*/
bool is_uint64(mpff const & a) const;
/**
\brief Delete the resources associated with n.
*/
void del(mpff & n);
/**
\brief a <- -a
*/
static void neg(mpff & a) { if (!is_zero(a)) a.m_sign = !a.m_sign; }
/**
\brief a <- |a|
*/
static void abs(mpff & a) { a.m_sign = 0; }
static void swap(mpff & a, mpff & b) { a.swap(b); }
/**
\brief c <- a + b
*/
void add(mpff const & a, mpff const & b, mpff & c);
/**
\brief c <- a - b
*/
void sub(mpff const & a, mpff const & b, mpff & c);
/**
\brief a <- a + 1
*/
void inc(mpff & a) { add(a, m_one, a); }
/**
\brief a <- a - 1
*/
void dec(mpff & a) { sub(a, m_one, a); }
/**
\brief c <- a * b
*/
void mul(mpff const & a, mpff const & b, mpff & c);
/**
\brief c <- a / b
\pre !is_zero(b)
*/
void div(mpff const & a, mpff const & b, mpff & c);
/**
\brief a <- 1/a
\pre !is_zero(a);
*/
void inv(mpff & a) { div(m_one, a, a); }
void inv(mpff const & a, mpff & b) { set(b, a); inv(b); }
/**
\brief b <- a^k
*/
void power(mpff const & a, unsigned k, mpff & b);
/**
\brief Return true if \c a is a power of 2. That is, a is equal to 2^k for some k >= 0.
*/
bool is_power_of_two(mpff const & a, unsigned & k) const;
bool is_power_of_two(mpff const & a) const;
bool eq(mpff const & a, mpff const & b) const;
bool neq(mpff const & a, mpff const & b) const { return !eq(a, b); }
bool lt(mpff const & a, mpff const & b) const;
bool gt(mpff const & a, mpff const & b) const { return lt(b, a); }
bool le(mpff const & a, mpff const & b) const { return !lt(b, a); }
bool ge(mpff const & a, mpff const & b) const { return !lt(a, b); }
void set(mpff & n, int v);
void set(mpff & n, unsigned v);
void set(mpff & n, int64 v);
void set(mpff & n, uint64 v);
void set(mpff & n, int num, unsigned den);
void set(mpff & n, int64 num, uint64 den);
void set(mpff & n, mpff const & v);
void set(mpff & n, unsynch_mpz_manager & m, mpz const & v);
void set(mpff & n, synch_mpz_manager & m, mpz const & v);
void set(mpff & n, unsynch_mpq_manager & m, mpq const & v);
void set(mpff & n, synch_mpq_manager & m, mpq const & v);
void set_plus_epsilon(mpff & n);
void set_minus_epsilon(mpff & n);
void set_max(mpff & n);
void set_min(mpff & n);
/**
\brief n <- floor(n)
*/
void floor(mpff & n);
void floor(mpff const & n, mpff & o) { set(o, n); floor(o); }
/**
\brief n <- ceil(n)
*/
void ceil(mpff & n);
void ceil(mpff const & n, mpff & o) { set(o, n); ceil(o); }
/**
\brief Update \c a to the next representable float.
Throws an exception if \c a is the maximal representable float.
*/
void next(mpff & a);
/**
\brief Update \c a to the previous representable float.
Throws an exception if \c a is the minimal representable float.
*/
void prev(mpff & a);
/**
\brief Convert n into a mpz numeral.
\pre is_int(n)
\remark if exponent(n) is too big, we may run out of memory.
*/
void to_mpz(mpff const & n, unsynch_mpz_manager & m, mpz & t);
/**
\brief Convert n into a mpz numeral.
\pre is_int(n)
\remark if exponent(n) is too big, we may run out of memory.
*/
void to_mpz(mpff const & n, synch_mpz_manager & m, mpz & t);
/**
\brief Convert n into a mpq numeral.
\remark if exponent(n) is too big, we may run out of memory.
*/
void to_mpq(mpff const & n, unsynch_mpq_manager & m, mpq & t);
/**
\brief Convert n into a mpq numeral.
\remark if exponent(n) is too big, we may run out of memory.
*/
void to_mpq(mpff const & n, synch_mpq_manager & m, mpq & t);
/**
\brief Return n as an int64.
\pre is_int64(n)
*/
int64 get_int64(mpff const & n) const;
/**
\brief Return n as an uint64.
\pre is_uint64(n)
*/
uint64 get_uint64(mpff const & n) const;
/**
\brief Return the biggest k s.t. 2^k <= a.
\remark Return 0 if a is not positive.
*/
unsigned prev_power_of_two(mpff const & a);
void display_raw(std::ostream & out, mpff const & n) const;
void display(std::ostream & out, mpff const & n) const;
void display_decimal(std::ostream & out, mpff const & n, unsigned prec=32, unsigned max_power=128) const;
void display_smt2(std::ostream & out, mpff const & n, bool decimal=true) const;
std::string to_string(mpff const & a) const;
std::string to_rational_string(mpff const & a) const;
bool check(mpff const & n) const;
};
typedef _scoped_numeral<mpff_manager> scoped_mpff;
typedef _scoped_numeral_vector<mpff_manager> scoped_mpff_vector;
#endif

866
src/util/mpfx.cpp Normal file
View file

@ -0,0 +1,866 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
mpfx.h
Abstract:
Multi precision fixed point numbers.
Author:
Leonardo de Moura (leonardo) 2012-09-19
Revision History:
--*/
#include<sstream>
#include<iomanip>
#include"mpfx.h"
#include"mpn.h"
#include"mpz.h"
#include"mpq.h"
#include"bit_util.h"
#include"trace.h"
mpfx_manager::mpfx_manager(unsigned int_sz, unsigned frac_sz, unsigned initial_capacity) {
SASSERT(initial_capacity > 0);
SASSERT(int_sz > 0);
SASSERT(frac_sz > 0);
m_int_part_sz = int_sz;
m_frac_part_sz = frac_sz;
m_total_sz = m_int_part_sz + m_frac_part_sz;
m_words.resize(initial_capacity * m_total_sz, 0);
m_capacity = initial_capacity;
m_to_plus_inf = false;
m_buffer0.resize(2*m_total_sz, 0);
m_buffer1.resize(2*m_total_sz, 0);
m_buffer2.resize(2*m_total_sz, 0);
unsigned zero_sig_idx = m_id_gen.mk();
SASSERT(zero_sig_idx == 0);
set(m_one, 1);
}
mpfx_manager::~mpfx_manager() {
del(m_one);
}
void mpfx_manager::expand() {
m_capacity = 2*m_capacity;
m_words.resize(m_capacity * m_total_sz, 0);
}
void mpfx_manager::allocate(mpfx & n) {
SASSERT(n.m_sig_idx == 0);
unsigned sig_idx = m_id_gen.mk();
ensure_capacity(sig_idx);
n.m_sig_idx = sig_idx;
SASSERT(::is_zero(m_total_sz, words(n)));
}
unsigned mpfx_manager::sz(unsigned * ws) const {
SASSERT(!::is_zero(m_total_sz, ws));
unsigned r = m_total_sz;
while (true) {
SASSERT(r > 0);
--r;
if (ws[r] != 0)
return r + 1;
}
}
void mpfx_manager::del(mpfx & n) {
unsigned sig_idx = n.m_sig_idx;
if (sig_idx != 0) {
m_id_gen.recycle(sig_idx);
unsigned * w = words(n);
for (unsigned i = 0; i < m_total_sz; i++)
w[i] = 0;
}
}
void mpfx_manager::reset(mpfx & n) {
del(n);
n.m_sign = false;
n.m_sig_idx = 0;
SASSERT(check(n));
}
bool mpfx_manager::is_int(mpfx const & n) const {
unsigned * w = words(n);
for (unsigned i = 0; i < m_frac_part_sz; i++)
if (w[i] != 0)
return false;
return true;
}
bool mpfx_manager::is_abs_one(mpfx const & n) const {
unsigned * w = words(n);
return is_int(n) && w[m_frac_part_sz] == 1 && ::is_zero(m_int_part_sz - 1, w + m_frac_part_sz + 1);
}
bool mpfx_manager::is_int64(mpfx const & a) const {
if (!is_int(a))
return false;
if (is_zero(a) || m_int_part_sz <= 1)
return true;
unsigned * w = words(a);
w += m_frac_part_sz;
if (w[1] < 0x80000000u || (w[1] == 0x80000000u && is_neg(a))) {
for (unsigned i = 2; i < m_int_part_sz; i++)
if (w[i] != 0)
return false;
return true;
}
else {
return false;
}
}
bool mpfx_manager::is_uint64(mpfx const & a) const {
if (!is_int(a) || is_neg(a))
return false;
if (is_zero(a) || m_int_part_sz <= 2)
return true;
unsigned * w = words(a);
for (unsigned i = m_frac_part_sz + 2; i < m_total_sz; i++)
if (w[i] != 0)
return false;
return true;
}
void mpfx_manager::set(mpfx & n, int v) {
if (v == 0) {
reset(n);
}
else {
if (v < 0) {
set(n, static_cast<unsigned>(-v));
n.m_sign = 1;
}
else {
set(n, static_cast<unsigned>(v));
}
}
SASSERT(get_int64(n) == v);
SASSERT(check(n));
}
void mpfx_manager::set(mpfx & n, unsigned v) {
if (v == 0) {
reset(n);
}
else {
allocate_if_needed(n);
n.m_sign = 0;
unsigned * w = words(n);
for (unsigned i = 0; i < m_total_sz; i++)
w[i] = 0;
w[m_frac_part_sz] = v;
}
SASSERT(is_int(n));
SASSERT(get_uint64(n) == v);
SASSERT(check(n));
}
void mpfx_manager::set(mpfx & n, int64 v) {
if (m_int_part_sz == 1) {
if (v < -static_cast<int64>(static_cast<uint64>(UINT_MAX)) ||
v > static_cast<int64>(static_cast<uint64>(UINT_MAX)))
throw overflow_exception();
}
if (v == 0) {
reset(n);
}
else {
if (v < 0) {
set(n, static_cast<uint64>(-v));
n.m_sign = 1;
}
else {
set(n, static_cast<uint64>(v));
}
}
SASSERT(is_int(n));
SASSERT(get_int64(n) == v);
SASSERT(check(n));
}
void mpfx_manager::set(mpfx & n, uint64 v) {
if (m_int_part_sz == 1) {
if (v > static_cast<uint64>(UINT_MAX))
throw overflow_exception();
}
if (v == 0) {
reset(n);
}
else {
allocate_if_needed(n);
n.m_sign = 0;
unsigned * _v = reinterpret_cast<unsigned*>(&v);
unsigned * w = words(n);
for (unsigned i = 0; i < m_total_sz; i++)
w[i] = 0;
w[m_frac_part_sz] = _v[0];
if (m_int_part_sz == 1) {
SASSERT(_v[1] == 0);
}
else {
w[m_frac_part_sz+1] = _v[1];
}
}
SASSERT(is_int(n));
SASSERT(get_uint64(n) == v);
SASSERT(check(n));
}
void mpfx_manager::set(mpfx & n, int num, unsigned den) {
scoped_mpfx a(*this), b(*this);
set(a, num);
set(b, den);
div(a, b, n);
SASSERT(check(n));
}
void mpfx_manager::set(mpfx & n, int64 num, uint64 den) {
scoped_mpfx a(*this), b(*this);
set(a, num);
set(b, den);
div(a, b, n);
SASSERT(check(n));
}
void mpfx_manager::set(mpfx & n, mpfx const & v) {
if (is_zero(v)) {
reset(n);
return;
}
allocate_if_needed(n);
n.m_sign = v.m_sign;
unsigned * w1 = words(n);
unsigned * w2 = words(v);
for (unsigned i = 0; i < m_total_sz; i++)
w1[i] = w2[i];
SASSERT(check(n));
}
template<bool SYNCH>
void mpfx_manager::set_core(mpfx & n, mpz_manager<SYNCH> & m, mpz const & v) {
if (m.is_zero(v)) {
reset(n);
}
else {
m_tmp_digits.reset();
allocate_if_needed(n);
n.m_sign = m.decompose(v, m_tmp_digits);
unsigned sz = m_tmp_digits.size();
if (sz > m_int_part_sz)
throw overflow_exception();
unsigned * w = words(n);
for (unsigned i = 0; i < m_frac_part_sz; i++)
w[i] = 0;
::copy(sz, m_tmp_digits.c_ptr(), m_int_part_sz, w + m_frac_part_sz);
}
SASSERT(check(n));
}
void mpfx_manager::set(mpfx & n, unsynch_mpz_manager & m, mpz const & v) {
set_core(n, m, v);
}
void mpfx_manager::set(mpfx & n, synch_mpz_manager & m, mpz const & v) {
set_core(n, m, v);
}
template<bool SYNCH>
void mpfx_manager::set_core(mpfx & n, mpq_manager<SYNCH> & m, mpq const & v) {
if (m.is_int(v)) {
set_core(n, m, v.numerator());
}
else {
allocate_if_needed(n);
_scoped_numeral<mpz_manager<SYNCH> > tmp(m);
n.m_sign = is_neg(n);
m.mul2k(v.numerator(), 8 * sizeof(unsigned) * m_frac_part_sz, tmp);
m.abs(tmp);
if ((n.m_sign == 1) != m_to_plus_inf && !m.divides(v.denominator(), tmp)) {
m.div(tmp, v.denominator(), tmp);
m.inc(tmp);
}
else {
m.div(tmp, v.denominator(), tmp);
}
m_tmp_digits.reset();
m.decompose(tmp, m_tmp_digits);
unsigned sz = m_tmp_digits.size();
if (sz > m_total_sz)
throw overflow_exception();
unsigned * w = words(n);
::copy(sz, m_tmp_digits.c_ptr(), m_total_sz, w);
}
SASSERT(check(n));
}
void mpfx_manager::set(mpfx & n, unsynch_mpq_manager & m, mpq const & v) {
set_core(n, m, v);
}
void mpfx_manager::set(mpfx & n, synch_mpq_manager & m, mpq const & v) {
set_core(n, m, v);
}
bool mpfx_manager::eq(mpfx const & a, mpfx const & b) const {
if (is_zero(a) && is_zero(b))
return true;
if (is_zero(a) || is_zero(b))
return false;
if (a.m_sign != b.m_sign)
return false;
unsigned * w1 = words(a);
unsigned * w2 = words(b);
for (unsigned i = 0; i < m_total_sz; i++)
if (w1[i] != w2[i])
return false;
return true;
}
bool mpfx_manager::lt(mpfx const & a, mpfx const & b) const {
STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << " < "; display(tout, b); tout << ") == ";);
bool r;
if (is_zero(a)) {
r = !is_zero(b) && !is_neg(b);
}
else if (is_zero(b)) {
r = is_neg(a);
}
else {
SASSERT(!is_zero(a));
SASSERT(!is_zero(b));
if (is_neg(a)) {
r = is_pos(b) || ::lt(m_total_sz, words(b), words(a));
}
else {
SASSERT(is_pos(a));
r = is_pos(b) && ::lt(m_total_sz, words(a), words(b));
}
}
STRACE("mpfx_trace", tout << "(" << r << " == 1)\n";);
return r;
}
void mpfx_manager::add_sub(bool is_sub, mpfx const & a, mpfx const & b, mpfx & c) {
if (is_zero(a)) {
set(c, b);
if (is_sub)
neg(c);
return;
}
if (is_zero(b)) {
set(c, a);
return;
}
TRACE("mpfx", tout << (is_sub ? "sub" : "add") << "("; display(tout, a); tout << ", "; display(tout, b); tout << ")\n";);
allocate_if_needed(c);
bool sgn_a = a.m_sign;
bool sgn_b = b.m_sign;
unsigned * w_a = words(a);
unsigned * w_b = words(b);
if (is_sub)
sgn_b = !sgn_b;
// Compute c
unsigned * w_c = words(c);
if (sgn_a == sgn_b) {
c.m_sign = sgn_a;
if (!::add(m_total_sz, w_a, w_b, w_c))
throw overflow_exception();
}
else {
unsigned borrow;
SASSERT(sgn_a != sgn_b);
if (::lt(m_total_sz, w_a, w_b)) {
c.m_sign = sgn_b;
sub_diff(w_b, m_total_sz, w_a, m_total_sz, w_c, &borrow, 0);
SASSERT(!::is_zero(m_total_sz, w_c));
}
else {
c.m_sign = sgn_a;
sub_diff(w_a, m_total_sz, w_b, m_total_sz, w_c, &borrow, 0);
if (::is_zero(m_total_sz, w_c))
reset(c);
}
SASSERT(borrow == 0);
}
TRACE("mpfx", tout << "result: "; display(tout, c); tout << "\n";);
SASSERT(check(c));
}
void mpfx_manager::add(mpfx const & a, mpfx const & b, mpfx & c) {
STRACE("mpfx_trace", tout << "[mpfx] "; display(tout, a); tout << " + "; display(tout, b); tout << " == ";);
add_sub(false, a, b, c);
STRACE("mpfx_trace", display(tout, c); tout << "\n";);
}
void mpfx_manager::sub(mpfx const & a, mpfx const & b, mpfx & c) {
STRACE("mpfx_trace", tout << "[mpfx] "; display(tout, a); tout << " - "; display(tout, b); tout << " == ";);
add_sub(true, a, b, c);
STRACE("mpfx_trace", display(tout, c); tout << "\n";);
}
void mpfx_manager::mul(mpfx const & a, mpfx const & b, mpfx & c) {
STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << ") * ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";);
if (is_zero(a) || is_zero(b)) {
reset(c);
}
else {
allocate_if_needed(c);
c.m_sign = a.m_sign ^ b.m_sign;
unsigned * r = m_buffer0.c_ptr();
multiply(words(a), m_total_sz, words(b), m_total_sz, r, 0);
// round result
unsigned * _r = r + m_frac_part_sz;
if ((c.m_sign == 1) != m_to_plus_inf && !::is_zero(m_frac_part_sz, r)) {
if (!::inc(m_total_sz, _r))
throw overflow_exception();
}
// check for overflows
if (!::is_zero(m_int_part_sz, _r + m_total_sz))
throw overflow_exception();
// copy result to c
unsigned * w_c = words(c);
for (unsigned i = 0; i < m_total_sz; i++)
w_c[i] = _r[i];
}
STRACE("mpfx_trace", display(tout, c); tout << "\n";);
SASSERT(check(c));
}
void mpfx_manager::div(mpfx const & a, mpfx const & b, mpfx & c) {
if (is_zero(b))
throw div0_exception();
STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << ") / ("; display(tout, b); tout << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";);
if (is_zero(a)) {
reset(c);
}
else {
allocate_if_needed(c);
c.m_sign = a.m_sign ^ b.m_sign;
unsigned * w_a = words(a);
unsigned * w_a_shft = m_buffer0.c_ptr();
unsigned a_shft_sz = sz(w_a) + m_frac_part_sz;
// copy a to buffer 0, and shift by m_frac_part_sz
for (unsigned i = 0; i < m_frac_part_sz; i++)
w_a_shft[i] = 0;
for (unsigned i = 0; i < m_total_sz; i++)
w_a_shft[i+m_frac_part_sz] = w_a[i];
unsigned * w_b = words(b);
unsigned b_sz = sz(w_b);
unsigned * w_q = m_buffer1.c_ptr();
if (b_sz > a_shft_sz) {
if ((c.m_sign == 1) != m_to_plus_inf)
set_epsilon(c);
else
reset(c);
}
else {
unsigned q_sz = a_shft_sz - b_sz + 1;
unsigned * w_r = m_buffer2.c_ptr();
unsigned r_sz = b_sz;
divide(w_a_shft, a_shft_sz,
w_b, b_sz,
reciprocal_1_NULL,
w_q,
w_r,
0);
for (unsigned i = m_total_sz; i < q_sz; i++)
if (w_q[i] != 0)
throw overflow_exception();
if (((c.m_sign == 1) != m_to_plus_inf) && !::is_zero(r_sz, w_r)) {
// round the result
if (!::inc(m_total_sz, w_q))
throw overflow_exception();
}
unsigned * w_c = words(c);
bool zero_q = true;
if (m_total_sz >= q_sz) {
unsigned i;
for (i = 0; i < q_sz; i++) {
if (w_q[i] != 0)
zero_q = false;
w_c[i] = w_q[i];
}
for (; i < m_total_sz; i++)
w_c[i] = 0;
}
else {
for (unsigned i = 0; i < m_total_sz; i++) {
if (w_q[i] != 0)
zero_q = false;
w_c[i] = w_q[i];
}
}
if (zero_q) {
if ((c.m_sign == 1) != m_to_plus_inf)
set_epsilon(c);
else
reset(c);
}
}
}
STRACE("mpfx_trace", display(tout, c); tout << "\n";);
SASSERT(check(c));
}
void mpfx_manager::div2k(mpfx & a, unsigned k) {
STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, a); tout << ") / (2^" << k << ") " << (m_to_plus_inf ? "<=" : ">=") << " ";);
if (!is_zero(a) && k > 0) {
unsigned * w = words(a);
bool _inc = ((a.m_sign == 1) != m_to_plus_inf) && has_one_at_first_k_bits(m_total_sz, w, k);
shr(m_total_sz, w, k, m_total_sz, w);
if (_inc) {
VERIFY(::inc(m_total_sz, w));
SASSERT(!::is_zero(m_total_sz, w));
}
else if (::is_zero(m_total_sz, w)) {
reset(a);
}
}
STRACE("mpfx_trace", display(tout, a); tout << "\n";);
SASSERT(check(a));
}
void mpfx_manager::set_epsilon(mpfx & n) {
unsigned * w = words(n);
w[0] = 1;
for (unsigned i = 1; i < m_total_sz; i++)
w[i] = 0;
}
void mpfx_manager::set_minus_epsilon(mpfx & n) {
set_epsilon(n);
n.m_sign = true;
SASSERT(check(n));
}
void mpfx_manager::set_plus_epsilon(mpfx & n) {
set_epsilon(n);
n.m_sign = 0;
SASSERT(check(n));
}
void mpfx_manager::floor(mpfx & n) {
STRACE("mpfx_trace", tout << "[mpfx] Floor["; display(tout, n); tout << "] == ";);
unsigned * w = words(n);
if (is_neg(n)) {
bool is_int = true;
for (unsigned i = 0; i < m_frac_part_sz; i++) {
if (w[i] != 0) {
is_int = false;
w[i] = 0;
}
}
if (!is_int && !::inc(m_int_part_sz, w + m_frac_part_sz))
throw overflow_exception();
}
else {
for (unsigned i = 0; i < m_frac_part_sz; i++)
w[i] = 0;
}
if (::is_zero(m_int_part_sz, w + m_frac_part_sz))
reset(n);
SASSERT(check(n));
STRACE("mpfx_trace", display(tout, n); tout << "\n";);
}
void mpfx_manager::ceil(mpfx & n) {
STRACE("mpfx_trace", tout << "[mpfx] Ceiling["; display(tout, n); tout << "] == ";);
unsigned * w = words(n);
if (is_pos(n)) {
bool is_int = true;
for (unsigned i = 0; i < m_frac_part_sz; i++) {
if (w[i] != 0) {
is_int = false;
w[i] = 0;
}
}
if (!is_int && !::inc(m_int_part_sz, w + m_frac_part_sz))
throw overflow_exception();
}
else {
for (unsigned i = 0; i < m_frac_part_sz; i++)
w[i] = 0;
}
if (::is_zero(m_int_part_sz, w + m_frac_part_sz))
reset(n);
SASSERT(check(n));
STRACE("mpfx_trace", display(tout, n); tout << "\n";);
}
void mpfx_manager::power(mpfx const & a, unsigned p, mpfx & b) {
#ifdef _TRACE
scoped_mpfx _a(*this); _a = a;
unsigned _p = p;
#endif
#define SMALL_POWER 8
SASSERT(check(a));
if (is_zero(a)) {
SASSERT(p != 0);
reset(b);
}
else if (p == 0) {
set(b, 1);
}
else if (p == 1) {
set(b, a);
}
else if (p == 2) {
mul(a, a, b);
}
else if (p <= SMALL_POWER && &a != &b) {
SASSERT(p > 2);
--p;
set(b, a);
while (p > 0) {
--p;
mul(a, b, b);
}
}
else {
unsigned mask = 1;
scoped_mpfx pw(*this);
set(pw, a);
set(b, 1);
while (mask <= p) {
if (mask & p)
mul(b, pw, b);
mul(pw, pw, pw);
mask = mask << 1;
}
}
STRACE("mpfx_trace", tout << "[mpfx] ("; display(tout, _a); tout << ") ^ " << _p << (m_to_plus_inf ? "<=" : ">="); display(tout, b); tout << "\n";);
TRACE("mpfx_power", display_raw(tout, b); tout << "\n";);
SASSERT(check(b));
}
bool mpfx_manager::is_power_of_two(mpfx const & a, unsigned & k) const {
if (!is_int(a) || is_zero(a))
return false;
unsigned * w = words(a);
unsigned i = m_total_sz;
while (true) {
SASSERT (i > m_frac_part_sz);
--i;
if (w[i] != 0) {
if (!::is_power_of_two(w[i]))
return false;
k = (i - m_frac_part_sz) * 8 * sizeof(unsigned) + log2(w[i]);
while (i > m_frac_part_sz) {
--i;
if (w[i] != 0)
return false;
}
return true;
}
}
}
bool mpfx_manager::is_power_of_two(mpfx const & a) const {
unsigned k;
return is_power_of_two(a, k);
}
int64 mpfx_manager::get_int64(mpfx const & n) const {
SASSERT(is_int64(n));
unsigned * w = words(n);
w += m_frac_part_sz;
uint64 r = *reinterpret_cast<uint64*>(w);
if (r == 0x8000000000000000ull) {
SASSERT(is_neg(n));
return INT64_MIN;
}
else {
return is_neg(n) ? -static_cast<int64>(r) : r;
}
}
uint64 mpfx_manager::get_uint64(mpfx const & n) const {
SASSERT(is_uint64(n));
unsigned * w = words(n);
w += m_frac_part_sz;
return *reinterpret_cast<uint64*>(w);
}
template<bool SYNCH>
void mpfx_manager::to_mpz_core(mpfx const & n, mpz_manager<SYNCH> & m, mpz & t) {
SASSERT(is_int(n));
unsigned * w = words(n);
m.set(t, m_int_part_sz, w+m_frac_part_sz);
if (is_neg(n))
m.neg(t);
}
void mpfx_manager::to_mpz(mpfx const & n, unsynch_mpz_manager & m, mpz & t) {
to_mpz_core(n, m, t);
}
void mpfx_manager::to_mpz(mpfx const & n, synch_mpz_manager & m, mpz & t) {
to_mpz_core(n, m, t);
}
template<bool SYNCH>
void mpfx_manager::to_mpq_core(mpfx const & n, mpq_manager<SYNCH> & m, mpq & t) {
_scoped_numeral<mpz_manager<SYNCH> > a(m), b(m);
unsigned * w = words(n);
m.set(a, m_total_sz, w);
m.set(b, 1);
m.mul2k(b, sizeof(unsigned)*8*m_frac_part_sz);
m.rat_div(a, b, t);
if (is_neg(n))
m.neg(t);
}
void mpfx_manager::to_mpq(mpfx const & n, unsynch_mpq_manager & m, mpq & t) {
to_mpq_core(n, m, t);
}
void mpfx_manager::to_mpq(mpfx const & n, synch_mpq_manager & m, mpq & t) {
to_mpq_core(n, m, t);
}
void mpfx_manager::display_raw(std::ostream & out, mpfx const & n) const {
if (is_neg(n))
out << "-";
unsigned * w = words(n);
unsigned i = m_total_sz;
while(i > 0) {
if (i == m_frac_part_sz)
out << ".";
--i;
out << std::hex << std::setfill('0') << std::setw(2 * sizeof(unsigned)) << w[i];
}
}
void mpfx_manager::display(std::ostream & out, mpfx const & n) const {
if (is_neg(n))
out << "-";
unsigned * w = words(n);
unsigned sz = m_total_sz;
unsigned shift = UINT_MAX;
if (is_int(n)) {
w += m_frac_part_sz;
sz -= m_frac_part_sz;
}
else {
shift = ntz(m_total_sz, w);
if (shift > 0)
shr(m_total_sz, w, shift, m_total_sz, w);
}
sbuffer<char, 1024> str_buffer(11*sz, 0);
out << mp_decimal(w, sz, str_buffer.begin(), str_buffer.size(), 0);
if (!is_int(n)) {
SASSERT(shift != UINT_MAX);
// reverse effect of shr
if (shift > 0)
shl(m_total_sz, w, shift, m_total_sz, w);
// display denominator as a power of 2
unsigned k = sizeof(unsigned)*8*m_frac_part_sz - shift;
out << "/2";
if (k > 1)
out << "^" << k;
}
}
void mpfx_manager::display_smt2(std::ostream & out, mpfx const & n) const {
if (is_neg(n))
out << "(- ";
unsigned * w = words(n);
unsigned sz = m_total_sz;
if (is_int(n)) {
w += m_frac_part_sz;
sz -= m_frac_part_sz;
}
else {
out << "(/ ";
}
sbuffer<char, 1024> str_buffer(11*sz, 0);
out << mp_decimal(w, sz, str_buffer.begin(), str_buffer.size(), 0);
if (!is_int(n)) {
out << " ";
unsigned * w = m_buffer0.c_ptr();
for (unsigned i = 0; i < m_frac_part_sz; i++)
w[i] = 0;
w[m_frac_part_sz] = 1;
sbuffer<char, 1024> str_buffer2(11*(m_frac_part_sz+1), 0);
out << mp_decimal(w, m_frac_part_sz+1, str_buffer2.begin(), str_buffer2.size(), 0);
out << ")";
}
if (is_neg(n))
out << ")";
}
void mpfx_manager::display_decimal(std::ostream & out, mpfx const & n, unsigned prec) const {
if (is_neg(n))
out << "-";
unsigned * w = words(n);
sbuffer<char, 1024> str_buffer(11*m_int_part_sz, 0);
out << mp_decimal(w + m_frac_part_sz, m_int_part_sz, str_buffer.begin(), str_buffer.size(), 0);
if (!is_int(n)) {
out << ".";
unsigned * frac = m_buffer0.c_ptr();
::copy(m_frac_part_sz, w, m_frac_part_sz, frac);
unsigned ten = 10;
unsigned * n_frac = m_buffer1.c_ptr();
bool frac_is_zero = false;
unsigned i = 0;
while (!frac_is_zero) {
if (i >= prec) {
out << "?";
return;
}
multiply(frac, m_frac_part_sz, &ten, 1, n_frac, 0);
frac_is_zero = ::is_zero(m_frac_part_sz, n_frac);
SASSERT(n_frac[m_frac_part_sz] <= 9);
if (!frac_is_zero || n_frac[m_frac_part_sz] != 0)
out << n_frac[m_frac_part_sz];
n_frac[m_frac_part_sz] = 0;
std::swap(frac, n_frac);
i++;
}
}
}
std::string mpfx_manager::to_string(mpfx const & a) const {
std::ostringstream buffer;
display(buffer, a);
return buffer.str();
}
std::string mpfx_manager::to_rational_string(mpfx const & a) const {
return to_string(a);
}
bool mpfx_manager::check(mpfx const & a) const {
SASSERT(!is_zero(a) || a.m_sign == 0);
SASSERT(is_zero(a) == ::is_zero(m_total_sz, words(a)));
return true;
}
unsigned mpfx_manager::prev_power_of_two(mpfx const & a) {
if (!is_pos(a))
return 0;
return m_int_part_sz * sizeof(unsigned) * 8 - nlz(m_int_part_sz, words(a) + m_frac_part_sz) - 1;
}

398
src/util/mpfx.h Normal file
View file

@ -0,0 +1,398 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
mpfx.h
Abstract:
Multi precision fixed point numbers.
Author:
Leonardo de Moura (leonardo) 2012-09-19
Revision History:
--*/
#ifndef _MPFX_H_
#define _MPFX_H_
#include"id_gen.h"
#include"util.h"
#include"vector.h"
#include"z3_exception.h"
#include"scoped_numeral.h"
#include"scoped_numeral_vector.h"
class mpfx_manager;
class mpfx {
friend class mpfx_manager;
unsigned m_sign:1;
unsigned m_sig_idx:31; // position where the data is stored in the mpfx_manager.
public:
mpfx():
m_sign(0),
m_sig_idx(0) {
}
void swap(mpfx & other) {
unsigned sign = m_sign; m_sign = other.m_sign; other.m_sign = sign;
unsigned sig_idx = m_sig_idx; m_sig_idx = other.m_sig_idx; other.m_sig_idx = sig_idx;
}
};
inline void swap(mpfx & m1, mpfx & m2) { m1.swap(m2); }
class mpz;
class mpq;
template<bool SYNCH> class mpz_manager;
template<bool SYNCH> class mpq_manager;
typedef mpz_manager<true> synch_mpz_manager;
typedef mpz_manager<false> unsynch_mpz_manager;
typedef mpq_manager<true> synch_mpq_manager;
typedef mpq_manager<false> unsynch_mpq_manager;
class mpfx_manager {
// Every mpfx numeral from a given mpfx_manager uses the same number of words
// to encode the integer and fractional parts.
//
// The number of words used to encode the integer part may be different from the number of words
// used to encode the fractional part.
//
// There are two rounding modes: towards plus infinity, and towards minus infinity.
//
// If the result of an operation does not fit in the integer part, then an overflow exception is thrown.
//
// If the fractional part uses n words, then the error of every operation is less than 1/2^(32*n).
//
// Machine integer values (int, unsigned, int64, uint64) can be easily converted into mpfx numerals.
//
// The result of addition and subtraction operations are always precise. Note that overflows will trigger
// an exception instead of an incorrect result.
//
unsigned m_int_part_sz;
unsigned m_frac_part_sz;
unsigned m_total_sz; //!< == m_int_part_sz + m_frac_part_sz
unsigned_vector m_words; //!< Array containing all words
unsigned m_capacity; //!< Number of mpfx numerals that can be stored in m_words.
bool m_to_plus_inf; //!< If True, then round to plus infinity, otherwise to minus infinity
id_gen m_id_gen;
unsigned_vector m_buffer0, m_buffer1, m_buffer2;
unsigned_vector m_tmp_digits;
mpfx m_one;
unsigned * words(mpfx const & n) const { return m_words.c_ptr() + (n.m_sig_idx * m_total_sz); }
unsigned sz(unsigned * ws) const;
void ensure_capacity(unsigned sig_idx) {
while (sig_idx >= m_capacity)
expand();
}
void expand();
void allocate_if_needed(mpfx & n) {
if (n.m_sig_idx == 0)
allocate(n);
}
void allocate(mpfx & n);
void set_epsilon(mpfx & n);
void add_sub(bool is_sub, mpfx const & a, mpfx const & b, mpfx & c);
template<bool SYNCH>
void set_core(mpfx & n, mpz_manager<SYNCH> & m, mpz const & v);
template<bool SYNCH>
void set_core(mpfx & n, mpq_manager<SYNCH> & m, mpq const & v);
template<bool SYNCH>
void to_mpz_core(mpfx const & n, mpz_manager<SYNCH> & m, mpz & t);
template<bool SYNCH>
void to_mpq_core(mpfx const & n, mpq_manager<SYNCH> & m, mpq & t);
public:
typedef mpfx numeral;
static bool precise() { return false; }
static bool field() { return true; }
class exception : public z3_exception {
virtual char const * msg() const { return "multi-precision fixed point (mpfx) exception"; }
};
class overflow_exception : public exception {
virtual char const * msg() const { return "multi-precision fixed point (mpfx) overflow"; }
};
class div0_exception : public exception {
virtual char const * msg() const { return "multi-precision fixed point (mpfx) division by zero"; }
};
mpfx_manager(unsigned int_sz = 2, unsigned frac_sz = 1, unsigned initial_capacity = 1024);
~mpfx_manager();
void round_to_plus_inf() { m_to_plus_inf = true; }
void round_to_minus_inf() { m_to_plus_inf = false; }
void set_rounding(bool to_plus_inf) { m_to_plus_inf = to_plus_inf; }
bool rounding_to_plus_inf() const { return m_to_plus_inf; }
/**
\brief Return true if n is negative
*/
static bool sign(mpfx const & n) { return is_neg(n); }
/**
\brief Set n to zero.
*/
void reset(mpfx & n);
/**
\brief Return true if n is an integer.
*/
bool is_int(mpfx const & n) const;
/**
\brief Return true if n is zero.
*/
static bool is_zero(mpfx const & n) { return n.m_sig_idx == 0; }
/**
\brief Return true if n is positive.
*/
static bool is_pos(mpfx const & n) { return n.m_sign == 0 && !is_zero(n); }
/**
\brief Return true if n is negative.
*/
static bool is_neg(mpfx const & n) { return n.m_sign != 0; }
/**
\brief Return true if n is non positive.
*/
static bool is_nonpos(mpfx const & n) { return !is_pos(n); }
/**
\brief Return true if n is non negative.
*/
static bool is_nonneg(mpfx const & n) { return !is_neg(n); }
/**
\brief Return true if the absolute value of n is 1.
*/
bool is_abs_one(mpfx const & n) const;
/**
\brief Return true if n is one.
*/
bool is_one(mpfx const & n) const { return is_pos(n) && is_abs_one(n); }
/**
\brief Return true if n is minus one.
*/
bool is_minus_one(mpfx const & n) const { return is_neg(n) && is_abs_one(n); }
/**
\brief Return true if \c a is an integer and fits in an int64 machine integer.
*/
bool is_int64(mpfx const & a) const;
/**
\brief Return true if \c a is a non-negative integer and fits in an int64 machine integer.
*/
bool is_uint64(mpfx const & a) const;
/**
\brief Delete the resources associated with n.
*/
void del(mpfx & n);
/**
\brief a <- -a
*/
static void neg(mpfx & a) { if (!is_zero(a)) a.m_sign = !a.m_sign; }
/**
\brief a <- |a|
*/
static void abs(mpfx & a) { a.m_sign = 0; }
static void swap(mpfx & a, mpfx & b) { a.swap(b); }
/**
\brief c <- a + b
*/
void add(mpfx const & a, mpfx const & b, mpfx & c);
/**
\brief c <- a - b
*/
void sub(mpfx const & a, mpfx const & b, mpfx & c);
/**
\brief a <- a + 1
*/
void inc(mpfx & a) { add(a, m_one, a); }
/**
\brief a <- a - 1
*/
void dec(mpfx & a) { sub(a, m_one, a); }
/**
\brief c <- a * b
*/
void mul(mpfx const & a, mpfx const & b, mpfx & c);
/**
\brief c <- a / b
\pre !is_zero(b)
*/
void div(mpfx const & a, mpfx const & b, mpfx & c);
/**
\brief a <- 1/a
\pre !is_zero(a);
*/
void inv(mpfx & a) { div(m_one, a, a); }
void inv(mpfx const & a, mpfx & b) { set(b, a); inv(b); }
/**
\brief a <- a/2^k
*/
void div2k(mpfx & a, unsigned k);
/**
\brief b <- a/2^k
*/
void div2k(mpfx const & a, unsigned k, mpfx & b) { set(b, a); div2k(b, k); }
/**
\brief a <- a/2
*/
void div2(mpfx & a) { div2k(a, 1); }
/**
\brief b <- a/2
*/
void div2(mpfx const & a, mpfx & b) { div2k(a, 1, b); }
/**
\brief b <- a^k
*/
void power(mpfx const & a, unsigned k, mpfx & b);
/**
\brief Return true if \c a is a power of 2. That is, a is equal to 2^k for some k >= 0.
*/
bool is_power_of_two(mpfx const & a, unsigned & k) const;
bool is_power_of_two(mpfx const & a) const;
bool eq(mpfx const & a, mpfx const & b) const;
bool neq(mpfx const & a, mpfx const & b) const { return !eq(a, b); }
bool lt(mpfx const & a, mpfx const & b) const;
bool gt(mpfx const & a, mpfx const & b) const { return lt(b, a); }
bool le(mpfx const & a, mpfx const & b) const { return !lt(b, a); }
bool ge(mpfx const & a, mpfx const & b) const { return !lt(a, b); }
void set(mpfx & n, int v);
void set(mpfx & n, unsigned v);
void set(mpfx & n, int64 v);
void set(mpfx & n, uint64 v);
void set(mpfx & n, int num, unsigned den);
void set(mpfx & n, int64 num, uint64 den);
void set(mpfx & n, mpfx const & v);
void set(mpfx & n, unsynch_mpz_manager & m, mpz const & v);
void set(mpfx & n, synch_mpz_manager & m, mpz const & v);
void set(mpfx & n, unsynch_mpq_manager & m, mpq const & v);
void set(mpfx & n, synch_mpq_manager & m, mpq const & v);
/**
\brief Set n to the smallest representable numeral greater than zero.
*/
void set_plus_epsilon(mpfx & n);
/**
\brief Set n to the greatest representable numeral less than zero.
*/
void set_minus_epsilon(mpfx & n);
/**
\brief n <- floor(n)
*/
void floor(mpfx & n);
void floor(mpfx const & n, mpfx & o) { set(o, n); floor(o); }
/**
\brief n <- ceil(n)
*/
void ceil(mpfx & n);
void ceil(mpfx const & n, mpfx & o) { set(o, n); ceil(o); }
/**
\brief Return n as an int64.
\pre is_int64(n)
*/
int64 get_int64(mpfx const & n) const;
/**
\brief Return n as an uint64.
\pre is_uint64(n)
*/
uint64 get_uint64(mpfx const & n) const;
/**
\brief Convert n into a mpz numeral.
\pre is_int(n)
*/
void to_mpz(mpfx const & n, unsynch_mpz_manager & m, mpz & t);
/**
\brief Convert n into a mpz numeral.
\pre is_int(n)
*/
void to_mpz(mpfx const & n, synch_mpz_manager & m, mpz & t);
/**
\brief Convert n into a mpq numeral.
*/
void to_mpq(mpfx const & n, unsynch_mpq_manager & m, mpq & t);
/**
\brief Convert n into a mpq numeral.
*/
void to_mpq(mpfx const & n, synch_mpq_manager & m, mpq & t);
/**
\brief Return the biggest k s.t. 2^k <= a.
\remark Return 0 if a is not positive.
*/
unsigned prev_power_of_two(mpfx const & a);
void display(std::ostream & out, mpfx const & n) const;
void display_smt2(std::ostream & out, mpfx const & n) const;
void display_decimal(std::ostream & out, mpfx const & n, unsigned prec = UINT_MAX) const;
void display_raw(std::ostream & out, mpfx const & n) const;
std::string to_string(mpfx const & a) const;
std::string to_rational_string(mpfx const & a) const;
bool check(mpfx const & a) const;
};
typedef _scoped_numeral<mpfx_manager> scoped_mpfx;
typedef _scoped_numeral_vector<mpfx_manager> scoped_mpfx_vector;
#endif

439
src/util/mpn.cpp Normal file
View file

@ -0,0 +1,439 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
mpn.cpp
Abstract:
Multi Precision Natural Numbers
Author:
Christoph Wintersteiger (cwinter) 2011-11-16.
Revision History:
--*/
#include"debug.h"
#include"trace.h"
#include"buffer.h"
#include"mpn.h"
#define max(a,b) (((a) > (b)) ? (a) : (b))
typedef uint64 mpn_double_digit;
COMPILE_TIME_ASSERT(sizeof(mpn_double_digit) == 2 * sizeof(mpn_digit));
mpn_manager static_mpn_manager;
const mpn_digit mpn_manager::zero = 0;
mpn_manager::mpn_manager() {
#ifdef _DEBUG
trace_enabled=true;
#endif
}
mpn_manager::~mpn_manager() {
}
int mpn_manager::compare(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb) const {
int res = 0;
#ifdef _DEBUG
if (trace_enabled)
STRACE("mpn", tout << "[mpn] "; );
#endif
trace(a, lnga);
size_t j = max(lnga, lngb) - 1;
for (; j != (size_t)-1 && res == 0; j--) {
mpn_digit const & u_j = (j < lnga) ? a[j] : zero;
mpn_digit const & v_j = (j < lngb) ? b[j] : zero;
if (u_j > v_j)
res = 1;
else if (u_j < v_j)
res = -1;
}
#ifdef _DEBUG
if (trace_enabled)
STRACE("mpn", tout << ((res == 1) ? " > " : (res == -1) ? " < " : " == "); );
#endif
trace_nl(b, lngb);
return res;
}
bool mpn_manager::add(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb,
mpn_digit * c, size_t const lngc_alloc,
size_t * plngc) const {
trace(a, lnga, b, lngb, "+");
// Essentially Knuth's Algorithm A
size_t len = max(lnga, lngb);
SASSERT(lngc_alloc == len+1 && len > 0);
mpn_digit k = 0;
mpn_digit r;
bool c1, c2;
for (size_t j = 0; j < len; j++) {
mpn_digit const & u_j = (j < lnga) ? a[j] : zero;
mpn_digit const & v_j = (j < lngb) ? b[j] : zero;
r = u_j + v_j; c1 = r < u_j;
c[j] = r + k; c2 = c[j] < r;
k = c1 | c2;
}
c[len] = k;
size_t &os = *plngc;
for (os = len+1; os > 1 && c[os-1] == 0; ) os--;
SASSERT(os > 0 && os <= len+1);
trace_nl(c, os);
return true; // return k != 0? What would MSBignum return?
}
bool mpn_manager::sub(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb,
mpn_digit * c, mpn_digit * pborrow) const {
trace(a, lnga, b, lngb, "-");
// Essentially Knuth's Algorithm S
size_t len = max(lnga, lngb);
mpn_digit & k = *pborrow; k = 0;
mpn_digit r;
bool c1, c2;
for (size_t j = 0; j < len; j++) {
mpn_digit const & u_j = (j < lnga) ? a[j] : zero;
mpn_digit const & v_j = (j < lngb) ? b[j] : zero;
r = u_j - v_j; c1 = r > u_j;
c[j] = r - k; c2 = c[j] > r;
k = c1 | c2;
}
trace_nl(c, lnga);
return true; // return k != 0?
}
bool mpn_manager::mul(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb,
mpn_digit * c) const {
trace(a, lnga, b, lngb, "*");
// Essentially Knuth's Algorithm M.
// Perhaps implement a more efficient version, see e.g., Knuth, Section 4.3.3.
size_t i;
mpn_digit k;
#define DIGIT_BITS (sizeof(mpn_digit)*8)
#define HALF_BITS (sizeof(mpn_digit)*4)
for (unsigned i = 0; i < lnga; i++)
c[i] = 0;
for (size_t j = 0; j < lngb; j++) {
mpn_digit const & v_j = b[j];
if (v_j == 0) { // This branch may be omitted according to Knuth.
c[j+lnga] = 0;
}
else {
k = 0;
for (i = 0; i < lnga; i++) {
mpn_digit const & u_i = a[i];
mpn_double_digit t;
t = ((mpn_double_digit)u_i * (mpn_double_digit)v_j) +
(mpn_double_digit) c[i+j] +
(mpn_double_digit) k;
c[i+j] = (t << DIGIT_BITS) >> DIGIT_BITS;
k = t >> DIGIT_BITS;
}
c[j+lnga] = k;
}
}
trace_nl(c, lnga+lngb);
return true;
}
#define MASK_FIRST (~((mpn_digit)(-1) >> 1))
#define FIRST_BITS(N, X) ((X) >> (DIGIT_BITS-(N)))
#define LAST_BITS(N, X) (((X) << (DIGIT_BITS-(N))) >> (DIGIT_BITS-(N)))
#define BASE ((mpn_double_digit)0x01 << DIGIT_BITS)
bool mpn_manager::div(mpn_digit const * numer, size_t const lnum,
mpn_digit const * denom, size_t const lden,
mpn_digit * quot,
mpn_digit * rem) {
trace(numer, lnum, denom, lden, "/");
bool res = false;
if (lnum < lden) {
for (size_t i = 0; i < (lnum-lden+1); i++)
quot[i] = 0;
for (size_t i = 0; i < lden; i++)
rem[i] = (i < lnum) ? numer[i] : 0;
return false;
}
bool all_zero = true;
for (size_t i = 0; i < lden && all_zero; i++)
if (denom[i] != zero) all_zero = false;
if (all_zero) {
// Division by 0. What would the MSBignum divide function do?
UNREACHABLE();
return res;
}
SASSERT(denom[lden-1] != 0);
if (lnum == 1 && lden == 1) {
*quot = numer[0] / denom[0];
*rem = numer[0] % denom[0];
}
else if (lnum < lden || (lnum == lden && numer[lnum-1] < denom[lden-1])) {
*quot = 0;
for (size_t i = 0; i < lden; i++)
rem[i] = (i < lnum) ? numer[i] : 0;
}
else {
size_t d = div_normalize(numer, lnum, denom, lden, u, v);
if (lden == 1)
res = div_1(u, v[0], quot);
else
res = div_n(u, v, quot, rem);
div_unnormalize(u, v, d, rem);
}
// STRACE("mpn_dbg", display_raw(tout, quot, lnum - lden + 1); tout << ", ";
// display_raw(tout, rem, lden); tout << std::endl; );
trace_nl(quot, lnum-lden+1);
trace(numer, lnum, denom, lden, "%");
trace_nl(rem, lden);
#ifdef _DEBUG
mpn_sbuffer temp(lnum+1, 0);
mul(quot, lnum-lden+1, denom, lden, temp.c_ptr());
size_t real_size;
add(temp.c_ptr(), lnum, rem, lden, temp.c_ptr(), lnum+1, &real_size);
bool ok = true;
for (size_t i = 0; i < lnum && ok; i++)
if (temp[i] != numer[i]) ok = false;
if (temp[lnum] != 0) ok = false;
CTRACE("mpn_dbg", !ok, tout << "DIV BUG: quot * denom + rem = "; display_raw(tout, temp.c_ptr(), lnum+1); tout << std::endl; );
SASSERT(ok);
#endif
return res;
}
size_t mpn_manager::div_normalize(mpn_digit const * numer, size_t const lnum,
mpn_digit const * denom, size_t const lden,
mpn_sbuffer & n_numer,
mpn_sbuffer & n_denom) const
{
size_t d = 0;
while (((denom[lden-1] << d) & MASK_FIRST) == 0) d++;
SASSERT(d < DIGIT_BITS);
n_numer.resize(lnum+1);
n_denom.resize(lden);
if (d == 0) {
n_numer[lnum] = 0;
for (size_t i = 0; i < lnum; i++)
n_numer[i] = numer[i];
for (size_t i = 0; i < lden; i++)
n_denom[i] = denom[i];
}
else {
mpn_digit q = FIRST_BITS(d, numer[lnum-1]);
n_numer[lnum] = q;
for (size_t i = lnum-1; i > 0; i--)
n_numer[i] = (numer[i] << d) | FIRST_BITS(d, numer[i-1]);
n_numer[0] = numer[0] << d;
for (size_t i = lden-1; i > 0; i--)
n_denom[i] = denom[i] << d | FIRST_BITS(d, denom[i-1]);
n_denom[0] = denom[0] << d;
}
STRACE("mpn_norm", tout << "Normalized: n_numer="; display_raw(tout, n_numer.c_ptr(), n_numer.size());
tout << " n_denom="; display_raw(tout, n_denom.c_ptr(), n_denom.size()); tout << std::endl; );
return d;
}
void mpn_manager::div_unnormalize(mpn_sbuffer & numer, mpn_sbuffer & denom,
size_t const d, mpn_digit * rem) const {
if (d == 0) {
for (size_t i = 0; i < denom.size(); i++)
rem[i] = numer[i];
}
else {
for (size_t i = 0; i < denom.size()-1; i++)
rem[i] = numer[i] >> d | (LAST_BITS(d, numer[i+1]) << (DIGIT_BITS-d));
rem[denom.size()-1] = numer[denom.size()-1] >> d;
}
}
bool mpn_manager::div_1(mpn_sbuffer & numer, mpn_digit const denom,
mpn_digit * quot) const
{
mpn_double_digit q_hat, temp, r_hat, ms;
mpn_digit borrow;
for (size_t j = numer.size()-1; j > 0; j--) {
temp = (((mpn_double_digit)numer[j]) << DIGIT_BITS) | ((mpn_double_digit)numer[j-1]);
q_hat = temp / (mpn_double_digit) denom;
r_hat = temp % (mpn_double_digit) denom;
if (q_hat >= BASE) {
UNREACHABLE(); // is this reachable with normalized v?
}
SASSERT(q_hat < BASE);
ms = temp - (q_hat * (mpn_double_digit) denom);
borrow = ms > temp;
numer[j-1] = (mpn_digit) ms;
numer[j] = ms >> DIGIT_BITS;
quot[j-1] = (mpn_digit) q_hat;
if (borrow) {
quot[j-1]--;
numer[j] = numer[j-1] + denom;
}
STRACE("mpn_div1", tout << "j=" << j << " q_hat=" << q_hat << " r_hat=" << r_hat;
tout << " ms=" << ms;
tout << " new numer="; display_raw(tout, numer.c_ptr(), numer.size());
tout << " borrow=" << borrow;
tout << std::endl; );
}
return true; // return rem != 0 or something like that?
}
bool mpn_manager::div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom,
mpn_digit * quot, mpn_digit * rem) {
SASSERT(denom.size() > 1);
// This is essentially Knuth's Algorithm D.
size_t m = numer.size() - denom.size();
size_t n = denom.size();
SASSERT(numer.size() == m+n);
t_ms.resize(n+1);
mpn_double_digit q_hat, temp, r_hat;
mpn_digit borrow;
for (size_t j = m-1; j != (size_t)-1; j--) {
temp = (((mpn_double_digit)numer[j+n]) << DIGIT_BITS) | ((mpn_double_digit)numer[j+n-1]);
q_hat = temp / (mpn_double_digit) denom[n-1];
r_hat = temp % (mpn_double_digit) denom[n-1];
recheck:
if (q_hat >= BASE ||
((q_hat * denom[n-2]) > ((r_hat << DIGIT_BITS) + numer[j+n-2]))) {
q_hat--;
r_hat += denom[n-1];
if (r_hat < BASE) goto recheck;
}
SASSERT(q_hat < BASE);
// Replace numer[j+n]...numer[j] with
// numer[j+n]...numer[j] - q * (denom[n-1]...denom[0])
mpn_digit q_hat_small = (mpn_digit)q_hat;
#ifdef _DEBUG
trace_enabled = false;
#endif
mul(&q_hat_small, 1, denom.c_ptr(), n, t_ms.c_ptr());
sub(&numer[j], n+1, t_ms.c_ptr(), n+1, &numer[j], &borrow);
quot[j] = q_hat_small;
if (borrow) {
quot[j]--;
t_ab.resize(n+2);
size_t real_size;
add(denom.c_ptr(), n, &numer[j], n+1, t_ab.c_ptr(), n+2, &real_size);
for (size_t i = 0; i < n+1; i++)
numer[j+i] = t_ab[i];
}
#ifdef _DEBUG
trace_enabled = true;
#endif
STRACE("mpn_div", tout << "q_hat=" << q_hat << " r_hat=" << r_hat;
tout << " t_ms="; display_raw(tout, t_ms.c_ptr(), n);
tout << " new numer="; display_raw(tout, numer.c_ptr(), m+n+1);
tout << " borrow=" << borrow;
tout << std::endl; );
}
return true; // return rem != 0 or something like that?
}
char * mpn_manager::to_string(mpn_digit const * a, size_t const lng, char * buf, size_t const lbuf) const {
SASSERT(buf && lbuf > 0);
STRACE("mpn_to_string", tout << "[mpn] to_string "; display_raw(tout, a, lng); tout << " == "; );
if (lng == 1) {
#ifdef _WINDOWS
sprintf_s(buf, lbuf, "%u", *a);
#else
snprintf(buf, lbuf, "%u", *a);
#endif
}
else {
mpn_sbuffer temp(lng, 0), t_numer(lng+1, 0), t_denom(1, 0);
for (unsigned i = 0; i < lng; i++)
temp[i] = a[i];
size_t j = 0;
mpn_digit rem;
mpn_digit ten = 10;
while (!temp.empty() && (temp.size() > 1 || temp[0] != 0)) {
size_t d = div_normalize(&temp[0], temp.size(), &ten, 1, t_numer, t_denom);
div_1(t_numer, t_denom[0], &temp[0]);
div_unnormalize(t_numer, t_denom, d, &rem);
buf[j++] = '0' + rem;
while (temp.size() > 0 && temp.back() == 0)
temp.pop_back();
}
buf[j] = 0;
j--;
size_t mid = (j/2) + ((j % 2) ? 1 : 0);
for (size_t i = 0; i < mid; i++)
std::swap(buf[i], buf[j-i]);
}
STRACE("mpn_to_string", tout << buf << std::endl; );
return buf;
}
void mpn_manager::display_raw(std::ostream & out, mpn_digit const * a, size_t const lng) const {
out << "[";
for (size_t i = lng-1; i != (size_t)-1; i-- ) { out << a[i]; if (i != 0) out << "|"; }
out << "]";
}
void mpn_manager::trace(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb,
const char * op) const {
#ifdef _DEBUG
if (trace_enabled)
STRACE("mpn", tout << "[mpn] " << to_string(a, lnga, char_buf, sizeof(char_buf));
tout << " " << op << " " << to_string(b, lngb, char_buf, sizeof(char_buf));
tout << " == "; );
#endif
}
void mpn_manager::trace(mpn_digit const * a, size_t const lnga) const {
#ifdef _DEBUG
if (trace_enabled)
STRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)); );
#endif
}
void mpn_manager::trace_nl(mpn_digit const * a, size_t const lnga) const {
#ifdef _DEBUG
if (trace_enabled)
STRACE("mpn", tout << to_string(a, lnga, char_buf, sizeof(char_buf)) << std::endl; );
#endif
}

196
src/util/mpn.h Normal file
View file

@ -0,0 +1,196 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
mpn.h
Abstract:
Multi Precision Natural Numbers
Author:
Christoph Wintersteiger (cwinter) 2011-11-16.
Revision History:
--*/
#ifndef _MPN_H_
#define _MPN_H_
#include<ostream>
#include"util.h"
#include"buffer.h"
// we supply a definition of a basic max because mpz relies on it.
#define max(a,b) (((a) > (b)) ? (a) : (b))
typedef unsigned int mpn_digit;
class mpn_manager {
public:
mpn_manager();
~mpn_manager();
int compare(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb) const;
bool add(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb,
mpn_digit *c, size_t const lngc_alloc,
size_t * plngc) const;
bool sub(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb,
mpn_digit * c, mpn_digit * pborrow) const;
bool mul(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb,
mpn_digit * c) const;
bool div(mpn_digit const * numer, size_t const lnum,
mpn_digit const * denom, size_t const lden,
mpn_digit * quot,
mpn_digit * rem);
char * to_string(mpn_digit const * a, size_t const lng,
char * buf, size_t const lbuf) const;
private:
#ifdef _AMD64_
class mpn_sbuffer : public sbuffer<mpn_digit> {
public:
mpn_sbuffer() : sbuffer<mpn_digit>() {}
mpn_sbuffer(size_t nsz, const mpn_digit & elem = 0) :
sbuffer<mpn_digit>(static_cast<unsigned>(nsz), elem)
{
}
void resize(size_t nsz, const mpn_digit & elem = 0) {
sbuffer<mpn_digit>::resize(static_cast<unsigned>(nsz), elem);
}
mpn_digit & operator[](size_t idx) {
return sbuffer<mpn_digit>::operator[](static_cast<unsigned>(idx));
}
const mpn_digit & operator[](size_t idx) const {
return sbuffer<mpn_digit>::operator[](static_cast<unsigned>(idx));
}
};
#else
typedef sbuffer<mpn_digit> mpn_sbuffer;
#endif
static const mpn_digit zero;
mpn_sbuffer u, v, t_ms, t_ab;
void display_raw(std::ostream & out, mpn_digit const * a, size_t const lng) const;
size_t div_normalize(mpn_digit const * numer, size_t const lnum,
mpn_digit const * denom, size_t const lden,
mpn_sbuffer & n_numer,
mpn_sbuffer & n_denom) const;
void div_unnormalize(mpn_sbuffer & numer, mpn_sbuffer & denom,
size_t const d, mpn_digit * rem) const;
bool div_1(mpn_sbuffer & numer, mpn_digit const denom,
mpn_digit * quot) const;
bool div_n(mpn_sbuffer & numer, mpn_sbuffer const & denom,
mpn_digit * quot, mpn_digit * rem);
#ifdef _DEBUG
mutable char char_buf[4096];
bool trace_enabled;
#endif
void trace(mpn_digit const * a, size_t const lnga,
mpn_digit const * b, size_t const lngb,
const char * op) const;
void trace(mpn_digit const * a, size_t const lnga) const;
void trace_nl(mpn_digit const * a, size_t const lnga) const;
public:
// This function is needed because of the static_mpn_manager global variable.
// It must be invoked by the memory_manager during finalization.
// After we remove MSBignum from the code base, the global variable will
// not be needed anymore, and we will be able to eliminate this function.
void finalize() {
u.finalize();
v.finalize();
t_ms.finalize();
t_ab.finalize();
}
};
// MSBignum compatible interface
// Note: The `owner' parameter is ignored. We use separate mpn_manager objects for the
// same purpose. Multiple owners are not supported in these compatibility functions,
// instead a static mpn_manager is used.
extern mpn_manager static_mpn_manager;
typedef unsigned int digit_t;
typedef struct {
mpn_digit multiplier;
size_t shiftamt;
} reciprocal_1_t;
#define reciprocal_1_NULL ((reciprocal_1_t*)0)
inline int compare_diff(digit_t const * a, size_t const lnga,
digit_t const * b, size_t const lngb)
{
return static_mpn_manager.compare(a, lnga, b, lngb);
}
inline char * mp_decimal(digit_t const * a, size_t const lng, // Number to be converted and its length
char * buf, size_t const lbuf, // output buffer and its length
int owner)
{
return static_mpn_manager.to_string(a, lng, buf, lbuf);
}
inline bool add_full(digit_t const * a, size_t const lnga,
digit_t const * b, size_t const lngb,
digit_t *c, size_t const lngc_alloc,
size_t * plngc,
int owner)
{
return static_mpn_manager.add(a, lnga, b, lngb, c, lngc_alloc, plngc);
}
inline bool sub_diff(digit_t const * a, size_t const lnga,
digit_t const * b, size_t const lngb,
digit_t * c, digit_t * pborrow,
int owner)
{
return static_mpn_manager.sub(a, lnga, b, lngb, c, pborrow);
}
inline bool multiply(digit_t const * a, size_t const lnga,
digit_t const * b, size_t const lngb,
digit_t * c,
int owner)
{
return static_mpn_manager.mul(a, lnga, b, lngb, c);
}
inline bool divide(digit_t const * numer, size_t const lnum,
digit_t const * denom, size_t const lden,
reciprocal_1_t const * supplied_reciprocal, /* reciprocal_t struct for this denominator,
or reciprocal_1_NULL
if not previously precomputed */
digit_t * quot, /* Quotient -- length MAX(lnum - lden + 1, 0) */
digit_t * rem, /* Remainder -- length lden */
int owner)
{
return static_mpn_manager.div(numer, lnum, denom, lden, quot, rem);
}
#endif

317
src/util/mpq.cpp Normal file
View file

@ -0,0 +1,317 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
mpq.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-06-21.
Revision History:
--*/
#include"mpq.h"
#include"warning.h"
#include"z3_exception.h"
template<bool SYNCH>
mpq_manager<SYNCH>::mpq_manager() {
}
template<bool SYNCH>
mpq_manager<SYNCH>::~mpq_manager() {
del(m_n_tmp);
del(m_add_tmp1);
del(m_add_tmp2);
del(m_lt_tmp1);
del(m_lt_tmp2);
del(m_addmul_tmp);
}
template<bool SYNCH>
bool mpq_manager<SYNCH>::rat_lt(mpq const & a, mpq const & b) {
mpz const & na = a.numerator();
mpz const & nb = b.numerator();
int sign_a = this->sign(na);
int sign_b = this->sign(nb);
if (sign_a < 0) {
if (sign_b >= 0) return true;
}
else if (sign_a == 0) {
if (sign_b > 0) return true;
SASSERT(sign_b <= 0); return false;
}
else {
SASSERT(sign_a > 0);
if (sign_b <= 0) return false;
}
SASSERT((sign_a > 0 && sign_b > 0) ||
(sign_a < 0 && sign_b < 0));
mpz const & da = a.denominator();
mpz const & db = b.denominator();
if (SYNCH) {
mpq tmp1;
mpq tmp2;
mul(na, db, tmp1);
mul(nb, da, tmp2);
bool r = lt(tmp1, tmp2);
del(tmp1);
del(tmp2);
return r;
}
else {
mul(na, db, m_lt_tmp1);
mul(nb, da, m_lt_tmp2);
return lt(m_lt_tmp1, m_lt_tmp2);
}
}
template<bool SYNCH>
void mpq_manager<SYNCH>::floor(mpq const & a, mpz & f) {
if (is_int(a)) {
set(f, a.m_num);
return;
}
bool is_neg_num = is_neg(a.m_num);
machine_div(a.m_num, a.m_den, f);
if (is_neg_num)
sub(f, this->mk_z(1), f);
}
template<bool SYNCH>
void mpq_manager<SYNCH>::ceil(mpq const & a, mpz & c) {
if (is_int(a)) {
set(c, a.m_num);
return;
}
bool is_pos_num = is_pos(a.m_num);
machine_div(a.m_num, a.m_den, c);
if (is_pos_num)
add(c, this->mk_z(1), c);
}
template<bool SYNCH>
void mpq_manager<SYNCH>::gcd(unsigned sz, mpq const * as, mpq & g) {
switch (sz) {
case 0:
reset(g);
return;
case 1:
set(g, as[0]);
abs(g);
return;
default:
break;
}
gcd(as[0], as[1], g);
for (unsigned i = 2; i < sz; i++) {
if (is_one(g))
return;
gcd(g, as[i], g);
}
}
template<bool SYNCH>
std::string mpq_manager<SYNCH>::to_string(mpq const & a) const {
if (is_int(a))
return to_string(a.m_num);
return to_string(a.m_num) + "/" + to_string(a.m_den);
}
template<bool SYNCH>
void mpq_manager<SYNCH>::display(std::ostream & out, mpq const & a) const {
if (is_int(a)) {
display(out, a.m_num);
}
else {
display(out, a.m_num);
out << "/";
display(out, a.m_den);
}
}
template<bool SYNCH>
void mpq_manager<SYNCH>::display_smt2(std::ostream & out, mpq const & a, bool decimal) const {
if (is_int(a)) {
display_smt2(out, a.m_num, decimal);
}
else {
out << "(/ ";
display_smt2(out, a.m_num, decimal);
out << " ";
display_smt2(out, a.m_den, decimal);
out << ")";
}
}
template<bool SYNCH>
void mpq_manager<SYNCH>::display_decimal(std::ostream & out, mpq const & a, unsigned prec) {
mpz n1, d1, v1;
get_numerator(a, n1);
get_denominator(a, d1);
if (is_neg(a)) {
out << "-";
neg(n1);
}
mpz ten(10);
div(n1, d1, v1);
display(out, v1);
rem(n1, d1, n1);
if (is_zero(n1))
goto end; // number is an integer
out << ".";
for (unsigned i = 0; i < prec; i++) {
mul(n1, ten, n1);
div(n1, d1, v1);
SASSERT(lt(v1, ten));
display(out, v1);
rem(n1, d1, n1);
if (is_zero(n1))
goto end; // number is precise
}
out << "?";
end:
del(ten); del(n1); del(d1); del(v1);
}
template<bool SYNCH>
void mpq_manager<SYNCH>::set(mpq & a, char const * val) {
reset(a.m_num);
mpz ten(10);
_scoped_numeral<mpz_manager<SYNCH> > tmp(*this);
char const * str = val;
bool sign = false;
while (str[0] == ' ') ++str;
if (str[0] == '-')
sign = true;
while (str[0] && (str[0] != '/') && (str[0] != '.') && (str[0] != 'e') && (str[0] != 'E')) {
if ('0' <= str[0] && str[0] <= '9') {
SASSERT(str[0] - '0' <= 9);
mul(a.m_num, ten, tmp);
add(tmp, this->mk_z(str[0] - '0'), a.m_num);
}
++str;
}
TRACE("mpq_set", tout << "[before] a: " << to_string(a) << "\n";);
if (str[0] == '/' || str[0] == '.' || str[0] == 'e' || str[0] == 'E') {
bool is_rat = str[0] == '/';
_scoped_numeral<mpz_manager<SYNCH> > tmp2(*this);
set(tmp2, 1);
bool has_den = false;
if (str[0] == '/' || str[0] == '.') {
has_den = true;
++str;
reset(a.m_den);
while (str[0] && (str[0] != 'e') && (str[0] != 'E')) {
if ('0' <= str[0] && str[0] <= '9') {
mul(a.m_den, ten, tmp);
add(tmp, this->mk_z(str[0] - '0'), a.m_den);
if (!is_rat)
mul(tmp2, ten, tmp2);
}
++str;
}
}
unsigned long long exp = 0;
bool exp_sign = false;
if (str[0] == 'e' || str[0] == 'E') {
if (is_rat)
throw default_exception("mixing rational/scientific notation");
++str;
if (str[0] == '-') {
exp_sign = true;
++str;
}
while (str[0]) {
if ('0' <= str[0] && str[0] <= '9') {
SASSERT(str[0] - '0' <= 9);
exp = (10*exp) + (str[0] - '0');
}
TRACE("mpq_set", tout << "[exp]: " << exp << ", str[0]: " << (str[0] - '0') << std::endl;);
++str;
}
}
if (!is_rat) {
// a <- a.m_num + a.m_den/tmp2
if (exp > static_cast<unsigned long long>(UINT_MAX))
throw default_exception("exponent is too big");
_scoped_numeral<mpq_manager<SYNCH> > b(*this);
if (has_den) {
set(b, a.m_den, tmp2);
set(a.m_den, 1);
add(a, b, a);
}
if (exp > 0) {
_scoped_numeral<mpq_manager<SYNCH> > _exp(*this);
_scoped_numeral<mpq_manager<SYNCH> > _ten(*this);
set(_ten, 10);
power(_ten, static_cast<unsigned>(exp), _exp);
TRACE("mpq_set", tout << "a: " << to_string(a) << ", exp_sign:" << exp_sign << ", exp: " << exp << " " << to_string(_exp) << std::endl;);
if (exp_sign)
div(a, _exp, a);
else
mul(a, _exp, a);
}
}
else {
// rational case
if (is_zero(a.m_den))
throw default_exception("division by zero");
}
}
else {
reset_denominator(a);
}
if (sign)
neg(a.m_num);
normalize(a);
}
template<bool SYNCH>
void mpq_manager<SYNCH>::power(mpq const & a, unsigned p, mpq & b) {
unsigned mask = 1;
mpq power;
set(power, a);
set(b, 1);
while (mask <= p) {
if (mask & p)
mul(b, power, b);
mul(power, power, power);
mask = mask << 1;
}
del(power);
}
template<bool SYNCH>
double mpq_manager<SYNCH>::get_double(mpq const & a) const {
double n;
double d;
n = get_double(a.m_num);
d = get_double(a.m_den);
SASSERT(d > 0.0);
return n/d;
}
template<bool SYNCH>
bool mpq_manager<SYNCH>::root(mpq const & a, unsigned n, mpq & r) {
return root(a.m_num, n, r.m_num) && root(a.m_den, n, r.m_den);
}
template<bool SYNCH>
unsigned mpq_manager<SYNCH>::prev_power_of_two(mpq const & a) {
_scoped_numeral<mpz_manager<SYNCH> > _tmp(*this);
floor(a, _tmp);
return prev_power_of_two(_tmp);
}
template class mpq_manager<true>;
template class mpq_manager<false>;

848
src/util/mpq.h Normal file
View file

@ -0,0 +1,848 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
mpq.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-06-21.
Revision History:
--*/
#ifndef _MPQ_H_
#define _MPQ_H_
#include"mpz.h"
#include"trace.h"
class mpq {
mpz m_num;
mpz m_den;
friend class mpq_manager<true>;
friend class mpq_manager<false>;
mpq & operator=(mpq const & other) { UNREACHABLE(); return *this; }
public:
mpq(int v):m_num(v), m_den(1) {}
mpq():m_den(1) {}
void swap(mpq & other) { m_num.swap(other.m_num); m_den.swap(other.m_den); }
mpz const & numerator() const { return m_num; }
mpz const & denominator() const { return m_den; }
};
inline void swap(mpq & m1, mpq & m2) { m1.swap(m2); }
template<bool SYNCH = true>
class mpq_manager : public mpz_manager<SYNCH> {
mpz m_n_tmp;
mpz m_add_tmp1;
mpz m_add_tmp2;
mpq m_addmul_tmp;
mpq m_lt_tmp1;
mpq m_lt_tmp2;
void reset_denominator(mpq & a) {
del(a.m_den);
a.m_den.m_val = 1;
}
void normalize(mpq & a) {
if (SYNCH) {
mpz tmp;
gcd(a.m_num, a.m_den, tmp);
if (is_one(tmp)) {
del(tmp);
return;
}
div(a.m_num, tmp, a.m_num);
div(a.m_den, tmp, a.m_den);
del(tmp);
}
else {
gcd(a.m_num, a.m_den, m_n_tmp);
if (is_one(m_n_tmp))
return;
div(a.m_num, m_n_tmp, a.m_num);
div(a.m_den, m_n_tmp, a.m_den);
}
}
void rat_add(mpq const & a, mpq const & b, mpq & c) {
STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";);
if (SYNCH) {
mpz tmp1, tmp2;
mul(a.m_num, b.m_den, tmp1);
mul(b.m_num, a.m_den, tmp2);
mul(a.m_den, b.m_den, c.m_den);
add(tmp1, tmp2, c.m_num);
normalize(c);
del(tmp1);
del(tmp2);
}
else {
mul(a.m_num, b.m_den, m_add_tmp1);
mul(b.m_num, a.m_den, m_add_tmp2);
mul(a.m_den, b.m_den, c.m_den);
add(m_add_tmp1, m_add_tmp2, c.m_num);
normalize(c);
}
STRACE("rat_mpq", tout << to_string(c) << "\n";);
}
void rat_add(mpq const & a, mpz const & b, mpq & c) {
STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";);
if (SYNCH) {
mpz tmp1;
mul(b, a.m_den, tmp1);
set(c.m_den, a.m_den);
add(a.m_num, tmp1, c.m_num);
normalize(c);
del(tmp1);
}
else {
mul(b, a.m_den, m_add_tmp1);
set(c.m_den, a.m_den);
add(a.m_num, m_add_tmp1, c.m_num);
normalize(c);
}
STRACE("rat_mpq", tout << to_string(c) << "\n";);
}
void rat_sub(mpq const & a, mpq const & b, mpq & c) {
STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";);
if (SYNCH) {
mpz tmp1, tmp2;
mul(a.m_num, b.m_den, tmp1);
mul(b.m_num, a.m_den, tmp2);
mul(a.m_den, b.m_den, c.m_den);
sub(tmp1, tmp2, c.m_num);
normalize(c);
del(tmp1);
del(tmp2);
}
else {
mul(a.m_num, b.m_den, m_add_tmp1);
mul(b.m_num, a.m_den, m_add_tmp2);
mul(a.m_den, b.m_den, c.m_den);
sub(m_add_tmp1, m_add_tmp2, c.m_num);
normalize(c);
}
STRACE("rat_mpq", tout << to_string(c) << "\n";);
}
void rat_mul(mpq const & a, mpq const & b, mpq & c) {
STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";);
mul(a.m_num, b.m_num, c.m_num);
mul(a.m_den, b.m_den, c.m_den);
normalize(c);
STRACE("rat_mpq", tout << to_string(c) << "\n";);
}
void rat_mul(mpz const & a, mpq const & b, mpq & c) {
STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";);
mul(a, b.m_num, c.m_num);
set(c.m_den, b.m_den);
normalize(c);
STRACE("rat_mpq", tout << to_string(c) << "\n";);
}
bool rat_lt(mpq const & a, mpq const & b);
public:
typedef mpq numeral;
typedef mpq rational;
typedef mpz integer;
static bool precise() { return true; }
static bool field() { return true; }
mpq_manager();
~mpq_manager();
void reset(mpz & a) { mpz_manager<SYNCH>::reset(a); }
void reset(mpq & a) {
reset(a.m_num);
reset_denominator(a);
}
static bool is_small(mpz const & a) { return mpz_manager<SYNCH>::is_small(a); }
static bool is_small(mpq const & a) { return is_small(a.m_num) && is_small(a.m_den); }
static mpq mk_q(int v) { return mpq(v); }
mpq mk_q(int n, int d) { mpq r; set(r, n, d); return r; }
void del(mpz & a) { mpz_manager<SYNCH>::del(a); }
void del(mpq & a) {
del(a.m_num);
del(a.m_den);
}
void get_numerator(mpq const & a, mpz & n) { set(n, a.m_num); }
void get_denominator(mpq const & a, mpz & d) { set(d, a.m_den); }
void get_numerator(mpq const & a, mpq & n) { get_numerator(a, n.m_num); reset_denominator(n); }
void get_denominator(mpq const & a, mpq & d) { get_denominator(a, d.m_num); reset_denominator(d); }
void neg(mpz & a) { mpz_manager<SYNCH>::neg(a); }
void neg(mpq & a) { mpz_manager<SYNCH>::neg(a.m_num); }
void abs(mpz & a) { mpz_manager<SYNCH>::abs(a); }
void abs(mpq & a) { mpz_manager<SYNCH>::abs(a.m_num); }
static int sign(mpz const & a) { return mpz_manager<SYNCH>::sign(a); }
static int sign(mpq const & a) { return mpz_manager<SYNCH>::sign(a.m_num); }
static bool is_pos(mpz const & a) { return mpz_manager<SYNCH>::is_pos(a); }
static bool is_neg(mpz const & a) { return mpz_manager<SYNCH>::is_neg(a); }
static bool is_zero(mpz const & a) { return mpz_manager<SYNCH>::is_zero(a); }
static bool is_nonpos(mpz const & a) { return mpz_manager<SYNCH>::is_nonpos(a); }
static bool is_nonneg(mpz const & a) { return mpz_manager<SYNCH>::is_nonneg(a); }
static bool is_pos(mpq const & a) { return is_pos(a.m_num); }
static bool is_neg(mpq const & a) { return is_neg(a.m_num); }
static bool is_zero(mpq const & a) { return is_zero(a.m_num); }
static bool is_nonpos(mpq const & a) { return is_nonpos(a.m_num); }
static bool is_nonneg(mpq const & a) { return is_nonneg(a.m_num); }
static bool is_one(mpz const & a) { return mpz_manager<SYNCH>::is_one(a); }
static bool is_one(mpq const & a) { return is_one(a.m_num) && is_one(a.m_den); }
static bool is_minus_one(mpz const & a) { return mpz_manager<SYNCH>::is_minus_one(a); }
static bool is_minus_one(mpq const & a) { return is_minus_one(a.m_num) && is_one(a.m_den); }
void floor(mpq const & a, mpz & f);
void floor(mpq const & a, mpq & f) {
floor(a, f.m_num);
reset_denominator(f);
}
void ceil(mpq const & a, mpz & f);
void ceil(mpq const & a, mpq & f) {
ceil(a, f.m_num);
reset_denominator(f);
}
static bool is_int(mpq const & a) { return is_one(a.m_den); }
std::string to_string(mpq const & a) const;
std::string to_rational_string(numeral const & a) { return to_string(a); }
std::string to_string(mpz const & a) const { return mpz_manager<SYNCH>::to_string(a); }
void display(std::ostream & out, mpz const & a) const { return mpz_manager<SYNCH>::display(out, a); }
void display(std::ostream & out, mpq const & a) const;
void display_smt2(std::ostream & out, mpz const & a, bool decimal) const { return mpz_manager<SYNCH>::display_smt2(out, a, decimal); }
void display_smt2(std::ostream & out, mpq const & a, bool decimal) const;
void display_decimal(std::ostream & out, mpq const & a, unsigned prec);
void add(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::add(a, b, c); }
void add(mpq const & a, mpq const & b, mpq & c) {
STRACE("mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";);
if (is_int(a) && is_int(b)) {
mpz_manager<SYNCH>::add(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
else
rat_add(a, b, c);
STRACE("mpq", tout << to_string(c) << "\n";);
}
void add(mpq const & a, mpz const & b, mpq & c) {
STRACE("mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";);
if (is_int(a)) {
mpz_manager<SYNCH>::add(a.m_num, b, c.m_num);
reset_denominator(c);
}
else {
rat_add(a, b, c);
}
STRACE("mpq", tout << to_string(c) << "\n";);
}
void sub(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::sub(a, b, c); }
void sub(mpq const & a, mpq const & b, mpq & c) {
STRACE("mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";);
if (is_int(a) && is_int(b)) {
mpz_manager<SYNCH>::sub(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
else
rat_sub(a, b, c);
STRACE("mpq", tout << to_string(c) << "\n";);
}
void inc(mpz & a) { mpz_manager<SYNCH>::inc(a); }
void dec(mpz & a) { mpz_manager<SYNCH>::dec(a); }
void inc(mpq & a) { add(a, mpz(1), a); }
void dec(mpq & a) { add(a, mpz(-1), a); }
void mul(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::mul(a, b, c); }
void mul(mpz const & a, mpz const & b, mpq & c) {
mpz_manager<SYNCH>::mul(a, b, c.m_num);
reset_denominator(c);
}
void mul(mpq const & a, mpq const & b, mpq & c) {
STRACE("mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";);
if (is_int(a) && is_int(b)) {
mpz_manager<SYNCH>::mul(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
else
rat_mul(a, b, c);
STRACE("mpq", tout << to_string(c) << "\n";);
}
void mul(mpz const & a, mpq const & b, mpq & c) {
STRACE("mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";);
if (is_int(b)) {
mpz_manager<SYNCH>::mul(a, b.m_num, c.m_num);
reset_denominator(c);
}
else
rat_mul(a, b, c);
STRACE("mpq", tout << to_string(c) << "\n";);
}
void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) {
return mpz_manager<SYNCH>::addmul(a, b, c, d);
}
void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) {
return mpz_manager<SYNCH>::submul(a, b, c, d);
}
// d <- a + b*c
void addmul(mpq const & a, mpq const & b, mpq const & c, mpq & d) {
if (is_one(b)) {
add(a, c, d);
}
else if (is_minus_one(b)) {
sub(a, c, d);
}
else {
if (SYNCH) {
mpq tmp;
mul(b,c,tmp);
add(a,tmp,d);
del(tmp);
}
else {
mul(b,c,m_addmul_tmp);
add(a, m_addmul_tmp, d);
}
}
}
// d <- a + b*c
void addmul(mpq const & a, mpz const & b, mpq const & c, mpq & d) {
if (is_one(b)) {
add(a, c, d);
}
else if (is_minus_one(b)) {
sub(a, c, d);
}
else {
if (SYNCH) {
mpq tmp;
mul(b,c,tmp);
add(a,tmp,d);
del(tmp);
}
else {
mul(b,c,m_addmul_tmp);
add(a, m_addmul_tmp, d);
}
}
}
// d <- a - b*c
void submul(mpq const & a, mpq const & b, mpq const & c, mpq & d) {
if (is_one(b)) {
sub(a, c, d);
}
else if (is_minus_one(b)) {
add(a, c, d);
}
else {
if (SYNCH) {
mpq tmp;
mul(b,c,tmp);
sub(a,tmp,d);
del(tmp);
}
else {
mul(b,c,m_addmul_tmp);
sub(a, m_addmul_tmp, d);
}
}
}
// d <- a - b*c
void submul(mpq const & a, mpz const & b, mpq const & c, mpq & d) {
if (is_one(b)) {
sub(a, c, d);
}
else if (is_minus_one(b)) {
add(a, c, d);
}
else {
if (SYNCH) {
mpq tmp;
mul(b,c,tmp);
sub(a,tmp,d);
del(tmp);
}
else {
mul(b,c,m_addmul_tmp);
sub(a, m_addmul_tmp, d);
}
}
}
void inv(mpq & a) {
SASSERT(!is_zero(a));
if (is_neg(a)) {
neg(a.m_num);
neg(a.m_den);
}
mpz_manager<SYNCH>::swap(a.m_num, a.m_den);
}
void inv(mpq const & a, mpq & b) {
set(b, a);
inv(b);
}
void div(mpq const & a, mpq const & b, mpq & c) {
STRACE("mpq", tout << "[mpq] " << to_string(a) << " / " << to_string(b) << " == ";);
if (&b == &c) {
mpz tmp; // it is not safe to use c.m_num at this point.
mul(a.m_num, b.m_den, tmp);
mul(a.m_den, b.m_num, c.m_den);
set(c.m_num, tmp);
del(tmp);
}
else {
mul(a.m_num, b.m_den, c.m_num);
mul(a.m_den, b.m_num, c.m_den);
}
if (mpz_manager<SYNCH>::is_neg(c.m_den)) {
neg(c.m_num);
neg(c.m_den);
}
normalize(c);
STRACE("mpq", tout << to_string(c) << "\n";);
}
void div(mpq const & a, mpz const & b, mpq & c) {
STRACE("mpq", tout << "[mpq] " << to_string(a) << " / " << to_string(b) << " == ";);
set(c.m_num, a.m_num);
mul(a.m_den, b, c.m_den);
if (mpz_manager<SYNCH>::is_neg(b)) {
neg(c.m_num);
neg(c.m_den);
}
normalize(c);
STRACE("mpq", tout << to_string(c) << "\n";);
}
void acc_div(mpq & a, mpz const & b) {
STRACE("mpq", tout << "[mpq] " << to_string(a) << " / " << to_string(b) << " == ";);
mul(a.m_den, b, a.m_den);
if (mpz_manager<SYNCH>::is_neg(b)) {
neg(a.m_num);
neg(a.m_den);
}
normalize(a);
STRACE("mpq", tout << to_string(a) << "\n";);
}
void machine_div(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::machine_div(a, b, c); }
void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::div(a, b, c); }
void rat_div(mpz const & a, mpz const & b, mpq & c) {
set(c.m_num, a);
set(c.m_den, b);
normalize(c);
}
void machine_idiv(mpq const & a, mpq const & b, mpq & c) {
SASSERT(is_int(a) && is_int(b));
machine_div(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
void machine_idiv(mpq const & a, mpq const & b, mpz & c) {
SASSERT(is_int(a) && is_int(b));
machine_div(a.m_num, b.m_num, c);
}
void idiv(mpq const & a, mpq const & b, mpq & c) {
SASSERT(is_int(a) && is_int(b));
div(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
void idiv(mpq const & a, mpq const & b, mpz & c) {
SASSERT(is_int(a) && is_int(b));
div(a.m_num, b.m_num, c);
}
void rem(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::rem(a, b, c); }
void rem(mpq const & a, mpq const & b, mpq & c) {
SASSERT(is_int(a) && is_int(b));
rem(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
void rem(mpq const & a, mpq const & b, mpz & c) {
SASSERT(is_int(a) && is_int(b));
rem(a.m_num, b.m_num, c);
}
void mod(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::mod(a, b, c); }
void mod(mpq const & a, mpq const & b, mpq & c) {
SASSERT(is_int(a) && is_int(b));
mod(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
void mod(mpq const & a, mpq const & b, mpz & c) {
SASSERT(is_int(a) && is_int(b));
mod(a.m_num, b.m_num, c);
}
static unsigned hash(mpz const & a) { return mpz_manager<SYNCH>::hash(a); }
static unsigned hash(mpq const & a) { return hash(a.m_num); }
bool eq(mpz const & a, mpz const & b) { return mpz_manager<SYNCH>::eq(a, b); }
bool eq(mpq const & a, mpq const & b) {
return eq(a.m_num, b.m_num) && eq(a.m_den, b.m_den);
}
bool lt(mpz const & a, mpz const & b) { return mpz_manager<SYNCH>::lt(a, b); }
bool lt(mpq const & a, mpq const & b) {
if (is_int(a) && is_int(b))
return lt(a.m_num, b.m_num);
else
return rat_lt(a, b);
}
bool neq(mpz const & a, mpz const & b) { return mpz_manager<SYNCH>::neq(a, b); }
bool gt(mpz const & a, mpz const & b) { return mpz_manager<SYNCH>::gt(a, b); }
bool ge(mpz const & a, mpz const & b) { return mpz_manager<SYNCH>::ge(a, b); }
bool le(mpz const & a, mpz const & b) { return mpz_manager<SYNCH>::le(a, b); }
bool neq(mpq const & a, mpq const & b) { return !eq(a, b); }
bool gt(mpq const & a, mpq const & b) { return lt(b, a); }
bool ge(mpq const & a, mpq const & b) { return !lt(a, b); }
bool le(mpq const & a, mpq const & b) { return !lt(b, a); }
void gcd(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::gcd(a, b, c); }
void gcd(unsigned sz, mpz const * as, mpz & g) { mpz_manager<SYNCH>::gcd(sz, as, g); }
void gcd(unsigned sz, mpq const * as, mpq & g);
void gcd(mpq const & a, mpq const & b, mpq & c) {
SASSERT(is_int(a) && is_int(b));
gcd(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
void gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & g) { mpz_manager<SYNCH>::gcd(r1, r2, a, b, g); }
void gcd(mpq const & r1, mpq const & r2, mpq & a, mpq & b, mpq & g) {
SASSERT(is_int(r1) && is_int(r2));
reset_denominator(a);
reset_denominator(b);
reset_denominator(g);
gcd(r1.m_num, r2.m_num, a.m_num, b.m_num, g.m_num);
}
void lcm(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::lcm(a, b, c); }
void lcm(mpq const & a, mpq const & b, mpq & c) {
SASSERT(is_int(a) && is_int(b));
lcm(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
bool divides(mpz const & a, mpz const & b) { return mpz_manager<SYNCH>::divides(a, b); }
bool divides(mpq const & a, mpq const & b) {
SASSERT(is_int(a) && is_int(b));
return divides(a.m_num, b.m_num);
}
void bitwise_or(mpz const & a, mpz const & b, mpz & c) { return mpz_manager<SYNCH>::bitwise_or(a, b, c); }
void bitwise_or(mpq const & a, mpq const & b, mpq & c) {
SASSERT(is_int(a) && is_int(b));
bitwise_or(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
void bitwise_and(mpz const & a, mpz const & b, mpz & c) { return mpz_manager<SYNCH>::bitwise_and(a, b, c); }
void bitwise_and(mpq const & a, mpq const & b, mpq & c) {
SASSERT(is_int(a) && is_int(b));
bitwise_and(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
void bitwise_xor(mpz const & a, mpz const & b, mpz & c) { return mpz_manager<SYNCH>::bitwise_xor(a, b, c); }
void bitwise_xor(mpq const & a, mpq const & b, mpq & c) {
SASSERT(is_int(a) && is_int(b));
bitwise_xor(a.m_num, b.m_num, c.m_num);
reset_denominator(c);
}
void bitwise_not(unsigned sz, mpz const & a, mpz & c) { return mpz_manager<SYNCH>::bitwise_not(sz, a, c); }
void bitwise_not(unsigned sz, mpq const & a, mpq & c) {
SASSERT(is_int(a));
bitwise_not(sz, a.m_num, c.m_num);
reset_denominator(c);
}
void set(mpz & target, mpz const & source) { mpz_manager<SYNCH>::set(target, source); }
void set(mpq & target, mpq const & source) {
set(target.m_num, source.m_num);
set(target.m_den, source.m_den);
}
void set(mpz & a, int val) { mpz_manager<SYNCH>::set(a, val); }
void set(mpq & a, int val) {
set(a.m_num, val);
reset_denominator(a);
}
void set(mpq & a, int n, int d) {
SASSERT(d != 0);
if (d < 0) {
n = -n;
d = -d;
}
set(a.m_num, n);
set(a.m_den, d);
normalize(a);
}
void set(mpq & a, int64 n, uint64 d) {
SASSERT(d != 0);
set(a.m_num, n);
set(a.m_den, d);
normalize(a);
}
void set(mpq & a, mpz const & n, mpz const & d) {
if (is_neg(d)) {
set(a.m_num, n);
set(a.m_den, d);
neg(a.m_num);
neg(a.m_den);
}
else {
set(a.m_num, n);
set(a.m_den, d);
}
normalize(a);
}
void set(mpz & a, unsigned val) { mpz_manager<SYNCH>::set(a, val); }
void set(mpq & a, unsigned val) {
set(a.m_num, val);
reset_denominator(a);
}
void set(mpz & a, char const * val) { mpz_manager<SYNCH>::set(a, val); }
void set(mpq & a, char const * val);
void set(mpz & a, int64 val) { mpz_manager<SYNCH>::set(a, val); }
void set(mpq & a, int64 val) {
set(a.m_num, val);
reset_denominator(a);
}
void set(mpz & a, uint64 val) { mpz_manager<SYNCH>::set(a, val); }
void set(mpq & a, uint64 val) {
set(a.m_num, val);
reset_denominator(a);
}
void set(mpq & a, mpz const & val) {
mpz_manager<SYNCH>::set(a.m_num, val);
reset_denominator(a);
}
void set(mpz & a, unsigned sz, digit_t const * digits) { mpz_manager<SYNCH>::set(a, sz, digits); }
void set(mpq & a, unsigned sz, digit_t const * digits) {
mpz_manager<SYNCH>::set(a.m_num, sz, digits);
reset_denominator(a);
}
void swap(mpz & a, mpz & b) { mpz_manager<SYNCH>::swap(a, b); }
void swap(mpq & a, mpq & b) {
swap(a.m_num, b.m_num);
swap(a.m_den, b.m_den);
}
void swap_numerator(mpz & a, mpq & b) {
swap(a, b.m_num);
}
bool is_uint64(mpz const & a) const { return mpz_manager<SYNCH>::is_uint64(a); }
bool is_int64(mpz const & a) const { return mpz_manager<SYNCH>::is_int64(a); }
uint64 get_uint64(mpz const & a) const { return mpz_manager<SYNCH>::get_uint64(a); }
int64 get_int64(mpz const & a) const { return mpz_manager<SYNCH>::get_int64(a); }
bool is_uint64(mpq const & a) const { return is_int(a) && is_uint64(a.m_num); }
bool is_int64(mpq const & a) const { return is_int(a) && is_int64(a.m_num); }
uint64 get_uint64(mpq const & a) const { SASSERT(is_uint64(a)); return get_uint64(a.m_num); }
int64 get_int64(mpq const & a) const { SASSERT(is_int64(a)); return get_int64(a.m_num); }
double get_double(mpz const & a) const { return mpz_manager<SYNCH>::get_double(a); }
double get_double(mpq const & a) const;
void power(mpz const & a, unsigned p, mpz & b) { mpz_manager<SYNCH>::power(a, p, b); }
void power(mpq const & a, unsigned p, mpq & b);
bool is_power_of_two(mpz const & a, unsigned & shift) { return mpz_manager<SYNCH>::is_power_of_two(a, shift); }
bool is_power_of_two(mpq const & a, unsigned & shift) { return is_int(a) && is_power_of_two(a.m_num, shift); }
unsigned bitsize(mpz const & a) { return mpz_manager<SYNCH>::bitsize(a); }
unsigned bitsize(mpq const & a) { return is_int(a) ? bitsize(a.m_num) : bitsize(a.m_num) + bitsize(a.m_den); }
/**
\brief Return true if the number is a perfect square, and
store the square root in 'root'.
If the number n is positive and the result is false, then
root will contain the smallest integer r such that r*r > n.
*/
bool is_perfect_square(mpz const & a, mpz & r) { return mpz_manager<SYNCH>::is_perfect_square(a, r); }
/**
\brief Return true if the numerator and denominators are perfect squares.
Store the square root in root.
If the result is false, then the value of root should be ignored.
*/
bool is_perfect_square(mpq const & a, mpq & r) {
if (is_int(a)) {
reset_denominator(r);
return is_perfect_square(a.m_num, r.m_num);
}
if (is_perfect_square(a.m_num, r.m_num) && is_perfect_square(a.m_den, r.m_den)) {
normalize(r);
return true;
}
return false;
}
bool root(mpz & a, unsigned n) { return mpz_manager<SYNCH>::root(a, n); }
bool root(mpz const & a, unsigned n, mpz & r) { return mpz_manager<SYNCH>::root(a, n, r); }
/**
\brief Return true if n-th root of a is rational, and store result in r.
*/
bool root(mpq const & a, unsigned n, mpq & r);
/**
\brief Return the biggest k s.t. 2^k <= a.
\remark Return 0 if a is not positive.
*/
unsigned prev_power_of_two(mpz const & a) { return mpz_manager<SYNCH>::prev_power_of_two(a); }
unsigned prev_power_of_two(mpq const & a);
bool is_int_perfect_square(mpq const & a, mpq & r) {
SASSERT(is_int(a));
reset_denominator(r);
return is_perfect_square(a.m_num, r.m_num);
}
bool is_even(mpz const & a) { return mpz_manager<SYNCH>::is_even(a); }
bool is_even(mpq const & a) { return is_int(a) && is_even(a.m_num); }
};
typedef mpq_manager<true> synch_mpq_manager;
typedef mpq_manager<false> unsynch_mpq_manager;
typedef _scoped_numeral<unsynch_mpq_manager> scoped_mpq;
typedef _scoped_numeral<synch_mpq_manager> scoped_synch_mpq;
typedef _scoped_numeral_vector<unsynch_mpq_manager> scoped_mpq_vector;
#endif /* _MPQ_H_ */

42
src/util/mpq_inf.cpp Normal file
View file

@ -0,0 +1,42 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
mpq_inf.cpp
Abstract:
MPQ numbers with infinitesimals
Author:
Leonardo de Moura (leonardo) 2011-06-28
Revision History:
--*/
#include"mpq_inf.h"
template<bool SYNCH>
std::string mpq_inf_manager<SYNCH>::to_string(mpq_inf const & a) const {
if (m.is_zero(a.second))
return m.to_string(a.first);
std::string s = "(";
s += m.to_string(a.first);
if (m.is_neg(a.second))
s += " -e*";
else
s += " +e*";
mpq tmp;
m.set(tmp, a.second);
m.abs(tmp);
s += m.to_string(tmp);
m.del(tmp);
s += ")";
return s;
}
template class mpq_inf_manager<true>;
template class mpq_inf_manager<false>;

256
src/util/mpq_inf.h Normal file
View file

@ -0,0 +1,256 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
mpq_inf.h
Abstract:
MPQ numbers with infinitesimals
Author:
Leonardo de Moura (leonardo) 2011-06-28
Revision History:
--*/
#ifndef _MPQ_INF_H_
#define _MPQ_INF_H_
#include"mpq.h"
#include"hash.h"
#include"params.h"
typedef std::pair<mpq, mpq> mpq_inf;
template<bool SYNCH = true>
class mpq_inf_manager {
mpq_manager<SYNCH> & m;
double m_inf;
public:
typedef mpq_inf numeral;
mpq_inf_manager(mpq_manager<SYNCH> & _m, params_ref const & p = params_ref()):m(_m) {
updt_params(p);
}
void updt_params(params_ref const & p) {
m_inf = p.get_double(":infinitesimal-as-double", 0.00001);
}
enum inf_kind { NEG=-1, ZERO, POS };
void reset(mpq_inf & a) {
m.reset(a.first);
m.reset(a.second);
}
unsigned hash(mpq_inf const & a) const { return hash_u_u(m.hash(a.first), m.hash(a.second)); }
void del(mpq_inf & a) {
m.del(a.first);
m.del(a.second);
}
void swap(mpq_inf & a, mpq_inf & b) {
m.swap(a.first, b.first);
m.swap(a.second, b.second);
}
void set(mpq_inf & a, mpq_inf const & b) {
m.set(a.first, b.first);
m.set(a.second, b.second);
}
void set(mpq_inf & a, mpq const & r) {
m.set(a.first, r);
m.reset(a.second);
}
void set(mpq_inf & a, mpq const & r, inf_kind k) {
m.set(a.first, r);
switch (k) {
case NEG: m.set(a.second, -1); break;
case ZERO: m.reset(a.second); break;
case POS: m.set(a.second, 1); break;
}
}
void set(mpq_inf & a, mpq const & r, mpq const & i) {
m.set(a.first, r);
m.set(a.second, i);
}
bool is_int(mpq_inf const & a) const { return m.is_int(a.first) && m.is_zero(a.second); }
bool is_rational(mpq_inf const & a) const { return m.is_zero(a.second); }
void get_rational(mpq_inf const & a, mpq & r) { m.set(r, a.first); }
void get_infinitesimal(mpq_inf const & a, mpq & r) { m.set(r, a.second); }
double get_double(mpq_inf const & a) {
double r = m.get_double(a.first);
if (m.is_pos(a.second))
return r + m_inf;
else if (m.is_neg(a.second))
return r - m_inf;
else
return r;
}
bool is_zero(mpq_inf const & a) const {
return m.is_zero(a.first) && m.is_zero(a.second);
}
bool eq(mpq_inf const & a, mpq_inf const & b) const {
return m.eq(a.first, b.first) && m.eq(a.second, b.second);
}
bool eq(mpq_inf const & a, mpq const & b) const {
return m.eq(a.first, b) && m.is_zero(a.second);
}
bool eq(mpq_inf const & a, mpq const & b, inf_kind k) const {
if (!m.eq(a.first, b))
return false;
switch (k) {
case NEG: return m.is_minus_one(a.second);
case ZERO: return m.is_zero(a.second);
case POS: return m.is_one(a.second);
}
UNREACHABLE();
return false;
}
bool lt(mpq_inf const & a, mpq_inf const & b) const {
return m.lt(a.first, b.first) || (m.lt(a.second, b.second) && m.eq(a.first, b.first));
}
bool lt(mpq_inf const & a, mpq const & b) const {
return m.lt(a.first, b) || (m.is_neg(a.second) && m.eq(a.first, b));
}
bool lt(mpq_inf const & a, mpq const & b, inf_kind k) const {
if (m.lt(a.first, b))
return true;
if (m.eq(a.first, b)) {
switch (k) {
case NEG: return m.lt(a.second, mpq(-1));
case ZERO: return m.is_neg(a.second);
case POS: return m.lt(a.second, mpq(1));
}
UNREACHABLE();
}
return false;
}
bool gt(mpq_inf const & a, mpq_inf const & b) const { return lt(b, a); }
bool gt(mpq_inf const & a, mpq const & b) const {
return m.gt(a.first, b) || (m.is_pos(a.second) && m.eq(a.first, b));
}
bool gt(mpq_inf const & a, mpq const & b, inf_kind k) const {
if (m.gt(a.first, b))
return true;
if (m.eq(a.first, b)) {
switch (k) {
case NEG: return m.gt(a.second, mpq(-1));
case ZERO: return m.is_pos(a.second);
case POS: return m.gt(a.second, mpq(1));
}
UNREACHABLE();
}
return false;
}
bool le(mpq_inf const & a, mpq_inf const & b) const { return !gt(a, b); }
bool le(mpq_inf const & a, mpq const & b) const { return !gt(a, b); }
bool le(mpq_inf const & a, mpq const & b, inf_kind k) const { return !gt(a, b, k); }
bool ge(mpq_inf const & a, mpq_inf const & b) const { return !lt(a, b); }
bool ge(mpq_inf const & a, mpq const & b) const { return !lt(a, b); }
bool ge(mpq_inf const & a, mpq const & b, inf_kind k) const { return !lt(a, b, k); }
void add(mpq_inf const & a, mpq_inf const & b, mpq_inf & c) {
m.add(a.first, b.first, c.first);
m.add(a.second, b.second, c.second);
}
void sub(mpq_inf const & a, mpq_inf const & b, mpq_inf & c) {
m.sub(a.first, b.first, c.first);
m.sub(a.second, b.second, c.second);
}
void add(mpq_inf const & a, mpq const & b, mpq_inf & c) {
m.add(a.first, b, c.first);
m.set(c.second, a.second);
}
void sub(mpq_inf const & a, mpq const & b, mpq_inf & c) {
m.sub(a.first, b, c.first);
m.set(c.second, a.second);
}
void mul(mpq_inf const & a, mpq const & b, mpq_inf & c) {
m.mul(a.first, b, c.first);
m.mul(a.second, b, c.second);
}
void mul(mpq_inf const & a, mpz const & b, mpq_inf & c) {
m.mul(b, a.first, c.first);
m.mul(b, a.second, c.second);
}
void inc(mpq_inf & a) {
m.inc(a.first);
}
void dec(mpq_inf & a) {
m.dec(a.first);
}
void neg(mpq_inf & a) {
m.neg(a.first);
m.neg(a.second);
}
void ceil(mpq_inf const & a, mpq & b) {
if (m.is_int(a.first)) {
// special cases for k - delta*epsilon where k is an integer
if (m.is_pos(a.first))
m.add(a.first, mpq(1), b); // ceil(k + delta*epsilon) --> k+1
else
m.set(b, a.first);
}
else {
m.ceil(a.first, b);
}
}
void floor(mpq_inf const & a, mpq & b) {
if (m.is_int(a.first)) {
if (m.is_neg(a.first))
m.sub(a.first, mpq(1), b); // floor(k - delta*epsilon) --> k-1
else
m.set(b, a.first);
}
else {
m.floor(a.first, b);
}
}
std::string to_string(mpq_inf const & a) const;
};
typedef mpq_inf_manager<true> synch_mpq_inf_manager;
typedef mpq_inf_manager<false> unsynch_mpq_inf_manager;
#endif

2116
src/util/mpz.cpp Normal file

File diff suppressed because it is too large Load diff

798
src/util/mpz.h Normal file
View file

@ -0,0 +1,798 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
mpz.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2010-06-17.
Revision History:
--*/
#ifndef _MPZ_H_
#define _MPZ_H_
#include<limits.h>
#include<string>
#include"util.h"
#include"small_object_allocator.h"
#include"trace.h"
#include"scoped_numeral.h"
#include"scoped_numeral_vector.h"
#include"z3_omp.h"
unsigned u_gcd(unsigned u, unsigned v);
uint64 u64_gcd(uint64 u, uint64 v);
#ifdef _MP_GMP
typedef unsigned digit_t;
#endif
#ifdef _MSC_VER
#pragma warning(disable : 4200)
#endif
template<bool SYNCH> class mpz_manager;
template<bool SYNCH> class mpq_manager;
#if !defined(_MP_GMP) && !defined(_MP_MSBIGNUM) && !defined(_MP_INTERNAL)
#ifdef _WINDOWS
#define _MP_INTERNAL
#else
#define _MP_GMP
#endif
#endif
#if defined(_MP_MSBIGNUM)
typedef size_t digit_t;
#elif defined(_MP_INTERNAL)
typedef unsigned int digit_t;
#endif
#ifndef _MP_GMP
class mpz_cell {
unsigned m_size;
unsigned m_capacity;
digit_t m_digits[0];
friend class mpz_manager<true>;
friend class mpz_manager<false>;
};
#else
#include<gmp.h>
#endif
/**
\brief Multi-precision integer.
If m_ptr == 0, the it is a small number and the value is stored at m_val.
Otherwise, m_val contains the sign (-1 negative, 1 positive), and m_ptr points to a mpz_cell that
store the value. <<< This last statement is true only in Windows.
*/
class mpz {
int m_val;
#ifndef _MP_GMP
mpz_cell * m_ptr;
#else
mpz_t * m_ptr;
#endif
friend class mpz_manager<true>;
friend class mpz_manager<false>;
friend class mpq_manager<true>;
friend class mpq_manager<false>;
friend class mpq;
friend class mpbq;
friend class mpbq_manager;
mpz & operator=(mpz const & other) { UNREACHABLE(); return *this; }
public:
mpz(int v):m_val(v), m_ptr(0) {}
mpz():m_val(0), m_ptr(0) {}
void swap(mpz & other) {
std::swap(m_val, other.m_val);
std::swap(m_ptr, other.m_ptr);
}
};
inline void swap(mpz & m1, mpz & m2) { m1.swap(m2); }
template<bool SYNCH = true>
class mpz_manager {
small_object_allocator m_allocator;
omp_nest_lock_t m_lock;
#define MPZ_BEGIN_CRITICAL() if (SYNCH) omp_set_nest_lock(&m_lock);
#define MPZ_END_CRITICAL() if (SYNCH) omp_unset_nest_lock(&m_lock);
#ifndef _MP_GMP
unsigned m_init_cell_capacity;
mpz_cell * m_tmp[2];
mpz_cell * m_arg[2];
mpz m_int_min;
static unsigned cell_size(unsigned capacity) { return sizeof(mpz_cell) + sizeof(digit_t) * capacity; }
mpz_cell * allocate(unsigned capacity) {
SASSERT(capacity >= m_init_cell_capacity);
mpz_cell * cell = reinterpret_cast<mpz_cell *>(m_allocator.allocate(cell_size(capacity)));
cell->m_capacity = capacity;
return cell;
}
// make sure that n is a big number and has capacity equal to at least c.
void allocate_if_needed(mpz & n, unsigned c) {
if (c < m_init_cell_capacity)
c = m_init_cell_capacity;
if (is_small(n)) {
n.m_val = 1;
n.m_ptr = allocate(c);
n.m_ptr->m_capacity = c;
}
else if (capacity(n) < c) {
deallocate(n.m_ptr);
n.m_val = 1;
n.m_ptr = allocate(c);
n.m_ptr->m_capacity = c;
}
}
void deallocate(mpz_cell * ptr) {
m_allocator.deallocate(cell_size(ptr->m_capacity), ptr);
}
/**
\brief Make sure that m_tmp[IDX] can hold the given number of digits
*/
template<int IDX>
void ensure_tmp_capacity(unsigned capacity) {
if (m_tmp[IDX]->m_capacity >= capacity)
return;
deallocate(m_tmp[IDX]);
unsigned new_capacity = (3 * capacity + 1) >> 1;
m_tmp[IDX] = allocate(new_capacity);
SASSERT(m_tmp[IDX]->m_capacity >= capacity);
}
// Expand capacity of a while preserving its content.
void ensure_capacity(mpz & a, unsigned sz);
void normalize(mpz & a);
#else
// GMP code
mpz_t m_tmp, m_tmp2;
mpz_t m_two32;
mpz_t * m_arg[2];
mpz_t m_uint64_max;
mpz_t m_int64_max;
mpz_t * allocate() {
mpz_t * cell = reinterpret_cast<mpz_t*>(m_allocator.allocate(sizeof(mpz_t)));
mpz_init(*cell);
return cell;
}
void deallocate(mpz_t * ptr) { mpz_clear(*ptr); m_allocator.deallocate(sizeof(mpz_t), ptr); }
#endif
mpz m_two64;
/**
\brief Set \c a with the value stored at m_tmp[IDX], and the given sign.
\c sz is an overapproximation of the the size of the number stored at \c tmp.
*/
template<int IDX>
void set(mpz & a, int sign, unsigned sz);
static int64 i64(mpz const & a) { return static_cast<int64>(a.m_val); }
void set_big_i64(mpz & c, int64 v);
void set_i64(mpz & c, int64 v) {
if (v >= INT_MIN && v <= INT_MAX) {
del(c);
c.m_val = static_cast<int>(v);
}
else {
MPZ_BEGIN_CRITICAL();
set_big_i64(c, v);
MPZ_END_CRITICAL();
}
}
void set_big_ui64(mpz & c, uint64 v);
#ifndef _MP_GMP
static unsigned capacity(mpz const & c) { return c.m_ptr->m_capacity; }
static unsigned size(mpz const & c) { return c.m_ptr->m_size; }
static digit_t * digits(mpz const & c) { return c.m_ptr->m_digits; }
template<int IDX>
void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell) {
if (is_small(a)) {
if (a.m_val == INT_MIN) {
sign = -1;
cell = m_int_min.m_ptr;
}
else {
cell = m_arg[IDX];
SASSERT(cell->m_size == 1);
if (a.m_val < 0) {
sign = -1;
cell->m_digits[0] = -a.m_val;
}
else {
sign = 1;
cell->m_digits[0] = a.m_val;
}
}
}
else {
sign = a.m_val;
cell = a.m_ptr;
}
}
#else
// GMP code
template<int IDX>
void get_arg(mpz const & a, mpz_t * & result) {
if (is_small(a)) {
result = m_arg[IDX];
mpz_set_si(*result, a.m_val);
}
else {
result = a.m_ptr;
}
}
void mk_big(mpz & a) {
if (a.m_ptr == 0)
a.m_ptr = allocate();
}
#endif
#ifndef _MP_GMP
template<bool SUB>
void big_add_sub(mpz const & a, mpz const & b, mpz & c);
#endif
void big_add(mpz const & a, mpz const & b, mpz & c);
void big_sub(mpz const & a, mpz const & b, mpz & c);
void big_mul(mpz const & a, mpz const & b, mpz & c);
void big_set(mpz & target, mpz const & source);
#ifndef _MP_GMP
#define QUOT_ONLY 0
#define REM_ONLY 1
#define QUOT_AND_REM 2
#define qr_mode int
template<qr_mode MODE>
void quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r);
#endif
void big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r);
void big_div(mpz const & a, mpz const & b, mpz & c);
void big_rem(mpz const & a, mpz const & b, mpz & c);
int big_compare(mpz const & a, mpz const & b);
unsigned size_info(mpz const & a);
struct sz_lt;
public:
static bool precise() { return true; }
static bool field() { return false; }
typedef mpz numeral;
mpz_manager();
~mpz_manager();
static bool is_small(mpz const & a) { return a.m_ptr == 0; }
static mpz mk_z(int val) { return mpz(val); }
void del(mpz & a) {
if (a.m_ptr != 0) {
MPZ_BEGIN_CRITICAL();
deallocate(a.m_ptr);
MPZ_END_CRITICAL();
a.m_ptr = 0;
}
}
void add(mpz const & a, mpz const & b, mpz & c) {
STRACE("mpz", tout << "[mpz] " << to_string(a) << " + " << to_string(b) << " == ";);
if (is_small(a) && is_small(b)) {
set_i64(c, i64(a) + i64(b));
}
else {
MPZ_BEGIN_CRITICAL();
big_add(a, b, c);
MPZ_END_CRITICAL();
}
STRACE("mpz", tout << to_string(c) << "\n";);
}
void sub(mpz const & a, mpz const & b, mpz & c) {
STRACE("mpz", tout << "[mpz] " << to_string(a) << " - " << to_string(b) << " == ";);
if (is_small(a) && is_small(b)) {
set_i64(c, i64(a) - i64(b));
}
else {
MPZ_BEGIN_CRITICAL();
big_sub(a, b, c);
MPZ_END_CRITICAL();
}
STRACE("mpz", tout << to_string(c) << "\n";);
}
void inc(mpz & a) { add(a, mpz(1), a); }
void dec(mpz & a) { add(a, mpz(-1), a); }
void mul(mpz const & a, mpz const & b, mpz & c) {
STRACE("mpz", tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";);
if (is_small(a) && is_small(b)) {
set_i64(c, i64(a) * i64(b));
}
else {
MPZ_BEGIN_CRITICAL();
big_mul(a, b, c);
MPZ_END_CRITICAL();
}
STRACE("mpz", tout << to_string(c) << "\n";);
}
// d <- a + b*c
void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) {
if (is_one(b)) {
add(a, c, d);
}
else if (is_minus_one(b)) {
sub(a, c, d);
}
else {
mpz tmp;
mul(b,c,tmp);
add(a,tmp,d);
del(tmp);
}
}
// d <- a + b*c
void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) {
if (is_one(b)) {
sub(a, c, d);
}
else if (is_minus_one(b)) {
add(a, c, d);
}
else {
mpz tmp;
mul(b,c,tmp);
sub(a,tmp,d);
del(tmp);
}
}
void machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) {
STRACE("mpz", tout << "[mpz-ext] divrem(" << to_string(a) << ", " << to_string(b) << ") == ";);
if (is_small(a) && is_small(b)) {
int64 _a = i64(a);
int64 _b = i64(b);
set_i64(q, _a / _b);
set_i64(r, _a % _b);
}
else {
MPZ_BEGIN_CRITICAL();
big_div_rem(a, b, q, r);
MPZ_END_CRITICAL();
}
STRACE("mpz", tout << "(" << to_string(q) << ", " << to_string(r) << ")\n";);
}
void machine_div(mpz const & a, mpz const & b, mpz & c) {
STRACE("mpz", tout << "[mpz-ext] machine-div(" << to_string(a) << ", " << to_string(b) << ") == ";);
if (is_small(a) && is_small(b)) {
set_i64(c, i64(a) / i64(b));
}
else {
MPZ_BEGIN_CRITICAL();
big_div(a, b, c);
MPZ_END_CRITICAL();
}
STRACE("mpz", tout << to_string(c) << "\n";);
}
void rem(mpz const & a, mpz const & b, mpz & c) {
STRACE("mpz", tout << "[mpz-ext] rem(" << to_string(a) << ", " << to_string(b) << ") == ";);
if (is_small(a) && is_small(b)) {
set_i64(c, i64(a) % i64(b));
}
else {
MPZ_BEGIN_CRITICAL();
big_rem(a, b, c);
MPZ_END_CRITICAL();
}
STRACE("mpz", tout << to_string(c) << "\n";);
}
void div(mpz const & a, mpz const & b, mpz & c) {
STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";);
if (is_neg(a)) {
mpz tmp;
machine_div_rem(a, b, c, tmp);
if (!is_zero(tmp)) {
if (is_neg(b))
add(c, mk_z(1), c);
else
sub(c, mk_z(1), c);
}
del(tmp);
}
else {
machine_div(a, b, c);
}
STRACE("mpz", tout << to_string(c) << "\n";);
}
void mod(mpz const & a, mpz const & b, mpz & c) {
STRACE("mpz", tout << "[mpz-ext] mod(" << to_string(a) << ", " << to_string(b) << ") == ";);
rem(a, b, c);
if (is_neg(c)) {
if (is_pos(b))
add(c, b, c);
else
sub(c, b, c);
}
STRACE("mpz", tout << to_string(c) << "\n";);
}
void neg(mpz & a) {
STRACE("mpz", tout << "[mpz] 0 - " << to_string(a) << " == ";);
if (is_small(a) && a.m_val == INT_MIN) {
// neg(INT_MIN) is not a small int
set_big_i64(a, - static_cast<long long>(INT_MIN));
return;
}
#ifndef _MP_GMP
a.m_val = -a.m_val;
#else
if (is_small(a)) {
a.m_val = -a.m_val;
}
else {
mpz_neg(*a.m_ptr, *a.m_ptr);
}
#endif
STRACE("mpz", tout << to_string(a) << "\n";);
}
void abs(mpz & a) {
if (is_small(a)) {
if (a.m_val < 0) {
if (a.m_val == INT_MIN) {
// abs(INT_MIN) is not a small int
set_big_i64(a, - static_cast<long long>(INT_MIN));
}
else
a.m_val = -a.m_val;
}
}
else {
#ifndef _MP_GMP
a.m_val = 1;
#else
mpz_abs(*a.m_ptr, *a.m_ptr);
#endif
}
}
static bool is_pos(mpz const & a) {
#ifndef _MP_GMP
return a.m_val > 0;
#else
if (is_small(a))
return a.m_val > 0;
else
return mpz_sgn(*a.m_ptr) > 0;
#endif
}
static bool is_neg(mpz const & a) {
#ifndef _MP_GMP
return a.m_val < 0;
#else
if (is_small(a))
return a.m_val < 0;
else
return mpz_sgn(*a.m_ptr) < 0;
#endif
}
static bool is_zero(mpz const & a) {
#ifndef _MP_GMP
return a.m_val == 0;
#else
if (is_small(a))
return a.m_val == 0;
else
return mpz_sgn(*a.m_ptr) == 0;
#endif
}
static int sign(mpz const & a) {
#ifndef _MP_GMP
return a.m_val;
#else
if (is_small(a))
return a.m_val;
else
return mpz_sgn(*a.m_ptr);
#endif
}
static bool is_nonpos(mpz const & a) { return !is_pos(a); }
static bool is_nonneg(mpz const & a) { return !is_neg(a); }
bool eq(mpz const & a, mpz const & b) {
if (is_small(a) && is_small(b)) {
return a.m_val == b.m_val;
}
else {
MPZ_BEGIN_CRITICAL();
bool res = big_compare(a, b) == 0;
MPZ_END_CRITICAL();
return res;
}
}
bool lt(mpz const & a, mpz const & b) {
if (is_small(a) && is_small(b)) {
return a.m_val < b.m_val;
}
else {
MPZ_BEGIN_CRITICAL();
bool res = big_compare(a, b) < 0;
MPZ_END_CRITICAL();
return res;
}
}
bool neq(mpz const & a, mpz const & b) { return !eq(a, b); }
bool gt(mpz const & a, mpz const & b) { return lt(b, a); }
bool ge(mpz const & a, mpz const & b) { return !lt(a, b); }
bool le(mpz const & a, mpz const & b) { return !lt(b, a); }
void gcd(mpz const & a, mpz const & b, mpz & c);
void gcd(unsigned sz, mpz const * as, mpz & g);
/**
\brief Extended Euclid:
r1*a + r2*b = g
*/
void gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & g);
void lcm(mpz const & a, mpz const & b, mpz & c);
/**
\brief Return true if a | b
*/
bool divides(mpz const & a, mpz const & b);
// not a field
void inv(mpz & a) {
SASSERT(false);
}
void bitwise_or(mpz const & a, mpz const & b, mpz & c);
void bitwise_and(mpz const & a, mpz const & b, mpz & c);
void bitwise_xor(mpz const & a, mpz const & b, mpz & c);
void bitwise_not(unsigned sz, mpz const & a, mpz & c);
void set(mpz & target, mpz const & source) {
if (is_small(source)) {
del(target);
target.m_val = source.m_val;
}
else {
MPZ_BEGIN_CRITICAL();
big_set(target, source);
MPZ_END_CRITICAL();
}
}
void set(mpz & a, int val) {
del(a);
a.m_val = val;
}
void set(mpz & a, unsigned val) {
if (val <= INT_MAX)
set(a, static_cast<int>(val));
else
set(a, static_cast<int64>(static_cast<uint64>(val)));
}
void set(mpz & a, char const * val);
void set(mpz & a, int64 val) {
set_i64(a, val);
}
void set(mpz & a, uint64 val) {
if (val < INT_MAX) {
del(a);
a.m_val = static_cast<int>(val);
}
else {
MPZ_BEGIN_CRITICAL();
set_big_ui64(a, val);
MPZ_END_CRITICAL();
}
}
void set(mpz & target, unsigned sz, digit_t const * digits);
void reset(mpz & a) {
del(a);
a.m_val = 0;
}
void swap(mpz & a, mpz & b) {
std::swap(a.m_val, b.m_val);
std::swap(a.m_ptr, b.m_ptr);
}
bool is_uint64(mpz const & a) const;
bool is_int64(mpz const & a) const;
uint64 get_uint64(mpz const & a) const;
int64 get_int64(mpz const & a) const;
double get_double(mpz const & a) const;
std::string to_string(mpz const & a) const;
void display(std::ostream & out, mpz const & a) const;
/**
\brief Display mpz number in SMT 2.0 format.
If decimal == true, then ".0" is appended.
*/
void display_smt2(std::ostream & out, mpz const & a, bool decimal) const;
static unsigned hash(mpz const & a);
static bool is_one(mpz const & a) {
#ifndef _MP_GMP
return is_small(a) && a.m_val == 1;
#else
if (is_small(a))
return a.m_val == 1;
return mpz_cmp_si(*a.m_ptr, 1) == 0;
#endif
}
static bool is_minus_one(mpz const & a) {
#ifndef _MP_GMP
return is_small(a) && a.m_val == -1;
#else
if (is_small(a))
return a.m_val == -1;
return mpz_cmp_si(*a.m_ptr, -1) == 0;
#endif
}
void power(mpz const & a, unsigned p, mpz & b);
bool is_power_of_two(mpz const & a);
bool is_power_of_two(mpz const & a, unsigned & shift);
void machine_div2k(mpz & a, unsigned k);
void machine_div2k(mpz const & a, unsigned k, mpz & r) { set(r, a); machine_div2k(r, k); }
void mul2k(mpz & a, unsigned k);
void mul2k(mpz const & a, unsigned k, mpz & r) { set(r, a); mul2k(r, k); }
/**
\brief Return largest k s.t. n is a multiple of 2^k
*/
unsigned power_of_two_multiple(mpz const & n);
/**
\brief Return the position of the most significant bit.
Return 0 if the number is negative
*/
unsigned log2(mpz const & n);
/**
\brief log2(-n)
Return 0 if the number is nonegative
*/
unsigned mlog2(mpz const & n);
/**
\brief Return the bit-size of n. This method is mainly used for collecting statistics.
*/
unsigned bitsize(mpz const & n);
/**
\brief Return true if the number is a perfect square, and
store the square root in 'root'.
If the number n is positive and the result is false, then
root will contain the smallest integer r such that r*r > n.
*/
bool is_perfect_square(mpz const & a, mpz & root);
/**
\brief Return the biggest k s.t. 2^k <= a.
\remark Return 0 if a is not positive.
*/
unsigned prev_power_of_two(mpz const & a) { return log2(a); }
/**
\brief Return true if a^{1/n} is an integer, and store the result in a.
Otherwise return false, and update a with the smallest
integer r such that r*r > n.
\remark This method assumes that if n is even, then a is nonegative
*/
bool root(mpz & a, unsigned n);
bool root(mpz const & a, unsigned n, mpz & r) { set(r, a); return root(r, n); }
bool is_even(mpz const & a) {
if (is_small(a))
return !(a.m_val & 0x1);
#ifndef _MP_GMP
return !(0x1 & digits(a)[0]);
#else
return mpz_even_p(*a.m_ptr);
#endif
}
bool is_odd(mpz const & n) { return !is_even(n); }
// Store the digits of n into digits, and return the sign.
bool decompose(mpz const & n, svector<digit_t> & digits);
};
typedef mpz_manager<true> synch_mpz_manager;
typedef mpz_manager<false> unsynch_mpz_manager;
typedef _scoped_numeral<unsynch_mpz_manager> scoped_mpz;
typedef _scoped_numeral<synch_mpz_manager> scoped_synch_mpz;
typedef _scoped_numeral_vector<unsynch_mpz_manager> scoped_mpz_vector;
#endif /* _MPZ_H_ */

285
src/util/mpzzp.h Normal file
View file

@ -0,0 +1,285 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
mpzzp.h
Abstract:
Combines Z ring, GF(p) finite field, and Z_p ring (when p is not a prime)
in a single manager;
That is, the manager may be dynamically configured
to be Z Ring, GF(p), etc.
Author:
Leonardo 2012-01-17.
Revision History:
This code is based on mpzp.h.
In the future, it will replace it.
--*/
#ifndef _MPZZP_H_
#define _MPZZP_H_
#include "mpz.h"
class mpzzp_manager {
typedef unsynch_mpz_manager numeral_manager;
numeral_manager & m_manager;
bool m_z;
// instead the usual [0..p) we will keep the numbers in [lower, upper]
mpz m_p, m_lower, m_upper;
bool m_p_prime;
mpz m_inv_tmp1, m_inv_tmp2, m_inv_tmp3;
mpz m_div_tmp;
bool is_p_normalized_core(mpz const & x) const {
return m().ge(x, m_lower) && m().le(x, m_upper);
}
void setup_p() {
SASSERT(m().is_pos(m_p) && !m().is_one(m_p));
bool even = m().is_even(m_p);
m().div(m_p, 2, m_upper);
m().set(m_lower, m_upper);
m().neg(m_lower);
if (even) {
m().inc(m_lower);
}
TRACE("mpzzp", tout << "lower: " << m_manager.to_string(m_lower) << ", upper: " << m_manager.to_string(m_upper) << "\n";);
}
void p_normalize_core(mpz & x) {
SASSERT(!m_z);
m().rem(x, m_p, x);
if (m().gt(x, m_upper)) {
m().sub(x, m_p, x);
} else {
if (m().lt(x, m_lower)) {
m().add(x, m_p, x);
}
}
SASSERT(is_p_normalized(x));
}
public:
typedef mpz numeral;
static bool precise() { return true; }
bool field() { return !m_z && m_p_prime; }
bool finite() const { return !m_z; }
bool modular() const { return !m_z; }
mpzzp_manager(numeral_manager & _m):
m_manager(_m),
m_z(true) {
}
mpzzp_manager(numeral_manager & _m, mpz const & p, bool prime = true):
m_manager(_m),
m_z(false) {
m().set(m_p, p);
setup_p();
}
mpzzp_manager(numeral_manager & _m, uint64 p, bool prime = true):
m_manager(_m),
m_z(false) {
m().set(m_p, p);
setup_p();
}
~mpzzp_manager() {
m().del(m_p);
m().del(m_lower);
m().del(m_upper);
m().del(m_inv_tmp1);
m().del(m_inv_tmp2);
m().del(m_inv_tmp3);
m().del(m_div_tmp);
}
bool is_p_normalized(mpz const & x) const {
return m_z || is_p_normalized_core(x);
}
void p_normalize(mpz & x) {
if (!m_z)
p_normalize_core(x);
SASSERT(is_p_normalized(x));
}
numeral_manager & m() const { return m_manager; }
mpz const & p() const { return m_p; }
void set_z() { m_z = true; }
void set_zp(mpz const & new_p) { m_z = false; m_p_prime = true; m().set(m_p, new_p); setup_p(); }
void set_zp(uint64 new_p) { m_z = false; m_p_prime = true; m().set(m_p, new_p); setup_p(); }
// p = p^2
void set_p_sq() { SASSERT(!m_z); m_p_prime = false; m().mul(m_p, m_p, m_p); setup_p(); }
void set_zp_swap(mpz & new_p) { SASSERT(!m_z); m().swap(m_p, new_p); setup_p(); }
void reset(mpz & a) { m().reset(a); }
bool is_small(mpz const & a) { return m().is_small(a); }
void del(mpz & a) { m().del(a); }
void neg(mpz & a) { m().neg(a); p_normalize(a); }
void abs(mpz & a) { m().abs(a); p_normalize(a); }
bool is_zero(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_zero(a); }
bool is_one(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_one(a); }
bool is_pos(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_pos(a); }
bool is_neg(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_neg(a); }
bool is_nonpos(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_nonpos(a); }
bool is_nonneg(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_nonneg(a); }
bool is_minus_one(mpz const & a) { SASSERT(is_p_normalized(a)); return numeral_manager::is_minus_one(a); }
bool eq(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().eq(a, b); }
bool lt(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().lt(a, b); }
bool le(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().le(a, b); }
bool gt(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().gt(a, b); }
bool ge(mpz const & a, mpz const & b) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); return m().ge(a, b); }
std::string to_string(mpz const & a) const {
SASSERT(is_p_normalized(a));
return m().to_string(a);
}
void display(std::ostream & out, mpz const & a) const { m().display(out, a); }
void add(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().add(a, b, c); p_normalize(c); }
void sub(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().sub(a, b, c); p_normalize(c); }
void inc(mpz & a) { SASSERT(is_p_normalized(a)); m().inc(a); p_normalize(a); }
void dec(mpz & a) { SASSERT(is_p_normalized(a)); m().dec(a); p_normalize(a); }
void mul(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_p_normalized(a) && is_p_normalized(b)); m().mul(a, b, c); p_normalize(c); }
void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) {
SASSERT(is_p_normalized(a) && is_p_normalized(b) && is_p_normalized(c)); m().addmul(a, b, c, d); p_normalize(d);
}
// d <- a - b*c
void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) {
SASSERT(is_p_normalized(a));
SASSERT(is_p_normalized(b));
SASSERT(is_p_normalized(c));
m().submul(a, b, c, d);
p_normalize(d);
}
void inv(mpz & a) {
if (m_z) {
UNREACHABLE();
}
else {
SASSERT(!is_zero(a));
// eulers theorem a^(p - 2), but gcd could be more efficient
// a*t1 + p*t2 = 1 => a*t1 = 1 (mod p) => t1 is the inverse (t3 == 1)
TRACE("mpzp_inv_bug", tout << "a: " << m().to_string(a) << ", p: " << m().to_string(m_p) << "\n";);
p_normalize(a);
TRACE("mpzp_inv_bug", tout << "after normalization a: " << m().to_string(a) << "\n";);
m().gcd(a, m_p, m_inv_tmp1, m_inv_tmp2, m_inv_tmp3);
TRACE("mpzp_inv_bug", tout << "tmp1: " << m().to_string(m_inv_tmp1) << "\ntmp2: " << m().to_string(m_inv_tmp2)
<< "\ntmp3: " << m().to_string(m_inv_tmp3) << "\n";);
p_normalize(m_inv_tmp1);
m().swap(a, m_inv_tmp1);
SASSERT(m().is_one(m_inv_tmp3)); // otherwise p is not prime and inverse is not defined
}
}
void swap(mpz & a, mpz & b) {
SASSERT(is_p_normalized(a) && is_p_normalized(b));
m().swap(a, b);
}
bool divides(mpz const & a, mpz const & b) { return (field() && !is_zero(a)) || m().divides(a, b); }
// a/b = a*inv(b)
void div(mpz const & a, mpz const & b, mpz & c) {
if (m_z) {
return m().div(a, b, c);
}
else {
SASSERT(m_p_prime);
SASSERT(is_p_normalized(a));
m().set(m_div_tmp, b);
inv(m_div_tmp);
mul(a, m_div_tmp, c);
SASSERT(is_p_normalized(c));
}
}
static unsigned hash(mpz const & a) { return numeral_manager::hash(a); }
void gcd(mpz const & a, mpz const & b, mpz & c) {
SASSERT(is_p_normalized(a) && is_p_normalized(b));
m().gcd(a, b, c);
SASSERT(is_p_normalized(c));
}
void gcd(unsigned sz, mpz const * as, mpz & g) {
m().gcd(sz, as, g);
SASSERT(is_p_normalized(g));
}
void gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & g) {
SASSERT(is_p_normalized(r1) && is_p_normalized(r2));
m().gcd(r1, r2, a, b, g);
p_normalize(a);
p_normalize(b);
}
void set(mpz & a, mpz & val) { m().set(a, val); p_normalize(a); }
void set(mpz & a, int val) { m().set(a, val); p_normalize(a); }
void set(mpz & a, unsigned val) { m().set(a, val); p_normalize(a); }
void set(mpz & a, char const * val) { m().set(a, val); p_normalize(a); }
void set(mpz & a, int64 val) { m().set(a, val); p_normalize(a); }
void set(mpz & a, uint64 val) { m().set(a, val); p_normalize(a); }
void set(mpz & a, mpz const & val) { m().set(a, val); p_normalize(a); }
bool is_uint64(mpz & a) const { const_cast<mpzzp_manager*>(this)->p_normalize(a); return m().is_uint64(a); }
bool is_int64(mpz & a) const { const_cast<mpzzp_manager*>(this)->p_normalize(a); return m().is_int64(a); }
uint64 get_uint64(mpz & a) const { const_cast<mpzzp_manager*>(this)->p_normalize(a); return m().get_uint64(a); }
int64 get_int64(mpz & a) const { const_cast<mpzzp_manager*>(this)->p_normalize(a); return m().get_int64(a); }
double get_double(mpz & a) const { const_cast<mpzzp_manager*>(this)->p_normalize(a); return m().get_double(a); }
void power(mpz const & a, unsigned k, mpz & b) {
SASSERT(is_p_normalized(a));
unsigned mask = 1;
mpz power;
set(power, a);
set(b, 1);
while (mask <= k) {
if (mask & k)
mul(b, power, b);
mul(power, power, power);
mask = mask << 1;
}
del(power);
}
bool is_perfect_square(mpz const & a, mpz & root) {
if (m_z) {
return m().is_perfect_square(a, root);
}
else {
NOT_IMPLEMENTED_YET();
return false;
}
}
bool is_uint64(mpz const & a) const { return m().is_uint64(a); }
bool is_int64(mpz const & a) const { return m().is_int64(a); }
uint64 get_uint64(mpz const & a) const { return m().get_uint64(a); }
int64 get_int64(mpz const & a) const { return m().get_int64(a); }
void mul2k(mpz & a, unsigned k) { m().mul2k(a, k); p_normalize(a); }
void mul2k(mpz const & a, unsigned k, mpz & r) { m().mul2k(a, k, r); p_normalize(r); }
unsigned power_of_two_multiple(mpz const & n) { return m().power_of_two_multiple(n); }
unsigned log2(mpz const & n) { return m().log2(n); }
unsigned mlog2(mpz const & n) { return m().mlog2(n); }
void machine_div2k(mpz & a, unsigned k) { m().machine_div2k(a, k); SASSERT(is_p_normalized(a)); }
void machine_div2k(mpz const & a, unsigned k, mpz & r) { m().machine_div2k(a, k, r); SASSERT(is_p_normalized(r)); }
bool root(mpz & a, unsigned n) { SASSERT(!modular()); return m().root(a, n); }
bool root(mpz const & a, unsigned n, mpz & r) { SASSERT(!modular()); return m().root(a, n, r); }
};
#endif

88
src/util/nat_set.h Normal file
View file

@ -0,0 +1,88 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
nat_set.h
Abstract:
Set of natural number with fast reset method.
Author:
Leonardo de Moura (leonardo) 2007-03-27.
Revision History:
--*/
#ifndef _NAT_SET_H_
#define _NAT_SET_H_
#include"vector.h"
#include"limits.h"
class nat_set {
unsigned m_curr_timestamp;
svector<unsigned> m_timestamps;
public:
nat_set(unsigned s = 0):
m_curr_timestamp(0),
m_timestamps() {
if (s > 0) {
m_timestamps.resize(s, 0);
}
}
// A nat_set is a function from [0..s-1] -> boolean.
// This method sets the domain of this function.
void set_domain(unsigned s) {
m_timestamps.resize(s, 0);
}
unsigned get_domain() const {
return m_timestamps.size();
}
// Assure that v is in the domain of the set.
void assure_domain(unsigned v) {
if (v >= get_domain()) {
set_domain(v+1);
}
}
bool contains(unsigned v) const {
return m_timestamps[v] > m_curr_timestamp;
}
void insert(unsigned v) {
m_timestamps[v] = m_curr_timestamp + 1;
}
void remove(unsigned v) {
m_timestamps[v] = m_curr_timestamp;
}
void reset() {
m_curr_timestamp++;
if (m_curr_timestamp == UINT_MAX) {
m_timestamps.fill(0);
m_curr_timestamp = 0;
}
}
bool empty() const {
svector<unsigned>::const_iterator it = m_timestamps.begin();
svector<unsigned>::const_iterator end = m_timestamps.end();
for (; it != end; ++it) {
if (*it > m_curr_timestamp) {
return false;
}
}
return true;
}
};
#endif /* _NAT_SET_H_ */

91
src/util/numeral_buffer.h Normal file
View file

@ -0,0 +1,91 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
numeral_buffer.h
Abstract:
Basic buffer for managing big nums.
Author:
Leonardo de Moura (leonardo) 2011-06-18.
Revision History:
--*/
#ifndef _NUMERAL_BUFFER_H_
#define _NUMERAL_BUFFER_H_
#include"vector.h"
template<typename Numeral, typename NumeralManager>
class numeral_buffer {
NumeralManager & m_manager;
svector<Numeral> m_buffer;
public:
typedef Numeral numeral;
typedef Numeral data;
typedef NumeralManager manager;
numeral_buffer(NumeralManager & m):m_manager(m) {}
~numeral_buffer() {
reset();
}
NumeralManager & m() const { return m_manager; }
unsigned size() const { return m_buffer.size(); }
bool empty() const { return m_buffer.empty(); }
void push_back(Numeral const & num) {
m_buffer.push_back(Numeral());
m().set(m_buffer.back(), num);
}
void pop_back() {
m().del(m_buffer.back());
m_buffer.pop_back();
}
Numeral & back() {
return m_buffer.back();
}
Numeral const & back() const {
return m_buffer.back();
}
Numeral const & operator[](unsigned idx) const {
return m_buffer[idx];
}
Numeral & operator[](unsigned idx) {
return m_buffer[idx];
}
void reset() {
typename vector<Numeral>::iterator it = m_buffer.begin();
typename vector<Numeral>::iterator end = m_buffer.end();
for (; it != end; ++it)
m().del(*it);
m_buffer.reset();
}
Numeral * c_ptr() { return m_buffer.c_ptr(); }
void reserve(unsigned sz) {
m_buffer.reserve(sz);
}
void swap(svector<Numeral> & other) {
m_buffer.swap(other);
}
};
#endif

219
src/util/obj_hashtable.h Normal file
View file

@ -0,0 +1,219 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
obj_hashtable.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-16.
Revision History:
--*/
#ifndef _OBJ_HASHTABLE_H_
#define _OBJ_HASHTABLE_H_
#include"hash.h"
#include"hashtable.h"
/**
\brief Special entry for a hashtable of obj pointers (i.e.,
objects that have a hash() method).
This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED.
*/
template<typename T>
class obj_hash_entry {
T * m_ptr;
public:
typedef T * data;
obj_hash_entry():m_ptr(0) {}
unsigned get_hash() const { return m_ptr->hash(); }
bool is_free() const { return m_ptr == 0; }
bool is_deleted() const { return m_ptr == reinterpret_cast<T *>(1); }
bool is_used() const { return m_ptr != reinterpret_cast<T *>(0) && m_ptr != reinterpret_cast<T *>(1); }
T * get_data() const { return m_ptr; }
T * & get_data() { return m_ptr; }
void set_data(T * d) { m_ptr = d; }
void set_hash(unsigned h) { SASSERT(h == m_ptr->hash()); }
void mark_as_deleted() { m_ptr = reinterpret_cast<T *>(1); }
void mark_as_free() { m_ptr = 0; }
};
template<typename T>
class obj_hashtable : public core_hashtable<obj_hash_entry<T>, obj_ptr_hash<T>, ptr_eq<T> > {
public:
obj_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY):
core_hashtable<obj_hash_entry<T>, obj_ptr_hash<T>, ptr_eq<T> >(initial_capacity) {}
};
template<typename Key, typename Value>
class obj_map {
public:
struct key_data {
Key * m_key;
Value m_value;
key_data():m_key(0) {
}
key_data(Key * k):
m_key(k) {
}
key_data(Key * k, Value const & v):
m_key(k),
m_value(v) {
}
Value const & get_value() const { return m_value; }
unsigned hash() const { return m_key->hash(); }
bool operator==(key_data const & other) const { return m_key == other.m_key; }
};
class obj_map_entry {
key_data m_data;
public:
typedef key_data data;
obj_map_entry() {}
unsigned get_hash() const { return m_data.hash(); }
bool is_free() const { return m_data.m_key == 0; }
bool is_deleted() const { return m_data.m_key == reinterpret_cast<Key *>(1); }
bool is_used() const { return m_data.m_key != reinterpret_cast<Key *>(0) && m_data.m_key != reinterpret_cast<Key *>(1); }
key_data const & get_data() const { return m_data; }
key_data & get_data() { return m_data; }
void set_data(key_data const & d) { m_data = d; }
void set_hash(unsigned h) { SASSERT(h == m_data.hash()); }
void mark_as_deleted() { m_data.m_key = reinterpret_cast<Key *>(1); }
void mark_as_free() { m_data.m_key = 0; }
};
typedef core_hashtable<obj_map_entry, obj_hash<key_data>, default_eq<key_data> > table;
table m_table;
public:
obj_map():
m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY) {}
typedef typename table::iterator iterator;
typedef Key key;
typedef Value value;
void reset() {
m_table.reset();
}
void finalize() {
m_table.finalize();
}
bool empty() const {
return m_table.empty();
}
unsigned size() const {
return m_table.size();
}
unsigned capacity() const {
return m_table.capacity();
}
iterator begin() const {
return m_table.begin();
}
iterator end() const {
return m_table.end();
}
void insert(Key * k, Value const & v) {
m_table.insert(key_data(k, v));
}
key_data const & insert_if_not_there(Key * k, Value const & v) {
return m_table.insert_if_not_there(key_data(k, v));
}
obj_map_entry * insert_if_not_there2(Key * k, Value const & v) {
return m_table.insert_if_not_there2(key_data(k, v));
}
obj_map_entry * find_core(Key * k) const {
return m_table.find_core(key_data(k));
}
bool find(Key * k, Value & v) const {
obj_map_entry * e = find_core(k);
if (e) {
v = e->get_data().m_value;
}
return (0 != e);
}
value const & find(key * k) const {
obj_map_entry * e = find_core(k);
SASSERT(e);
return e->get_data().m_value;
}
value & find(key * k) {
obj_map_entry * e = find_core(k);
SASSERT(e);
return e->get_data().m_value;
}
iterator find_iterator(Key * k) const {
return m_table.find(key_data(k));
}
bool contains(Key * k) const {
return find_core(k) != 0;
}
void remove(Key * k) {
m_table.remove(key_data(k));
}
void erase(Key * k) {
remove(k);
}
unsigned long long get_num_collision() const { return m_table.get_num_collision(); }
void swap(obj_map & other) {
m_table.swap(other.m_table);
}
};
/**
\brief Reset and deallocate the values stored in a mapping of the form obj_map<Key, Value*>
*/
template<typename Key, typename Value>
void reset_dealloc_values(obj_map<Key, Value*> & m) {
typename obj_map<Key, Value*>::iterator it = m.begin();
typename obj_map<Key, Value*>::iterator end = m.end();
for (; it != end; ++it) {
dealloc(it->m_value);
}
m.reset();
}
/**
\brief Remove the key k from the mapping m, and delete the value associated with k.
*/
template<typename Key, typename Value>
void erase_dealloc_value(obj_map<Key, Value*> & m, Key * k) {
Value * v = 0;
bool contains = m.find(k, v);
m.erase(k);
if (contains) {
dealloc(v);
}
}
#endif /* _OBJ_HASHTABLE_H_ */

53
src/util/obj_mark.h Normal file
View file

@ -0,0 +1,53 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
obj_mark.h
Abstract:
A mapping from object to boolean (for objects that can be mapped to unsigned integers).
Author:
Leonardo de Moura (leonardo) 2008-01-02.
Revision History:
--*/
#ifndef _OBJ_MARK_H_
#define _OBJ_MARK_H_
#include"bit_vector.h"
template<typename T>
struct default_t2uint {
unsigned operator()(T const & obj) const { return obj.get_id(); }
};
template<typename T, typename BV = bit_vector, typename T2UInt = default_t2uint<T> >
class obj_mark {
T2UInt m_proc;
BV m_marks;
public:
obj_mark(T2UInt const & p = T2UInt()):m_proc(p) {}
bool is_marked(T const & obj) const {
unsigned id = m_proc(obj);
return id < m_marks.size() && m_marks.get(id);
}
bool is_marked(T * obj) const { return is_marked(*obj); }
void mark(T const & obj, bool flag) {
unsigned id = m_proc(obj);
if (id >= m_marks.size()) {
m_marks.resize(id+1, 0);
}
m_marks.set(id, flag);
}
void mark(T const * obj, bool flag) { mark(*obj, flag); }
void mark(T const & obj) { mark(obj, true); }
void mark(T const * obj) { mark(obj, true); }
void reset() { m_marks.reset(); }
};
#endif /* _OBJ_MARK_H_ */

View file

@ -0,0 +1,174 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
obj_pair_hashtable.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#ifndef _OBJ_PAIR_HASHTABLE_H_
#define _OBJ_PAIR_HASHTABLE_H_
#include"hash.h"
#include"hashtable.h"
/**
\brief Special entry for a hashtable of pairs of obj pointers (i.e.,
objects that have a hash() method).
This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED.
*/
template<typename T1, typename T2>
class obj_pair_hash_entry {
unsigned m_hash; // cached hash code
std::pair<T1*, T2*> m_data;
public:
typedef std::pair<T1*, T2*> data;
obj_pair_hash_entry():m_data(static_cast<T1*>(0),static_cast<T2*>(0)) {}
unsigned get_hash() const { return m_hash; }
bool is_free() const { return m_data.first == 0; }
bool is_deleted() const { return m_data.first == reinterpret_cast<T1 *>(1); }
bool is_used() const { return m_data.first != reinterpret_cast<T1 *>(0) && m_data.first != reinterpret_cast<T1 *>(1); }
data const & get_data() const { return m_data; }
data & get_data() { return m_data; }
void set_data(data const d) { m_data = d; }
void set_hash(unsigned h) { m_hash = h; }
void mark_as_deleted() { m_data.first = reinterpret_cast<T1 *>(1); }
void mark_as_free() { m_data.first = 0; }
};
template<typename T1, typename T2>
class obj_pair_hashtable : public core_hashtable<obj_pair_hash_entry<T1, T2>, obj_ptr_pair_hash<T1, T2>, default_eq<std::pair<T1*, T2*> > > {
public:
obj_pair_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY):
core_hashtable<obj_pair_hash_entry<T1, T2>, obj_ptr_pair_hash<T1, T2>, default_eq<std::pair<T1*, T2*> > >(initial_capacity) {
}
};
template<typename Key1, typename Key2, typename Value>
class obj_pair_map {
protected:
class entry;
public:
class key_data {
Key1 * m_key1;
Key2 * m_key2;
Value m_value;
unsigned m_hash;
friend class entry;
public:
key_data():
m_key1(0),
m_key2(0),
m_hash(0) {
}
key_data(Key1 * k1, Key2 * k2):
m_key1(k1),
m_key2(k2) {
m_hash = combine_hash(m_key1->hash(), m_key2->hash());
}
key_data(Key1 * k1, Key2 * k2, const Value & v):
m_key1(k1),
m_key2(k2),
m_value(v) {
m_hash = combine_hash(m_key1->hash(), m_key2->hash());
}
unsigned hash() const { return m_hash; }
bool operator==(key_data const & other) const { return m_key1 == other.m_key1 && m_key2 == other.m_key2; }
Key1 * get_key1() const { return m_key1; }
Key2 * get_key2() const { return m_key2; }
Value const & get_value() const { return m_value; }
};
protected:
class entry {
key_data m_data;
public:
typedef key_data data;
entry() {}
unsigned get_hash() const { return m_data.hash(); }
bool is_free() const { return m_data.m_key1 == 0; }
bool is_deleted() const { return m_data.m_key1 == reinterpret_cast<Key1 *>(1); }
bool is_used() const { return m_data.m_key1 != reinterpret_cast<Key1 *>(0) && m_data.m_key1 != reinterpret_cast<Key1 *>(1); }
key_data const & get_data() const { return m_data; }
key_data & get_data() { return m_data; }
void set_data(key_data const & d) { m_data = d; }
void set_hash(unsigned h) { SASSERT(h == m_data.hash()); }
void mark_as_deleted() { m_data.m_key1 = reinterpret_cast<Key1 *>(1); }
void mark_as_free() { m_data.m_key1 = 0; }
};
typedef core_hashtable<entry, obj_hash<key_data>, default_eq<key_data> > table;
table m_table;
entry * find_core(Key1 * k1, Key2 * k2) const {
return m_table.find_core(key_data(k1, k2));
}
public:
obj_pair_map():
m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY) {}
typedef typename table::iterator iterator;
void reset() {
m_table.reset();
}
bool empty() const {
return m_table.empty();
}
unsigned size() const {
return m_table.size();
}
unsigned capacity() const {
return m_table.capacity();
}
iterator begin() const {
return m_table.begin();
}
iterator end() const {
return m_table.end();
}
void insert(Key1 * k1, Key2 * k2, Value const & v) {
m_table.insert(key_data(k1, k2, v));
}
key_data const & insert_if_not_there(Key1 * k1, Key2 * k2, Value const & v) {
return m_table.insert_if_not_there(key_data(k1, k2, v));
}
bool find(Key1 * k1, Key2 * k2, Value & v) const {
entry * e = find_core(k1, k2);
if (e) {
v = e->get_data().get_value();
}
return (0 != e);
}
bool contains(Key1 * k1, Key2 * k2) const {
return find_core(k1, k2) != 0;
}
void erase(Key1 * k1, Key2 * k2) {
m_table.remove(key_data(k1, k2));
}
};
#endif /* _OBJ_PAIR_HASHTABLE_H_ */

50
src/util/obj_pair_set.h Normal file
View file

@ -0,0 +1,50 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
obj_pair_set.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2011-04-19
Revision History:
--*/
#ifndef _OBJ_PAIR_SET_H_
#define _OBJ_PAIR_SET_H_
#include"chashtable.h"
template<typename T1, typename T2>
class obj_pair_set {
public:
typedef std::pair<T1*, T2*> obj_pair;
protected:
struct hash_proc {
unsigned operator()(obj_pair const & p) const { return combine_hash(p.first->hash(), p.second->hash()); }
};
struct eq_proc {
bool operator()(obj_pair const & p1, obj_pair const & p2) const { return p1 == p2; }
};
typedef chashtable<obj_pair, hash_proc, eq_proc> set;
set m_set;
public:
obj_pair_set() {}
void insert(T1 * t1, T2 * t2) { m_set.insert(obj_pair(t1, t2)); }
void insert(obj_pair const & p) { m_set.insert(p); }
bool insert_if_not_there(T1 * t1, T2 * t2) { return m_set.insert_if_not_there2(obj_pair(t1, t2)); }
bool insert_if_not_there(obj_pair const & p) { return m_set.insert_if_not_there2(p); }
void erase(T1 * t1, T2 * t2) { return m_set.erase(obj_pair(t1, t2)); }
void erase(obj_pair const & p) { return m_set.erase(p); }
bool contains(T1 * t1, T2 * t2) const { return m_set.contains(obj_pair(t1, t2)); }
bool contains(obj_pair const & p) const { return m_set.contains(p); }
void reset() { m_set.reset(); }
};
#endif

139
src/util/obj_ref.h Normal file
View file

@ -0,0 +1,139 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
obj_ref.h
Abstract:
Smart pointer.
Author:
Leonardo de Moura (leonardo) 2008-01-03.
Revision History:
--*/
#ifndef _OBJ_REF_H_
#define _OBJ_REF_H_
/**
Smart pointer for T objects.
TManager must provide the functions:
- void dec_ref(T * obj)
- void inc_ref(T * obj)
*/
template<typename T, typename TManager>
class obj_ref {
T * m_obj;
TManager & m_manager;
void dec_ref() { if (m_obj) m_manager.dec_ref(m_obj); }
void inc_ref() { if (m_obj) m_manager.inc_ref(m_obj); }
public:
typedef TManager manager;
obj_ref(T * n, TManager & m):
m_obj(n),
m_manager(m) {
inc_ref();
}
explicit obj_ref(TManager & m):
m_obj(0),
m_manager(m) {
}
obj_ref(obj_ref const & n):
m_obj(n.m_obj),
m_manager(n.m_manager) {
inc_ref();
}
~obj_ref() { dec_ref(); }
TManager & get_manager() const { return m_manager; }
TManager & m() const { return m_manager; }
T * operator->() const { return m_obj; }
T * get() const { return m_obj; }
operator bool() const { return m_obj != 0; }
bool operator!() const { return m_obj == 0; }
operator T*() const { return m_obj; }
T const & operator*() const { return *m_obj; }
obj_ref & operator=(T * n) {
if (n) {
m_manager.inc_ref(n);
}
dec_ref();
m_obj = n;
return *this;
}
obj_ref & operator=(obj_ref & n) {
SASSERT(&m_manager == &n.m_manager);
n.inc_ref();
dec_ref();
m_obj = n.m_obj;
return *this;
}
void reset() {
dec_ref();
m_obj = 0;
}
void swap(obj_ref & n) {
std::swap(m_obj, n.m_obj);
}
/**
\brief Steal ownership without decrementing the reference counter.
*/
T * steal() {
T * r = m_obj;
m_obj = 0;
return r;
}
};
template<typename T, typename TManager>
inline bool operator==(obj_ref<T, TManager> const & n1, obj_ref<T, TManager> const & n2) {
return n1.get() == n2.get();
}
template<typename T1, typename T2, typename TManager>
inline bool operator==(obj_ref<T1, TManager> const & n1, obj_ref<T2, TManager> const & n2) {
return n1.get() == n2.get();
}
template<typename T, typename TManager>
inline bool operator!=(obj_ref<T, TManager> const & n1, obj_ref<T, TManager> const & n2) {
return n1.get() != n2.get();
}
template<typename T1, typename T2, typename TManager>
inline bool operator!=(obj_ref<T1, TManager> const & n1, obj_ref<T2, TManager> const & n2) {
return n1.get() != n2.get();
}
template<typename IT, typename TManager>
inline void dec_range_ref(IT const & begin, IT const & end, TManager & m) {
for (IT it = begin; it != end; ++it) {
if (*it) {
m.dec_ref(*it);
}
}
}
#endif /* _OBJ_REF_H_ */

View file

@ -0,0 +1,180 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
obj_triple_hashtable.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-19.
Revision History:
--*/
#ifndef _OBJ_TRIPLE_HASHTABLE_H_
#define _OBJ_TRIPLE_HASHTABLE_H_
#include"hashtable.h"
/**
\brief Special entry for a hashtable of pairs of obj pointers (i.e.,
objects that have a hash() method).
This entry uses 0x0 and 0x1 to represent HT_FREE and HT_DELETED.
*/
template<typename T1, typename T2, typename T3>
class obj_triple_hash_entry {
unsigned m_hash; // cached hash code
triple<T1*, T2*, T3*> m_data;
public:
typedef triple<T1*, T2*, T3*> data;
obj_triple_hash_entry():m_data(0,0,0) {}
unsigned get_hash() const { return m_hash; }
bool is_free() const { return m_data.first == 0; }
bool is_deleted() const { return m_data.first == reinterpret_cast<T1 *>(1); }
bool is_used() const { return m_data.first != reinterpret_cast<T1 *>(0) && m_data.first != reinterpret_cast<T1 *>(1); }
data const & get_data() const { return m_data; }
data & get_data() { return m_data; }
void set_data(data const d) { m_data = d; }
void set_hash(unsigned h) { m_hash = h; }
void mark_as_deleted() { m_data.first = reinterpret_cast<T1 *>(1); }
void mark_as_free() { m_data.first = 0; }
};
template<typename T1, typename T2, typename T3>
class obj_triple_hashtable : public core_hashtable<obj_triple_hash_entry<T1, T2, T3>, obj_ptr_triple_hash<T1, T2, T3>, default_eq<triple<T1*, T2*, T3*> > > {
public:
obj_triple_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY):
core_hashtable<obj_triple_hash_entry<T1, T2, T3>, obj_ptr_triple_hash<T1, T2, T3>, default_eq<triple<T1*, T2*, T3*> > >(initial_capacity) {
}
};
template<typename Key1, typename Key2, typename Key3, typename Value>
class obj_triple_map {
protected:
class entry;
public:
class key_data {
Key1 * m_key1;
Key2 * m_key2;
Key3 * m_key3;
Value m_value;
unsigned m_hash;
friend class entry;
public:
key_data():
m_key1(0),
m_key2(0),
m_key3(0),
m_hash(0) {
}
key_data(Key1 * k1, Key2 * k2, Key3 * k3):
m_key1(k1),
m_key2(k2),
m_key3(k3){
m_hash = combine_hash(combine_hash(m_key1->hash(), m_key2->hash()), m_key3->hash());
}
key_data(Key1 * k1, Key2 * k2, Key3* k3, const Value & v):
m_key1(k1),
m_key2(k2),
m_key3(k3),
m_value(v) {
m_hash = combine_hash(combine_hash(m_key1->hash(), m_key2->hash()), m_key3->hash());
}
unsigned hash() const { return m_hash; }
bool operator==(key_data const & other) const { return m_key1 == other.m_key1 && m_key2 == other.m_key2 && m_key3 == other.m_key3; }
Key1 * get_key1() const { return m_key1; }
Key2 * get_key2() const { return m_key2; }
Key3 * get_key3() const { return m_key3; }
Value const & get_value() const { return m_value; }
};
protected:
class entry {
key_data m_data;
public:
typedef key_data data;
entry() {}
unsigned get_hash() const { return m_data.hash(); }
bool is_free() const { return m_data.m_key1 == 0; }
bool is_deleted() const { return m_data.m_key1 == reinterpret_cast<Key1 *>(1); }
bool is_used() const { return m_data.m_key1 != reinterpret_cast<Key1 *>(0) && m_data.m_key1 != reinterpret_cast<Key1 *>(1); }
key_data const & get_data() const { return m_data; }
key_data & get_data() { return m_data; }
void set_data(key_data const & d) { m_data = d; }
void set_hash(unsigned h) { SASSERT(h == m_data.hash()); }
void mark_as_deleted() { m_data.m_key1 = reinterpret_cast<Key1 *>(1); }
void mark_as_free() { m_data.m_key1 = 0; }
};
typedef core_hashtable<entry, obj_hash<key_data>, default_eq<key_data> > table;
table m_table;
entry * find_core(Key1 * k1, Key2 * k2, Key3 * k3) const {
return m_table.find_core(key_data(k1, k2, k3));
}
public:
obj_triple_map():
m_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY) {}
typedef typename table::iterator iterator;
void reset() {
m_table.reset();
}
bool empty() const {
return m_table.empty();
}
unsigned size() const {
return m_table.size();
}
unsigned capacity() const {
return m_table.capacity();
}
iterator begin() const {
return m_table.begin();
}
iterator end() const {
return m_table.end();
}
void insert(Key1 * k1, Key2 * k2, Key3* k3, Value const & v) {
m_table.insert(key_data(k1, k2, k3, v));
}
key_data const & insert_if_not_there(Key1 * k1, Key2 * k2, Key3 * k3, Value const & v) {
return m_table.insert_if_not_there(key_data(k1, k2, k3, v));
}
bool find(Key1 * k1, Key2 * k2,Key3 * k3, Value & v) const {
entry * e = find_core(k1, k2, k3);
if (e) {
v = e->get_data().get_value();
}
return (0 != e);
}
bool contains(Key1 * k1, Key2 * k2, Key3 * k3) const {
return find_core(k1, k2, k3) != 0;
}
void erase(Key1 * k1, Key2 * k2, Key3 * k3) {
m_table.remove(key_data(k1, k2, k3));
}
};
#endif /* _OBJ_TRIPLE_HASHTABLE_H_ */

289
src/util/object_allocator.h Normal file
View file

@ -0,0 +1,289 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
object_allocator.h
Abstract:
Yet another object allocator. This allocator is supposed to be efficient
when there is a collection of worker threads accessing it.
Author:
Leonardo de Moura (leonardo) 2010-06-09.
Revision History:
--*/
#ifndef _OBJECT_ALLOCATOR_H_
#define _OBJECT_ALLOCATOR_H_
#include"util.h"
#include"vector.h"
#define DEFAULT_NUM_WORKERS 8
#define NUM_OBJECTS_PER_PAGE 1024
template<typename T>
struct do_nothing_reset_proc {
public:
void operator()(T * obj) {}
};
template<typename T>
struct simple_reset_proc {
public:
void operator()(T * obj) { obj->reset(); }
};
/**
\brief Allocator for T objects. This allocator is supposed to be efficient even
when a collection of working threads are accessing it.
Assumptions:
- T must have an empty constructor.
- The destructors for T objects are only invoked when the object_allocator is deleted.
- The destructors are not invoked if CallDestructors == false.
- The functor ResetProc is invoked for \c ptr when recycle(ptr) or recycle(worker_id, ptr) are invoked.
The default ResetProc does nothing.
*/
template<typename T, bool CallDestructors = true, typename ResetProc = do_nothing_reset_proc<T> >
class object_allocator : public ResetProc {
/**
\brief Auxiliary allocator for storing object into chunks of memory.
*/
class region {
ptr_vector<T> m_pages;
unsigned m_idx; //!< next position in the current page.
void allocate_new_page() {
T * new_page = static_cast<T*>(memory::allocate(sizeof(T) * NUM_OBJECTS_PER_PAGE));
m_pages.push_back(new_page);
m_idx = 0;
}
void call_destructors_for_page(T * page, unsigned end) {
T * page_end = page + end;
for (; page < page_end; page++)
page->~T();
}
void call_destructors() {
if (CallDestructors) {
SASSERT(!m_pages.empty());
typename ptr_vector<T>::iterator it = m_pages.begin();
typename ptr_vector<T>::iterator end = m_pages.end();
end--;
call_destructors_for_page(*end, m_idx);
for (; it != end; ++it)
call_destructors_for_page(*it, NUM_OBJECTS_PER_PAGE);
}
}
void free_memory() {
call_destructors();
typename ptr_vector<T>::iterator it = m_pages.begin();
typename ptr_vector<T>::iterator end = m_pages.end();
for (; it != end; ++it)
memory::deallocate(*it);
}
public:
region() {
allocate_new_page();
}
~region() {
free_memory();
}
template<bool construct>
T * allocate() {
SASSERT(!m_pages.empty());
T * r = m_pages.back() + m_idx;
if (construct) new (r) T();
m_idx++;
if (m_idx == NUM_OBJECTS_PER_PAGE)
allocate_new_page();
return r;
}
void reset() {
free_memory();
m_pages.reset();
allocate_new_page();
}
unsigned get_objects_count() {
return (m_pages.size() - 1) * NUM_OBJECTS_PER_PAGE + m_idx;
}
};
#ifdef Z3DEBUG
bool m_concurrent; //!< True when the allocator can be accessed concurrently.
#endif
ptr_vector<region> m_regions;
vector<ptr_vector<T> > m_free_lists;
template <bool construct>
T * allocate_core(unsigned idx) {
ptr_vector<T> & free_list = m_free_lists[idx];
if (!free_list.empty()) {
T * r = free_list.back();
free_list.pop_back();
return r;
}
return m_regions[idx]->allocate<construct>();
}
void recycle_core(unsigned idx, T * ptr) {
ResetProc::operator()(ptr);
m_free_lists[idx].push_back(ptr);
}
public:
object_allocator(ResetProc const & r = ResetProc()):ResetProc(r) {
DEBUG_CODE(m_concurrent = false;);
reserve(DEFAULT_NUM_WORKERS);
}
~object_allocator() {
std::for_each(m_regions.begin(), m_regions.end(), delete_proc<region>());
}
/**
\brief Enable/Disable concurrent access.
*/
void enable_concurrent(bool flag) {
DEBUG_CODE(m_concurrent = flag;);
}
/**
\brief Make sure that \c num_workers can access this object allocator concurrently.
This method must only be invoked if the allocator is not in concurrent mode.
*/
void reserve(unsigned num_workers) {
SASSERT(!m_concurrent);
unsigned old_capacity = capacity();
if (num_workers > old_capacity) {
m_regions.resize(num_workers);
m_free_lists.resize(num_workers);
for (unsigned i = old_capacity; i < capacity(); i++) {
m_regions[i] = alloc(region);
}
}
}
/**
\brief Return the number of workers supported by this object allocator.
*/
unsigned capacity() const {
return m_regions.size();
}
/**
\brief Free all memory allocated using this allocator.
This method must only be invoked when the allocator is not in concurrent mode.
*/
void reset() {
SASSERT(!m_concurrent);
unsigned c = capacity();
for (unsigned i = 0; i < c; i++) {
m_regions[i]->reset();
m_free_lists[i].reset();
}
}
/**
\brief Allocate a new object.
This method must only be invoked when the object_allocator is not in concurrent mode.
*/
template<bool construct>
T * allocate() {
SASSERT(!m_concurrent);
return allocate_core<construct>(0);
}
/**
\brief Recycle the given object.
This method must only be invoked when the object_allocator is not in concurrent mode.
\remark It is OK to recycle an object allocated by a worker when the object_allocator was
in concurrent mode.
*/
void recycle(T * ptr) {
SASSERT(!m_concurrent);
recycle_core(0, ptr);
}
/**
\brief Allocate a new object for the given worker.
This method must only be invoked when the object_allocator is in concurrent mode.
*/
template <bool construct>
T * allocate(unsigned worker_id) {
SASSERT(m_concurrent);
return allocate_core<construct>(worker_id);
}
/**
\brief Recycle the given object.
This method must only be invoked when the object_allocator is in concurrent mode.
\remark It is OK to recycle an object allocated by a different worker, or allocated when the
object_allocator was not in concurrent mode.
*/
void recycle(unsigned worker_id, T * ptr) {
SASSERT(m_concurrent);
return recycle_core(worker_id, ptr);
}
/**
\brief Wrapper for currying worker_id in allocate and recycle methods.
*/
class worker_object_allocator {
object_allocator & m_owner;
unsigned m_worker_id;
friend class object_allocator;
worker_object_allocator(object_allocator & owner, unsigned id):m_owner(owner), m_worker_id(id) {}
public:
template<bool construct>
T * allocate() { return m_owner.allocate<construct>(m_worker_id); }
void recycle(T * ptr) { return m_owner.recycle(m_worker_id, ptr); }
};
/**
\brief Return a wrapper for allocating memory for the given worker.
The wrapper remains valid even when the object_allocator is not in concurrent mode.
However, the methods allocate/recycle of the wrapper must only be invoked when the object_allocator is in concurrent mode.
*/
worker_object_allocator get_worker_allocator(unsigned worker_id) {
SASSERT(worker_id < capacity());
return worker_object_allocator(*this, worker_id);
}
unsigned get_objects_count() const {
unsigned count = 0;
unsigned n_regions = m_regions.size();
for (unsigned i = 0; i < n_regions; i++) {
count += m_regions[i]->get_objects_count();
count -= m_free_lists[i].size();
}
return count;
}
};
#endif /* _OBJECT_ALLOCATOR_H_ */

642
src/util/old_interval.cpp Normal file
View file

@ -0,0 +1,642 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
old_interval.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-12-09.
Revision History:
--*/
#include"old_interval.h"
void ext_numeral::neg() {
switch (m_kind) {
case MINUS_INFINITY: m_kind = PLUS_INFINITY; break;
case FINITE: m_value.neg(); break;
case PLUS_INFINITY: m_kind = MINUS_INFINITY; break;
}
}
ext_numeral & ext_numeral::operator+=(ext_numeral const & other) {
SASSERT(!is_infinite() || !other.is_infinite() || m_kind == other.m_kind);
if (is_infinite())
return *this;
SASSERT(m_kind == FINITE);
switch (other.m_kind) {
case MINUS_INFINITY:
m_kind = MINUS_INFINITY;
m_value.reset();
return *this;
case FINITE:
m_value += other.m_value;
return *this;
case PLUS_INFINITY:
m_kind = PLUS_INFINITY;
m_value.reset();
return *this;
}
UNREACHABLE();
return *this;
}
ext_numeral & ext_numeral::operator-=(ext_numeral const & other) {
SASSERT(!is_infinite() || !other.is_infinite() || (m_kind != other.m_kind));
if (is_infinite())
return *this;
SASSERT(m_kind == FINITE);
switch (other.m_kind) {
case MINUS_INFINITY:
m_kind = PLUS_INFINITY;
m_value.reset();
return *this;
case FINITE:
m_value -= other.m_value;
return *this;
case PLUS_INFINITY:
m_kind = MINUS_INFINITY;
m_value.reset();
return *this;
}
UNREACHABLE();
return *this;
}
ext_numeral & ext_numeral::operator*=(ext_numeral const & other) {
if (is_zero() || other.is_zero()) {
m_kind = FINITE;
m_value.reset();
return *this;
}
if (is_infinite() || other.is_infinite()) {
if (sign() == other.sign())
m_kind = PLUS_INFINITY;
else
m_kind = MINUS_INFINITY;
m_value.reset();
return *this;
}
SASSERT(m_kind == FINITE);
m_value *= other.m_value;
return *this;
}
void ext_numeral::expt(unsigned n) {
switch (m_kind) {
case MINUS_INFINITY:
if (n % 2 == 0)
m_kind = PLUS_INFINITY;
return;
case FINITE:
m_value = m_value.expt(n);
break;
case PLUS_INFINITY:
// do nothing
break;
}
}
void ext_numeral::inv() {
SASSERT(!is_zero());
if (is_infinite()) {
m_kind = FINITE;
m_value.reset();
}
else {
m_value = rational(1) / m_value;
}
}
void ext_numeral::display(std::ostream & out) const {
switch (m_kind) {
case MINUS_INFINITY:
out << "-oo";
break;
case FINITE:
out << m_value;
break;
case PLUS_INFINITY:
out << "oo";
break;
}
}
bool operator==(ext_numeral const & n1, ext_numeral const & n2) {
return n1.m_kind == n2.m_kind && (n1.is_infinite() || n1.m_value == n2.m_value);
}
bool operator<(ext_numeral const & n1, ext_numeral const & n2) {
if (n1.is_infinite())
return n1.m_kind == ext_numeral::MINUS_INFINITY && n2.m_kind != ext_numeral::MINUS_INFINITY;
if (n2.is_infinite())
return n2.m_kind != ext_numeral::MINUS_INFINITY;
return n1.m_value < n2.m_value;
}
/**
\brief Create interval (-oo, oo)
*/
interval::interval(v_dependency_manager & m):
m_manager(m),
m_lower(false),
m_upper(true),
m_lower_open(true),
m_upper_open(true),
m_lower_dep(0),
m_upper_dep(0) {
}
/**
\brief Create intervals [l,u], (l,u], [l, u), (l,u), where l and u are numerals.
*/
interval::interval(v_dependency_manager & m, rational const & lower, bool l_open, v_dependency * l_dep, rational const & upper, bool u_open, v_dependency * u_dep):
m_manager(m),
m_lower(lower),
m_upper(upper),
m_lower_open(l_open),
m_upper_open(u_open),
m_lower_dep(l_dep),
m_upper_dep(u_dep) {
SASSERT(lower <= upper);
SASSERT(lower != upper || !l_open || !u_open);
}
/**
\brief Create intervals [l,u], (l,u], [l, u), (l,u), where l and u are ext_numerals.
*/
interval::interval(v_dependency_manager & m, ext_numeral const & lower, bool l_open, v_dependency * l_dep, ext_numeral const & upper, bool u_open, v_dependency * u_dep):
m_manager(m),
m_lower(lower),
m_upper(upper),
m_lower_open(l_open),
m_upper_open(u_open),
m_lower_dep(l_dep),
m_upper_dep(u_dep) {
SASSERT(lower <= upper);
SASSERT(lower != upper || !l_open || !u_open);
}
/**
\brief Create interval [val,val]
*/
interval::interval(v_dependency_manager & m, rational const & val, v_dependency * l_dep, v_dependency * u_dep):
m_manager(m),
m_lower(val),
m_upper(val),
m_lower_open(false),
m_upper_open(false),
m_lower_dep(l_dep),
m_upper_dep(u_dep) {
}
/**
\brief Create intervals (-oo, val], (-oo, val), [val, oo), (val, oo)
*/
interval::interval(v_dependency_manager & m, rational const & val, bool open, bool lower, v_dependency * d):
m_manager(m) {
if (lower) {
m_lower = ext_numeral(val);
m_lower_open = open;
m_lower_dep = d;
m_upper = ext_numeral(true);
m_upper_open = true;
m_upper_dep = 0;
}
else {
m_lower = ext_numeral(false);
m_lower_open = true;
m_lower_dep = 0;
m_upper = ext_numeral(val);
m_upper_open = open;
m_upper_dep = d;
}
}
interval::interval(interval const & other):
m_manager(other.m_manager),
m_lower(other.m_lower),
m_upper(other.m_upper),
m_lower_open(other.m_lower_open),
m_upper_open(other.m_upper_open),
m_lower_dep(other.m_lower_dep),
m_upper_dep(other.m_upper_dep) {
}
interval & interval::operator=(interval const & other) {
m_lower = other.m_lower;
m_upper = other.m_upper;
m_lower_open = other.m_lower_open;
m_upper_open = other.m_upper_open;
m_lower_dep = other.m_lower_dep;
m_upper_dep = other.m_upper_dep;
return *this;
}
interval & interval::operator+=(interval const & other) {
m_lower += other.m_lower;
m_upper += other.m_upper;
m_lower_open |= other.m_lower_open;
m_upper_open |= other.m_upper_open;
m_lower_dep = m_lower.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, other.m_lower_dep);
m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_upper_dep, other.m_upper_dep);
return *this;
}
void interval::neg() {
std::swap(m_lower, m_upper);
std::swap(m_lower_open, m_upper_open);
std::swap(m_lower_dep, m_upper_dep);
m_lower.neg();
m_upper.neg();
}
interval & interval::operator-=(interval const & other) {
interval tmp(other);
tmp.neg();
return operator+=(tmp);
}
v_dependency * interval::join(v_dependency * d1, v_dependency * d2, v_dependency * d3, v_dependency * d4) {
return m_manager.mk_join(m_manager.mk_join(d1, d2), m_manager.mk_join(d3,d4));
}
/**
\brief Create a new v_dependency using d1, d2, and (opt1 or opt2).
*/
v_dependency * interval::join_opt(v_dependency * d1, v_dependency * d2, v_dependency * opt1, v_dependency * opt2) {
if (opt1 == d1 || opt1 == d2)
return join(d1, d2);
if (opt2 == d1 || opt2 == d2)
return join(d1, d2);
if (opt1 == 0 || opt2 == 0)
return join(d1, d2);
// TODO: more opts...
return join(d1, d2, opt1);
}
interval & interval::operator*=(interval const & other) {
#if Z3DEBUG || _TRACE
bool contains_zero1 = contains_zero();
bool contains_zero2 = other.contains_zero();
#endif
if (is_zero()) {
return *this;
}
if (other.is_zero()) {
*this = other;
return *this;
}
ext_numeral const & a = m_lower;
ext_numeral const & b = m_upper;
ext_numeral const & c = other.m_lower;
ext_numeral const & d = other.m_upper;
bool a_o = m_lower_open;
bool b_o = m_upper_open;
bool c_o = other.m_lower_open;
bool d_o = other.m_upper_open;
v_dependency * a_d = m_lower_dep;
v_dependency * b_d = m_upper_dep;
v_dependency * c_d = other.m_lower_dep;
v_dependency * d_d = other.m_upper_dep;
TRACE("interval_bug", tout << "operator*= " << *this << " " << other << "\n";);
if (is_N()) {
if (other.is_N()) {
// x <= b <= 0, y <= d <= 0 --> b*d <= x*y
// a <= x <= b <= 0, c <= y <= d <= 0 --> x*y <= a*c (we can use the fact that x or y is always negative (i.e., b is neg or d is neg))
TRACE("interval_bug", tout << "(N, N)\n";);
ext_numeral new_lower = b * d;
ext_numeral new_upper = a * c;
// if b = 0 (and the interval is closed), then the lower bound is closed
m_lower_open = (is_N0() || other.is_N0()) ? false : (b_o || d_o);
m_upper_open = a_o || c_o; SASSERT(a.is_neg() && c.is_neg());
m_lower = new_lower;
m_upper = new_upper;
m_lower_dep = m_lower.is_infinite() ? 0 : join(b_d, d_d);
m_upper_dep = m_upper.is_infinite() ? 0 : join_opt(a_d, c_d, b_d, d_d);
}
else if (other.is_M()) {
// a <= x <= b <= 0, y <= d, d > 0 --> a*d <= x*y (uses the fact that b is not positive)
// a <= x <= b <= 0, c <= y, c < 0 --> x*y <= a*c (uses the fact that b is not positive)
TRACE("interval_bug", tout << "(N, M)\n";);
ext_numeral new_lower = a * d; SASSERT(new_lower.is_neg());
ext_numeral new_upper = a * c; SASSERT(new_upper.is_pos());
m_lower_open = a_o || d_o;
m_upper_open = a_o || c_o;
m_lower = new_lower;
m_upper = new_upper;
m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, d_d, b_d);
m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, c_d, b_d);
}
else {
// a <= x <= b <= 0, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that x is neg (b is not positive) or y is pos (c is not negative))
// x <= b <= 0, 0 <= c <= y --> x*y <= b*c
TRACE("interval_bug", tout << "(N, P)\n";);
SASSERT(other.is_P());
ext_numeral new_lower = a * d;
ext_numeral new_upper = b * c;
bool is_N0_old = is_N0(); // see comment in (P, N) case
m_lower_open = a_o || d_o; SASSERT(a.is_neg() && d.is_pos());
m_upper_open = (is_N0_old || other.is_P0()) ? false : (b_o || c_o);
m_lower = new_lower;
m_upper = new_upper;
m_lower_dep = m_lower.is_infinite() ? 0 : join_opt(a_d, d_d, b_d, c_d);
m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, c_d);
}
}
else if (is_M()) {
if (other.is_N()) {
// b > 0, x <= b, c <= y <= d <= 0 --> b*c <= x*y (uses the fact that d is not positive)
// a < 0, a <= x, c <= y <= d <= 0 --> x*y <= a*c (uses the fact that d is not positive)
TRACE("interval_bug", tout << "(M, N)\n";);
ext_numeral new_lower = b * c; SASSERT(new_lower.is_neg());
ext_numeral new_upper = a * c; SASSERT(new_upper.is_pos());
m_lower_open = b_o || c_o; SASSERT(b.is_pos() && c.is_neg());
m_upper_open = a_o || c_o; SASSERT(a.is_neg() && c.is_neg());
m_lower = new_lower;
m_upper = new_upper;
m_lower_dep = m_lower.is_infinite() ? 0 : join(b_d, c_d, d_d);
m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, c_d, d_d);
}
else if (other.is_M()) {
TRACE("interval_bug", tout << "(M, M)\n";);
SASSERT(!a.is_zero() && !b.is_zero() && !c.is_zero() && !d.is_zero());
ext_numeral ad = a*d; SASSERT(!ad.is_zero());
ext_numeral bc = b*c; SASSERT(!bc.is_zero());
ext_numeral ac = a*c; SASSERT(!ac.is_zero());
ext_numeral bd = b*d; SASSERT(!bd.is_zero());
bool ad_o = a_o || d_o;
bool bc_o = b_o || c_o;
bool ac_o = a_o || c_o;
bool bd_o = b_o || d_o;
if (ad < bc || (ad == bc && !ad_o && bc_o)) {
m_lower = ad;
m_lower_open = ad_o;
}
else {
m_lower = bc;
m_lower_open = bc_o;
}
if (ac > bd || (ac == bd && !ac_o && bd_o)) {
m_upper = ac;
m_upper_open = ac_o;
}
else {
m_upper = bd;
m_upper_open = bd_o;
}
m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, b_d, c_d, d_d);
m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, b_d, c_d, d_d);
}
else {
// a < 0, a <= x, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that c is not negative)
// b > 0, x <= b, 0 <= c <= y <= d --> x*y <= b*d (uses the fact that c is not negative)
TRACE("interval_bug", tout << "(M, P)\n";);
SASSERT(other.is_P());
ext_numeral new_lower = a * d; SASSERT(new_lower.is_neg());
ext_numeral new_upper = b * d; SASSERT(new_upper.is_pos());
m_lower_open = a_o || d_o; SASSERT(a.is_neg() && d.is_pos());
m_upper_open = b_o || d_o; SASSERT(b.is_pos() && d.is_pos());
m_lower = new_lower;
m_upper = new_upper;
m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, d_d, c_d);
m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, d_d, c_d);
}
}
else {
SASSERT(is_P());
if (other.is_N()) {
// 0 <= a <= x <= b, c <= y <= d <= 0 --> x*y <= b*c (uses the fact that x is pos (a is not neg) or y is neg (d is not pos))
// 0 <= a <= x, y <= d <= 0 --> a*d <= x*y
TRACE("interval_bug", tout << "(P, N)\n";);
ext_numeral new_lower = b * c;
ext_numeral new_upper = a * d;
bool is_P0_old = is_P0(); // cache the value of is_P0(), since it may be affected by the next update.
m_lower_open = b_o || c_o; SASSERT(b.is_pos() && c.is_neg());
m_upper_open = (is_P0_old || other.is_N0()) ? false : a_o || d_o;
m_lower = new_lower;
m_upper = new_upper;
m_lower_dep = m_lower.is_infinite() ? 0 : join_opt(b_d, c_d, a_d, d_d);
m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, d_d);
}
else if (other.is_M()) {
// 0 <= a <= x <= b, c <= y --> b*c <= x*y (uses the fact that a is not negative)
// 0 <= a <= x <= b, y <= d --> x*y <= b*d (uses the fact that a is not negative)
TRACE("interval_bug", tout << "(P, M)\n";);
ext_numeral new_lower = b * c; SASSERT(new_lower.is_neg());
ext_numeral new_upper = b * d; SASSERT(new_upper.is_pos());
m_lower_open = b_o || c_o;
m_upper_open = b_o || d_o;
m_lower = new_lower;
m_upper = new_upper;
m_lower_dep = m_lower.is_infinite() ? 0 : join(b_d, c_d, a_d);
m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, d_d, a_d);
}
else {
// 0 <= a <= x, 0 <= c <= y --> a*c <= x*y
// x <= b, y <= d --> x*y <= b*d (uses the fact that x is pos (a is not negative) or y is pos (c is not negative))
TRACE("interval_bug", tout << "(P, P)\n";);
SASSERT(other.is_P());
ext_numeral new_lower = a * c;
ext_numeral new_upper = b * d;
m_lower_open = (is_P0() || other.is_P0()) ? false : a_o || c_o;
m_upper_open = b_o || d_o; SASSERT(b.is_pos() && d.is_pos());
m_lower = new_lower;
m_upper = new_upper;
m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, c_d);
m_upper_dep = m_upper.is_infinite() ? 0 : join_opt(b_d, d_d, a_d, c_d);
}
}
TRACE("interval_bug", tout << "operator*= result: " << *this << "\n";);
CTRACE("interval", !(!(contains_zero1 || contains_zero2) || contains_zero()),
tout << "contains_zero1: " << contains_zero1 << ", contains_zero2: " << contains_zero2 << ", contains_zero(): " << contains_zero() << "\n";);
SASSERT(!(contains_zero1 || contains_zero2) || contains_zero());
return *this;
}
bool interval::contains_zero() const {
TRACE("interval_zero_bug", tout << "contains_zero info: " << *this << "\n";
tout << "m_lower.is_neg(): " << m_lower.is_neg() << "\n";
tout << "m_lower.is_zero: " << m_lower.is_zero() << "\n";
tout << "m_lower_open: " << m_lower_open << "\n";
tout << "m_upper.is_pos(): " << m_upper.is_pos() << "\n";
tout << "m_upper.is_zero: " << m_upper.is_zero() << "\n";
tout << "m_upper_open: " << m_upper_open << "\n";
tout << "result: " << ((m_lower.is_neg() || (m_lower.is_zero() && !m_lower_open)) && (m_upper.is_pos() || (m_upper.is_zero() && !m_upper_open))) << "\n";);
return
(m_lower.is_neg() || (m_lower.is_zero() && !m_lower_open)) &&
(m_upper.is_pos() || (m_upper.is_zero() && !m_upper_open));
}
bool interval::contains(rational const& v) const {
if (!inf().is_infinite()) {
if (v < inf().to_rational()) return false;
if (v == inf().to_rational() && m_lower_open) return false;
}
if (!sup().is_infinite()) {
if (v > sup().to_rational()) return false;
if (v == sup().to_rational() && m_upper_open) return false;
}
return true;
}
interval & interval::inv() {
// If the interval [l,u] does not contain 0, then 1/[l,u] = [1/u, 1/l]
SASSERT(!contains_zero());
if (is_P1()) {
// 0 < a <= x --> 1/x <= 1/a
// 0 < a <= x <= b --> 1/b <= 1/x (use lower and upper bounds)
ext_numeral new_lower = m_upper; SASSERT(!m_upper.is_zero());
new_lower.inv();
ext_numeral new_upper;
if (m_lower.is_zero()) {
SASSERT(m_lower_open);
ext_numeral plus_infinity(true);
new_upper = plus_infinity;
}
else {
new_upper = m_lower;
new_upper.inv();
}
m_lower = new_lower;
m_upper = new_upper;
std::swap(m_lower_open, m_upper_open);
v_dependency * new_upper_dep = m_lower_dep;
SASSERT(!m_lower.is_infinite());
m_lower_dep = m_manager.mk_join(m_lower_dep, m_upper_dep);
m_upper_dep = new_upper_dep;
}
else if (is_N1()) {
// x <= a < 0 --> 1/a <= 1/x
// b <= x <= a < 0 --> 1/b <= 1/x (use lower and upper bounds)
ext_numeral new_upper = m_lower; SASSERT(!m_lower.is_zero());
new_upper.inv();
ext_numeral new_lower;
if (m_upper.is_zero()) {
SASSERT(m_upper_open);
ext_numeral minus_infinity(false);
new_lower = minus_infinity;
}
else {
new_lower = m_upper;
new_lower.inv();
}
m_lower = new_lower;
m_upper = new_upper;
std::swap(m_lower_open, m_upper_open);
v_dependency * new_lower_dep = m_upper_dep;
SASSERT(!m_upper.is_infinite());
m_upper_dep = m_manager.mk_join(m_lower_dep, m_upper_dep);
m_lower_dep = new_lower_dep;
}
else {
UNREACHABLE();
}
return *this;
}
interval & interval::operator/=(interval const & other) {
SASSERT(!other.contains_zero());
if (is_zero()) {
// 0/other = 0 if other != 0
TRACE("interval", other.display_with_dependencies(tout););
if (other.m_lower.is_pos() || (other.m_lower.is_zero() && other.m_lower_open)) {
// other.lower > 0
m_lower_dep = join(m_lower_dep, other.m_lower_dep);
m_upper_dep = join(m_upper_dep, other.m_lower_dep);
}
else {
// assertion must hold since !other.contains_zero()
SASSERT(other.m_upper.is_neg() || (other.m_upper.is_zero() && other.m_upper_open));
// other.upper < 0
m_lower_dep = join(m_lower_dep, other.m_upper_dep);
m_upper_dep = join(m_upper_dep, other.m_upper_dep);
}
return *this;
}
else {
interval tmp(other);
tmp.inv();
return operator*=(tmp);
}
}
void interval::expt(unsigned n) {
if (n == 1)
return;
if (n % 2 == 0) {
if (m_lower.is_pos()) {
// [l, u]^n = [l^n, u^n] if l > 0
// 0 < a <= x --> a^n <= x^n (lower bound guarantees that is positive)
// 0 < a <= x <= b --> x^n <= b^n (use lower and upper bound -- need the fact that x is positive)
m_lower.expt(n);
m_upper.expt(n);
m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep);
}
else if (m_upper.is_neg()) {
// [l, u]^n = [u^n, l^n] if u < 0
// a <= x <= b < 0 --> x^n <= a^n (use lower and upper bound -- need the fact that x is negative)
// x <= b < 0 --> b^n <= x^n
std::swap(m_lower, m_upper);
std::swap(m_lower_open, m_upper_open);
std::swap(m_lower_dep, m_upper_dep);
m_lower.expt(n);
m_upper.expt(n);
m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep);
}
else {
// [l, u]^n = [0, max{l^n, u^n}] otherwise
// we need both bounds to justify upper bound
TRACE("interval", tout << "before: " << m_lower << " " << m_upper << " " << n << "\n";);
m_lower.expt(n);
m_upper.expt(n);
TRACE("interval", tout << "after: " << m_lower << " " << m_upper << "\n";);
if (m_lower > m_upper || (m_lower == m_upper && !m_lower_open && m_upper_open)) {
m_upper = m_lower;
m_upper_open = m_lower_open;
}
m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep);
m_lower = ext_numeral(0);
m_lower_open = false;
m_lower_dep = 0;
}
}
else {
// Remark: when n is odd x^n is monotonic.
m_lower.expt(n);
m_upper.expt(n);
}
}
void interval::display(std::ostream & out) const {
out << (m_lower_open ? "(" : "[") << m_lower << ", " << m_upper << (m_upper_open ? ")" : "]");
}
void interval::display_with_dependencies(std::ostream & out) const {
ptr_vector<void> vs;
m_manager.linearize(m_lower_dep, vs);
m_manager.linearize(m_upper_dep, vs);
out << "[";
display(out);
out << ", ";
bool first = true;
::display(out, vs.begin(), vs.end(), ", ", first);
out << "]";
}

138
src/util/old_interval.h Normal file
View file

@ -0,0 +1,138 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
old_interval.h
Abstract:
Old interval class. It is still used in some modules.
Author:
Leonardo de Moura (leonardo) 2008-12-09.
Revision History:
--*/
#ifndef _OLD_INTERVAL_H_
#define _OLD_INTERVAL_H_
#include"rational.h"
#include"dependency.h"
class ext_numeral {
public:
enum kind { MINUS_INFINITY, FINITE, PLUS_INFINITY };
private:
kind m_kind;
rational m_value;
explicit ext_numeral(kind k):m_kind(k) {}
public:
ext_numeral():m_kind(FINITE) {} /* zero */
explicit ext_numeral(bool plus_infinity):m_kind(plus_infinity ? PLUS_INFINITY : MINUS_INFINITY) {}
explicit ext_numeral(rational const & val):m_kind(FINITE), m_value(val) {}
explicit ext_numeral(int i):m_kind(FINITE), m_value(i) {}
ext_numeral(ext_numeral const & other):m_kind(other.m_kind), m_value(other.m_value) {}
bool is_infinite() const { return m_kind != FINITE; }
bool sign() const { return m_kind == MINUS_INFINITY || (m_kind == FINITE && m_value.is_neg()); }
void neg();
bool is_zero() const { return m_kind == FINITE && m_value.is_zero(); }
bool is_neg() const { return sign(); }
bool is_pos() const { return !is_neg() && !is_zero(); }
rational const & to_rational() const { SASSERT(!is_infinite()); return m_value; }
ext_numeral & operator+=(ext_numeral const & other);
ext_numeral & operator-=(ext_numeral const & other);
ext_numeral & operator*=(ext_numeral const & other);
void inv();
void expt(unsigned n);
void display(std::ostream & out) const;
friend bool operator==(ext_numeral const & n1, ext_numeral const & n2);
friend bool operator<(ext_numeral const & n1, ext_numeral const & n2);
};
bool operator==(ext_numeral const & n1, ext_numeral const & n2);
bool operator<(ext_numeral const & n1, ext_numeral const & n2);
inline bool operator!=(ext_numeral const & n1, ext_numeral const & n2) { return !operator==(n1,n2); }
inline bool operator>(ext_numeral const & n1, ext_numeral const & n2) { return operator<(n2, n1); }
inline bool operator<=(ext_numeral const & n1, ext_numeral const & n2) { return !operator>(n1, n2); }
inline bool operator>=(ext_numeral const & n1, ext_numeral const & n2) { return !operator<(n1, n2); }
inline ext_numeral operator+(ext_numeral const & n1, ext_numeral const & n2) { return ext_numeral(n1) += n2; }
inline ext_numeral operator-(ext_numeral const & n1, ext_numeral const & n2) { return ext_numeral(n1) -= n2; }
inline ext_numeral operator*(ext_numeral const & n1, ext_numeral const & n2) { return ext_numeral(n1) *= n2; }
inline std::ostream & operator<<(std::ostream & out, ext_numeral const & n) { n.display(out); return out; }
class old_interval {
v_dependency_manager & m_manager;
ext_numeral m_lower;
ext_numeral m_upper;
bool m_lower_open;
bool m_upper_open;
v_dependency * m_lower_dep; // justification for the lower bound
v_dependency * m_upper_dep; // justification for the upper bound
v_dependency * join(v_dependency * d1, v_dependency * d2) { return m_manager.mk_join(d1, d2); }
v_dependency * join(v_dependency * d1, v_dependency * d2, v_dependency * d3) { return m_manager.mk_join(m_manager.mk_join(d1, d2), d3); }
v_dependency * join(v_dependency * d1, v_dependency * d2, v_dependency * d3, v_dependency * d4);
v_dependency * join_opt(v_dependency * d1, v_dependency * d2, v_dependency * opt1, v_dependency * opt2);
public:
explicit old_interval(v_dependency_manager & m);
explicit old_interval(v_dependency_manager & m, rational const & lower, bool l_open, v_dependency * l_dep, rational const & upper, bool u_open, v_dependency * u_dep);
explicit old_interval(v_dependency_manager & m, rational const & val, v_dependency * l_dep = 0, v_dependency * u_dep = 0);
explicit old_interval(v_dependency_manager & m, rational const & val, bool open, bool lower, v_dependency * d);
explicit old_interval(v_dependency_manager & m, ext_numeral const& lower, bool l_open, v_dependency * l_dep, ext_numeral const & upper, bool u_open, v_dependency * u_dep);
old_interval(old_interval const & other);
bool minus_infinity() const { return m_lower.is_infinite(); }
bool plus_infinity() const { return m_upper.is_infinite(); }
bool is_lower_open() const { return m_lower_open; }
bool is_upper_open() const { return m_upper_open; }
v_dependency * get_lower_dependencies() const { return m_lower_dep; }
v_dependency * get_upper_dependencies() const { return m_upper_dep; }
rational const & get_lower_value() const { SASSERT(!minus_infinity()); return m_lower.to_rational(); }
rational const & get_upper_value() const { SASSERT(!plus_infinity()); return m_upper.to_rational(); }
old_interval & operator=(old_interval const & other);
old_interval & operator+=(old_interval const & other);
old_interval & operator-=(old_interval const & other);
old_interval & operator*=(old_interval const & other);
old_interval & operator*=(rational const & value);
old_interval & operator/=(old_interval const & other);
bool operator==(old_interval const & other) const { return m_lower == other.m_lower && m_upper == other.m_upper && m_lower_open == other.m_lower_open && m_upper_open == other.m_upper_open; }
bool contains_zero() const;
bool contains(rational const& v) const;
old_interval & inv();
void expt(unsigned n);
void neg();
void display(std::ostream & out) const;
void display_with_dependencies(std::ostream & out) const;
bool is_P() const { return m_lower.is_pos() || m_lower.is_zero(); }
bool is_P0() const { return m_lower.is_zero() && !m_lower_open; }
bool is_P1() const { return m_lower.is_pos() || (m_lower.is_zero() && m_lower_open); }
bool is_N() const { return m_upper.is_neg() || m_upper.is_zero(); }
bool is_N0() const { return m_upper.is_zero() && !m_upper_open; }
bool is_N1() const { return m_upper.is_neg() || (m_upper.is_zero() && m_upper_open); }
bool is_M() const { return m_lower.is_neg() && m_upper.is_pos(); }
bool is_zero() const { return m_lower.is_zero() && m_upper.is_zero(); }
ext_numeral const& inf() const { return m_lower; }
ext_numeral const& sup() const { return m_upper; }
};
inline old_interval operator+(old_interval const & i1, old_interval const & i2) { return old_interval(i1) += i2; }
inline old_interval operator-(old_interval const & i1, old_interval const & i2) { return old_interval(i1) -= i2; }
inline old_interval operator*(old_interval const & i1, old_interval const & i2) { return old_interval(i1) *= i2; }
inline old_interval operator/(old_interval const & i1, old_interval const & i2) { return old_interval(i1) /= i2; }
inline old_interval expt(old_interval const & i, unsigned n) { old_interval tmp(i); tmp.expt(n); return tmp; }
inline std::ostream & operator<<(std::ostream & out, old_interval const & i) { i.display(out); return out; }
struct interval_detail{};
inline std::pair<old_interval, interval_detail> wd(old_interval const & i) { interval_detail d; return std::make_pair(i, d); }
inline std::ostream & operator<<(std::ostream & out, std::pair<old_interval, interval_detail> const & p) { p.first.display_with_dependencies(out); return out; }
// allow "customers" of this file to keep using interval
#define interval old_interval
#endif /* _OLD_INTERVAL_H_ */

165
src/util/optional.h Normal file
View file

@ -0,0 +1,165 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
optional.h
Abstract:
Discriminated union of a type T.
It defines the notion of initialized/uninitialized objects.
Author:
Leonardo de Moura (leonardo) 2006-09-29.
Revision History:
--*/
#ifndef _OPTIONAL_H_
#define _OPTIONAL_H_
template<typename T>
class optional {
char m_obj[sizeof(T)];
char m_initialized;
void construct(const T & val) {
m_initialized = 1;
new (reinterpret_cast<void *>(m_obj)) T(val);
}
void destroy() {
if (m_initialized == 1) {
reinterpret_cast<T *>(m_obj)->~T();
}
m_initialized = 0;
}
public:
optional():
m_initialized(0) {}
explicit optional(const T & val) {
construct(val);
}
optional(const optional<T> & val):
m_initialized(0) {
if (val.m_initialized == 1) {
construct(*val);
}
}
~optional() {
destroy();
}
static optional const & undef() { static optional u; return u; }
bool initialized() const { return m_initialized == 1; }
operator bool() const { return m_initialized == 1; }
bool operator!() const { return m_initialized == 0; }
T * get() const {
if (m_initialized == 1) {
return reinterpret_cast<T *>(m_obj);
}
else {
return 0;
}
}
void set_invalid() {
if (m_initialized == 1) {
destroy();
}
}
T * operator->() {
SASSERT(m_initialized==1);
return reinterpret_cast<T *>(m_obj);
}
T const * operator->() const {
SASSERT(m_initialized==1);
return reinterpret_cast<T const *>(m_obj);
}
const T & operator*() const {
SASSERT(m_initialized==1);
return *reinterpret_cast<T const*>(m_obj);
}
T & operator*() {
SASSERT(m_initialized==1);
return *reinterpret_cast<T *>(m_obj);
}
optional & operator=(const T & val) {
destroy();
construct(val);
return * this;
}
optional & operator=(const optional & val) {
if (&val != this) {
destroy();
if (val.m_initialized) {
construct(*val);
}
}
return *this;
}
};
/**
\brief Template specialization for pointers. NULL represents uninitialized pointers.
*/
template<typename T>
class optional<T*> {
T * m_ptr;
static optional m_undef;
public:
optional():m_ptr(0) {}
explicit optional(T * val):m_ptr(val) {}
optional(const optional & val):m_ptr(val.m_ptr) {}
static optional const & undef() { return m_undef; }
bool initialized() const { return m_ptr != 0 ; }
operator bool() const { return m_ptr != 0; }
bool operator!() const { return m_ptr == 0; }
void reset() { m_ptr = 0; }
optional & operator=(T * val) {
m_ptr = val;
return *this;
}
optional & operator=(const optional & val) {
m_ptr = val.m_ptr;
return *this;
}
T ** operator->() { return &m_ptr; }
T * operator*() const { return m_ptr; }
T * & operator*() { return m_ptr; }
};
#endif /* _OPTIONAL_H_ */

68
src/util/page.cpp Normal file
View file

@ -0,0 +1,68 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
page.cpp
Abstract:
Goodies for manipulating pages of memory.
Author:
Leonardo de Moura (leonardo) 2011-02-27.
Revision History:
--*/
#include"page.h"
#include"debug.h"
inline void set_page_header(char * page, char * prev, bool default_page) {
size_t header = reinterpret_cast<size_t>(prev) | static_cast<size_t>(default_page);
reinterpret_cast<size_t *>(page)[-1] = header;
SASSERT(is_default_page(page) == default_page);
SASSERT(prev_page(page) == prev);
}
inline char * alloc_page(size_t s) { char * r = alloc_svect(char, s+PAGE_HEADER_SZ); return r + PAGE_HEADER_SZ; }
inline void del_page(char * page) { dealloc_svect(page - PAGE_HEADER_SZ); }
void del_pages(char * page) {
while (page != 0) {
char * prev = prev_page(page);
del_page(page);
page = prev;
}
}
char * allocate_default_page(char * prev, char * & free_pages) {
char * r;
if (free_pages) {
r = free_pages;
free_pages = prev_page(free_pages);
}
else {
r = alloc_page(DEFAULT_PAGE_SIZE);
}
set_page_header(r, prev, true);
return r;
}
char * allocate_page(char * prev, size_t sz) {
char * r = alloc_page(sz);
set_page_header(r, prev, false);
return r;
}
void recycle_page(char * p, char * & free_pages) {
if (is_default_page(p)) {
set_page_header(p, free_pages, true);
free_pages = p;
}
else {
del_page(p);
}
}

42
src/util/page.h Normal file
View file

@ -0,0 +1,42 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
page.h
Abstract:
Goodies for manipulating pages of memory.
Author:
Leonardo de Moura (leonardo) 2011-02-27.
Revision History:
--*/
#ifndef _PAGE_H_
#define _PAGE_H_
#include"memory_manager.h"
#define PAGE_HEADER_SZ sizeof(size_t)
#define DEFAULT_PAGE_SIZE (8192 - PAGE_HEADER_SZ)
#define PAGE_HEADER_MASK (static_cast<size_t>(-1) - 1)
inline char * prev_page(char * page) {
size_t tagged_ptr = reinterpret_cast<size_t *>(page)[-1];
return reinterpret_cast<char *>(tagged_ptr & PAGE_HEADER_MASK);
}
inline bool is_default_page(char * page) {
size_t tagged_ptr = reinterpret_cast<size_t *>(page)[-1];
return static_cast<bool>(tagged_ptr & 1);
}
inline char * end_of_default_page(char * p) { return p + DEFAULT_PAGE_SIZE; }
void del_pages(char * page);
char * allocate_default_page(char * prev, char * & free_pages);
char * allocate_page(char * prev, size_t sz);
void recycle_page(char * p, char * & free_pages);
#endif

621
src/util/parray.h Normal file
View file

@ -0,0 +1,621 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
parray.h
Abstract:
Persistent Arrays.
Author:
Leonardo de Moura (leonardo) 2011-02-21.
Revision History:
--*/
#ifndef _PARRAY_H_
#define _PARRAY_H_
#include"vector.h"
#include"trace.h"
template<typename C>
class parray_manager {
public:
typedef typename C::value value;
typedef typename C::value_manager value_manager;
typedef typename C::allocator allocator;
private:
static size_t capacity(value * vs) {
return vs == 0 ? 0 : (reinterpret_cast<size_t*>(vs))[-1];
}
value * allocate_values(size_t c) {
size_t * mem = static_cast<size_t*>(m_allocator.allocate(sizeof(value)*c + sizeof(size_t)));
*mem = c;
++mem;
value * r = reinterpret_cast<value*>(mem);
SASSERT(capacity(r) == c);
TRACE("parray_mem", tout << "allocated values[" << c << "]: " << r << "\n";);
return r;
}
void deallocate_values(value * vs) {
if (vs == 0)
return;
size_t c = capacity(vs);
TRACE("parray_mem", tout << "deallocated values[" << c << "]: " << vs << "\n";);
size_t * mem = reinterpret_cast<size_t*>(vs);
--mem;
m_allocator.deallocate(sizeof(value)*c + sizeof(size_t), mem);
}
enum ckind { SET, PUSH_BACK, POP_BACK, ROOT };
struct cell {
unsigned m_ref_count:30;
unsigned m_kind:2;
union {
unsigned m_idx;
unsigned m_size;
};
value m_elem;
union {
cell * m_next;
value * m_values;
};
ckind kind() const { return static_cast<ckind>(m_kind); }
unsigned idx() const { SASSERT(kind() != ROOT); return m_idx; }
unsigned size() const { SASSERT(kind() == ROOT); return m_size; }
cell * next() const { SASSERT(kind() != ROOT); return m_next; }
value const & elem() const { SASSERT(kind() == SET || kind() == PUSH_BACK); return m_elem; }
cell(ckind k):m_ref_count(1), m_kind(k), m_size(0), m_values(0) {}
};
value_manager & m_vmanager;
allocator & m_allocator;
ptr_vector<cell> m_get_values_tmp;
ptr_vector<cell> m_reroot_tmp;
void inc_ref(value const & v) {
if (C::ref_count)
m_vmanager.inc_ref(v);
}
void dec_ref(value const & v) {
if (C::ref_count)
m_vmanager.dec_ref(v);
}
void dec_ref(unsigned sz, value * vs) {
if (C::ref_count)
for (unsigned i = 0; i < sz; i++)
m_vmanager.dec_ref(vs[i]);
}
cell * mk(ckind k) {
cell * r = new (m_allocator.allocate(sizeof(cell))) cell(k);
TRACE("parray_mem", tout << "allocated cell: " << r << "\n";);
return r;
}
void del(cell * c) {
while (true) {
cell * next = 0;
switch (c->kind()) {
case SET:
case PUSH_BACK:
dec_ref(c->elem());
next = c->next();
break;
case POP_BACK:
next = c->next();
break;
case ROOT:
dec_ref(c->size(), c->m_values);
deallocate_values(c->m_values);
break;
}
TRACE("parray_mem", tout << "deallocated cell: " << c << "\n";);
c->~cell();
m_allocator.deallocate(sizeof(cell), c);
if (next == 0)
return;
SASSERT(next->m_ref_count > 0);
next->m_ref_count--;
if (next->m_ref_count > 0)
return;
c = next;
}
}
void inc_ref(cell * c) {
if (!c) return;
c->m_ref_count++;
}
void dec_ref(cell * c) {
if (!c) return;
TRACE("parray_mem", tout << "dec_ref(" << c << "), ref_count: " << c->m_ref_count << "\n";);
SASSERT(c->m_ref_count > 0);
c->m_ref_count--;
if (c->m_ref_count == 0)
del(c);
}
void expand(value * & vs) {
size_t curr_capacity = capacity(vs);
size_t new_capacity = curr_capacity == 0 ? 2 : (3 * curr_capacity + 1) >> 1;
value * new_vs = allocate_values(new_capacity);
if (curr_capacity > 0) {
for (size_t i = 0; i < curr_capacity; i++)
new_vs[i] = vs[i];
deallocate_values(vs);
}
vs = new_vs;
}
void rset(value * vs, unsigned i, value const & v) {
inc_ref(v);
dec_ref(vs[i]);
vs[i] = v;
}
void rset(cell * c, unsigned i, value const & v) {
SASSERT(c->kind() == ROOT);
SASSERT(i < c->size());
rset(c->m_values, i, v);
}
void rpush_back(value * & vs, unsigned & sz, value const & v) {
if (sz == capacity(vs))
expand(vs);
SASSERT(sz < capacity(vs));
inc_ref(v);
vs[sz] = v;
sz++;
}
void rpush_back(cell * c, value const & v) {
SASSERT(c->kind() == ROOT);
rpush_back(c->m_values, c->m_size, v);
}
void rpop_back(value * vs, unsigned & sz) {
sz--;
dec_ref(vs[sz]);
}
void rpop_back(cell * c) {
SASSERT(c->kind() == ROOT);
rpop_back(c->m_values, c->m_size);
}
void copy_values(value * s, unsigned sz, value * & t) {
SASSERT(t == 0);
t = allocate_values(capacity(s));
for (unsigned i = 0; i < sz; i++) {
t[i] = s[i];
inc_ref(t[i]);
}
}
unsigned get_values(cell * s, value * & vs) {
ptr_vector<cell> & cs = m_get_values_tmp;
cs.reset();
cell * r = s;
while (r->kind() != ROOT) {
cs.push_back(r);
r = r->next();
}
SASSERT(r->kind() == ROOT);
unsigned sz = r->m_size;
vs = 0;
copy_values(r->m_values, sz, vs);
unsigned i = cs.size();
while (i > 0) {
--i;
cell * curr = cs[i];
switch (curr->kind()) {
case SET:
rset(vs, curr->m_idx, curr->m_elem);
break;
case POP_BACK:
rpop_back(vs, sz);
break;
case PUSH_BACK:
rpush_back(vs, sz, curr->m_elem);
break;
case ROOT:
UNREACHABLE();
break;
}
}
return sz;
}
void unfold(cell * c) {
if (c->kind() == ROOT)
return;
value * vs;
unsigned sz = get_values(c, vs);
dec_ref(c->m_next);
if (c->kind() == SET || c->kind() == PUSH_BACK)
dec_ref(c->m_elem);
c->m_next = 0;
c->m_kind = ROOT;
c->m_size = sz;
c->m_values = vs;
SASSERT(c->kind() == ROOT);
}
public:
class ref {
cell * m_ref;
unsigned m_updt_counter; // counter for minimizing memory consumption when using preserve_roots option
ref(cell * r):m_ref(r), m_updt_counter(0) {}
bool root() const { return m_ref->kind() == ROOT; }
bool unshared() const { return m_ref->m_ref_count == 1; }
friend class parray_manager;
public:
ref():m_ref(0), m_updt_counter(0) {}
};
public:
parray_manager(value_manager & m, allocator & a):
m_vmanager(m),
m_allocator(a) {
}
value_manager & manager() { return m_vmanager; }
void mk(ref & r) {
dec_ref(r.m_ref);
cell * new_c = mk(ROOT);
r.m_ref = new_c;
r.m_updt_counter = 0;
SASSERT(new_c->m_ref_count == 1);
}
void del(ref & r) {
dec_ref(r.m_ref);
r.m_ref = 0;
r.m_updt_counter = 0;
}
void copy(ref const & s, ref & t) {
inc_ref(s.m_ref);
dec_ref(t.m_ref);
t.m_ref = s.m_ref;
t.m_updt_counter = 0;
}
unsigned size(ref const & r) const {
cell * c = r.m_ref;
if (c == 0) return 0;
while (true) {
switch (c->kind()) {
case SET:
c = c->next();
break;
case PUSH_BACK:
return c->idx() + 1;
case POP_BACK:
return c->idx() - 1;
case ROOT:
return c->size();
}
}
}
bool empty(ref const & r) const { return size(r) == 0; }
value const & get(ref const & r, unsigned i) const {
SASSERT(i < size(r));
unsigned trail_sz = 0;
cell * c = r.m_ref;
while (true) {
if (trail_sz > C::max_trail_sz) {
const_cast<parray_manager*>(this)->reroot(const_cast<ref&>(r));
SASSERT(r.m_ref->kind() == ROOT);
return r.m_ref->m_values[i];
}
switch (c->kind()) {
case SET:
case PUSH_BACK:
if (i == c->idx())
return c->elem();
trail_sz++;
c = c->next();
break;
case POP_BACK:
trail_sz++;
c = c->next();
break;
case ROOT:
return c->m_values[i];
}
}
}
void set(ref & r, unsigned i, value const & v) {
SASSERT(i < size(r));
if (r.root()) {
if (r.unshared()) {
rset(r.m_ref, i, v);
return;
}
if (C::preserve_roots) {
if (r.m_updt_counter > size(r)) {
unshare(r);
SASSERT(r.unshared());
SASSERT(r.m_updt_counter == 0);
rset(r.m_ref, i, v);
return;
}
r.m_updt_counter++;
cell * c = r.m_ref;
cell * new_c = mk(ROOT);
new_c->m_size = c->m_size;
new_c->m_values = c->m_values;
inc_ref(new_c);
c->m_kind = SET;
c->m_idx = i;
c->m_elem = c->m_values[i];
inc_ref(c->m_elem);
c->m_next = new_c;
dec_ref(c);
r.m_ref = new_c;
rset(new_c, i, v);
SASSERT(new_c->m_ref_count == 2);
return;
}
}
cell * new_c = mk(SET);
new_c->m_idx = i;
inc_ref(v);
new_c->m_elem = v;
new_c->m_next = r.m_ref;
r.m_ref = new_c;
SASSERT(new_c->m_ref_count == 1);
}
void set(ref const & s, unsigned i, value const & v, ref & r) {
SASSERT(i < size(s));
if (&s == &r) {
set(r, i, v);
return;
}
copy(s, r);
set(r, i, v);
}
void push_back(ref & r, value const & v) {
if (r.m_ref == 0)
mk(r);
if (r.root()) {
if (r.unshared()) {
rpush_back(r.m_ref, v);
return;
}
if (C::preserve_roots) {
if (r.m_updt_counter > size(r)) {
unshare(r);
SASSERT(r.unshared());
SASSERT(r.m_updt_counter == 0);
rpush_back(r.m_ref, v);
return;
}
r.m_updt_counter++;
cell * c = r.m_ref;
SASSERT(c->m_ref_count > 1);
cell * new_c = mk(ROOT);
new_c->m_size = c->m_size;
new_c->m_values = c->m_values;
inc_ref(new_c);
c->m_kind = POP_BACK;
c->m_idx = new_c->m_size + 1;
c->m_next = new_c;
dec_ref(c);
r.m_ref = new_c;
rpush_back(new_c, v);
SASSERT(new_c->m_ref_count == 2);
return;
}
}
cell * new_c = mk(PUSH_BACK);
new_c->m_idx = size(r.m_ref);
inc_ref(v);
new_c->m_elem = v;
new_c->m_next = r.m_ref;
r.m_ref = new_c;
SASSERT(new_c->m_ref_count == 1);
}
void push_back(ref const & s, value const & v, ref & r) {
if (&s == &r) {
push_back(r, v);
return;
}
copy(s, r);
push_back(r, v);
}
void pop_back(ref & r) {
SASSERT(!empty(r));
if (r.root()) {
if (r.unshared()) {
rpop_back(r.m_ref);
return;
}
if (C::preserve_roots) {
if (r.m_updt_counter > size(r)) {
unshare(r);
SASSERT(r.unshared());
SASSERT(r.m_updt_counter == 0);
rpop_back(r.m_ref);
return;
}
r.m_updt_counter++;
cell * c = r.m_ref;
SASSERT(c->m_ref_count > 1);
cell * new_c = mk(ROOT);
new_c->m_size = c->m_size;
new_c->m_values = c->m_values;
inc_ref(new_c);
c->m_kind = PUSH_BACK;
c->m_idx = new_c->m_size - 1;
c->m_elem = new_c->m_values[c->m_idx];
inc_ref(c->m_elem);
c->m_next = new_c;
dec_ref(c);
r.m_ref = new_c;
rpop_back(new_c);
SASSERT(new_c->m_ref_count == 2);
return;
}
}
cell * new_c = mk(POP_BACK);
new_c->m_idx = size(r.m_ref);
new_c->m_next = r.m_ref;
r.m_ref = new_c;
SASSERT(new_c->m_ref_count == 1);
}
void pop_back(ref const & s, ref & r) {
SASSERT(!empty(s));
if (&s == &r) {
pop_back(r);
return;
}
copy(s, r);
pop_back(r);
}
void unshare(ref & r) {
if (r.root() && r.unshared())
return;
cell * c = r.m_ref;
cell * new_c = mk(ROOT);
new_c->m_size = get_values(c, new_c->m_values);
SASSERT(new_c->m_ref_count == 1);
dec_ref(c);
r.m_ref = new_c;
r.m_updt_counter = 0;
SASSERT(r.root());
SASSERT(r.unshared());
}
void unfold(ref & r) {
if (r.root())
return;
unfold(r.m_ref);
r.m_updt_counter = 0;
SASSERT(r.root());
}
void reroot(ref & r) {
if (r.root())
return;
ptr_vector<cell> & cs = m_reroot_tmp;
cs.reset();
unsigned r_sz = size(r);
unsigned trail_split_idx = r_sz / C::factor;
unsigned i = 0;
cell * c = r.m_ref;
while (c->kind() != ROOT && i < trail_split_idx) {
cs.push_back(c);
c = c->next();
i++;
}
if (c->kind() != ROOT) {
// root is too far away.
unfold(c);
}
SASSERT(c->kind() == ROOT);
i = cs.size();
while (i > 0) {
--i;
cell * p = cs[i];
SASSERT(c->m_kind == ROOT);
unsigned sz = c->m_size;
value * vs = c->m_values;
SASSERT(p->m_kind != ROOT);
SASSERT(p->m_next == c);
switch (p->m_kind) {
case SET:
c->m_kind = SET;
c->m_idx = p->m_idx;
c->m_elem = vs[c->m_idx];
vs[p->m_idx] = p->m_elem;
break;
case PUSH_BACK:
c->m_kind = POP_BACK;
if (sz == capacity(vs))
expand(vs);
c->m_idx = sz;
vs[sz] = p->m_elem;
sz++;
break;
case POP_BACK:
c->m_kind = PUSH_BACK;
--sz;
c->m_idx = sz;
c->m_elem = vs[sz];
break;
case ROOT:
UNREACHABLE();
break;
}
inc_ref(p);
c->m_next = p;
// p does not point to c anymore
dec_ref(c);
p->m_kind = ROOT;
p->m_size = sz;
p->m_values = vs;
c = p;
}
SASSERT(c == r.m_ref);
SASSERT(c->kind() == ROOT);
SASSERT(c->m_size == r_sz);
r.m_updt_counter = 0;
SASSERT(r.root());
}
void display_info(std::ostream & out, ref const & r) {
cell * c = r.m_ref;
if (c == 0) {
out << "<null>";
return;
}
while (true) {
out << "cell[" << c << ", ";
switch (c->kind()) {
case SET: out << "set, " << c->m_idx; break;
case PUSH_BACK: out << "push, " << c->m_idx; break;
case POP_BACK: out << "pop, " << c->m_idx; break;
case ROOT: out << "root, " << c->m_size << ", " << capacity(c->m_values); break;
}
out << "]#" << c->m_ref_count;
if (c->kind() == ROOT)
break;
out << " -> ";
c = c->next();
}
}
};
template<typename T>
struct dummy_value_manager {
void inc_ref(T const &) {}
void dec_ref(T const &) {}
};
#endif

76
src/util/permutation.cpp Normal file
View file

@ -0,0 +1,76 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
permutation.cpp
Abstract:
Goodies for managing permutations.
Author:
Leonardo de Moura (leonardo) 2011-06-10.
Revision History:
--*/
#include"permutation.h"
permutation::permutation(unsigned size) {
reset(size);
}
void permutation::reset(unsigned size) {
m_p.reset();
m_inv_p.reset();
for (unsigned i = 0; i < size; i++) {
m_p.push_back(i);
m_inv_p.push_back(i);
}
}
void permutation::swap(unsigned i, unsigned j) {
unsigned i_prime = m_p[i];
unsigned j_prime = m_p[j];
std::swap(m_p[i], m_p[j]);
std::swap(m_inv_p[i_prime], m_inv_p[j_prime]);
}
/**
\brief Move i after j.
*/
void permutation::move_after(unsigned i, unsigned j) {
if (i >= j)
return;
unsigned i_prime = m_p[i];
for (unsigned k = i; k < j; k++) {
m_p[k] = m_p[k+1];
m_inv_p[m_p[k]] = k;
}
m_p[j] = i_prime;
m_inv_p[i_prime] = j;
SASSERT(check_invariant());
}
void permutation::display(std::ostream & out) const {
unsigned n = m_p.size();
for (unsigned i = 0; i < n; i++) {
if (i > 0)
out << " ";
out << i << ":" << m_p[i];
}
}
bool permutation::check_invariant() const {
SASSERT(m_p.size() == m_inv_p.size());
unsigned n = m_p.size();
for (unsigned i = 0; i < n; i++) {
SASSERT(m_p[i] < n);
SASSERT(m_inv_p[i] < n);
SASSERT(m_p[m_inv_p[i]] == i);
SASSERT(m_inv_p[m_p[i]] == i);
}
return true;
}

93
src/util/permutation.h Normal file
View file

@ -0,0 +1,93 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
permutation.h
Abstract:
Simple abstraction for managing permutations.
Author:
Leonardo de Moura (leonardo) 2011-06-10.
Revision History:
--*/
#ifndef _PERMUTATION_H_
#define _PERMUTATION_H_
#include<iostream>
#include"vector.h"
class permutation {
unsigned_vector m_p;
unsigned_vector m_inv_p;
public:
permutation(unsigned size = 0);
void reset(unsigned size = 0);
unsigned operator()(unsigned i) const { return m_p[i]; }
unsigned inv(unsigned i_prime) const { return m_inv_p[i_prime]; }
void swap(unsigned i, unsigned j);
void move_after(unsigned i, unsigned j);
void display(std::ostream & out) const;
bool check_invariant() const;
};
inline std::ostream & operator<<(std::ostream & out, permutation const & p) {
p.display(out);
return out;
}
/**
\brief Apply permutation p to data.
The algorithm does not use any extra memory.
Requirement: swap(T, T) must be available.
This version will perform destructive updates to p.
Use apply_permutation if p must not be preserved
*/
template<typename T>
void apply_permutation_core(unsigned sz, T * data, unsigned * p) {
int * p1 = reinterpret_cast<int*>(p);
for (int i = 0; i < static_cast<int>(sz); i++) {
if (p1[i] < 0)
continue; // already processed
int j = i;
while (true) {
SASSERT(j >= 0);
int p_j = p1[j];
SASSERT(p_j >= 0);
SASSERT(p_j < static_cast<int>(sz));
p1[j] = - p1[j] - 1; // mark as done
if (p_j == i)
break; // cycle starting at i is done
swap(data[j], data[p_j]);
j = p_j;
}
}
}
/**
\brief Apply permutation p to data.
The algorithm does not use any extra memory.
Requirement: swap(T, T) must be available.
*/
template<typename T>
void apply_permutation(unsigned sz, T * data, unsigned const * p) {
apply_permutation_core(sz, data, const_cast<unsigned*>(p));
// restore p
int * p1 = reinterpret_cast<int*>(const_cast<unsigned*>(p));
for (unsigned i = 0; i < sz; i++) {
p1[i] = - p1[i] - 1;
}
}
#endif

50
src/util/pool.h Normal file
View file

@ -0,0 +1,50 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
pool.h
Abstract:
Object pool.
Author:
Leonardo de Moura (leonardo) 2007-02-15.
Revision History:
--*/
#ifndef _POOL_H_
#define _POOL_H_
#include"util.h"
#include"vector.h"
template<typename T>
class pool {
ptr_vector<T> m_objs;
public:
~pool() {
std::for_each(m_objs.begin(), m_objs.end(), delete_proc<T>());
}
T * mk() {
if (m_objs.empty()) {
return alloc(T);
}
else {
T * r = m_objs.back();
m_objs.pop_back();
return r;
}
}
void recycle(T * obj) {
m_objs.push_back(obj);
}
};
#endif /* _POOL_H_ */

37
src/util/pop_scopes.h Normal file
View file

@ -0,0 +1,37 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
pop_scopes.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-02.
Revision History:
--*/
#ifndef _POP_SCOPES_H_
#define _POP_SCOPES_H_
#define POP_SCOPES(_num_scopes, _lim, _trail, _action) \
if (_num_scopes > 0) \
{ \
unsigned scope_lvl = _lim.size(); \
unsigned new_lvl = scope_lvl - _num_scopes; \
unsigned curr_size = _trail.size(); \
unsigned old_size = _lim[new_lvl]; \
for (unsigned i = curr_size-1; i >= old_size && i != static_cast<unsigned>(-1); --i) { \
_action; \
} \
_trail.shrink(old_size); \
_lim.shrink(new_lvl); \
}
#endif /* _POP_SCOPES_H_ */

View file

@ -0,0 +1,129 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
prime_generator.cpp
Abstract:
Prime generator
Author:
Leonardo (leonardo) 2011-12-23
Notes:
--*/
#include"prime_generator.h"
#define PRIME_LIST_MAX_SIZE 1<<20
prime_generator::prime_generator() {
m_primes.push_back(2);
m_primes.push_back(3);
process_next_k_numbers(128);
}
void prime_generator::process_next_k_numbers(uint64 k) {
svector<uint64> todo;
uint64 begin = m_primes.back() + 2;
uint64 end = begin + k;
for (uint64 i = begin; i < end; i+=2) {
todo.push_back(i);
}
unsigned j = 1;
SASSERT(m_primes[j] == 3);
while (!todo.empty()) {
unsigned sz = m_primes.size();
for (; j < sz; j++) {
uint64 p = m_primes[j];
unsigned todo_sz = todo.size();
unsigned k1 = 0;
unsigned k2 = 0;
for (; k1 < todo_sz; k1++) {
if (todo[k1] % p == 0)
continue;
todo[k2] = todo[k1];
k2++;
}
todo.shrink(k2);
if (k2 == 0)
return;
if (p > (todo[k2-1] / p) + 1) {
// all numbers in todo are primes
for (unsigned k1 = 0; k1 < k2; k1++) {
m_primes.push_back(todo[k1]);
}
return;
}
}
uint64 p = m_primes.back();
p = p*p;
unsigned todo_sz = todo.size();
unsigned k1 = 0;
for (k1 = 0; k1 < todo_sz; k1++) {
if (todo[k1] < p) {
m_primes.push_back(todo[k1]);
}
break;
}
unsigned k2 = 0;
for (; k1 < todo_sz; k1++, k2++) {
todo[k2] = todo[k1];
}
todo.shrink(k2);
}
}
void prime_generator::finalize() {
m_primes.finalize();
}
uint64 prime_generator::operator()(unsigned idx) {
if (idx < m_primes.size())
return m_primes[idx];
if (idx > PRIME_LIST_MAX_SIZE)
throw prime_generator_exception("prime generator capacity exceeded");
process_next_k_numbers(1024);
if (idx < m_primes.size())
return m_primes[idx];
while (idx <= m_primes.size())
process_next_k_numbers(1024*16);
return m_primes[idx];
}
prime_generator g_prime_generator;
prime_iterator::prime_iterator(prime_generator * g):m_idx(0) {
if (g == 0) {
m_generator = &g_prime_generator;
m_global = true;
}
else {
m_generator = g;
m_global = false;
}
}
uint64 prime_iterator::next() {
unsigned idx = m_idx;
m_idx++;
if (!m_global) {
return (*m_generator)(idx);
}
else {
uint64 r;
#pragma omp critical (prime_iterator)
{
r = (*m_generator)(idx);
}
return r;
}
}
void prime_iterator::finalize() {
g_prime_generator.finalize();
}

View file

@ -0,0 +1,53 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
prime_generator.h
Abstract:
Prime generator
Author:
Leonardo (leonardo) 2011-12-23
Notes:
--*/
#ifndef _PRIME_GENERATOR_H_
#define _PRIME_GENERATOR_H_
#include"vector.h"
#include"z3_exception.h"
#include"util.h"
class prime_generator_exception : public default_exception {
public:
prime_generator_exception(char const * msg):default_exception(msg) {}
};
/**
\brief Prime generator
*/
class prime_generator {
svector<uint64> m_primes;
void process_next_k_numbers(uint64 k);
public:
prime_generator();
uint64 operator()(unsigned idx);
void finalize();
};
class prime_iterator {
unsigned m_idx;
prime_generator * m_generator;
bool m_global;
public:
prime_iterator(prime_generator * g = 0);
uint64 next();
static void finalize();
};
#endif

View file

@ -0,0 +1,56 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
ptr_scoped_buffer.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2011-03-03.
Revision History:
--*/
#ifndef _PTR_SCOPED_BUFFER_H_
#define _PTR_SCOPED_BUFFER_H_
#include"util.h"
#include"debug.h"
#include"buffer.h"
template<typename T, unsigned INITIAL_SIZE=16, typename D = delete_proc<T> >
class ptr_scoped_buffer : private ptr_buffer<T, INITIAL_SIZE> {
D m_deallocator;
void deallocate_all() {
typename ptr_buffer<T, INITIAL_SIZE>::iterator it = ptr_buffer<T, INITIAL_SIZE>::begin();
typename ptr_buffer<T, INITIAL_SIZE>::iterator end = ptr_buffer<T, INITIAL_SIZE>::end();
for (; it != end; ++it)
m_deallocator(*it);
}
public:
typedef typename ptr_buffer<T, INITIAL_SIZE>::const_iterator const_iterator;
ptr_scoped_buffer(D const & m = D()):ptr_buffer<T, INITIAL_SIZE>(), m_deallocator(m) {}
~ptr_scoped_buffer() { deallocate_all(); }
void reset() { deallocate_all(); ptr_buffer<T, INITIAL_SIZE>::reset(); }
void finalize() { deallocate_all(); ptr_buffer<T, INITIAL_SIZE>::finalize(); }
/** \brief Release ownership of the pointers stored in the buffer */
void release() { ptr_buffer<T, INITIAL_SIZE>::reset(); }
unsigned size() const { return ptr_buffer<T, INITIAL_SIZE>::size(); }
bool empty() const { return ptr_buffer<T, INITIAL_SIZE>::empty(); }
const_iterator begin() const { return ptr_buffer<T, INITIAL_SIZE>::begin(); }
const_iterator end() const { return ptr_buffer<T, INITIAL_SIZE>::end(); }
void push_back(T * elem) { return ptr_buffer<T, INITIAL_SIZE>::push_back(elem); }
T * back() const { return ptr_buffer<T, INITIAL_SIZE>::back(); }
void pop_back() { m_deallocator(back()); ptr_buffer<T, INITIAL_SIZE>::pop_back(); }
T * get(unsigned idx) const { return ptr_buffer<T, INITIAL_SIZE>::get(idx); }
void set(unsigned idx, T * e) { T * old_e = get(idx); if (e != old_e) m_deallocator(old_e); ptr_buffer<T, INITIAL_SIZE>::set(idx, e); }
void append(unsigned n, T * const * elems) { ptr_buffer<T, INITIAL_SIZE>::append(n, elems); }
};
#endif

Some files were not shown because too many files have changed in this diff Show more