mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
re-organization of muz
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
9e61820125
commit
e4338f085b
37 changed files with 6 additions and 875 deletions
|
@ -1 +1,2 @@
|
|||
muZ and Quantifier Elimination modules
|
||||
muZ: routines related to solving satisfiability of Horn clauses and
|
||||
solving Datalog programs.
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
equiv_proof_converter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Proof converter that applies equivalence rule to leaves.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-11-23
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "equiv_proof_converter.h"
|
||||
#include "ast_pp.h"
|
||||
#include "scoped_proof.h"
|
||||
|
||||
void equiv_proof_converter::insert(expr* fml1, expr* fml2) {
|
||||
if (fml1 != fml2) {
|
||||
scoped_proof _sp(m);
|
||||
proof_ref p1(m), p2(m), p3(m);
|
||||
p1 = m.mk_asserted(fml1);
|
||||
p2 = m.mk_rewrite(fml1, fml2);
|
||||
p3 = m.mk_modus_ponens(p1, p2);
|
||||
TRACE("proof_converter", tout << mk_pp(p3.get(), m) << "\n";);
|
||||
SASSERT(m.has_fact(p3));
|
||||
m_replace.insert(p3);
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
equiv_proof_converter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Proof converter that applies equivalence rule to leaves.
|
||||
|
||||
Given a proof P with occurrences of [asserted fml]
|
||||
replace [asserted fml] by a proof of the form
|
||||
[mp [asserted fml'] [~ fml fml']]
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-11-23
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _EQUIV_PROOF_CONVERTER_H_
|
||||
#define _EQUIV_PROOF_CONVERTER_H_
|
||||
|
||||
#include "replace_proof_converter.h"
|
||||
|
||||
class equiv_proof_converter : public proof_converter {
|
||||
ast_manager& m;
|
||||
replace_proof_converter m_replace;
|
||||
public:
|
||||
|
||||
equiv_proof_converter(ast_manager& m): m(m), m_replace(m) {}
|
||||
|
||||
virtual ~equiv_proof_converter() {}
|
||||
|
||||
virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) {
|
||||
m_replace(m, num_source, source, result);
|
||||
}
|
||||
|
||||
virtual proof_converter * translate(ast_translation & translator) {
|
||||
return m_replace.translate(translator);
|
||||
}
|
||||
|
||||
void insert(expr* fml1, expr* fml2);
|
||||
|
||||
ast_manager& get_manager() { return m; }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,666 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
heap_trie.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Heap trie structure.
|
||||
|
||||
Structure that lets you retrieve point-wise smaller entries
|
||||
of a tuple. A lookup is to identify entries whose keys
|
||||
are point-wise dominated by the lookup key.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-02-15.
|
||||
|
||||
Notes:
|
||||
|
||||
tries are unordered vectors of keys. This could be enhanced to use either
|
||||
heaps or sorting. The problem with using the heap implementation directly is that there is no way to
|
||||
retrieve elements less or equal to a key that is not already in the heap.
|
||||
If nodes have only a few elements, then this would also be a bloated data-structure to maintain.
|
||||
|
||||
Nodes are not de-allocated. Their reference count indicates if they are valid.
|
||||
Possibly, add garbage collection.
|
||||
|
||||
Maintaining sorted ranges for larger domains is another option.
|
||||
|
||||
Another possible enhancement is to resplay the tree.
|
||||
Keep current key index in the nodes.
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _HEAP_TRIE_H_
|
||||
#define _HEAP_TRIE_H_
|
||||
|
||||
#include "map.h"
|
||||
#include "vector.h"
|
||||
#include "buffer.h"
|
||||
#include "statistics.h"
|
||||
#include "small_object_allocator.h"
|
||||
|
||||
|
||||
template<typename Key, typename KeyLE, typename KeyHash, typename Value>
|
||||
class heap_trie {
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_inserts;
|
||||
unsigned m_num_removes;
|
||||
unsigned m_num_find_eq;
|
||||
unsigned m_num_find_le;
|
||||
unsigned m_num_find_le_nodes;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
enum node_t {
|
||||
trie_t,
|
||||
leaf_t
|
||||
};
|
||||
|
||||
class node {
|
||||
node_t m_type;
|
||||
unsigned m_ref;
|
||||
public:
|
||||
node(node_t t): m_type(t), m_ref(0) {}
|
||||
virtual ~node() {}
|
||||
node_t type() const { return m_type; }
|
||||
void inc_ref() { ++m_ref; }
|
||||
void dec_ref() { SASSERT(m_ref > 0); --m_ref; }
|
||||
unsigned ref_count() const { return m_ref; }
|
||||
virtual void display(std::ostream& out, unsigned indent) const = 0;
|
||||
virtual unsigned num_nodes() const = 0;
|
||||
virtual unsigned num_leaves() const = 0;
|
||||
};
|
||||
|
||||
class leaf : public node {
|
||||
Value m_value;
|
||||
public:
|
||||
leaf(): node(leaf_t) {}
|
||||
virtual ~leaf() {}
|
||||
Value const& get_value() const { return m_value; }
|
||||
void set_value(Value const& v) { m_value = v; }
|
||||
virtual void display(std::ostream& out, unsigned indent) const {
|
||||
out << " value: " << m_value;
|
||||
}
|
||||
virtual unsigned num_nodes() const { return 1; }
|
||||
virtual unsigned num_leaves() const { return this->ref_count()>0?1:0; }
|
||||
};
|
||||
|
||||
typedef buffer<std::pair<Key,node*>, true, 2> children_t;
|
||||
|
||||
// lean trie node
|
||||
class trie : public node {
|
||||
children_t m_nodes;
|
||||
public:
|
||||
trie(): node(trie_t) {}
|
||||
|
||||
virtual ~trie() {
|
||||
}
|
||||
|
||||
node* find_or_insert(Key k, node* n) {
|
||||
for (unsigned i = 0; i < m_nodes.size(); ++i) {
|
||||
if (m_nodes[i].first == k) {
|
||||
return m_nodes[i].second;
|
||||
}
|
||||
}
|
||||
m_nodes.push_back(std::make_pair(k, n));
|
||||
return n;
|
||||
}
|
||||
|
||||
bool find(Key k, node*& n) const {
|
||||
for (unsigned i = 0; i < m_nodes.size(); ++i) {
|
||||
if (m_nodes[i].first == k) {
|
||||
n = m_nodes[i].second;
|
||||
return n->ref_count() > 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// push nodes whose keys are <= key into vector.
|
||||
void find_le(KeyLE& le, Key key, ptr_vector<node>& nodes) {
|
||||
for (unsigned i = 0; i < m_nodes.size(); ++i) {
|
||||
if (le.le(m_nodes[i].first, key)) {
|
||||
node* n = m_nodes[i].second;
|
||||
if (n->ref_count() > 0){
|
||||
nodes.push_back(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
children_t const& nodes() const { return m_nodes; }
|
||||
children_t & nodes() { return m_nodes; }
|
||||
|
||||
virtual void display(std::ostream& out, unsigned indent) const {
|
||||
for (unsigned j = 0; j < m_nodes.size(); ++j) {
|
||||
if (j != 0 || indent > 0) {
|
||||
out << "\n";
|
||||
}
|
||||
for (unsigned i = 0; i < indent; ++i) {
|
||||
out << " ";
|
||||
}
|
||||
node* n = m_nodes[j].second;
|
||||
out << m_nodes[j].first << " refs: " << n->ref_count();
|
||||
n->display(out, indent + 1);
|
||||
}
|
||||
}
|
||||
|
||||
virtual unsigned num_nodes() const {
|
||||
unsigned sz = 1;
|
||||
for (unsigned j = 0; j < m_nodes.size(); ++j) {
|
||||
sz += m_nodes[j].second->num_nodes();
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
virtual unsigned num_leaves() const {
|
||||
unsigned sz = 0;
|
||||
for (unsigned j = 0; j < m_nodes.size(); ++j) {
|
||||
sz += m_nodes[j].second->num_leaves();
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
private:
|
||||
bool contains(Key k) {
|
||||
for (unsigned j = 0; j < m_nodes.size(); ++j) {
|
||||
if (m_nodes[j].first == k) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
small_object_allocator m_alloc;
|
||||
KeyLE& m_le;
|
||||
unsigned m_num_keys;
|
||||
unsigned_vector m_keys;
|
||||
unsigned m_do_reshuffle;
|
||||
node* m_root;
|
||||
stats m_stats;
|
||||
node* m_spare_leaf;
|
||||
node* m_spare_trie;
|
||||
|
||||
public:
|
||||
|
||||
heap_trie(KeyLE& le):
|
||||
m_alloc("heap_trie"),
|
||||
m_le(le),
|
||||
m_num_keys(0),
|
||||
m_do_reshuffle(4),
|
||||
m_root(0),
|
||||
m_spare_leaf(0),
|
||||
m_spare_trie(0)
|
||||
{}
|
||||
|
||||
~heap_trie() {
|
||||
del_node(m_root);
|
||||
del_node(m_spare_leaf);
|
||||
del_node(m_spare_trie);
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
return m_root?m_root->num_leaves():0;
|
||||
}
|
||||
|
||||
void reset(unsigned num_keys) {
|
||||
del_node(m_root);
|
||||
del_node(m_spare_leaf);
|
||||
del_node(m_spare_trie);
|
||||
m_num_keys = num_keys;
|
||||
m_keys.resize(num_keys);
|
||||
for (unsigned i = 0; i < num_keys; ++i) {
|
||||
m_keys[i] = i;
|
||||
}
|
||||
m_root = mk_trie();
|
||||
m_spare_trie = mk_trie();
|
||||
m_spare_leaf = mk_leaf();
|
||||
}
|
||||
|
||||
void insert(Key const* keys, Value const& val) {
|
||||
++m_stats.m_num_inserts;
|
||||
insert(m_root, num_keys(), keys, m_keys.c_ptr(), val);
|
||||
#if 0
|
||||
if (m_stats.m_num_inserts == (1 << m_do_reshuffle)) {
|
||||
m_do_reshuffle++;
|
||||
reorder_keys();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool find_eq(Key const* keys, Value& value) {
|
||||
++m_stats.m_num_find_eq;
|
||||
node* n = m_root;
|
||||
node* m;
|
||||
for (unsigned i = 0; i < num_keys(); ++i) {
|
||||
if (!to_trie(n)->find(get_key(keys, i), m)) {
|
||||
return false;
|
||||
}
|
||||
n = m;
|
||||
}
|
||||
value = to_leaf(n)->get_value();
|
||||
return true;
|
||||
}
|
||||
|
||||
void find_all_le(Key const* keys, vector<Value>& values) {
|
||||
++m_stats.m_num_find_le;
|
||||
ptr_vector<node> todo[2];
|
||||
todo[0].push_back(m_root);
|
||||
bool index = false;
|
||||
for (unsigned i = 0; i < num_keys(); ++i) {
|
||||
for (unsigned j = 0; j < todo[index].size(); ++j) {
|
||||
++m_stats.m_num_find_le_nodes;
|
||||
to_trie(todo[index][j])->find_le(m_le, get_key(keys, i), todo[!index]);
|
||||
}
|
||||
todo[index].reset();
|
||||
index = !index;
|
||||
}
|
||||
for (unsigned j = 0; j < todo[index].size(); ++j) {
|
||||
values.push_back(to_leaf(todo[index][j])->get_value());
|
||||
}
|
||||
}
|
||||
|
||||
// callback based find function
|
||||
class check_value {
|
||||
public:
|
||||
virtual bool operator()(Value const& v) = 0;
|
||||
};
|
||||
|
||||
bool find_le(Key const* keys, check_value& check) {
|
||||
++m_stats.m_num_find_le;
|
||||
++m_stats.m_num_find_le_nodes;
|
||||
return find_le(m_root, 0, keys, check);
|
||||
}
|
||||
|
||||
void remove(Key const* keys) {
|
||||
++m_stats.m_num_removes;
|
||||
// assumption: key is in table.
|
||||
node* n = m_root;
|
||||
node* m;
|
||||
for (unsigned i = 0; i < num_keys(); ++i) {
|
||||
n->dec_ref();
|
||||
VERIFY (to_trie(n)->find(get_key(keys, i), m));
|
||||
n = m;
|
||||
}
|
||||
n->dec_ref();
|
||||
}
|
||||
|
||||
void reset_statistics() {
|
||||
m_stats.reset();
|
||||
}
|
||||
|
||||
void collect_statistics(statistics& st) const {
|
||||
st.update("heap_trie.num_inserts", m_stats.m_num_inserts);
|
||||
st.update("heap_trie.num_removes", m_stats.m_num_removes);
|
||||
st.update("heap_trie.num_find_eq", m_stats.m_num_find_eq);
|
||||
st.update("heap_trie.num_find_le", m_stats.m_num_find_le);
|
||||
st.update("heap_trie.num_find_le_nodes", m_stats.m_num_find_le_nodes);
|
||||
if (m_root) st.update("heap_trie.num_nodes", m_root->num_nodes());
|
||||
unsigned_vector nums;
|
||||
ptr_vector<node> todo;
|
||||
if (m_root) todo.push_back(m_root);
|
||||
while (!todo.empty()) {
|
||||
node* n = todo.back();
|
||||
todo.pop_back();
|
||||
if (is_trie(n)) {
|
||||
trie* t = to_trie(n);
|
||||
unsigned sz = t->nodes().size();
|
||||
if (nums.size() <= sz) {
|
||||
nums.resize(sz+1);
|
||||
}
|
||||
++nums[sz];
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
todo.push_back(t->nodes()[i].second);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nums.size() < 16) nums.resize(16);
|
||||
st.update("heap_trie.num_1_children", nums[1]);
|
||||
st.update("heap_trie.num_2_children", nums[2]);
|
||||
st.update("heap_trie.num_3_children", nums[3]);
|
||||
st.update("heap_trie.num_4_children", nums[4]);
|
||||
st.update("heap_trie.num_5_children", nums[5]);
|
||||
st.update("heap_trie.num_6_children", nums[6]);
|
||||
st.update("heap_trie.num_7_children", nums[7]);
|
||||
st.update("heap_trie.num_8_children", nums[8]);
|
||||
st.update("heap_trie.num_9_children", nums[9]);
|
||||
st.update("heap_trie.num_10_children", nums[10]);
|
||||
st.update("heap_trie.num_11_children", nums[11]);
|
||||
st.update("heap_trie.num_12_children", nums[12]);
|
||||
st.update("heap_trie.num_13_children", nums[13]);
|
||||
st.update("heap_trie.num_14_children", nums[14]);
|
||||
st.update("heap_trie.num_15_children", nums[15]);
|
||||
unsigned sz = 0;
|
||||
for (unsigned i = 16; i < nums.size(); ++i) {
|
||||
sz += nums[i];
|
||||
}
|
||||
st.update("heap_trie.num_16+_children", sz);
|
||||
}
|
||||
|
||||
void display(std::ostream& out) const {
|
||||
m_root->display(out, 0);
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
class iterator {
|
||||
ptr_vector<node> m_path;
|
||||
unsigned_vector m_idx;
|
||||
vector<Key> m_keys;
|
||||
unsigned m_count;
|
||||
public:
|
||||
iterator(node* n) {
|
||||
if (!n) {
|
||||
m_count = UINT_MAX;
|
||||
}
|
||||
else {
|
||||
m_count = 0;
|
||||
first(n);
|
||||
}
|
||||
}
|
||||
Key const* keys() {
|
||||
return m_keys.c_ptr();
|
||||
}
|
||||
|
||||
Value const& value() const {
|
||||
return to_leaf(m_path.back())->get_value();
|
||||
}
|
||||
iterator& operator++() { fwd(); return *this; }
|
||||
iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
|
||||
bool operator==(iterator const& it) const {return m_count == it.m_count; }
|
||||
bool operator!=(iterator const& it) const {return m_count != it.m_count; }
|
||||
|
||||
private:
|
||||
void first(node* r) {
|
||||
SASSERT(r->ref_count() > 0);
|
||||
while (is_trie(r)) {
|
||||
trie* t = to_trie(r);
|
||||
m_path.push_back(r);
|
||||
unsigned sz = t->nodes().size();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
r = t->nodes()[i].second;
|
||||
if (r->ref_count() > 0) {
|
||||
m_idx.push_back(i);
|
||||
m_keys.push_back(t->nodes()[i].first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SASSERT(is_leaf(r));
|
||||
m_path.push_back(r);
|
||||
}
|
||||
|
||||
void fwd() {
|
||||
if (m_path.empty()) {
|
||||
m_count = UINT_MAX;
|
||||
return;
|
||||
}
|
||||
m_path.pop_back();
|
||||
while (!m_path.empty()) {
|
||||
trie* t = to_trie(m_path.back());
|
||||
unsigned idx = m_idx.back();
|
||||
unsigned sz = t->nodes().size();
|
||||
m_idx.pop_back();
|
||||
m_keys.pop_back();
|
||||
for (unsigned i = idx+1; i < sz; ++i) {
|
||||
node* r = t->nodes()[i].second;
|
||||
if (r->ref_count() > 0) {
|
||||
m_idx.push_back(i);
|
||||
m_keys.push_back(t->nodes()[i].first);
|
||||
first(r);
|
||||
++m_count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_path.pop_back();
|
||||
}
|
||||
m_count = UINT_MAX;
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin() const {
|
||||
return iterator(m_root);
|
||||
}
|
||||
|
||||
iterator end() const {
|
||||
return iterator(0);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
inline unsigned num_keys() const {
|
||||
return m_num_keys;
|
||||
}
|
||||
|
||||
inline Key const& get_key(Key const* keys, unsigned i) const {
|
||||
return keys[m_keys[i]];
|
||||
}
|
||||
|
||||
struct KeyEq {
|
||||
bool operator()(Key const& k1, Key const& k2) const {
|
||||
return k1 == k2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef hashtable<Key, KeyHash, KeyEq> key_set;
|
||||
|
||||
struct key_info {
|
||||
unsigned m_index;
|
||||
unsigned m_index_size;
|
||||
key_info(unsigned i, unsigned sz):
|
||||
m_index(i),
|
||||
m_index_size(sz)
|
||||
{}
|
||||
|
||||
bool operator<(key_info const& other) const {
|
||||
return
|
||||
(m_index_size < other.m_index_size) ||
|
||||
((m_index_size == other.m_index_size) &&
|
||||
(m_index < other.m_index));
|
||||
}
|
||||
};
|
||||
|
||||
void reorder_keys() {
|
||||
vector<key_set> weights;
|
||||
weights.resize(num_keys());
|
||||
unsigned_vector depth;
|
||||
ptr_vector<node> nodes;
|
||||
depth.push_back(0);
|
||||
nodes.push_back(m_root);
|
||||
while (!nodes.empty()) {
|
||||
node* n = nodes.back();
|
||||
unsigned d = depth.back();
|
||||
nodes.pop_back();
|
||||
depth.pop_back();
|
||||
if (is_trie(n)) {
|
||||
trie* t = to_trie(n);
|
||||
unsigned sz = t->nodes().size();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
nodes.push_back(t->nodes()[i].second);
|
||||
depth.push_back(d+1);
|
||||
weights[d].insert(t->nodes()[i].first);
|
||||
}
|
||||
}
|
||||
}
|
||||
SASSERT(weights.size() == num_keys());
|
||||
svector<key_info> infos;
|
||||
unsigned sz = 0;
|
||||
bool is_sorted = true;
|
||||
for (unsigned i = 0; i < weights.size(); ++i) {
|
||||
unsigned sz2 = weights[i].size();
|
||||
if (sz > sz2) {
|
||||
is_sorted = false;
|
||||
}
|
||||
sz = sz2;
|
||||
infos.push_back(key_info(i, sz));
|
||||
}
|
||||
if (is_sorted) {
|
||||
return;
|
||||
}
|
||||
std::sort(infos.begin(), infos.end());
|
||||
unsigned_vector sorted_keys, new_keys;
|
||||
for (unsigned i = 0; i < num_keys(); ++i) {
|
||||
unsigned j = infos[i].m_index;
|
||||
sorted_keys.push_back(j);
|
||||
new_keys.push_back(m_keys[j]);
|
||||
}
|
||||
// m_keys: i |-> key_index
|
||||
// new_keys: i |-> new_key_index
|
||||
// permutation: key_index |-> new_key_index
|
||||
SASSERT(sorted_keys.size() == num_keys());
|
||||
SASSERT(new_keys.size() == num_keys());
|
||||
SASSERT(m_keys.size() == num_keys());
|
||||
iterator it = begin();
|
||||
trie* new_root = mk_trie();
|
||||
IF_VERBOSE(2, verbose_stream() << "before reshuffle: " << m_root->num_nodes() << " nodes\n";);
|
||||
for (; it != end(); ++it) {
|
||||
IF_VERBOSE(2,
|
||||
for (unsigned i = 0; i < num_keys(); ++i) {
|
||||
for (unsigned j = 0; j < num_keys(); ++j) {
|
||||
if (m_keys[j] == i) {
|
||||
verbose_stream() << it.keys()[j] << " ";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
verbose_stream() << " |-> " << it.value() << "\n";);
|
||||
|
||||
insert(new_root, num_keys(), it.keys(), sorted_keys.c_ptr(), it.value());
|
||||
}
|
||||
del_node(m_root);
|
||||
m_root = new_root;
|
||||
for (unsigned i = 0; i < m_keys.size(); ++i) {
|
||||
m_keys[i] = new_keys[i];
|
||||
}
|
||||
|
||||
IF_VERBOSE(2, verbose_stream() << "after reshuffle: " << new_root->num_nodes() << " nodes\n";);
|
||||
IF_VERBOSE(2,
|
||||
it = begin();
|
||||
for (; it != end(); ++it) {
|
||||
for (unsigned i = 0; i < num_keys(); ++i) {
|
||||
for (unsigned j = 0; j < num_keys(); ++j) {
|
||||
if (m_keys[j] == i) {
|
||||
verbose_stream() << it.keys()[j] << " ";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
verbose_stream() << " |-> " << it.value() << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
bool find_le(node* n, unsigned index, Key const* keys, check_value& check) {
|
||||
if (index == num_keys()) {
|
||||
SASSERT(n->ref_count() > 0);
|
||||
bool r = check(to_leaf(n)->get_value());
|
||||
IF_VERBOSE(2,
|
||||
for (unsigned j = 0; j < index; ++j) {
|
||||
verbose_stream() << " ";
|
||||
}
|
||||
verbose_stream() << to_leaf(n)->get_value() << (r?" hit\n":" miss\n"););
|
||||
return r;
|
||||
}
|
||||
else {
|
||||
Key const& key = get_key(keys, index);
|
||||
children_t& nodes = to_trie(n)->nodes();
|
||||
for (unsigned i = 0; i < nodes.size(); ++i) {
|
||||
++m_stats.m_num_find_le_nodes;
|
||||
node* m = nodes[i].second;
|
||||
IF_VERBOSE(2,
|
||||
for (unsigned j = 0; j < index; ++j) {
|
||||
verbose_stream() << " ";
|
||||
}
|
||||
verbose_stream() << nodes[i].first << " <=? " << key << " rc:" << m->ref_count() << "\n";);
|
||||
if (m->ref_count() > 0 && m_le.le(nodes[i].first, key) && find_le(m, index+1, keys, check)) {
|
||||
if (i > 0) {
|
||||
std::swap(nodes[i], nodes[0]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void insert(node* n, unsigned num_keys, Key const* keys, unsigned const* permutation, Value const& val) {
|
||||
// assumption: key is not in table.
|
||||
for (unsigned i = 0; i < num_keys; ++i) {
|
||||
n->inc_ref();
|
||||
n = insert_key(to_trie(n), (i + 1 == num_keys), keys[permutation[i]]);
|
||||
}
|
||||
n->inc_ref();
|
||||
to_leaf(n)->set_value(val);
|
||||
SASSERT(n->ref_count() == 1);
|
||||
}
|
||||
|
||||
node* insert_key(trie* n, bool is_leaf, Key const& key) {
|
||||
node* m1 = is_leaf?m_spare_leaf:m_spare_trie;
|
||||
node* m2 = n->find_or_insert(key, m1);
|
||||
if (m1 == m2) {
|
||||
if (is_leaf) {
|
||||
m_spare_leaf = mk_leaf();
|
||||
}
|
||||
else {
|
||||
m_spare_trie = mk_trie();
|
||||
}
|
||||
}
|
||||
return m2;
|
||||
}
|
||||
|
||||
leaf* mk_leaf() {
|
||||
void* mem = m_alloc.allocate(sizeof(leaf));
|
||||
return new (mem) leaf();
|
||||
}
|
||||
|
||||
trie* mk_trie() {
|
||||
void* mem = m_alloc.allocate(sizeof(trie));
|
||||
return new (mem) trie();
|
||||
}
|
||||
|
||||
void del_node(node* n) {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
if (is_trie(n)) {
|
||||
trie* t = to_trie(n);
|
||||
for (unsigned i = 0; i < t->nodes().size(); ++i) {
|
||||
del_node(t->nodes()[i].second);
|
||||
}
|
||||
t->~trie();
|
||||
m_alloc.deallocate(sizeof(trie), t);
|
||||
}
|
||||
else {
|
||||
leaf* l = to_leaf(n);
|
||||
l->~leaf();
|
||||
m_alloc.deallocate(sizeof(leaf), l);
|
||||
}
|
||||
}
|
||||
|
||||
static trie* to_trie(node* n) {
|
||||
SASSERT(is_trie(n));
|
||||
return static_cast<trie*>(n);
|
||||
}
|
||||
|
||||
static leaf* to_leaf(node* n) {
|
||||
SASSERT(is_leaf(n));
|
||||
return static_cast<leaf*>(n);
|
||||
}
|
||||
|
||||
static bool is_leaf(node* n) {
|
||||
return n->type() == leaf_t;
|
||||
}
|
||||
|
||||
static bool is_trie(node* n) {
|
||||
return n->type() == trie_t;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,201 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
hilbert_basis.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic Hilbert Basis computation.
|
||||
|
||||
hilbert_basis computes a Hilbert basis for linear
|
||||
homogeneous inequalities over naturals.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-02-09.
|
||||
|
||||
Revision History:
|
||||
|
||||
Hilbert basis can be templatized
|
||||
based on traits that define numeral:
|
||||
as rational, mpz, checked_int64
|
||||
(checked or unchecked).
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _HILBERT_BASIS_H_
|
||||
#define _HILBERT_BASIS_H_
|
||||
|
||||
#include "rational.h"
|
||||
#include "lbool.h"
|
||||
#include "statistics.h"
|
||||
#include "checked_int64.h"
|
||||
|
||||
typedef vector<rational> rational_vector;
|
||||
|
||||
class hilbert_basis {
|
||||
|
||||
static const bool check = true;
|
||||
typedef checked_int64<check> numeral;
|
||||
typedef vector<numeral> num_vector;
|
||||
static checked_int64<check> to_numeral(rational const& r) {
|
||||
if (!r.is_int64()) {
|
||||
throw checked_int64<check>::overflow_exception();
|
||||
}
|
||||
return checked_int64<check>(r.get_int64());
|
||||
}
|
||||
static rational to_rational(checked_int64<check> const& i) {
|
||||
return rational(i.get_int64(), rational::i64());
|
||||
}
|
||||
|
||||
class value_index1;
|
||||
class value_index2;
|
||||
class value_index3;
|
||||
class index;
|
||||
class passive;
|
||||
class passive2;
|
||||
struct offset_t {
|
||||
unsigned m_offset;
|
||||
offset_t(unsigned o) : m_offset(o) {}
|
||||
offset_t(): m_offset(0) {}
|
||||
bool operator<(offset_t const& other) const {
|
||||
return m_offset < other.m_offset;
|
||||
}
|
||||
};
|
||||
enum sign_t { pos, neg, zero };
|
||||
struct stats {
|
||||
unsigned m_num_subsumptions;
|
||||
unsigned m_num_resolves;
|
||||
unsigned m_num_saturations;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
class values {
|
||||
numeral* m_values;
|
||||
public:
|
||||
values(unsigned offset, numeral* v): m_values(v+offset) { }
|
||||
numeral& weight() { return m_values[-1]; } // value of a*x
|
||||
numeral const& weight() const { return m_values[-1]; } // value of a*x
|
||||
numeral& weight(int i) { return m_values[-2-i]; } // value of b_i*x for 0 <= i < current inequality.
|
||||
numeral const& weight(int i) const { return m_values[-2-i]; } // value of b_i*x
|
||||
numeral& operator[](unsigned i) { return m_values[i]; } // value of x_i
|
||||
numeral const& operator[](unsigned i) const { return m_values[i]; } // value of x_i
|
||||
numeral const* operator()() const { return m_values; }
|
||||
};
|
||||
|
||||
vector<num_vector> m_ineqs; // set of asserted inequalities
|
||||
svector<bool> m_iseq; // inequalities that are equalities
|
||||
num_vector m_store; // store of vectors
|
||||
svector<offset_t> m_basis; // vector of current basis
|
||||
svector<offset_t> m_free_list; // free list of unused storage
|
||||
svector<offset_t> m_active; // active set
|
||||
svector<offset_t> m_sos; // set of support
|
||||
svector<offset_t> m_zero; // zeros
|
||||
passive* m_passive; // passive set
|
||||
passive2* m_passive2; // passive set
|
||||
volatile bool m_cancel;
|
||||
stats m_stats;
|
||||
index* m_index; // index of generated vectors
|
||||
unsigned_vector m_ints; // indices that can be both positive and negative
|
||||
unsigned m_current_ineq;
|
||||
|
||||
bool m_use_support; // parameter: (associativity) resolve only against vectors that are initially in basis.
|
||||
bool m_use_ordered_support; // parameter: (commutativity) resolve in order
|
||||
bool m_use_ordered_subsumption; // parameter
|
||||
|
||||
class iterator {
|
||||
hilbert_basis const& hb;
|
||||
unsigned m_idx;
|
||||
public:
|
||||
iterator(hilbert_basis const& hb, unsigned idx): hb(hb), m_idx(idx) {}
|
||||
offset_t operator*() const { return hb.m_basis[m_idx]; }
|
||||
iterator& operator++() { ++m_idx; return *this; }
|
||||
iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; }
|
||||
bool operator==(iterator const& it) const {return m_idx == it.m_idx; }
|
||||
bool operator!=(iterator const& it) const {return m_idx != it.m_idx; }
|
||||
};
|
||||
|
||||
static offset_t mk_invalid_offset();
|
||||
static bool is_invalid_offset(offset_t offs);
|
||||
lbool saturate(num_vector const& ineq, bool is_eq);
|
||||
lbool saturate_orig(num_vector const& ineq, bool is_eq);
|
||||
void init_basis();
|
||||
void select_inequality();
|
||||
unsigned get_num_nonzeros(num_vector const& ineq);
|
||||
unsigned get_ineq_product(num_vector const& ineq);
|
||||
numeral get_ineq_diff(num_vector const& ineq);
|
||||
|
||||
void add_unit_vector(unsigned i, numeral const& e);
|
||||
unsigned get_num_vars() const;
|
||||
numeral get_weight(values const & val, num_vector const& ineq) const;
|
||||
bool is_geq(values const& v, values const& w) const;
|
||||
bool is_abs_geq(numeral const& v, numeral const& w) const;
|
||||
bool is_subsumed(offset_t idx);
|
||||
bool is_subsumed(offset_t i, offset_t j) const;
|
||||
void recycle(offset_t idx);
|
||||
bool can_resolve(offset_t i, offset_t j, bool check_sign) const;
|
||||
sign_t get_sign(offset_t idx) const;
|
||||
bool add_goal(offset_t idx);
|
||||
offset_t alloc_vector();
|
||||
void resolve(offset_t i, offset_t j, offset_t r);
|
||||
iterator begin() const { return iterator(*this,0); }
|
||||
iterator end() const { return iterator(*this, m_basis.size()); }
|
||||
|
||||
class vector_lt_t;
|
||||
bool vector_lt(offset_t i, offset_t j) const;
|
||||
|
||||
values vec(offset_t offs) const;
|
||||
|
||||
void display(std::ostream& out, offset_t o) const;
|
||||
void display(std::ostream& out, values const & v) const;
|
||||
void display_ineq(std::ostream& out, num_vector const& v, bool is_eq) const;
|
||||
|
||||
public:
|
||||
|
||||
hilbert_basis();
|
||||
~hilbert_basis();
|
||||
|
||||
void reset();
|
||||
|
||||
void set_use_support(bool b) { m_use_support = b; }
|
||||
void set_use_ordered_support(bool b) { m_use_ordered_support = b; }
|
||||
void set_use_ordered_subsumption(bool b) { m_use_ordered_subsumption = b; }
|
||||
|
||||
// add inequality v*x >= 0
|
||||
// add inequality v*x <= 0
|
||||
// add equality v*x = 0
|
||||
void add_ge(rational_vector const& v);
|
||||
void add_le(rational_vector const& v);
|
||||
void add_eq(rational_vector const& v);
|
||||
|
||||
// add inequality v*x >= b
|
||||
// add inequality v*x <= b
|
||||
// add equality v*x = b
|
||||
void add_ge(rational_vector const& v, rational const& b);
|
||||
void add_le(rational_vector const& v, rational const& b);
|
||||
void add_eq(rational_vector const& v, rational const& b);
|
||||
|
||||
void set_is_int(unsigned var_index);
|
||||
bool get_is_int(unsigned var_index) const;
|
||||
|
||||
lbool saturate();
|
||||
|
||||
unsigned get_basis_size() const { return m_basis.size(); }
|
||||
void get_basis_solution(unsigned i, rational_vector& v, bool& is_initial);
|
||||
|
||||
unsigned get_num_ineqs() const { return m_ineqs.size(); }
|
||||
void get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq);
|
||||
|
||||
void set_cancel(bool f) { m_cancel = f; }
|
||||
|
||||
void display(std::ostream& out) const;
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,233 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
horn_subsume_model_converter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Model converter for redundant Horn clauses.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-9-16
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "horn_subsume_model_converter.h"
|
||||
#include "var_subst.h"
|
||||
#include "ast_pp.h"
|
||||
#include "model_smt2_pp.h"
|
||||
#include "bool_rewriter.h"
|
||||
#include "th_rewriter.h"
|
||||
#include "for_each_expr.h"
|
||||
#include "well_sorted.h"
|
||||
|
||||
void horn_subsume_model_converter::insert(app* head, expr* body) {
|
||||
m_delay_head.push_back(head);
|
||||
m_delay_body.push_back(body);
|
||||
}
|
||||
|
||||
void horn_subsume_model_converter::insert(app* head, unsigned sz, expr* const* body) {
|
||||
expr_ref b(m);
|
||||
bool_rewriter(m).mk_and(sz, body, b);
|
||||
insert(head, b.get());
|
||||
}
|
||||
|
||||
|
||||
bool horn_subsume_model_converter::mk_horn(
|
||||
app* head, expr* body, func_decl_ref& pred, expr_ref& body_res) {
|
||||
|
||||
expr_ref_vector conjs(m), subst(m);
|
||||
ptr_vector<sort> sorts, sorts2;
|
||||
var_subst vs(m, false);
|
||||
|
||||
if (!is_uninterp(head)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pred = head->get_decl();
|
||||
unsigned arity = head->get_num_args();
|
||||
|
||||
get_free_vars(head, sorts);
|
||||
get_free_vars(body, sorts);
|
||||
|
||||
if (arity == 0 && sorts.empty()) {
|
||||
body_res = body;
|
||||
return true;
|
||||
}
|
||||
|
||||
svector<symbol> names;
|
||||
for (unsigned i = 0; i < sorts.size(); ++i) {
|
||||
if (!sorts[i]) {
|
||||
sorts[i] = m.mk_bool_sort();
|
||||
}
|
||||
names.push_back(symbol(i));
|
||||
}
|
||||
names.reverse();
|
||||
sorts.reverse();
|
||||
conjs.push_back(body);
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
expr* arg = head->get_arg(i);
|
||||
var_ref v(m);
|
||||
v = m.mk_var(sorts.size()+i, m.get_sort(arg));
|
||||
|
||||
if (is_var(arg)) {
|
||||
unsigned w = to_var(arg)->get_idx();
|
||||
if (w >= subst.size()) {
|
||||
subst.resize(w+1);
|
||||
}
|
||||
if (subst[w].get()) {
|
||||
conjs.push_back(m.mk_eq(v, subst[w].get()));
|
||||
}
|
||||
else {
|
||||
subst[w] = v;
|
||||
}
|
||||
}
|
||||
else {
|
||||
conjs.push_back(m.mk_eq(v, arg));
|
||||
}
|
||||
}
|
||||
expr_ref body_expr(m);
|
||||
body_expr = m.mk_and(conjs.size(), conjs.c_ptr());
|
||||
|
||||
// substitute variables directly.
|
||||
if (!subst.empty()) {
|
||||
expr_ref tmp(body_expr);
|
||||
vs(tmp, subst.size(), subst.c_ptr(), body_expr);
|
||||
}
|
||||
|
||||
if (sorts.empty()) {
|
||||
SASSERT(subst.empty());
|
||||
body_res = body_expr;
|
||||
}
|
||||
else {
|
||||
body_res = m.mk_exists(sorts.size(), sorts.c_ptr(), names.c_ptr(), body_expr.get());
|
||||
m_rewrite(body_res);
|
||||
|
||||
}
|
||||
TRACE("mc",
|
||||
tout << mk_pp(head, m) << " :- " << mk_pp(body, m) << "\n";
|
||||
tout << pred->get_name() << " :- " << mk_pp(body_res.get(), m) << "\n";);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool horn_subsume_model_converter::mk_horn(
|
||||
expr* clause, func_decl_ref& pred, expr_ref& body) {
|
||||
ptr_vector<sort> sorts;
|
||||
|
||||
// formula is closed.
|
||||
DEBUG_CODE(get_free_vars(clause, sorts); SASSERT(sorts.empty()););
|
||||
|
||||
while (is_quantifier(clause) && to_quantifier(clause)->is_forall()) {
|
||||
quantifier* q = to_quantifier(clause);
|
||||
clause = q->get_expr();
|
||||
}
|
||||
expr* e1, *e2;
|
||||
if (m.is_implies(clause, e1, e2)) {
|
||||
if (!is_uninterp(e2)) {
|
||||
return false;
|
||||
}
|
||||
return mk_horn(to_app(e2), e1, pred, body);
|
||||
}
|
||||
else if (m.is_or(clause)) {
|
||||
// todo?
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void horn_subsume_model_converter::add_default_proc::operator()(app* n) {
|
||||
|
||||
//
|
||||
// predicates that have not been assigned values
|
||||
// in the Horn model are assumed false.
|
||||
//
|
||||
if (m.is_bool(n) &&
|
||||
!m_md->has_interpretation(n->get_decl()) &&
|
||||
(n->get_family_id() == null_family_id)) {
|
||||
TRACE("mc", tout << "adding: " << n->get_decl()->get_name() << "\n";);
|
||||
if (n->get_decl()->get_arity() == 0) {
|
||||
m_md->register_decl(n->get_decl(), m.mk_false());
|
||||
}
|
||||
else {
|
||||
func_interp* fi = alloc(func_interp, m, n->get_decl()->get_arity());
|
||||
fi->set_else(m.mk_false());
|
||||
m_md->register_decl(n->get_decl(), fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void horn_subsume_model_converter::add_default_false_interpretation(expr* e, model_ref& md) {
|
||||
add_default_proc proc(m, md);
|
||||
for_each_expr(proc, e);
|
||||
}
|
||||
|
||||
|
||||
void horn_subsume_model_converter::operator()(model_ref& mr) {
|
||||
|
||||
func_decl_ref pred(m);
|
||||
expr_ref body_res(m);
|
||||
for (unsigned i = 0; i < m_delay_head.size(); ++i) {
|
||||
VERIFY(mk_horn(m_delay_head[i].get(), m_delay_body[i].get(), pred, body_res));
|
||||
insert(pred.get(), body_res.get());
|
||||
}
|
||||
m_delay_head.reset();
|
||||
m_delay_body.reset();
|
||||
|
||||
TRACE("mc", tout << m_funcs.size() << "\n"; model_smt2_pp(tout, m, *mr, 0););
|
||||
for (unsigned i = m_funcs.size(); i > 0; ) {
|
||||
--i;
|
||||
func_decl* h = m_funcs[i].get();
|
||||
expr_ref body(m_bodies[i].get(), m);
|
||||
unsigned arity = h->get_arity();
|
||||
add_default_false_interpretation(body, mr);
|
||||
SASSERT(m.is_bool(body));
|
||||
|
||||
TRACE("mc", tout << "eval: " << h->get_name() << "\n" << mk_pp(body, m) << "\n";);
|
||||
expr_ref tmp(body);
|
||||
mr->eval(tmp, body);
|
||||
|
||||
TRACE("mc", tout << "to:\n" << mk_pp(body, m) << "\n";);
|
||||
|
||||
if (arity == 0) {
|
||||
expr* e = mr->get_const_interp(h);
|
||||
if (e) {
|
||||
body = m.mk_or(e, body);
|
||||
}
|
||||
m_rewrite(body);
|
||||
mr->register_decl(h, body);
|
||||
}
|
||||
else {
|
||||
func_interp* f = mr->get_func_interp(h);
|
||||
if (f) {
|
||||
expr* e = f->get_else();
|
||||
body = m.mk_or(e, body);
|
||||
}
|
||||
else {
|
||||
f = alloc(func_interp, m, arity);
|
||||
mr->register_decl(h, f);
|
||||
}
|
||||
m_rewrite(body);
|
||||
f->set_else(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
model_converter* horn_subsume_model_converter::translate(ast_translation & translator) {
|
||||
horn_subsume_model_converter* mc = alloc(horn_subsume_model_converter, translator.to());
|
||||
for (unsigned i = 0; i < m_funcs.size(); ++i) {
|
||||
mc->insert(translator(m_funcs[i].get()), translator(m_bodies[i].get()));
|
||||
}
|
||||
return mc;
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
horn_subsume_model_converter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Model converter for redundant Horn clauses.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-9-16
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
|
||||
Subsumption transformation (remove Horn clause):
|
||||
|
||||
P(x) :- Body(x,y) Rules
|
||||
----------------------------
|
||||
Rules
|
||||
|
||||
|
||||
Model converter:
|
||||
|
||||
P(x) := P(x) or (exists y. Body(x,y))
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _HORN_SUBSUME_MODEL_CONVERTER_H_
|
||||
#define _HORN_SUBSUME_MODEL_CONVERTER_H_
|
||||
|
||||
#include "model_converter.h"
|
||||
#include "th_rewriter.h"
|
||||
|
||||
class horn_subsume_model_converter : public model_converter {
|
||||
ast_manager& m;
|
||||
func_decl_ref_vector m_funcs;
|
||||
expr_ref_vector m_bodies;
|
||||
th_rewriter m_rewrite;
|
||||
app_ref_vector m_delay_head;
|
||||
expr_ref_vector m_delay_body;
|
||||
|
||||
void add_default_false_interpretation(expr* e, model_ref& md);
|
||||
|
||||
struct add_default_proc {
|
||||
ast_manager& m;
|
||||
model_ref& m_md;
|
||||
add_default_proc(ast_manager& m, model_ref& md): m(m), m_md(md) {}
|
||||
void operator()(app* n);
|
||||
void operator()(expr* n) {}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
horn_subsume_model_converter(ast_manager& m):
|
||||
m(m), m_funcs(m), m_bodies(m), m_rewrite(m),
|
||||
m_delay_head(m), m_delay_body(m) {}
|
||||
|
||||
bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body);
|
||||
|
||||
bool mk_horn(app* head, expr* body, func_decl_ref& pred, expr_ref& body_res);
|
||||
|
||||
void insert(app* head, expr* body);
|
||||
|
||||
void insert(app* head, unsigned sz, expr* const* body);
|
||||
|
||||
void insert(func_decl* p, expr* body) { m_funcs.push_back(p); m_bodies.push_back(body); }
|
||||
|
||||
virtual void operator()(model_ref& m);
|
||||
|
||||
virtual model_converter * translate(ast_translation & translator);
|
||||
|
||||
ast_manager& get_manager() { return m; }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,157 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
model2expr.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Convert model to logical formula that forces it.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-09-17
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include "model2expr.h"
|
||||
#include "for_each_ast.h"
|
||||
#include "bool_rewriter.h"
|
||||
#include "var_subst.h"
|
||||
|
||||
struct for_each_symbol_proc {
|
||||
symbol_set& m_symbols;
|
||||
for_each_symbol_proc(symbol_set& syms): m_symbols(syms) {}
|
||||
|
||||
void operator()(func_decl* n) {
|
||||
m_symbols.insert(n->get_name());
|
||||
}
|
||||
|
||||
void operator()(quantifier* n) {
|
||||
for (unsigned i = 0; i < n->get_num_decls(); ++i) {
|
||||
m_symbols.insert(n->get_decl_name(i));
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(var* n) {}
|
||||
void operator()(sort* s) {}
|
||||
void operator()(app* a) {}
|
||||
};
|
||||
|
||||
void mk_fresh_name::add(ast* a) {
|
||||
for_each_symbol_proc proc(m_symbols);
|
||||
for_each_ast(proc, a);
|
||||
}
|
||||
|
||||
symbol mk_fresh_name::next() {
|
||||
for (; ; ++m_num) {
|
||||
for(; m_char <= 'Z'; ++m_char) {
|
||||
std::stringstream _name;
|
||||
_name << m_char;
|
||||
if (m_num > 0) _name << m_num;
|
||||
++m_char;
|
||||
symbol name(_name.str().c_str());
|
||||
if (!m_symbols.contains(name)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
m_char = 'A';
|
||||
}
|
||||
}
|
||||
|
||||
static void mk_entry_cond(unsigned arity, func_entry const* entry, expr_ref& result) {
|
||||
ast_manager& m = result.get_manager();
|
||||
expr_ref_vector conjs(m);
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
expr* e = entry->get_arg(i);
|
||||
if (is_var(e) && to_var(e)->get_idx() == i) {
|
||||
// no-op
|
||||
}
|
||||
else {
|
||||
conjs.push_back(m.mk_eq(m.mk_var(i, m.get_sort(e)), e));
|
||||
}
|
||||
}
|
||||
bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result);
|
||||
}
|
||||
|
||||
void model2expr(model& md, expr_ref& result) {
|
||||
ast_manager& m = result.get_manager();
|
||||
|
||||
expr_ref_vector conjs(m);
|
||||
expr_ref tmp(m);
|
||||
unsigned sz;
|
||||
|
||||
sz = md.get_num_constants();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
func_decl* c = md.get_constant(i);
|
||||
expr* v = md.get_const_interp(c);
|
||||
conjs.push_back(m.mk_eq(m.mk_const(c), v));
|
||||
}
|
||||
|
||||
sz = md.get_num_functions();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
|
||||
func_decl* f = md.get_function(i);
|
||||
func_interp* fi = md.get_func_interp(f);
|
||||
|
||||
// Register names.
|
||||
mk_fresh_name fresh_name;
|
||||
unsigned num_entries = fi->num_entries();
|
||||
fresh_name.add(f);
|
||||
for (unsigned j = 0; j < num_entries; ++j) {
|
||||
func_entry const* entry = fi->get_entry(j);
|
||||
fresh_name.add(entry->get_result());
|
||||
for (unsigned k = 0; k < f->get_arity(); ++k) {
|
||||
fresh_name.add(entry->get_arg(k));
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref func(m), cond(m);
|
||||
expr_ref_vector args(m);
|
||||
for (unsigned j = 0; j < f->get_arity(); ++j) {
|
||||
args.push_back(m.mk_var(j, f->get_domain(j)));
|
||||
}
|
||||
func = m.mk_app(f, args.size(), args.c_ptr());
|
||||
if (fi->is_partial()) {
|
||||
if (num_entries == 0) {
|
||||
continue;
|
||||
}
|
||||
mk_entry_cond(f->get_arity(), fi->get_entry(num_entries-1), cond);
|
||||
tmp = m.mk_implies(cond, m.mk_eq(func, fi->get_entry(num_entries-1)->get_result()));
|
||||
for (unsigned j = num_entries-1; j > 0; ) {
|
||||
--j;
|
||||
mk_entry_cond(f->get_arity(), fi->get_entry(j), cond);
|
||||
tmp = m.mk_ite(cond, m.mk_eq(func, fi->get_entry(j)->get_result()), tmp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
fresh_name.add(fi->get_else());
|
||||
tmp = fi->get_else();
|
||||
for (unsigned j = num_entries; j > 0; ) {
|
||||
--j;
|
||||
mk_entry_cond(f->get_arity(), fi->get_entry(j), cond);
|
||||
tmp = m.mk_ite(cond, fi->get_entry(j)->get_result(), tmp);
|
||||
}
|
||||
tmp = m.mk_eq(func, tmp);
|
||||
}
|
||||
ptr_vector<sort> sorts;
|
||||
expr_ref_vector rev_vars(m);
|
||||
svector<symbol> names;
|
||||
unsigned sz = f->get_arity();
|
||||
for (unsigned j = 0; j < sz; ++j) {
|
||||
sorts.push_back(f->get_domain(j));
|
||||
rev_vars.push_back(m.mk_var(sz-j-1, f->get_domain(j)));
|
||||
names.push_back(fresh_name.next());
|
||||
}
|
||||
if (f->get_arity() > 0) {
|
||||
var_subst vs(m, false);
|
||||
vs(tmp, rev_vars.size(), rev_vars.c_ptr(), tmp);
|
||||
tmp = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp);
|
||||
}
|
||||
conjs.push_back(tmp);
|
||||
}
|
||||
|
||||
bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result);
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
model2expr.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Convert model to logical formula that forces it.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-09-17
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _MODEL2EXPR_H_
|
||||
#define _MODEL2EXPR_H_
|
||||
|
||||
#include"model.h"
|
||||
|
||||
void model2expr(model& m, expr_ref& result);
|
||||
|
||||
inline void model2expr(model_ref& md, expr_ref& result) { model2expr(*md.get(), result); }
|
||||
|
||||
// TODO: move
|
||||
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
|
||||
|
||||
class mk_fresh_name {
|
||||
symbol_set m_symbols;
|
||||
char m_char;
|
||||
unsigned m_num;
|
||||
public:
|
||||
mk_fresh_name(): m_char('A'), m_num(0) {}
|
||||
void add(ast* a);
|
||||
void add(symbol const& s) { m_symbols.insert(s); }
|
||||
symbol next();
|
||||
bool contains(symbol const& s) const { return m_symbols.contains(s); }
|
||||
};
|
||||
|
||||
|
||||
#endif /* _MODEL2EXPR_H_ */
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
replace_proof_converter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Proof converter that replaces asserted by sub-proof.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-9-16
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "replace_proof_converter.h"
|
||||
#include "expr_functors.h"
|
||||
#include "ast_pp.h"
|
||||
#include "for_each_expr.h"
|
||||
|
||||
/**
|
||||
\brief Replace expressions by other expressions.
|
||||
|
||||
replace_map is caching, so inserting src |-> dst has no effect if
|
||||
src is a sub-expression of something that has already been visited.
|
||||
The assumption is that proof replacements are inserted into
|
||||
the replace_proof_converter in the order that they are introduced, so
|
||||
there are no such clashes.
|
||||
|
||||
map_proc is used as expr_replacer behaves differently
|
||||
when proof mode is turned on.
|
||||
*/
|
||||
class replace_map : public map_proc {
|
||||
public:
|
||||
replace_map(ast_manager& m): map_proc(m) {}
|
||||
|
||||
void insert(expr* src, expr* dst) {
|
||||
m_map.insert(src, dst, 0);
|
||||
}
|
||||
|
||||
void operator()(var* v) { visit(v); }
|
||||
void operator()(app* a) { if (!get_expr(a)) { reconstruct(a); } }
|
||||
void operator()(quantifier* q) { visit(q); }
|
||||
|
||||
void apply(expr_ref& e) {
|
||||
for_each_expr(*this, e);
|
||||
e = get_expr(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void replace_proof_converter::operator()(ast_manager & m, unsigned num_source,
|
||||
proof * const * source, proof_ref & result) {
|
||||
SASSERT(num_source == 1);
|
||||
replace_map replace(m);
|
||||
proof_ref p(m);
|
||||
expr_ref tmp(source[0], m), e(m), f(m);
|
||||
|
||||
// apply the substitution to the prefix before inserting it.
|
||||
for (unsigned i = 0; i < m_proofs.size(); ++i) {
|
||||
p = m_proofs[i].get();
|
||||
e = p;
|
||||
replace.apply(e);
|
||||
f = m.mk_asserted(m.get_fact(p));
|
||||
replace.insert(f, e);
|
||||
TRACE("proof_converter", tout << f->get_id() << " " << mk_pp(f, m) <<
|
||||
"\n|-> " << mk_pp(e, m) << "\n";);
|
||||
}
|
||||
replace.apply(tmp);
|
||||
TRACE("proof_converter", tout << mk_pp(source[0], m) << "\n";
|
||||
tout << mk_pp(tmp.get(), m) << "\n";);
|
||||
result = to_app(tmp);
|
||||
}
|
||||
|
||||
proof_converter * replace_proof_converter::translate(ast_translation & translator) {
|
||||
replace_proof_converter* rp = alloc(replace_proof_converter, m);
|
||||
for (unsigned i = 0; i < m_proofs.size(); ++i) {
|
||||
rp->insert(translator(m_proofs[i].get()));
|
||||
}
|
||||
return rp;
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
replace_proof_converter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Proof converter to replace asserted leaves by proofs.
|
||||
|
||||
Given a proof P with occurrences of [asserted fml]
|
||||
Replace [asserted fml] by proofs whose conclusions are fml.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2012-9-16
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _REPLACE_PROOF_CONVERTER_H_
|
||||
#define _REPLACE_PROOF_CONVERTER_H_
|
||||
|
||||
#include "proof_converter.h"
|
||||
|
||||
class replace_proof_converter : public proof_converter {
|
||||
ast_manager& m;
|
||||
proof_ref_vector m_proofs;
|
||||
public:
|
||||
|
||||
replace_proof_converter(ast_manager& m): m(m), m_proofs(m) {}
|
||||
|
||||
virtual ~replace_proof_converter() {}
|
||||
|
||||
virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result);
|
||||
|
||||
virtual proof_converter * translate(ast_translation & translator);
|
||||
|
||||
void insert(proof* p) { m_proofs.push_back(p); }
|
||||
|
||||
ast_manager& get_manager() { return m; }
|
||||
|
||||
// run the replacements the inverse direction.
|
||||
void invert() { m_proofs.reverse(); }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,55 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
scoped_proof.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Scoped proof environments. Toggles enabling proofs.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-08-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _SCOPED_PROOF__H_
|
||||
#define _SCOPED_PROOF_H_
|
||||
|
||||
#include "ast.h"
|
||||
|
||||
class scoped_proof_mode {
|
||||
ast_manager& m;
|
||||
proof_gen_mode m_mode;
|
||||
public:
|
||||
scoped_proof_mode(ast_manager& m, proof_gen_mode mode): m(m) {
|
||||
m_mode = m.proof_mode();
|
||||
m.toggle_proof_mode(mode);
|
||||
}
|
||||
~scoped_proof_mode() {
|
||||
m.toggle_proof_mode(m_mode);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class scoped_proof : public scoped_proof_mode {
|
||||
public:
|
||||
scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_FINE) {}
|
||||
};
|
||||
|
||||
class scoped_no_proof : public scoped_proof_mode {
|
||||
public:
|
||||
scoped_no_proof(ast_manager& m): scoped_proof_mode(m, PGM_DISABLED) {}
|
||||
};
|
||||
|
||||
class scoped_restore_proof : public scoped_proof_mode {
|
||||
public:
|
||||
scoped_restore_proof(ast_manager& m): scoped_proof_mode(m, m.proof_mode()) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -1,871 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
skip_list_base.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2010-10-01.
|
||||
|
||||
Revision History:
|
||||
|
||||
WARNING: IT IS NOT SAFE TO STORE KEYS, VALUES in the SKIP_LIST that need non-default constructors/destructors.
|
||||
|
||||
--*/
|
||||
#ifndef _SKIP_LIST_BASE_H_
|
||||
#define _SKIP_LIST_BASE_H_
|
||||
|
||||
#include<memory.h>
|
||||
#include"util.h"
|
||||
#include"memory_manager.h"
|
||||
#include"small_object_allocator.h"
|
||||
#include"trace.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4200)
|
||||
#endif
|
||||
|
||||
/*
|
||||
This file defines a base class for implementing skip-list like data-structures.
|
||||
This base class is relies on a manager for providing some basic services.
|
||||
The manager is a template parameter.
|
||||
|
||||
A Skip-list manager is responsible for:
|
||||
|
||||
- Providing primitives for allocating/deallocating memory
|
||||
void * allocate(size_t size);
|
||||
void deallocate(size_t size, void* p);
|
||||
- Generating random skip-list levels efficiently
|
||||
unsigned random_level(unsigned max_level);
|
||||
- Call-backs that will be invoked when a reference for a "value" stored in the skip-list is incremented/decremented.
|
||||
void inc_ref_eh(value const & v);
|
||||
void dec_ref_eh(value const & h);
|
||||
*/
|
||||
|
||||
/**
|
||||
\brief Base class for generating random_levels.
|
||||
*/
|
||||
class random_level_manager {
|
||||
#define SL_BITS_IN_RANDOM 16
|
||||
unsigned m_random_data;
|
||||
unsigned m_random_bits:16;
|
||||
unsigned m_random_left:16;
|
||||
|
||||
unsigned random_value() {
|
||||
return ((m_random_data = m_random_data * 214013L + 2531011L) >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
void init_random() {
|
||||
m_random_data = 0;
|
||||
m_random_bits = random_value();
|
||||
m_random_left = SL_BITS_IN_RANDOM/2;
|
||||
}
|
||||
public:
|
||||
random_level_manager() {
|
||||
init_random();
|
||||
}
|
||||
|
||||
unsigned random_level(unsigned max_level) {
|
||||
unsigned level = 1;
|
||||
unsigned b;
|
||||
do {
|
||||
b = m_random_bits&3;
|
||||
if (!b)
|
||||
level++;
|
||||
m_random_bits >>= 2;
|
||||
m_random_left--;
|
||||
if (m_random_left == 0) {
|
||||
m_random_bits = random_value();
|
||||
m_random_left = SL_BITS_IN_RANDOM/2;
|
||||
}
|
||||
} while (!b);
|
||||
return (level > max_level ? max_level : level);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Basic skip-list manager.
|
||||
The class is parametrized by the Value type that is stored in the skip-list.
|
||||
*/
|
||||
template<typename Value>
|
||||
class sl_manager_base : public random_level_manager {
|
||||
typedef Value value;
|
||||
|
||||
small_object_allocator m_alloc;
|
||||
|
||||
public:
|
||||
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(value const & v) {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
void dec_ref_eh(value const & h) {
|
||||
/* do nothing */
|
||||
}
|
||||
};
|
||||
|
||||
#define SL_SIZE_NUM_BITS 12
|
||||
#define SL_CAPACITY_NUM_BITS SL_SIZE_NUM_BITS
|
||||
#define SL_MAX_CAPACITY ((1 << SL_SIZE_NUM_BITS) - 1)
|
||||
#define SL_LEVEL_NUM_BITS 8
|
||||
#define SL_MAX_LEVEL ((1 << SL_LEVEL_NUM_BITS) - 1)
|
||||
COMPILE_TIME_ASSERT(SL_SIZE_NUM_BITS == SL_CAPACITY_NUM_BITS);
|
||||
COMPILE_TIME_ASSERT(SL_SIZE_NUM_BITS + SL_CAPACITY_NUM_BITS + SL_LEVEL_NUM_BITS == 32);
|
||||
|
||||
/**
|
||||
\brief Base (template) class for implementing skip-list like data-structures where
|
||||
entries are stored in buckets to improve cache behavior.
|
||||
|
||||
The Traits template parameter must provide:
|
||||
|
||||
- a definition for the class Traits::manager
|
||||
- a definition for the class Traits::entry which provides:
|
||||
- a definition for the types key and value
|
||||
- the methods:
|
||||
key const & begin_key() const
|
||||
key const & end_key() const
|
||||
value const & val() const
|
||||
void set_begin_key(key const & k)
|
||||
void set_end_key(key const & k)
|
||||
void set_val(value const & v)
|
||||
void display(ostream & out) const
|
||||
- the maximal number of levels Traits::max_level
|
||||
- the maximal capacity of each bucket Traits::max_capacity
|
||||
- the initial capacity of the first bucket Traits::initial_capacity
|
||||
- flag for reference counting support Traits::ref_count. If this flag is true
|
||||
the methods inc_ref_eh and dec_ref_eh in the manager object will be invoked.
|
||||
- the methods
|
||||
bool lt(key const & k1, key const & k2)
|
||||
bool eq(key const & k1, key const & k2)
|
||||
bool val_eq(value const & v1, value const & v2)
|
||||
key succ(key const & k)
|
||||
key pred(key const & k)
|
||||
*/
|
||||
template<typename Traits>
|
||||
class skip_list_base : protected Traits {
|
||||
protected:
|
||||
typedef typename Traits::entry entry;
|
||||
public:
|
||||
typedef typename Traits::manager manager;
|
||||
typedef typename entry::key key;
|
||||
typedef typename entry::value value;
|
||||
|
||||
struct bucket {
|
||||
unsigned m_size:SL_SIZE_NUM_BITS; //!< number of entries stored in the bucket.
|
||||
unsigned m_capacity:SL_CAPACITY_NUM_BITS; //!< capacity (number of entries) that can be stored in the bucket.
|
||||
unsigned m_level:SL_LEVEL_NUM_BITS;
|
||||
char m_extra[0];
|
||||
|
||||
static unsigned get_obj_size(unsigned num_lvls, unsigned capacity) {
|
||||
return sizeof(bucket) + num_lvls*sizeof(bucket*) + capacity*sizeof(entry);
|
||||
}
|
||||
|
||||
entry * get_entries() { return reinterpret_cast<entry*>(m_extra); }
|
||||
entry const * get_entries() const { return reinterpret_cast<entry const *>(m_extra); }
|
||||
|
||||
bucket ** next_vect() { return reinterpret_cast<bucket**>(get_entries() + m_capacity); }
|
||||
bucket * const * next_vect() const { return reinterpret_cast<bucket* const *>(get_entries() + m_capacity); }
|
||||
|
||||
bucket(unsigned lvl, unsigned capacity = Traits::max_capacity):
|
||||
m_size(0),
|
||||
m_capacity(capacity),
|
||||
m_level(lvl) {
|
||||
memset(next_vect(), 0, sizeof(bucket*)*lvl);
|
||||
}
|
||||
|
||||
unsigned level() const { return m_level; }
|
||||
unsigned size() const { return m_size; }
|
||||
unsigned capacity() const { return m_capacity; }
|
||||
bool empty() const { return size() == 0; }
|
||||
void set_size(unsigned sz) { m_size = sz; }
|
||||
void shrink(unsigned delta) { m_size -= delta; }
|
||||
void expand(unsigned delta) { m_size += delta; }
|
||||
entry & first_entry() { SASSERT(!empty()); return get_entries()[0]; }
|
||||
entry & last_entry() { SASSERT(!empty()); return get_entries()[size() - 1]; }
|
||||
entry const & first_entry() const { SASSERT(!empty()); return get_entries()[0]; }
|
||||
entry const & last_entry() const { SASSERT(!empty()); return get_entries()[size() - 1]; }
|
||||
entry const & get(unsigned idx) const { SASSERT(idx < size()); return get_entries()[idx]; }
|
||||
entry & get(unsigned idx) { SASSERT(idx < size()); return get_entries()[idx]; }
|
||||
void set(unsigned idx, entry const & e) { SASSERT(idx < capacity()); get_entries()[idx] = e; }
|
||||
bucket * get_next(unsigned idx) const { return next_vect()[idx]; }
|
||||
void set_next(unsigned idx, bucket * bt) { SASSERT(idx < level()); next_vect()[idx] = bt; }
|
||||
};
|
||||
|
||||
// Only the header bucket has zero entries.
|
||||
bucket * m_header;
|
||||
|
||||
bucket * first_bucket() const {
|
||||
return m_header->get_next(0);
|
||||
}
|
||||
|
||||
#ifdef Z3DEBUG
|
||||
/**
|
||||
\brief (debugging only) Return the predecessor bucket of the given bucket.
|
||||
|
||||
\pre bt != m_header, and bt is a bucket of the list.
|
||||
*/
|
||||
bucket * pred_bucket(bucket * bt) const {
|
||||
SASSERT(bt != m_header);
|
||||
bucket * curr = m_header;
|
||||
while (curr->get_next(0) != bt) {
|
||||
curr = curr->get_next(0);
|
||||
SASSERT(curr != 0); // bt is not in the list
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool lt(key const & k1, key const & k2) const { return Traits::lt(k1, k2); }
|
||||
|
||||
bool gt(key const & k1, key const & k2) const { return lt(k2, k1); }
|
||||
|
||||
bool geq(key const & k1, key const & k2) const { return !lt(k1, k2); }
|
||||
|
||||
bool leq(key const & k1, key const & k2) const { return !gt(k1, k2); }
|
||||
|
||||
/**
|
||||
\brief Create a new bucket of the given level.
|
||||
*/
|
||||
static bucket * mk_bucket(manager & m, unsigned lvl, unsigned capacity = Traits::max_capacity) {
|
||||
void * mem = m.allocate(bucket::get_obj_size(lvl, capacity));
|
||||
return new (mem) bucket(lvl, capacity);
|
||||
}
|
||||
|
||||
static bucket * mk_header(manager & m, unsigned lvl) {
|
||||
return mk_bucket(m, lvl, 0);
|
||||
}
|
||||
|
||||
static void inc_ref(manager & m, value const & v) {
|
||||
if (Traits::ref_count)
|
||||
m.inc_ref_eh(v);
|
||||
}
|
||||
|
||||
static void dec_ref(manager & m, value const & v) {
|
||||
if (Traits::ref_count)
|
||||
m.dec_ref_eh(v);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Invoke dec_ref_eh for each value stored in the bucket.
|
||||
*/
|
||||
static void dec_ref(manager & m, bucket * bt) {
|
||||
if (Traits::ref_count) {
|
||||
unsigned sz = bt->size();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
m.dec_ref_eh(bt->get(i).val());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Deallocate the given bucket.
|
||||
|
||||
\remark This method invokes dec_ref_eh for each value in the bucket.
|
||||
*/
|
||||
template<bool DecRef>
|
||||
static void deallocate_bucket(manager & m, bucket * bt) {
|
||||
if (DecRef)
|
||||
dec_ref(m, bt);
|
||||
unsigned sz = bucket::get_obj_size(bt->level(), bt->capacity());
|
||||
bt->~bucket();
|
||||
m.deallocate(sz, bt);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Deallocate all buckets in the skip list.
|
||||
|
||||
\remark This method invokes dec_ref_eh for each value in the list.
|
||||
*/
|
||||
template<bool DecRef>
|
||||
void deallocate_list(manager & m) {
|
||||
bucket * curr = m_header;
|
||||
while (curr != 0) {
|
||||
bucket * old = curr;
|
||||
curr = curr->get_next(0);
|
||||
deallocate_bucket<DecRef>(m, old);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Z3DEBUG
|
||||
/**
|
||||
\brief Check the following property
|
||||
|
||||
for all i \in [0, b->level()) . pred_vect[i]->get_next(i) == b
|
||||
*/
|
||||
bool check_pred_vect(bucket * bt, bucket * pred_vect[]) {
|
||||
if (bt == 0)
|
||||
return true;
|
||||
for (unsigned i = 0; i < bt->level(); i++) {
|
||||
SASSERT(pred_vect[i]->get_next(i) == bt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
\brief Delete the given buffer and update the forward/next pointer of the buckets in pred_vect.
|
||||
|
||||
\remark This method invokes dec_ref_eh for each value in the bucket.
|
||||
*/
|
||||
void del_bucket(manager & m, bucket * bt, bucket * pred_vect[]) {
|
||||
SASSERT(check_pred_vect(bt, pred_vect));
|
||||
for (unsigned i = 0; i < bt->level(); i++)
|
||||
pred_vect[i]->set_next(i, bt->get_next(i));
|
||||
deallocate_bucket<true>(m, bt);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Update the \c pred_vect vector from levels [0, bt->level()).
|
||||
That is, bt will be now the "predecessor" for these levels.
|
||||
*/
|
||||
static void update_predecessor_vector(bucket * pred_vect [], bucket * bt) {
|
||||
unsigned lvl = bt->level();
|
||||
for (unsigned i = 0; i < lvl; i++) {
|
||||
pred_vect[i] = bt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Similar to the previous method, but the updated vector is stored in new_pred_vect.
|
||||
*/
|
||||
void update_predecessor_vector(bucket * pred_vect[], bucket * bt, bucket * new_pred_vect[]) {
|
||||
unsigned bt_lvl = bt->level();
|
||||
for (unsigned i = 0; i < bt_lvl; i++) {
|
||||
new_pred_vect[i] = bt;
|
||||
}
|
||||
unsigned list_lvl = level();
|
||||
for (unsigned i = bt_lvl; i < list_lvl; i++) {
|
||||
new_pred_vect[i] = pred_vect[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return the list level.
|
||||
*/
|
||||
unsigned level() const {
|
||||
return m_header->level();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Expand/Increase the number of levels in the header.
|
||||
*/
|
||||
void expand_header(manager & m, unsigned new_lvl) {
|
||||
SASSERT(new_lvl > level());
|
||||
bucket * new_header = mk_header(m, new_lvl);
|
||||
// copy forward pointers of the old header.
|
||||
unsigned old_lvl = level();
|
||||
for (unsigned i = 0; i < old_lvl; i++)
|
||||
new_header->set_next(i, m_header->get_next(i));
|
||||
// update header
|
||||
deallocate_bucket<false>(m, m_header);
|
||||
m_header = new_header;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Increase list level to lvl if lvl > level()
|
||||
*/
|
||||
void update_list_level(manager & m, unsigned lvl) {
|
||||
if (lvl > level()) {
|
||||
expand_header(m, lvl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Increase list level (and store m_header in the new levels in pred_vect) if lvl > level().
|
||||
*/
|
||||
void update_list_level(manager & m, unsigned lvl, bucket * pred_vect[]) {
|
||||
if (lvl > level()) {
|
||||
bucket * old_header = m_header;
|
||||
unsigned old_lvl = m_header->level();
|
||||
expand_header(m, lvl);
|
||||
for (unsigned i = 0; i < old_lvl; i++) {
|
||||
if (pred_vect[i] == old_header)
|
||||
pred_vect[i] = m_header;
|
||||
}
|
||||
for (unsigned i = old_lvl; i < lvl; i++) {
|
||||
pred_vect[i] = m_header;
|
||||
}
|
||||
SASSERT(level() == lvl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Add first entry to the list.
|
||||
|
||||
\remark This method will invoke inc_ref_eh for e.val()
|
||||
*/
|
||||
void insert_first_entry(manager & m, entry const & e) {
|
||||
unsigned lvl = m.random_level(Traits::max_level);
|
||||
bucket * new_bucket = mk_bucket(m, lvl, Traits::initial_capacity);
|
||||
update_list_level(m, lvl);
|
||||
for (unsigned i = 0; i < lvl; i++) {
|
||||
m_header->set_next(i, new_bucket);
|
||||
}
|
||||
inc_ref(m, e.val());
|
||||
new_bucket->set_size(1);
|
||||
new_bucket->set(0, e);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Expand the capacity of the first-bucket in a skip-list with only one bucket.
|
||||
This method assumes the capacity of the first-bucket < Traits::max_capacity
|
||||
*/
|
||||
void expand_first_bucket(manager & m) {
|
||||
bucket * f = first_bucket();
|
||||
SASSERT(f != 0);
|
||||
SASSERT(f->get_next(0) == 0);
|
||||
SASSERT(f->capacity() < Traits::max_capacity);
|
||||
unsigned old_capacity = f->capacity();
|
||||
SASSERT(old_capacity > 0);
|
||||
unsigned new_capacity = old_capacity * 2;
|
||||
if (new_capacity > Traits::max_capacity)
|
||||
new_capacity = Traits::max_capacity;
|
||||
unsigned lvl = f->level();
|
||||
bucket * new_f = mk_bucket(m, lvl, new_capacity);
|
||||
unsigned sz = f->size();
|
||||
new_f->set_size(sz);
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
new_f->set(i, f->get(i));
|
||||
for (unsigned i = 0; i < lvl; i++)
|
||||
m_header->set_next(i, new_f);
|
||||
deallocate_bucket<false>(m, f);
|
||||
SASSERT(first_bucket() == new_f);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Create a new bucket and divide the elements in bt between bt and the new bucket.
|
||||
*/
|
||||
void splice(manager & m, bucket * bt, bucket * pred_vect[]) {
|
||||
SASSERT(bt->capacity() == Traits::max_capacity);
|
||||
unsigned bt_lvl = bt->level();
|
||||
unsigned new_bucket_lvl = m.random_level(Traits::max_level);
|
||||
bucket * new_bucket = mk_bucket(m, new_bucket_lvl);
|
||||
update_list_level(m, new_bucket_lvl, pred_vect);
|
||||
unsigned _lvl = std::min(bt_lvl, new_bucket_lvl);
|
||||
for (unsigned i = 0; i < _lvl; i++) {
|
||||
new_bucket->set_next(i, bt->get_next(i));
|
||||
bt->set_next(i, new_bucket);
|
||||
}
|
||||
for (unsigned i = bt_lvl; i < new_bucket_lvl; i++) {
|
||||
new_bucket->set_next(i, pred_vect[i]->get_next(i));
|
||||
pred_vect[i]->set_next(i, new_bucket);
|
||||
}
|
||||
unsigned old_size = bt->size();
|
||||
SASSERT(old_size >= 2);
|
||||
unsigned mid = old_size/2;
|
||||
new_bucket->set_size(old_size - mid);
|
||||
unsigned i = mid;
|
||||
unsigned j = 0;
|
||||
for (; i < old_size; i++, j++) {
|
||||
new_bucket->set(j, bt->get(i));
|
||||
}
|
||||
bt->set_size(mid);
|
||||
SASSERT(!bt->empty());
|
||||
SASSERT(!new_bucket->empty());
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Open space at position idx. The number of entries in bt is increased by one.
|
||||
|
||||
\remark This method will *NOT* invoke inc_ref_eh
|
||||
*/
|
||||
void open_space(bucket * bt, unsigned idx) {
|
||||
SASSERT(bt->size() < bt->capacity());
|
||||
SASSERT(idx <= bt->size());
|
||||
unsigned i = bt->size();
|
||||
while (i > idx) {
|
||||
bt->set(i, bt->get(i-1));
|
||||
i--;
|
||||
}
|
||||
bt->expand(1);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Open two spaces at position idx. The number of entries in bt is increased by one.
|
||||
|
||||
\remark This method will *NOT* invoke inc_ref_eh
|
||||
*/
|
||||
void open_2spaces(bucket * bt, unsigned idx) {
|
||||
SASSERT(bt->size() < bt->capacity() - 1);
|
||||
SASSERT(idx <= bt->size());
|
||||
unsigned i = bt->size() + 1;
|
||||
unsigned end = idx + 1;
|
||||
while (i > end) {
|
||||
bt->set(i, bt->get(i-2));
|
||||
i--;
|
||||
}
|
||||
bt->expand(2);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Delete entry at position idx.
|
||||
|
||||
\remark This method will invoke dec_ref_eh for the value stored in entry at position idx.
|
||||
*/
|
||||
void del_entry(manager & m, bucket * bt, unsigned idx) {
|
||||
SASSERT(!bt->empty());
|
||||
SASSERT(idx < bt->size());
|
||||
dec_ref(m, bt->get(idx).val());
|
||||
unsigned sz = bt->size();
|
||||
for (unsigned i = idx; i < sz - 1; i++) {
|
||||
bt->set(i, bt->get(i+1));
|
||||
}
|
||||
bt->shrink(1);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Create a copy of the skip list.
|
||||
|
||||
\remark This method will invoke inc_ref_eh for all values copied.
|
||||
*/
|
||||
void clone_core(manager & m, skip_list_base * new_list) const {
|
||||
bucket * pred_vect[Traits::max_level];
|
||||
unsigned lvl = level();
|
||||
new_list->update_list_level(m, lvl);
|
||||
bucket * new_header = new_list->m_header;
|
||||
for (unsigned i = 0; i < lvl; i++)
|
||||
pred_vect[i] = new_header;
|
||||
bucket * curr = first_bucket();
|
||||
while (curr != 0) {
|
||||
unsigned curr_lvl = curr->level();
|
||||
bucket * new_bucket = new_list->mk_bucket(m, curr_lvl, curr->capacity());
|
||||
for (unsigned i = 0; i < curr_lvl; i++) {
|
||||
pred_vect[i]->set_next(i, new_bucket);
|
||||
pred_vect[i] = new_bucket;
|
||||
}
|
||||
unsigned curr_sz = curr->size();
|
||||
for (unsigned i = 0; i < curr_sz; i++) {
|
||||
entry const & curr_entry = curr->get(i);
|
||||
inc_ref(m, curr_entry.val());
|
||||
new_bucket->set(i, curr_entry);
|
||||
}
|
||||
new_bucket->set_size(curr_sz);
|
||||
curr = curr->get_next(0);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
skip_list_base():
|
||||
m_header(0) {
|
||||
SASSERT(Traits::max_capacity >= 2);
|
||||
SASSERT(Traits::initial_capacity >= 2);
|
||||
SASSERT(Traits::initial_capacity <= Traits::max_capacity);
|
||||
SASSERT(Traits::max_level >= 1);
|
||||
SASSERT(Traits::max_capacity <= SL_MAX_CAPACITY);
|
||||
SASSERT(Traits::max_level <= SL_MAX_LEVEL);
|
||||
}
|
||||
|
||||
skip_list_base(manager & m):
|
||||
m_header(0) {
|
||||
SASSERT(Traits::max_capacity >= 2);
|
||||
SASSERT(Traits::initial_capacity >= 2);
|
||||
SASSERT(Traits::initial_capacity <= Traits::max_capacity);
|
||||
SASSERT(Traits::max_level >= 1);
|
||||
SASSERT(Traits::max_capacity <= SL_MAX_CAPACITY);
|
||||
SASSERT(Traits::max_level <= SL_MAX_LEVEL);
|
||||
init(m);
|
||||
}
|
||||
|
||||
~skip_list_base() {
|
||||
SASSERT(m_header == 0);
|
||||
}
|
||||
|
||||
void deallocate(manager & m) {
|
||||
deallocate_list<true>(m);
|
||||
m_header = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Deallocate the list but do not invoke dec_ref_eh.
|
||||
*/
|
||||
void deallocate_no_decref(manager & m) {
|
||||
deallocate_list<false>(m);
|
||||
m_header = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Initialize a list that was created using the default constructor.
|
||||
It can be used also to initialized a list deallocated using the method #deallocate.
|
||||
*/
|
||||
void init(manager & m) {
|
||||
SASSERT(m_header == 0);
|
||||
m_header = mk_header(m, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Remove all elements from the skip-list.
|
||||
*/
|
||||
void reset(manager & m) {
|
||||
deallocate_list<true>(m);
|
||||
m_header = mk_header(m, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Remove all elements from the skip-list without invoking dec_ref_eh.
|
||||
*/
|
||||
void reset_no_decref(manager & m) {
|
||||
deallocate_list<false>(m);
|
||||
m_header = mk_header(m, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the list is empty.
|
||||
*/
|
||||
bool empty() const {
|
||||
SASSERT(m_header != 0);
|
||||
return first_bucket() == 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
\brief Return the position of the bucket in the skip list.
|
||||
*/
|
||||
unsigned get_bucket_idx(bucket const * bt) const {
|
||||
bucket * curr = m_header;
|
||||
unsigned pos = 0;
|
||||
while (curr != 0) {
|
||||
if (curr == bt)
|
||||
return pos;
|
||||
pos++;
|
||||
curr = curr->get_next(0);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Display the given entry.
|
||||
*/
|
||||
void display(std::ostream & out, entry const & e) const {
|
||||
e.display(out);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Display a reference to the given bucket.
|
||||
*/
|
||||
void display_bucket_ref(std::ostream & out, bucket const * bt) const {
|
||||
if (bt == 0)
|
||||
out << "NIL";
|
||||
else
|
||||
out << "#" << get_bucket_idx(bt);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Display the predecessor vector.
|
||||
*/
|
||||
void display_predecessor_vector(std::ostream & out, bucket const * const pred_vect[]) const {
|
||||
for (unsigned i = 0; i < level(); i++) {
|
||||
out << i << ": ";
|
||||
display_bucket_ref(out, pred_vect[i]);
|
||||
if (pred_vect[i]) {
|
||||
out << " -> ";
|
||||
display_bucket_ref(out, pred_vect[i]->get_next(i));
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Display the successors of the given bucket.
|
||||
*/
|
||||
void display_successors(std::ostream & out, bucket const * bt) const {
|
||||
out << "[";
|
||||
for (unsigned i = 0; i < bt->level(); i++) {
|
||||
if (i > 0) out << ", ";
|
||||
display_bucket_ref(out, bt->get_next(i));
|
||||
}
|
||||
out << "]";
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Display the given bucket.
|
||||
*/
|
||||
void display(std::ostream & out, bucket const * bt) const {
|
||||
if (bt == 0) {
|
||||
out << "NIL\n";
|
||||
return;
|
||||
}
|
||||
out << "bucket ";
|
||||
display_bucket_ref(out, bt);
|
||||
out << ", capacity: " << bt->capacity() << "\n";
|
||||
out << "successors: ";
|
||||
display_successors(out, bt);
|
||||
out << "\n";
|
||||
out << "entries:\n";
|
||||
for (unsigned i = 0; i < bt->size(); i++) {
|
||||
display(out, bt->get(i));
|
||||
out << "\n";
|
||||
}
|
||||
out << "----------\n";
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
\brief Dump the skip list for debugging purposes.
|
||||
It assumes that key and value types implement operator <<.
|
||||
*/
|
||||
void display_physical(std::ostream & out) const {
|
||||
out << "{\nskip-list level: " << m_header->level() << "\n";
|
||||
bucket * curr = m_header;
|
||||
while (curr != 0) {
|
||||
display(out, curr);
|
||||
curr = curr->get_next(0);
|
||||
}
|
||||
out << "}\n";
|
||||
}
|
||||
|
||||
void display(std::ostream & out) const {
|
||||
bucket * curr = m_header;
|
||||
while (curr != 0) {
|
||||
unsigned sz = curr->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (i > 0)
|
||||
out << " ";
|
||||
curr->get(i).display(out);
|
||||
}
|
||||
curr = curr->get_next(0);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
\brief Return true if bucket b2 can be reached from b1 following get_next(i) pointers
|
||||
*/
|
||||
bool is_reachable_at_i(bucket const * bt1, bucket const * bt2, unsigned i) const {
|
||||
bucket * curr = bt1->get_next(i);
|
||||
while (curr != 0) {
|
||||
if (curr == bt2)
|
||||
return true;
|
||||
curr = curr->get_next(i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
static void display_size_info_core(std::ostream & out, unsigned cls_size) {
|
||||
out << "sizeof root: " << cls_size << "\n";
|
||||
out << "bucket max capacity: " << Traits::max_capacity << "\n";
|
||||
out << "bucket max level: " << Traits::max_level << "\n";
|
||||
out << "sizeof(bucket): " << sizeof(bucket) << " + " << sizeof(bucket*) << "*lvl + " << sizeof(entry) << "*capacity\n";
|
||||
out << "sizeof(usual bucket): " << (sizeof(bucket) + sizeof(entry)*Traits::max_capacity) << " + " << sizeof(bucket*) << "*lvl\n";
|
||||
out << "sizeof(max. bucket): " << (sizeof(bucket) + sizeof(entry)*Traits::max_capacity + sizeof(bucket*)*Traits::max_level) << "\n";
|
||||
out << "sizeof(entry): " << sizeof(entry) << "\n";
|
||||
out << "sizeof empty: " << cls_size + bucket::get_obj_size(1, 0) << "\n";;
|
||||
out << "sizeof singleton: ["
|
||||
<< (cls_size + bucket::get_obj_size(1, 0) + bucket::get_obj_size(1, Traits::initial_capacity)) << ", "
|
||||
<< (cls_size +
|
||||
bucket::get_obj_size(Traits::max_level, 0) +
|
||||
bucket::get_obj_size(Traits::max_level, Traits::max_capacity)) << "]\n";
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
\brief Return true if skip-list has more than k buckets (not considering the header).
|
||||
|
||||
\remark This method is for debugging purposes.
|
||||
*/
|
||||
bool has_more_than_k_buckets(unsigned k) const {
|
||||
bucket * curr = first_bucket();
|
||||
while (curr != 0 && k > 0) {
|
||||
curr = curr->get_next(0);
|
||||
k--;
|
||||
}
|
||||
return curr != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the skip-list has more than k entries.
|
||||
*/
|
||||
bool has_more_than_k_entries(unsigned k) const {
|
||||
bucket * curr = first_bucket();
|
||||
while (curr != 0 && k >= curr->size()) {
|
||||
k -= curr->size();
|
||||
curr = curr->get_next(0);
|
||||
}
|
||||
SASSERT(curr == 0 || curr->size() > k);
|
||||
return curr != 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
\brief Return the amount of memory consumed by the list.
|
||||
*/
|
||||
unsigned memory_core(unsigned cls_size) const {
|
||||
unsigned r = 0;
|
||||
r += cls_size;
|
||||
bucket * curr = m_header;
|
||||
while (curr != 0) {
|
||||
r += bucket::get_obj_size(curr->level(), curr->capacity());
|
||||
curr = curr->get_next(0);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
\brief Compress the buckets of the skip-list.
|
||||
Make sure that all, but the last bucket, have at least \c load entries.
|
||||
|
||||
\remark If load > Traits::max_capacity, then it assumes load = Traits::max_capacity.
|
||||
*/
|
||||
void compress(manager & m, unsigned load = Traits::max_capacity/2) {
|
||||
if (load > Traits::max_capacity)
|
||||
load = Traits::max_capacity;
|
||||
bucket * pred_vect[Traits::max_level];
|
||||
update_predecessor_vector(pred_vect, m_header);
|
||||
bucket * curr = first_bucket();
|
||||
while (curr != 0) {
|
||||
update_predecessor_vector(pred_vect, curr);
|
||||
bucket * next = curr->get_next(0);
|
||||
while (curr->size() < load && next != 0) {
|
||||
// steal entries of the successor bucket.
|
||||
unsigned deficit = load - curr->size();
|
||||
unsigned next_size = next->size();
|
||||
if (next_size <= deficit) {
|
||||
for (unsigned i = 0, j = curr->size(); i < next_size; i++, j++) {
|
||||
curr->set(j, next->get(i));
|
||||
}
|
||||
curr->expand(next_size);
|
||||
bucket * new_next = next->get_next(0);
|
||||
del_bucket(m, next, pred_vect);
|
||||
next = new_next;
|
||||
SASSERT(curr->size() <= load);
|
||||
}
|
||||
else {
|
||||
for (unsigned i = 0, j = curr->size(); i < deficit; i++, j++) {
|
||||
curr->set(j, next->get(i));
|
||||
}
|
||||
curr->expand(deficit);
|
||||
for (unsigned i = deficit, j = 0; i < next_size; i++, j++) {
|
||||
next->set(j, next->get(i));
|
||||
}
|
||||
next->set_size(next_size - deficit);
|
||||
SASSERT(curr->size() == load);
|
||||
}
|
||||
}
|
||||
curr = curr->get_next(0);
|
||||
}
|
||||
}
|
||||
|
||||
void swap(skip_list_base & other) {
|
||||
bucket * tmp = m_header;
|
||||
m_header = other.m_header;
|
||||
other.m_header = tmp;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue