3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-07 18:05:21 +00:00
z3/lib/imdd.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

3138 lines
110 KiB
C++

/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
imdd.cpp
Abstract:
Interval based Multiple-valued Decision Diagrams.
Author:
Leonardo de Moura (leonardo) 2010-10-13.
Revision History:
--*/
#ifndef _EXTERNAL_RELEASE
#include"imdd.h"
#include"map.h"
#define DEFAULT_SIMPLE_MAX_ENTRIES 32
inline bool is_cached(imdd2imdd_cache & cache, imdd * d, imdd * & r) {
if (d->is_shared()) {
if (cache.find(d, r) && (r == 0 || !r->is_dead()))
return true;
}
return false;
}
inline void cache_result(imdd2imdd_cache & cache, imdd * d, imdd * r) {
if (d->is_shared())
cache.insert(d, r);
}
inline bool is_cached(imdd_pair2imdd_cache & cache, imdd * d1, imdd * d2, imdd * & r) {
if (d1->is_shared() && d2->is_shared()) {
if (cache.find(d1, d2, r) && (r == 0 || !r->is_dead()))
return true;
}
return false;
}
inline void cache_result(imdd_pair2imdd_cache & cache, imdd * d1, imdd * d2, imdd * r) {
if (d1->is_shared() && d2->is_shared())
cache.insert(d1, d2, r);
}
inline bool destructive_update_at(bool destructive, imdd * d) {
return destructive && !d->is_memoized() && !d->is_shared();
}
void sl_imdd_manager::inc_ref_eh(imdd * v) {
m_manager->inc_ref(v);
}
void sl_imdd_manager::dec_ref_eh(imdd * v) {
m_manager->dec_ref(v);
}
unsigned imdd::hc_hash() const {
unsigned r = 0;
r = get_arity();
imdd_children::iterator it = begin_children();
imdd_children::iterator end = end_children();
for (; it != end; ++it) {
unsigned b = it->begin_key();
unsigned e = it->end_key();
imdd const * child = it->val();
mix(b, e, r);
if (child) {
SASSERT(child->is_memoized());
unsigned v = child->get_id();
mix(v, v, r);
}
}
return r;
}
bool imdd::hc_equal(imdd const * other) const {
if (m_arity != other->m_arity)
return false;
return m_children.is_equal(other->m_children);
}
imdd_manager::delay_dealloc::~delay_dealloc() {
SASSERT(m_manager.m_to_delete.size() >= m_to_delete_size);
ptr_vector<imdd>::iterator it = m_manager.m_to_delete.begin() + m_to_delete_size;
ptr_vector<imdd>::iterator end = m_manager.m_to_delete.end();
for (; it != end; ++it) {
SASSERT((*it)->is_dead());
m_manager.deallocate_imdd(*it);
}
m_manager.m_to_delete.shrink(m_to_delete_size);
m_manager.m_delay_dealloc = m_delay_dealloc_value;
}
imdd_manager::imdd_manager():
m_sl_manager(m_alloc),
m_simple_max_entries(DEFAULT_SIMPLE_MAX_ENTRIES),
m_delay_dealloc(false) {
m_sl_manager.m_manager = this;
m_swap_new_child = 0;
}
imdd * imdd_manager::_mk_empty(unsigned arity) {
SASSERT(arity > 0);
void * mem = m_alloc.allocate(sizeof(imdd));
return new (mem) imdd(m_sl_manager, m_id_gen.mk(), arity);
}
imdd * imdd_manager::defrag_core(imdd * d) {
if (d->is_memoized())
return d;
unsigned h = d->get_arity();
if (h == 1 && !is_simple_node(d))
return d;
imdd * new_d = 0;
if (is_cached(m_defrag_cache, d, new_d))
return new_d;
if (h == 1) {
SASSERT(is_simple_node(d));
imdd * new_can_d = memoize(d);
cache_result(m_defrag_cache, d, new_can_d);
return new_can_d;
}
SASSERT(h > 1);
new_d = _mk_empty(h);
imdd_children::push_back_proc push_back(m_sl_manager, new_d->m_children);
bool has_new = false;
bool children_memoized = true;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * child = it->val();
imdd * new_child = defrag_core(child);
if (child != new_child)
has_new = true;
if (!new_child->is_memoized())
children_memoized = false;
push_back(it->begin_key(), it->end_key(), new_child);
}
if (has_new) {
if (children_memoized && is_simple_node(new_d)) {
imdd * new_can_d = memoize(new_d);
if (new_can_d != new_d) {
SASSERT(new_d->get_ref_count() == 0);
delete_imdd(new_d);
}
new_d = new_can_d;
}
}
else {
SASSERT(!has_new);
delete_imdd(new_d);
new_d = d;
if (children_memoized && is_simple_node(new_d)) {
new_d = memoize(new_d);
}
}
cache_result(m_defrag_cache, d, new_d);
return new_d;
}
/**
\brief Compress the given IMDD by using hash-consing.
*/
void imdd_manager::defrag(imdd_ref & d) {
delay_dealloc delay(*this);
m_defrag_cache.reset();
d = defrag_core(d);
}
/**
\brief Memoize the given IMDD.
Return an IMDD structurally equivalent to d.
This method assumes the children of d are memoized.
If that is not the case, the user should invoke defrag instead.
*/
imdd * imdd_manager::memoize(imdd * d) {
if (d->is_memoized())
return d;
unsigned h = d->get_arity();
m_tables.reserve(h);
DEBUG_CODE({
if (h > 1) {
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
SASSERT(it->val()->is_memoized());
}
}
});
imdd * r = m_tables[h-1].insert_if_not_there(d);
if (r == d) {
r->mark_as_memoized();
}
SASSERT(r->is_memoized());
return r;
}
/**
\brief Remove the given IMDD from the hash-consing table
*/
void imdd_manager::unmemoize(imdd * d) {
SASSERT(d->is_memoized());
m_tables[d->get_arity()-1].erase(d);
d->mark_as_memoized(false);
}
/**
\brief Remove the given IMDD (and its children) from the hash-consing tables.
*/
void imdd_manager::unmemoize_rec(imdd * d) {
SASSERT(m_worklist.empty());
m_worklist.push_back(d);
while (!m_worklist.empty()) {
d = m_worklist.back();
if (d->is_memoized()) {
unmemoize(d);
SASSERT(!d->is_memoized());
if (d->get_arity() > 1) {
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it)
m_worklist.push_back(it->val());
}
}
}
}
bool imdd_manager::is_simple_node(imdd * d) const {
return !d->m_children.has_more_than_k_entries(m_simple_max_entries);
}
void imdd_manager::mark_as_dead(imdd * d) {
// The references to the children were decremented by delete_imdd.
SASSERT(!d->is_dead());
d->m_children.deallocate_no_decref(m_sl_manager);
d->mark_as_dead();
if (m_delay_dealloc)
m_to_delete.push_back(d);
else
deallocate_imdd(d);
}
void imdd_manager::deallocate_imdd(imdd * d) {
SASSERT(d->is_dead());
m_alloc.deallocate(sizeof(imdd), d);
}
void imdd_manager::delete_imdd(imdd * d) {
SASSERT(m_worklist.empty());
m_worklist.push_back(d);
while (!m_worklist.empty()) {
d = m_worklist.back();
m_worklist.pop_back();
m_id_gen.recycle(d->get_id());
SASSERT(d->get_ref_count() == 0);
if (d->is_memoized()) {
unsigned arity = d->get_arity();
SASSERT(m_tables[arity-1].contains(d));
m_tables[arity-1].erase(d);
}
if (d->get_arity() > 1) {
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * child = it->val();
SASSERT(child);
child->dec_ref();
if (child->get_ref_count() == 0)
m_worklist.push_back(child);
}
}
mark_as_dead(d);
}
}
/**
\brief Return a (non-memoized) shallow copy of d.
*/
imdd * imdd_manager::copy_main(imdd * d) {
imdd * d_copy = _mk_empty(d->get_arity());
d_copy->m_children.copy(m_sl_manager, d->m_children);
SASSERT(!d_copy->is_memoized());
SASSERT(d_copy->get_ref_count() == 0);
return d_copy;
}
/**
\brief Insert the values [b, e] into a IMDD of arity 1.
If destructive == true, d is not memoized and is not shared, then a
destructive update is performed, and d is returned.
Otherwise, a fresh IMDD is returned.
*/
imdd * imdd_manager::insert_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res) {
SASSERT(d->get_arity() == 1);
if (destructive_update_at(destructive, d)) {
add_child(d, b, e, 0);
return d;
}
else {
imdd * new_d = copy_main(d);
add_child(new_d, b, e, 0);
return memoize_new_imdd_if(memoize_res, new_d);
}
}
/**
\brief Remove the values [b, e] from an IMDD of arity 1.
If destructive == true, d is not memoized and is not shared, then a
destructive update is performed, and d is returned.
Otherwise, a fresh IMDD is returned.
*/
imdd * imdd_manager::remove_main(imdd * d, unsigned b, unsigned e, bool destructive, bool memoize_res) {
SASSERT(d->get_arity() == 1);
if (destructive_update_at(destructive, d)) {
remove_child(d, b, e);
return d;
}
else {
imdd * new_d = copy_main(d);
remove_child(new_d, b, e);
return memoize_new_imdd_if(memoize_res, new_d);
}
}
/**
\brief Auxiliary functor used to implement destructive version of mk_product.
*/
struct imdd_manager::null2imdd_proc {
imdd * m_d2;
null2imdd_proc(imdd * d2):m_d2(d2) {}
imdd * operator()(imdd * d) { SASSERT(d == 0); return m_d2; }
};
/**
\brief Auxiliary functor used to implement destructive version of mk_product.
*/
struct imdd_manager::mk_product_proc {
imdd_manager & m_manager;
imdd * m_d2;
bool m_memoize;
mk_product_proc(imdd_manager & m, imdd * d2, bool memoize):
m_manager(m),
m_d2(d2),
m_memoize(memoize) {
}
imdd * operator()(imdd * d1) { return m_manager.mk_product_core(d1, m_d2, true, m_memoize); }
};
imdd * imdd_manager::mk_product_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res) {
SASSERT(!d1->empty());
SASSERT(!d2->empty());
if (destructive && !d1->is_shared()) {
if (d1->is_memoized())
unmemoize(d1);
if (d1->get_arity() == 1) {
null2imdd_proc f(d2);
d1->m_children.update_values(m_sl_manager, f);
d1->m_arity += d2->m_arity;
}
else {
mk_product_proc f(*this, d2, memoize_res);
d1->m_children.update_values(m_sl_manager, f);
d1->m_arity += d2->m_arity;
}
return d1;
}
else {
imdd * new_d1 = 0;
if (is_cached(m_mk_product_cache, d1, new_d1))
return new_d1;
unsigned arity1 = d1->get_arity();
unsigned arity2 = d2->get_arity();
new_d1 = _mk_empty(arity1 + arity2);
imdd_children::push_back_proc push_back(m_sl_manager, new_d1->m_children);
imdd_children::iterator it = d1->begin_children();
imdd_children::iterator end = d1->end_children();
bool children_memoized = true;
for (; it != end; ++it) {
imdd * new_child;
if (arity1 == 1)
new_child = d2;
else
new_child = mk_product_core(it->val(), d2, false, memoize_res);
if (!new_child->is_memoized())
children_memoized = false;
push_back(it->begin_key(), it->end_key(), new_child);
}
new_d1 = memoize_new_imdd_if(memoize_res && children_memoized, new_d1);
cache_result(m_mk_product_cache, d1, new_d1);
return new_d1;
}
}
imdd * imdd_manager::mk_product_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res) {
if (d1->empty() || d2->empty()) {
unsigned a1 = d1->get_arity();
unsigned a2 = d2->get_arity();
return _mk_empty(a1 + a2);
}
delay_dealloc delay(*this);
if (d1 == d2)
destructive = false;
m_mk_product_cache.reset();
return mk_product_core(d1, d2, destructive, memoize_res);
}
void imdd_manager::init_add_facts_new_children(unsigned num, unsigned const * lowers, unsigned const * uppers, bool memoize_res) {
if (m_add_facts_new_children[num] != 0) {
DEBUG_CODE({
for (unsigned i = 1; i <= num; i++) {
SASSERT(m_add_facts_new_children[i] != 0);
}});
return;
}
for (unsigned i = 1; i <= num; i++) {
if (m_add_facts_new_children[i] == 0) {
imdd * new_child = _mk_empty(i);
unsigned b = lowers[num - i];
unsigned e = uppers[num - i];
bool prev_memoized = true;
if (i == 1) {
add_child(new_child, b, e, 0);
}
else {
SASSERT(m_add_facts_new_children[i-1] != 0);
prev_memoized = m_add_facts_new_children[i-1]->is_memoized();
add_child(new_child, b, e, m_add_facts_new_children[i-1]);
}
new_child = memoize_new_imdd_if(memoize_res && prev_memoized, new_child);
m_add_facts_new_children[i] = new_child;
}
}
DEBUG_CODE({
for (unsigned i = 1; i <= num; i++) {
SASSERT(m_add_facts_new_children[i] != 0);
}});
}
imdd * imdd_manager::add_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) {
SASSERT(d->get_arity() == num);
imdd_ref new_child(*this);
bool new_children_memoized = true;
#define INIT_NEW_CHILD() { \
if (new_child == 0) { \
init_add_facts_new_children(num - 1, lowers + 1, uppers + 1, memoize_res); \
new_child = m_add_facts_new_children[num-1]; \
if (!new_child->is_memoized()) new_children_memoized = false; \
}}
if (destructive_update_at(destructive, d)) {
if (num == 1)
return insert_main(d, *lowers, *uppers, destructive, memoize_res);
SASSERT(num > 1);
sbuffer<entry> to_insert;
new_child = m_add_facts_new_children[num-1];
unsigned b = *lowers;
unsigned e = *uppers;
imdd_children::iterator it = d->m_children.find_geq(b);
imdd_children::iterator end = d->end_children();
for (; it != end && b <= e; ++it) {
imdd_children::entry const & curr_entry = *it;
if (e < curr_entry.begin_key())
break;
if (b < curr_entry.begin_key()) {
INIT_NEW_CHILD();
SASSERT(b <= curr_entry.begin_key() - 1);
to_insert.push_back(entry(b, curr_entry.begin_key() - 1, new_child));
b = curr_entry.begin_key();
}
imdd * curr_child = curr_entry.val();
SASSERT(b >= curr_entry.begin_key());
bool cover = b == curr_entry.begin_key() && e >= curr_entry.end_key();
// If cover == true, then the curr_child is completely covered by the new facts, and it is not needed anymore.
// So, we can perform a destructive update.
imdd * new_curr_child = add_facts_core(curr_child, num - 1, lowers + 1, uppers + 1, cover, memoize_res);
if (e >= curr_entry.end_key()) {
SASSERT(b <= curr_entry.end_key());
to_insert.push_back(entry(b, curr_entry.end_key(), new_curr_child));
}
else {
SASSERT(e < curr_entry.end_key());
SASSERT(b <= e);
to_insert.push_back(entry(b, e, new_curr_child));
}
b = curr_entry.end_key() + 1;
}
// Re-insert entries in m_add_facts_to_insert into d->m_children,
// and if b <= e also insert [b, e] -> new_child
if (b <= e) {
INIT_NEW_CHILD();
add_child(d, b, e, new_child);
}
svector<entry>::iterator it2 = to_insert.begin();
svector<entry>::iterator end2 = to_insert.end();
for (; it2 != end2; ++it2) {
imdd_children::entry const & curr_entry = *it2;
add_child(d, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val());
}
return d;
}
else {
imdd * new_d = 0;
if (is_cached(m_add_facts_cache, d, new_d))
return new_d;
if (num == 1) {
new_d = insert_main(d, *lowers, *uppers, destructive, memoize_res);
}
else {
new_d = copy_main(d);
new_child = m_add_facts_new_children[num-1];
TRACE("add_facts_bug", tout << "after copying: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";);
unsigned b = *lowers;
unsigned e = *uppers;
imdd_children::iterator it = d->m_children.find_geq(b);
imdd_children::iterator end = d->end_children();
for (; it != end && b <= e; ++it) {
imdd_children::entry const & curr_entry = *it;
if (e < curr_entry.begin_key())
break;
if (b < curr_entry.begin_key()) {
INIT_NEW_CHILD();
SASSERT(b <= curr_entry.begin_key() - 1);
add_child(new_d, b, curr_entry.begin_key() - 1, new_child);
TRACE("add_facts_bug", tout << "after inserting new child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";);
b = curr_entry.begin_key();
}
imdd * curr_child = curr_entry.val();
imdd * new_curr_child = add_facts_core(curr_child, num - 1, lowers + 1, uppers + 1, false, memoize_res);
if (!new_curr_child->is_memoized())
new_children_memoized = false;
if (e >= curr_entry.end_key()) {
SASSERT(b <= curr_entry.end_key());
add_child(new_d, b, curr_entry.end_key(), new_curr_child);
TRACE("add_facts_bug", tout << "1) after inserting new curr child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";
tout << "new_curr_child: " << mk_ll_pp(new_curr_child, *this) << "\n";);
}
else {
SASSERT(e < curr_entry.end_key());
SASSERT(b <= e);
add_child(new_d, b, e, new_curr_child);
TRACE("add_facts_bug", tout << "2) after inserting new curr child: " << new_d << "\n" << mk_ll_pp(new_d, *this) << "\n";
tout << "new_curr_child: " << mk_ll_pp(new_curr_child, *this) << "\n";);
}
b = curr_entry.end_key() + 1;
}
if (b <= e) {
INIT_NEW_CHILD();
add_child(new_d, b, e, new_child);
}
new_d = memoize_new_imdd_if(memoize_res && d->is_memoized() && new_children_memoized, new_d);
}
cache_result(m_add_facts_cache, d, new_d);
return new_d;
}
}
imdd * imdd_manager::add_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) {
SASSERT(d->get_arity() == num);
delay_dealloc delay(*this);
m_add_facts_cache.reset();
m_add_facts_new_children.reset();
m_add_facts_new_children.resize(num, 0);
return add_facts_core(d, num, lowers, uppers, destructive, memoize_res);
}
inline void update_memoized_flag(imdd * d, bool & memoized_flag) {
if (d && !d->is_memoized())
memoized_flag = false;
}
void imdd_manager::push_back_entries(unsigned head, imdd_children::iterator & it, imdd_children::iterator & end,
imdd_children::push_back_proc & push_back, bool & children_memoized) {
if (it != end) {
push_back(head, it->end_key(), it->val());
++it;
for (; it != end; ++it) {
update_memoized_flag(it->val(), children_memoized);
push_back(it->begin_key(), it->end_key(), it->val());
}
}
}
/**
\brief Push the entries starting at head upto the given limit (no included).
That is, we are copying the entries in the interval [head, limit).
*/
void imdd_manager::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) {
SASSERT(it != end);
SASSERT(head <= it->end_key());
SASSERT(head >= it->begin_key());
SASSERT(head < limit);
while (head < limit && it != end) {
if (it->end_key() < limit) {
update_memoized_flag(it->val(), children_memoized);
push_back(head, it->end_key(), it->val());
++it;
if (it != end)
head = it->begin_key();
}
else {
SASSERT(it->end_key() >= limit);
update_memoized_flag(it->val(), children_memoized);
push_back(head, limit-1, it->val());
head = limit;
}
}
SASSERT(head == limit || it == end || head == it->begin_key());
}
void imdd_manager::move_head(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end, unsigned new_head) {
SASSERT(new_head >= head);
SASSERT(head >= it->begin_key());
SASSERT(head <= it->end_key());
SASSERT(new_head <= it->end_key());
if (new_head < it->end_key()) {
head = new_head+1;
SASSERT(head <= it->end_key());
}
else {
SASSERT(new_head == it->end_key());
++it;
if (it != end)
head = it->begin_key();
}
}
/**
\brief Copy the entries starting at head upto the given limit (no included).
That is, we are copying the entries in the interval [head, limit).
*/
void imdd_manager::copy_upto(unsigned & head, imdd_children::iterator & it, imdd_children::iterator & end,
unsigned limit, sbuffer<entry> & result) {
SASSERT(it != end);
SASSERT(head <= it->end_key());
SASSERT(head >= it->begin_key());
SASSERT(head < limit);
while (head < limit && it != end) {
if (it->end_key() < limit) {
result.push_back(entry(head, it->end_key(), it->val()));
++it;
if (it != end)
head = it->begin_key();
}
else {
SASSERT(it->end_key() >= limit);
result.push_back(entry(head, limit-1, it->val()));
head = limit;
}
}
SASSERT(head == limit || it == end || head == it->begin_key());
}
imdd * imdd_manager::mk_union_core(imdd * d1, imdd * d2, bool destructive, bool memoize_res) {
if (d1 == d2)
return d1;
if (destructive_update_at(destructive, d1)) {
if (d1->get_arity() == 1) {
imdd_children::iterator it = d2->begin_children();
imdd_children::iterator end = d2->end_children();
for (; it != end; ++it)
add_child(d1, it->begin_key(), it->end_key(), 0);
return d1;
}
else {
imdd_children::iterator it2 = d2->begin_children();
imdd_children::iterator end2 = d2->end_children();
imdd_children::iterator it1 = d1->m_children.find_geq(it2->begin_key());
imdd_children::iterator end1 = d1->end_children();
sbuffer<entry> to_insert;
unsigned head1 = it1 != end1 ? it1->begin_key() : UINT_MAX;
SASSERT(it2 != end2);
unsigned head2 = it2->begin_key();
while (true) {
if (it1 == end1) {
// copy it2 to d1
// Remark: we don't need to copy to to_insert, since we will not be using it1 anymore.
// That is, we can directly insert into d1->m_children.
if (it2 != end2) {
add_child(d1, head2, it2->end_key(), it2->val());
++it2;
for (; it2 != end2; ++it2)
add_child(d1, it2->begin_key(), it2->end_key(), it2->val());
}
break;
}
if (it2 == end2) {
break;
}
if (head1 < head2) {
head1 = head2;
it1.move_to(head2);
}
else if (head1 > head2) {
copy_upto(head2, it2, end2, head1, to_insert);
}
else {
SASSERT(head1 == head2);
unsigned tail = std::min(it1->end_key(), it2->end_key());
imdd * new_child = 0;
SASSERT(d1->get_arity() > 1);
bool cover = head1 == it1->begin_key() && tail == it1->end_key();
// If cover == true, then the it1->val() (curr_child) is completely covered by
// the new_child, and it is not needed anymore.
// So, we can perform a destructive update.
new_child = mk_union_core(it1->val(), it2->val(), cover, memoize_res);
if (new_child != it1->val())
to_insert.push_back(entry(head1, tail, new_child));
move_head(head1, it1, end1, tail);
move_head(head2, it2, end2, tail);
}
}
sbuffer<entry>::const_iterator it3 = to_insert.begin();
sbuffer<entry>::const_iterator end3 = to_insert.end();
for (; it3 != end3; ++it3)
add_child(d1, it3->begin_key(), it3->end_key(), it3->val());
return d1;
}
}
else {
imdd * r = 0;
if (is_cached(m_union_cache, d1, d2, r))
return r;
unsigned arity = d1->get_arity();
r = _mk_empty(arity);
imdd_children::push_back_proc push_back(m_sl_manager, r->m_children);
imdd_children::iterator it1 = d1->begin_children();
imdd_children::iterator end1 = d1->end_children();
imdd_children::iterator it2 = d2->begin_children();
imdd_children::iterator end2 = d2->end_children();
SASSERT(it1 != end1);
SASSERT(it2 != end2);
unsigned head1 = it1->begin_key();
unsigned head2 = it2->begin_key();
bool children_memoized = true;
while (true) {
if (it1 == end1) {
// copy it2 to result
push_back_entries(head2, it2, end2, push_back, children_memoized);
break;
}
if (it2 == end2) {
// copy it1 to result
push_back_entries(head1, it1, end1, push_back, children_memoized);
break;
}
if (head1 < head2) {
push_back_upto(head1, it1, end1, head2, push_back, children_memoized);
}
else if (head1 > head2) {
push_back_upto(head2, it2, end2, head1, push_back, children_memoized);
}
else {
SASSERT(head1 == head2);
unsigned tail = std::min(it1->end_key(), it2->end_key());
imdd * new_child = 0;
if (arity > 1) {
new_child = mk_union_core(it1->val(), it2->val(), false, memoize_res);
update_memoized_flag(new_child, children_memoized);
}
push_back(head1, tail, new_child);
move_head(head1, it1, end1, tail);
move_head(head2, it2, end2, tail);
}
}
r = memoize_new_imdd_if(memoize_res && children_memoized, r);
cache_result(m_union_cache, d1, d2, r);
return r;
}
}
void imdd_manager::reset_union_cache() {
m_union_cache.reset();
}
imdd * imdd_manager::mk_union_main(imdd * d1, imdd * d2, bool destructive, bool memoize_res) {
SASSERT(d1->get_arity() == d2->get_arity());
if (d1 == d2)
return d1;
if (d1->empty())
return d2;
if (d2->empty())
return d1;
delay_dealloc delay(*this);
reset_union_cache();
return mk_union_core(d1, d2, destructive, memoize_res);
}
void imdd_manager::mk_union_core_dupdt(imdd_ref & d1, imdd * d2, bool memoize_res) {
SASSERT(d1->get_arity() == d2->get_arity());
if (d1 == d2 || d2->empty())
return;
if (d1->empty()) {
d1 = d2;
return;
}
d1 = mk_union_core(d1, d2, true, memoize_res);
}
void imdd_manager::mk_union_core(imdd * d1, imdd * d2, imdd_ref & r, bool memoize_res) {
SASSERT(d1->get_arity() == d2->get_arity());
if (d1 == d2 || d2->empty()) {
r = d1;
return;
}
if (d1->empty()) {
r = d2;
return;
}
TRACE("mk_union_core",
tout << "d1:\n";
display_ll(tout, d1);
tout << "\nd2:\n";
display_ll(tout, d2);
tout << "\n";);
r = mk_union_core(d1, d2, false, memoize_res);
}
imdd * imdd_manager::mk_complement_core(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res) {
SASSERT(d->get_arity() == num);
SASSERT(!d->empty());
SASSERT(*mins <= *maxs);
unsigned arity = d->get_arity();
imdd_ref new_child(*this);
bool new_children_memoized = true;
#undef INIT_NEW_CHILD
#define INIT_NEW_CHILD() { \
if (arity > 1 && new_child == 0) { \
init_add_facts_new_children(num - 1, mins + 1, maxs + 1, memoize_res); \
new_child = m_add_facts_new_children[num-1]; \
SASSERT(new_child != 0); \
if (!new_child->is_memoized()) new_children_memoized = false; \
}}
if (false && destructive_update_at(destructive, d)) {
// TODO
NOT_IMPLEMENTED_YET();
return 0;
}
else {
destructive = false;
imdd * r = 0;
if (is_cached(m_complement_cache, d, r))
return r;
r = _mk_empty(arity);
imdd_children::push_back_proc push_back(m_sl_manager, r->m_children);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
unsigned prev_key = *mins;
for (; it != end; ++it) {
SASSERT(it->begin_key() >= *mins);
SASSERT(it->end_key() <= *maxs);
if (prev_key < it->begin_key()) {
INIT_NEW_CHILD();
push_back(prev_key, it->begin_key() - 1, new_child);
}
if (arity > 1) {
imdd * new_curr = mk_complement_core(it->val(), num - 1, mins + 1, maxs + 1, false, memoize_res);
if (new_curr != 0) {
push_back(it->begin_key(), it->end_key(), new_curr);
if (!new_curr->is_memoized())
new_children_memoized = false;
}
}
prev_key = it->end_key() + 1;
}
if (prev_key <= *maxs) {
INIT_NEW_CHILD();
push_back(prev_key, *maxs, new_child);
}
if (r->empty()) {
delete_imdd(r);
r = 0;
}
r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r);
cache_result(m_complement_cache, d, r);
return r;
}
}
imdd * imdd_manager::mk_complement_main(imdd * d, unsigned num, unsigned const * mins, unsigned const * maxs, bool destructive, bool memoize_res) {
unsigned arity = d->get_arity();
SASSERT(arity == num);
// reuse m_add_facts_new_children for creating the universe-set IMDDs
m_add_facts_new_children.reset();
m_add_facts_new_children.resize(num+1, 0);
if (d->empty()) {
// return the universe-set
init_add_facts_new_children(num, mins, maxs, memoize_res);
return m_add_facts_new_children[num];
}
delay_dealloc delay(*this);
m_complement_cache.reset();
imdd * r = mk_complement_core(d, num, mins, maxs, destructive, memoize_res);
if (r == 0)
return _mk_empty(arity);
else
return r;
}
/**
\brief Replace the IMDD children with new_children.
The function will also decrement the ref-counter of every IMDD in new_children, since
it assumes the counter was incremented when the IMDD was inserted into the buffer.
*/
void imdd::replace_children(sl_imdd_manager & m, sbuffer<imdd_children::entry> & new_children) {
m_children.reset(m);
imdd_children::push_back_proc push_back(m, m_children);
svector<imdd_children::entry>::iterator it = new_children.begin();
svector<imdd_children::entry>::iterator end = new_children.end();
for (; it != end; ++it) {
SASSERT(it->val() == 0 || it->val()->get_ref_count() > 0);
push_back(it->begin_key(), it->end_key(), it->val());
SASSERT(it->val() == 0 || it->val()->get_ref_count() > 1);
if (it->val() != 0)
it->val()->dec_ref();
}
}
imdd * imdd_manager::mk_filter_equal_core(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res) {
SASSERT(!d->empty());
SASSERT(vidx >= 0 && vidx < d->get_arity());
unsigned arity = d->get_arity();
if (destructive_update_at(destructive, d)) {
if (vidx == 0) {
imdd * child = 0;
if (d->m_children.find(value, child)) {
imdd_ref ref(*this);
ref = child; // protect child, we don't want the following reset to delete it.
d->m_children.reset(m_sl_manager);
add_child(d, value, child);
return d;
}
else {
return 0;
}
}
SASSERT(arity > 1);
sbuffer<entry> to_insert;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * curr_child = it->val();
imdd * new_child = mk_filter_equal_core(curr_child, vidx-1, value, true, memoize_res);
if (new_child != 0) {
new_child->inc_ref(); // protect new child, we will be resetting d->m_children later.
to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child));
}
}
if (to_insert.empty()) {
return 0;
}
d->replace_children(m_sl_manager, to_insert);
return d;
}
imdd * r = 0;
if (is_cached(m_filter_equal_cache, d, r))
return r;
bool new_children_memoized = true;
if (vidx == 0) {
// found filter variable
imdd * child = 0;
if (d->m_children.find(value, child)) {
r = _mk_empty(arity);
add_child(r, value, child);
if (child && !child->is_memoized())
new_children_memoized = false;
}
else {
r = 0;
}
}
else {
SASSERT(arity > 1);
r = _mk_empty(arity);
imdd_children::push_back_proc push_back(m_sl_manager, r->m_children);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * curr_child = it->val();
imdd * new_child = mk_filter_equal_core(curr_child, vidx-1, value, false, memoize_res);
if (new_child != 0) {
push_back(it->begin_key(), it->end_key(), new_child);
if (!new_child->is_memoized())
new_children_memoized = false;
}
}
if (r->empty()) {
delete_imdd(r);
r = 0;
}
}
r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r);
cache_result(m_filter_equal_cache, d, r);
return r;
}
imdd * imdd_manager::mk_filter_equal_main(imdd * d, unsigned vidx, unsigned value, bool destructive, bool memoize_res) {
unsigned arity = d->get_arity();
SASSERT(vidx >= 0 && vidx < arity);
if (d->empty())
return d;
delay_dealloc delay(*this);
m_filter_equal_cache.reset();
imdd * r = mk_filter_equal_core(d, vidx, value, destructive, memoize_res);
if (r == 0)
return _mk_empty(arity);
else
return r;
}
/**
\brief create map from imdd nodes to the interval of variables that are covered by variable.
*/
static void mk_interval_set_intersect(sl_manager_base<unsigned> &m, sl_interval_set const& src, unsigned b, unsigned e, sl_interval_set& dst) {
sl_interval_set::iterator it = src.find_geq(b);
sl_interval_set::iterator end = src.end();
for (; it != end; ++it) {
unsigned b1 = it->begin_key();
unsigned e1 = it->end_key();
if (e < b1) {
break;
}
if (b1 < b) {
b1 = b;
}
if (e < e1) {
e1 = e;
}
SASSERT(b <= b1 && b1 <= e1 && e1 <= e);
dst.insert(m, b1, e1);
}
}
static void mk_interval_set_union(sl_manager_base<unsigned> &m, sl_interval_set& dst, sl_interval_set const& src) {
sl_interval_set::iterator it = src.begin(), end = src.end();
for (; it != end; ++it) {
dst.insert(m, it->begin_key(), it->end_key());
}
}
void imdd_manager::reset_fi_intervals(sl_imanager& m) {
for (unsigned i = 0; i < m_alloc_is.size(); ++i) {
m_alloc_is[i]->deallocate(m);
dealloc(m_alloc_is[i]);
}
m_alloc_is.reset();
m_imdd2interval_set.reset();
}
sl_interval_set const* imdd_manager::init_fi_intervals(sl_imanager& m, imdd* d, unsigned var, unsigned num_found) {
sl_interval_set* result = 0;
#define ALLOC_RESULT() if (!result) { result = alloc(sl_interval_set, m); m_alloc_is.push_back(result); }
if (m_imdd2interval_set.find(d, result)) {
return result;
}
bool _is_fi_var = is_fi_var(var);
unsigned new_num_found = _is_fi_var?num_found+1:num_found;
bool last_fi_var = (new_num_found == m_fi_num_vars);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd_children::entry const & curr_entry = *it;
unsigned b = curr_entry.begin_key();
unsigned e = curr_entry.end_key();
if (last_fi_var) {
ALLOC_RESULT();
result->insert(m, b, e, 1);
}
else {
SASSERT(d->get_arity() > 1);
imdd* curr_child = curr_entry.val();
sl_interval_set const* is2 = init_fi_intervals(m, curr_child, var+1, new_num_found);
if (!is2) {
continue;
}
if (_is_fi_var) {
sl_interval_set is3(m);
mk_interval_set_intersect(m, *is2, b, e, is3);
if (!is3.empty()) {
ALLOC_RESULT();
mk_interval_set_union(m, *result, is3);
}
is3.deallocate(m);
}
else {
if (is2 && result != is2) {
ALLOC_RESULT();
mk_interval_set_union(m, *result, *is2);
}
}
}
}
m_imdd2interval_set.insert(d, result);
return result;
}
inline mk_fi_result mk(imdd * d) {
mk_fi_result r;
r.m_d = d;
return r;
}
inline mk_fi_result mk(fi_cache_entry * e) {
mk_fi_result r;
r.m_entry = e;
return r;
}
fi_cache_entry * imdd_manager::mk_fi_cache_entry(imdd * d, unsigned lower, unsigned upper, unsigned num_pairs, imdd_value_pair pairs[]) {
void * mem = m_fi_entries.allocate(sizeof(fi_cache_entry) + sizeof(imdd_value_pair) * num_pairs);
DEBUG_CODE({
for (unsigned i = 0; i < num_pairs; i++) {
SASSERT(pairs[i].first != 0);
}
});
return new (mem) fi_cache_entry(d, lower, upper, num_pairs, pairs);
}
struct imdd_value_pair_lt_value {
bool operator()(imdd_value_pair const & p1, imdd_value_pair const & p2) const {
return p1.second < p2.second;
}
};
// Review note: identical nodes should be unit intervals?
// 1 -> 1 -> x
mk_fi_result imdd_manager::mk_filter_identical_core(imdd * d, unsigned var, unsigned num_found, unsigned lower, unsigned upper,
bool destructive, bool memoize_res) {
TRACE("imdd", tout << "#" << d->get_id() << " var: " << var << " num_found: " << num_found <<
" lower: " << lower << " upper: " << upper << "\n";);
unsigned arity = d->get_arity();
#define MK_EMPTY_ENTRY (num_found == 0 && destructive_update_at(destructive, d))?mk(static_cast<imdd*>(0)):mk(static_cast<fi_cache_entry*>(0))
sl_interval_set* node_ranges = m_imdd2interval_set.find(d);
if (!node_ranges) {
return MK_EMPTY_ENTRY;
}
sl_interval_set::iterator sl_it = node_ranges->find_geq(lower);
if (sl_it == node_ranges->end() || upper < sl_it->begin_key()) {
return MK_EMPTY_ENTRY;
}
bool new_children_memoized = true;
bool _is_fi_var = is_fi_var(var);
if (num_found == 0) {
SASSERT(arity > 1);
if (destructive_update_at(destructive, d)) {
sbuffer<entry> to_insert;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * curr_child = it->val();
if (_is_fi_var) {
fi_cache_entry * child_entry = (mk_filter_identical_core(curr_child,
var+1,
1,
it->begin_key(),
it->end_key(),
false,
memoize_res)).m_entry;
for (unsigned i = 0; child_entry && i < child_entry->m_num_result; i++) {
imdd * new_child = child_entry->m_result[i].first;
unsigned value = child_entry->m_result[i].second;
to_insert.push_back(entry(value, value, new_child));
}
}
else {
imdd * new_child = (mk_filter_identical_core(curr_child, var+1, 0, 0, UINT_MAX, false, memoize_res)).m_d;
if (new_child != 0) {
new_child->inc_ref();
to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child));
}
}
}
if (to_insert.empty()) {
return mk(static_cast<imdd*>(0));
}
d->replace_children(m_sl_manager, to_insert);
return mk(d);
}
else {
// num_found == 0 && variable is not part of the filter && no destructive update.
imdd * r = 0;
if (is_cached(m_fi_top_cache, d, r))
return mk(r);
r = _mk_empty(arity);
imdd_children::push_back_proc push_back(m_sl_manager, r->m_children);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * curr_child = it->val();
if (_is_fi_var) {
fi_cache_entry * entry = (mk_filter_identical_core(curr_child,
var+1,
1,
it->begin_key(),
it->end_key(),
false,
memoize_res)).m_entry;
for (unsigned i = 0; entry && i < entry->m_num_result; i++) {
imdd * new_child = entry->m_result[i].first;
unsigned value = entry->m_result[i].second;
push_back(value, value, new_child);
if (!new_child->is_memoized())
new_children_memoized = false;
}
}
else {
imdd * new_child = (mk_filter_identical_core(curr_child, var+1, 0, 0, UINT_MAX, false, memoize_res)).m_d;
if (new_child != 0) {
push_back(it->begin_key(), it->end_key(), new_child);
if (!new_child->is_memoized())
new_children_memoized = false;
}
}
}
if (r->empty()) {
delete_imdd(r);
r = 0;
}
r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r);
cache_result(m_fi_top_cache, d, r);
return mk(r);
}
}
else {
SASSERT(num_found > 0);
fi_cache_entry d_entry(d, lower, upper);
fi_cache_entry * r_entry;
if (d->is_shared() && m_fi_bottom_cache.find(&d_entry, r_entry))
return mk(r_entry);
sbuffer<imdd_value_pair> result;
if (_is_fi_var) {
unsigned new_num_found = num_found + 1;
bool last_fi_var = (new_num_found == m_fi_num_vars);
imdd_children::iterator it = d->m_children.find_geq(lower);
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * curr_child = it->val();
unsigned curr_lower = it->begin_key();
unsigned curr_upper = it->end_key();
if (curr_lower < lower)
curr_lower = lower;
if (curr_upper > upper)
curr_upper = upper;
if (curr_lower <= curr_upper) {
if (last_fi_var) {
for (unsigned i = curr_lower; i <= curr_upper; i++) {
imdd * new_d = _mk_empty(arity);
add_child(new_d, i, curr_child);
new_d = memoize_new_imdd_if(memoize_res && (curr_child == 0 || curr_child->is_memoized()), new_d);
result.push_back(imdd_value_pair(new_d, i));
}
}
else {
fi_cache_entry * new_entry = (mk_filter_identical_core(curr_child,
var+1,
new_num_found,
curr_lower,
curr_upper,
false,
memoize_res)).m_entry;
for (unsigned i = 0; new_entry && i < new_entry->m_num_result; i++) {
SASSERT(new_entry->m_result[i].first != 0);
imdd * curr_child = new_entry->m_result[i].first;
unsigned value = new_entry->m_result[i].second;
imdd * new_d = _mk_empty(arity);
add_child(new_d, value, curr_child);
SASSERT(curr_child != 0);
new_d = memoize_new_imdd_if(memoize_res && curr_child->is_memoized(), new_d);
result.push_back(imdd_value_pair(new_d, value));
}
}
}
if (curr_upper == upper)
break;
}
}
else {
SASSERT(arity > 1);
u_map<imdd*> value2imdd;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * curr_child = it->val();
SASSERT(curr_child != 0);
fi_cache_entry * new_entry = (mk_filter_identical_core(curr_child,
var+1,
num_found,
lower,
upper,
false,
memoize_res)).m_entry;
for (unsigned i = 0; new_entry && i < new_entry->m_num_result; i++) {
unsigned value = new_entry->m_result[i].second;
imdd * new_child = new_entry->m_result[i].first;
imdd * new_d = 0;
if (!value2imdd.find(value, new_d)) {
new_d = _mk_empty(arity);
value2imdd.insert(value, new_d);
result.push_back(imdd_value_pair(new_d, value));
}
SASSERT(new_d != 0);
add_child(new_d, it->begin_key(), it->end_key(), new_child);
}
}
std::sort(result.begin(), result.end(), imdd_value_pair_lt_value());
}
r_entry = mk_fi_cache_entry(d, lower, upper, result.size(), result.c_ptr());
if (d->is_shared())
m_fi_bottom_cache.insert(r_entry);
return mk(r_entry);
}
}
imdd * imdd_manager::mk_filter_identical_main(imdd * d, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) {
if (d->empty() || num_vars < 2)
return d;
TRACE("imdd",
tout << "vars: ";
for (unsigned i = 0; i < num_vars; ++i) {
tout << vars[i] << " ";
}
tout << "\n";
tout << "rows: " << get_num_rows(d) << "\n";
display_ll(tout, d); );
unsigned arity = d->get_arity();
DEBUG_CODE(for (unsigned i = 0; i < num_vars; ++i) SASSERT(vars[i] < arity););
m_fi_num_vars = num_vars;
m_fi_begin_vars = vars;
m_fi_end_vars = vars + num_vars;
delay_dealloc delay(*this);
m_fi_bottom_cache.reset();
m_fi_top_cache.reset();
m_fi_entries.reset();
sl_imanager imgr;
init_fi_intervals(imgr, d, 0, 0);
TRACE("imdd_verbose",
ptr_addr_hashtable<sl_interval_set> ht;
imdd2intervals::iterator it = m_imdd2interval_set.begin();
imdd2intervals::iterator end = m_imdd2interval_set.end();
for (; it != end; ++it) {
tout << it->m_key->get_id() << " ";
if (it->m_value) {
if (ht.contains(it->m_value)) {
tout << "dup\n";
continue;
}
ht.insert(it->m_value);
sl_interval_set::iterator sit = it->m_value->begin();
sl_interval_set::iterator send = it->m_value->end();
for (; sit != send; ++sit) {
tout << "[" << sit->begin_key() << ":" << sit->end_key() << "] ";
}
}
else {
tout << "{}";
}
tout << "\n";
});
mk_fi_result r = mk_filter_identical_core(d, 0, 0, 0, UINT_MAX, destructive, memoize_res);
reset_fi_intervals(imgr);
TRACE("imdd", if (r.m_d) display_ll(tout << "result\n", r.m_d););
if (r.m_d == 0)
return _mk_empty(arity);
else
return r.m_d;
}
void imdd_manager::swap_in(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars) {
// variables are sorted.
DEBUG_CODE(for (unsigned i = 0; i + 1 < num_vars; ++i) SASSERT(vars[i] < vars[i+1]););
imdd_ref tmp1(*this), tmp2(*this);
tmp1 = d;
SASSERT(num_vars > 1);
unsigned v1 = vars[0]+1; // next position to swap to.
for (unsigned i = 1; i < num_vars; ++i) {
unsigned v2 = vars[i];
SASSERT(v1 <= v2);
for (unsigned j = v2-1; j >= v1; --j) {
mk_swap(tmp1, tmp2, j);
tmp1 = tmp2;
}
++v1;
}
TRACE("imdd",
for (unsigned i = 0; i < num_vars; ++i) tout << vars[i] << " ";
tout << "\n";
display_ll(tout << "in\n", d);
display_ll(tout << "out\n", tmp1);
tout << "\n";);
r = tmp1;
}
void imdd_manager::swap_out(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars) {
// variables are sorted.
DEBUG_CODE(for (unsigned i = 0; i + 1 < num_vars; ++i) SASSERT(vars[i] < vars[i+1]););
imdd_ref tmp1(*this), tmp2(*this);
tmp1 = d;
SASSERT(num_vars > 1);
unsigned v1 = vars[0]+num_vars-1; // position of next variable to be swapped.
for (unsigned i = num_vars; i > 1; ) {
--i;
unsigned v2 = vars[i];
for (unsigned j = v1; j < v2; ++j) {
mk_swap(tmp1, tmp2, j);
tmp1 = tmp2;
}
--v1;
}
TRACE("imdd",
for (unsigned i = 0; i < num_vars; ++i) tout << vars[i] << " ";
tout << "\n";
display_ll(tout << "out\n", d);
display_ll(tout << "in\n", tmp1);
tout << "\n";);
r = tmp1;
}
void imdd_manager::filter_identical_core2(imdd* d, unsigned num_vars, unsigned b, unsigned e, ptr_vector<imdd>& ch) {
SASSERT(num_vars > 0);
imdd* r = 0;
imdd_children::iterator it = d->m_children.find_geq(b);
imdd_children::iterator end = d->m_children.end();
for (; it != end; ++it) {
unsigned b1 = it->begin_key();
unsigned e1 = it->end_key();
if (e < b1) {
break;
}
if (b1 < b) {
b1 = b;
}
if (e <= e1) {
e1 = e;
}
SASSERT(b <= b1 && b1 <= e1 && e1 <= e);
imdd* curr_child = it->val();
if (num_vars == 1) {
for (unsigned i = b1; i <= e1; ++i) {
r = _mk_empty(d->get_arity());
add_child(r, i, i, it->val());
r = memoize_new_imdd_if(!it->val() || it->val()->is_memoized(), r);
ch.push_back(r);
}
continue;
}
ptr_vector<imdd> ch2;
filter_identical_core2(curr_child, num_vars-1, b1, e1, ch2);
for (unsigned i = 0; i < ch2.size(); ++i) {
r = _mk_empty(d->get_arity());
unsigned key = ch2[i]->begin_children()->begin_key();
SASSERT(ch2[i]->begin_children()->end_key() == key);
add_child(r, key, key, ch2[i]);
r = memoize_new_imdd_if(ch2[i]->is_memoized(), r);
ch.push_back(r);
}
}
}
imdd* imdd_manager::filter_identical_core2(imdd* d, unsigned var, unsigned num_vars, bool memoize_res) {
imdd* r = 0;
if (m_filter_identical_cache.find(d, r)) {
return r;
}
bool children_memoized = true;
r = _mk_empty(d->get_arity());
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
imdd_children::push_back_proc push_back(m_sl_manager, r->m_children);
if (var > 0) {
for (; it != end; ++it) {
imdd* curr_child = it->val();
imdd* new_child = filter_identical_core2(curr_child, var-1, num_vars, memoize_res);
if (new_child) {
push_back(it->begin_key(), it->end_key(), new_child);
if (!new_child->is_memoized()) {
children_memoized = false;
}
}
}
}
else {
ptr_vector<imdd> ch;
for (; it != end; ++it) {
imdd* curr_child = it->val();
filter_identical_core2(curr_child, num_vars-1, it->begin_key(), it->end_key(), ch);
}
for (unsigned i = 0; i < ch.size(); ++i) {
unsigned key = ch[i]->begin_children()->begin_key();
SASSERT(ch[i]->begin_children()->end_key() == key);
push_back(key, key, ch[i]);
if (!ch[i]->is_memoized()) {
children_memoized = false;
}
}
}
if (r->empty()) {
delete_imdd(r);
r = 0;
}
m_filter_identical_cache.insert(d, r);
r = memoize_new_imdd_if(r && memoize_res && children_memoized, r);
return r;
}
void imdd_manager::filter_identical_main2(imdd * d, imdd_ref& r, unsigned num_vars, unsigned * vars, bool destructive, bool memoize_res) {
m_filter_identical_cache.reset();
imdd_ref tmp1(*this), tmp2(*this);
swap_in(d, tmp1, num_vars, vars);
tmp2 = filter_identical_core2(tmp1, vars[0], num_vars, memoize_res);
if (!tmp2) {
r = _mk_empty(d->get_arity());
}
else {
swap_out(tmp2, r, num_vars, vars);
}
m_filter_identical_cache.reset();
}
void imdd_manager::mk_project_core(imdd * d, imdd_ref & r, unsigned var, unsigned num_found, bool memoize_res) {
unsigned arity = d->get_arity();
bool _is_proj_var = is_proj_var(var);
if (_is_proj_var && arity == (m_proj_num_vars - num_found)) {
r = 0; // 0 here means the unit element (aka the 0-tuple).
return;
}
imdd * _r = 0;
if (is_cached(m_proj_cache, d, _r)) {
r = _r;
return;
}
bool new_children_memoized = true;
if (_is_proj_var) {
SASSERT(d != 0);
SASSERT(d->get_arity() > 1);
unsigned new_var = var + 1;
unsigned new_num_found = num_found + 1;
bool found_all = new_num_found == m_proj_num_vars;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
imdd_ref tmp_r(*this);
CTRACE("imdd", it == end, display_ll(tout, d););
SASSERT(it != end);
for (; it != end; ++it) {
imdd_ref new_child(*this);
if (found_all)
new_child = it->val();
else
mk_project_core(it->val(), new_child, new_var, new_num_found, memoize_res);
if (tmp_r == 0)
tmp_r = new_child;
else
mk_union_core(tmp_r, new_child, tmp_r, memoize_res);
SASSERT(tmp_r != 0);
}
SASSERT(tmp_r != 0);
SASSERT(tmp_r->get_arity() == d->get_arity() - (m_proj_num_vars - num_found));
r = tmp_r;
}
else {
SASSERT(num_found < m_proj_num_vars);
unsigned new_var = var+1;
_r = _mk_empty(arity - (m_proj_num_vars - num_found));
imdd_children::push_back_proc push_back(m_sl_manager, _r->m_children);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
SASSERT(it != end);
for (; it != end; ++it) {
imdd * curr_child = it->val();
imdd_ref new_child(*this);
mk_project_core(curr_child, new_child, new_var, num_found, memoize_res);
push_back(it->begin_key(), it->end_key(), new_child);
if (new_child != 0 && !new_child->is_memoized())
new_children_memoized = false;
}
SASSERT(!_r->empty());
_r = memoize_new_imdd_if(memoize_res && new_children_memoized, _r);
r = _r;
}
cache_result(m_proj_cache, d, r);
}
void imdd_manager::mk_project_dupdt_core(imdd_ref & d, unsigned var, unsigned num_found, bool memoize_res) {
unsigned arity = d->get_arity();
bool _is_proj_var = is_proj_var(var);
if (_is_proj_var && arity == (m_proj_num_vars - num_found)) {
d = 0; // 0 here means the unit element (aka the 0-tuple).
return;
}
if (!destructive_update_at(true, d)) {
mk_project_core(d, d, var, num_found, memoize_res);
return;
}
if (_is_proj_var) {
SASSERT(d != 0);
SASSERT(d->get_arity() > 1);
unsigned new_var = var + 1;
unsigned new_num_found = num_found + 1;
bool found_all = new_num_found == m_proj_num_vars;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
imdd_ref r(*this);
for (; it != end; ++it) {
imdd_ref new_child(*this);
it.take_curr_ownership(m_sl_manager, new_child);
if (!found_all) {
mk_project_dupdt_core(new_child, new_var, new_num_found, memoize_res);
}
if (r == 0)
r = new_child;
else
mk_union_core_dupdt(r, new_child, memoize_res);
}
// we traverse the children of d, and decrement the reference counter of each one of them.
d->m_children.reset_no_decref(m_sl_manager);
d = r;
SASSERT(r != 0);
SASSERT(r->get_arity() == d->get_arity() - (m_proj_num_vars - num_found));
}
else {
SASSERT(!_is_proj_var);
sbuffer<entry> to_insert;
unsigned new_var = var+1;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd_ref new_child(*this);
it.take_curr_ownership(m_sl_manager, new_child);
mk_project_dupdt_core(new_child, new_var, num_found, memoize_res);
if (new_child)
new_child->inc_ref(); // protect new child, since we will insert it into to_insert
to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child));
}
SASSERT(!to_insert.empty());
d->replace_children(m_sl_manager, to_insert);
}
}
void imdd_manager::mk_project_init(unsigned num_vars, unsigned * vars) {
m_proj_num_vars = num_vars;
m_proj_begin_vars = vars;
m_proj_end_vars = vars + num_vars;
m_proj_cache.reset();
reset_union_cache();
}
void imdd_manager::mk_project_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, bool memoize_res) {
if (num_vars == 0) {
return;
}
unsigned arity = d->get_arity();
SASSERT(num_vars < arity);
if (d->empty()) {
d = _mk_empty(arity - num_vars);
return;
}
delay_dealloc delay(*this);
mk_project_init(num_vars, vars);
mk_project_dupdt_core(d, 0, 0, memoize_res);
}
void imdd_manager::mk_project(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, bool memoize_res) {
if (num_vars == 0) {
r = d;
return;
}
unsigned arity = d->get_arity();
SASSERT(num_vars < arity);
if (d->empty()) {
r = _mk_empty(arity - num_vars);
return;
}
delay_dealloc delay(*this);
mk_project_init(num_vars, vars);
mk_project_core(d, r, 0, 0, memoize_res);
STRACE("imdd_trace", tout << "mk_project(0x" << d << ", 0x" << r.get() << ", ";
for (unsigned i = 0; i < num_vars; i++) tout << vars[i] << ", ";
tout << memoize_res << ");\n";);
}
void imdd_manager::mk_join(
imdd * d1, imdd * d2, imdd_ref & r,
unsigned_vector const& vars1, unsigned_vector const& vars2,
bool memoize_res) {
SASSERT(vars1.size() == vars2.size());
imdd_ref tmp(*this);
unsigned d1_arity = d1->get_arity();
mk_product(d1, d2, tmp);
for (unsigned i = 0; i < vars1.size(); ++i) {
unsigned vars[2] = { vars1[i], vars2[i] + d1_arity };
mk_filter_identical_dupdt(tmp, 2, vars);
}
r = tmp; // memoize_new_imdd_if(memoize_res, tmp);
}
void imdd_manager::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) {
SASSERT(vars1.size() == vars2.size());
imdd_ref tmp(*this);
unsigned d1_arity = d1->get_arity();
mk_product(d1, d2, tmp);
for (unsigned i = 0; i < vars1.size(); ++i) {
unsigned vars[2] = { vars1[i], vars2[i] + d1_arity };
mk_filter_identical(tmp, tmp, 2, vars);
}
mk_project(tmp, tmp, proj_vars.size(), proj_vars.c_ptr());
TRACE("dl",
tout << "vars: ";
for (unsigned i = 0; i < vars1.size(); ++i) tout << vars1[i] << ":" << vars2[i] << " ";
tout << "\n";
tout << "proj: ";
for (unsigned i = 0; i < proj_vars.size(); ++i) tout << proj_vars[i] << " ";
tout << "\n";
tout << "arity: " << d1->get_arity() << " + " << d2->get_arity() << "\n";
display_ll(tout << "d1\n", d1);
display_ll(tout << "\nd2\n", d2);
display_ll(tout << "\nresult\n", tmp);
tout << "\n";
);
r = tmp; // memoize_new_imdd_if(memoize_res, tmp);
}
imdd * imdd_manager::mk_swap_new_child(unsigned lower, unsigned upper, imdd * grandchild) {
if (m_swap_new_child == 0) {
m_swap_new_child = _mk_empty(grandchild == 0 ? 1 : grandchild->get_arity() + 1);
add_child(m_swap_new_child, lower, upper, grandchild);
if (grandchild && !grandchild->is_memoized())
m_swap_granchildren_memoized = false;
inc_ref(m_swap_new_child);
}
SASSERT(m_swap_new_child != 0);
return m_swap_new_child;
}
void imdd_manager::mk_swap_acc1_dupdt(imdd_ref & d, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res) {
SASSERT(d->get_ref_count() == 1);
sbuffer<entry> to_insert;
imdd_children::ext_iterator it;
imdd_children::ext_iterator end;
d->m_children.move_geq(it, lower);
while (it != end && lower <= upper) {
imdd_children::entry const & curr_entry = *it;
unsigned curr_entry_begin_key = curr_entry.begin_key();
unsigned curr_entry_end_key = curr_entry.end_key();
imdd * curr_entry_val = curr_entry.val();
bool move_head = true;
if (upper < curr_entry_begin_key)
break;
if (lower < curr_entry_begin_key) {
to_insert.push_back(entry(lower, curr_entry_begin_key - 1, grandchild));
lower = curr_entry_begin_key;
}
SASSERT(lower >= curr_entry_begin_key);
imdd * curr_grandchild = curr_entry_val;
imdd_ref new_grandchild(*this);
SASSERT((curr_grandchild == 0) == (grandchild == 0));
if (curr_grandchild != 0) {
bool cover = lower == curr_entry_begin_key && upper >= curr_entry_end_key;
// If cover == true, then the curr_child is completely covered, and it is not needed anymore.
// So, we can perform a destructive update.
if (cover) {
new_grandchild = curr_grandchild; // take over the ownership of curr_grandchild
it.erase(m_sl_manager);
move_head = false; // it.erase is effectively moving the head.
mk_union_core_dupdt(new_grandchild, grandchild, memoize_res);
}
else {
mk_union_core(curr_grandchild, grandchild, new_grandchild, memoize_res);
}
if (!new_grandchild->is_memoized())
m_swap_granchildren_memoized = false;
// protect new_grandchild, since it will be stored in to_insert.
new_grandchild->inc_ref();
}
if (upper >= curr_entry_end_key) {
to_insert.push_back(entry(lower, curr_entry_end_key, new_grandchild));
}
else {
to_insert.push_back(entry(lower, upper, new_grandchild));
}
lower = curr_entry_end_key + 1;
if (move_head)
++it;
}
svector<entry>::iterator it2 = to_insert.begin();
svector<entry>::iterator end2 = to_insert.end();
for (; it2 != end2; ++it2) {
imdd_children::entry const & curr_entry = *it2;
add_child(d, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val());
if (curr_entry.val())
curr_entry.val()->dec_ref(); // to_insert will be destroyed, so decrement ref.
}
if (lower <= upper) {
add_child(d, lower, upper, grandchild);
}
}
void imdd_manager::mk_swap_acc1(imdd * d, imdd_ref & r, unsigned lower, unsigned upper, imdd * grandchild, bool memoize_res) {
copy(d, r);
imdd_children::iterator it = d->m_children.find_geq(lower);
imdd_children::iterator end = d->end_children();
for (; it != end && lower <= upper; ++it) {
imdd_children::entry const & curr_entry = *it;
if (upper < curr_entry.begin_key())
break;
if (lower < curr_entry.begin_key()) {
SASSERT(lower <= curr_entry.begin_key() - 1);
add_child(r, lower, curr_entry.begin_key() - 1, grandchild);
lower = curr_entry.begin_key();
}
SASSERT(lower >= curr_entry.begin_key());
imdd * curr_grandchild = curr_entry.val();
SASSERT((curr_grandchild == 0) == (grandchild == 0));
imdd_ref new_curr_grandchild(*this);
mk_union_core(curr_grandchild, grandchild, new_curr_grandchild, memoize_res);
if (new_curr_grandchild && !new_curr_grandchild->is_memoized())
m_swap_granchildren_memoized = false;
if (upper >= curr_entry.end_key()) {
add_child(r, lower, curr_entry.end_key(), new_curr_grandchild);
}
else {
SASSERT(upper < curr_entry.end_key());
SASSERT(lower <= upper);
add_child(r, lower, upper, new_curr_grandchild);
}
lower = curr_entry.end_key() + 1;
}
if (lower <= upper) {
add_child(r, lower, upper, grandchild);
}
}
/**
\brief Auxiliary function for mk_swap_core
*/
void imdd_manager::mk_swap_acc2(imdd_ref & r, unsigned lower1, unsigned upper1, unsigned lower2, unsigned upper2, imdd * grandchild, bool memoize_res) {
SASSERT((r->get_arity() == 2 && grandchild == 0) ||
(r->get_arity() == grandchild->get_arity() + 2));
SASSERT(r->get_ref_count() <= 1); // we perform destructive updates on r.
SASSERT(!r->is_memoized());
TRACE("mk_swap_bug",
tout << mk_ll_pp(r, *this) << "\n";
tout << "adding\n[" << lower1 << ", " << upper1 << "] -> [" << lower2 << ", " << upper2 << "] ->\n";
tout << mk_ll_pp(grandchild, *this) << "\n";);
sbuffer<entry> to_insert;
imdd_children::ext_iterator it;
imdd_children::ext_iterator end;
r->m_children.move_geq(it, lower1);
while(it != end && lower1 <= upper1) {
imdd_children::entry const & curr_entry = *it;
unsigned curr_entry_begin_key = curr_entry.begin_key();
unsigned curr_entry_end_key = curr_entry.end_key();
imdd * curr_entry_val = curr_entry.val();
bool move_head = true;
if (upper1 < curr_entry_begin_key)
break;
if (lower1 < curr_entry_begin_key) {
imdd * new_child = mk_swap_new_child(lower2, upper2, grandchild);
SASSERT(lower1 <= curr_entry_begin_key - 1);
add_child(r, lower1, curr_entry_begin_key - 1, new_child);
lower1 = curr_entry_begin_key;
}
SASSERT(lower1 >= curr_entry_begin_key);
imdd * curr_child = curr_entry_val;
SASSERT(curr_child != 0);
SASSERT(!curr_child->is_memoized());
imdd_ref new_curr_child(*this);
bool destructive = curr_child->get_ref_count() == 1 && lower1 == curr_entry_begin_key && upper1 >= curr_entry_end_key;
if (destructive) {
new_curr_child = curr_child; // take over curr_child.
it.erase(m_sl_manager);
move_head = false; // it.erase is effectively moving the head.
mk_swap_acc1_dupdt(new_curr_child, lower2, upper2, grandchild, memoize_res);
}
else {
mk_swap_acc1(curr_child, new_curr_child, lower2, upper2, grandchild, memoize_res);
}
new_curr_child->inc_ref(); // it will be stored in to_insert
if (upper1 >= curr_entry_end_key) {
to_insert.push_back(entry(lower1, curr_entry_end_key, new_curr_child));
}
else {
to_insert.push_back(entry(lower1, upper1, new_curr_child));
}
lower1 = curr_entry_end_key + 1;
if (move_head)
++it;
}
svector<entry>::iterator it2 = to_insert.begin();
svector<entry>::iterator end2 = to_insert.end();
for (; it2 != end2; ++it2) {
imdd_children::entry const & curr_entry = *it2;
add_child(r, curr_entry.begin_key(), curr_entry.end_key(), curr_entry.val());
SASSERT(curr_entry.val());
curr_entry.val()->dec_ref(); // to_insert will be destroyed, so decrement ref.
}
if (lower1 <= upper1) {
imdd * new_child = mk_swap_new_child(lower2, upper2, grandchild);
add_child(r, lower1, upper1, new_child);
}
TRACE("mk_swap_bug", tout << "after mk_swap_acc2\n" << mk_ll_pp(r, *this) << "\n";);
}
/**
\brief Memoize the given IMDD assuming all grandchildren of d are memoized.
*/
imdd * imdd_manager::mk_swap_memoize(imdd * d) {
if (d->is_memoized())
return d;
bool children_memoized = true;
imdd * new_d = _mk_empty(d->get_arity());
imdd_children::push_back_proc push_back(m_sl_manager, new_d->m_children);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * child = it->val();
imdd * new_child = memoize(child);
if (!new_child->is_memoized())
children_memoized = false;
push_back(it->begin_key(), it->end_key(), new_child);
}
if (children_memoized && is_simple_node(new_d)) {
imdd * new_can_d = memoize(new_d);
if (new_can_d != new_d) {
SASSERT(new_d->get_ref_count() == 0);
delete_imdd(new_d);
}
new_d = new_can_d;
}
return new_d;
}
/**
\brief Swap the two top vars.
*/
void imdd_manager::mk_swap_top_vars(imdd * d, imdd_ref & r, bool memoize_res) {
SASSERT(d->get_arity() >= 2);
r = _mk_empty(d->get_arity());
m_swap_granchildren_memoized = true;
m_swap_new_child = 0;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * curr_child = it->val();
SASSERT(curr_child != 0);
SASSERT(m_swap_new_child == 0);
imdd_children::iterator it2 = curr_child->begin_children();
imdd_children::iterator end2 = curr_child->end_children();
for (; it2 != end2; ++it2) {
imdd * grandchild = it2->val();
mk_swap_acc2(r, it2->begin_key(), it2->end_key(), it->begin_key(), it->end_key(), grandchild, memoize_res);
}
if (m_swap_new_child != 0) {
dec_ref(m_swap_new_child);
m_swap_new_child = 0;
}
}
if (memoize_res && m_swap_granchildren_memoized) {
r = mk_swap_memoize(r);
}
TRACE("mk_swap_bug", tout << "result:\n" << mk_ll_pp(r, *this) << "\n";);
}
void imdd_manager::mk_swap_core(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res) {
SASSERT(d);
unsigned arity = d->get_arity();
SASSERT(arity > 1);
imdd * _r = 0;
if (is_cached(m_swap_cache, d, _r)) {
r = _r;
return;
}
if (vidx == 0) {
mk_swap_top_vars(d, r, memoize_res);
}
else {
SASSERT(vidx > 0);
bool new_children_memoized = true;
unsigned new_vidx = vidx - 1;
_r = _mk_empty(arity);
imdd_children::push_back_proc push_back(m_sl_manager, _r->m_children);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
SASSERT(it != end);
for (; it != end; ++it) {
imdd * curr_child = it->val();
imdd_ref new_child(*this);
mk_swap_core(curr_child, new_child, new_vidx, memoize_res);
push_back(it->begin_key(), it->end_key(), new_child);
if (new_child != 0 && !new_child->is_memoized())
new_children_memoized = false;
}
SASSERT(!_r->empty());
_r = memoize_new_imdd_if(memoize_res && new_children_memoized, _r);
r = _r;
}
cache_result(m_swap_cache, d, r);
}
void imdd_manager::mk_swap_dupdt_core(imdd_ref & d, unsigned vidx, bool memoize_res) {
SASSERT(d);
unsigned arity = d->get_arity();
SASSERT(arity > 1);
if (!destructive_update_at(true, d)) {
mk_swap_core(d, d, vidx, memoize_res);
return;
}
if (vidx == 0) {
mk_swap_top_vars(d, d, memoize_res);
return;
}
SASSERT(vidx > 0);
sbuffer<entry> to_insert;
unsigned new_vidx = vidx-1;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd_ref new_child(*this);
it.take_curr_ownership(m_sl_manager, new_child);
mk_swap_dupdt_core(new_child, new_vidx, memoize_res);
new_child->inc_ref(); // protect new child, since we insert into to_insert
to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child));
}
SASSERT(!to_insert.empty());
d->replace_children(m_sl_manager, to_insert);
}
void imdd_manager::mk_swap_dupdt(imdd_ref & d, unsigned vidx, bool memoize_res) {
SASSERT(d);
unsigned arity = d->get_arity();
SASSERT(arity > 1);
SASSERT(vidx < arity - 1);
if (d->empty()) {
return;
}
m_swap_cache.reset();
reset_union_cache();
delay_dealloc delay(*this);
mk_swap_dupdt_core(d, vidx, memoize_res);
}
void imdd_manager::mk_swap(imdd * d, imdd_ref & r, unsigned vidx, bool memoize_res) {
SASSERT(d);
unsigned arity = d->get_arity();
SASSERT(arity > 1);
SASSERT(vidx < arity - 1);
if (d->empty()) {
r = d;
return;
}
TRACE("mk_swap_bug", tout << "mk_swap: " << vidx << "\n"; display_ll(tout, d); tout << "\n";);
TRACE("mk_swap_bug2", tout << "mk_swap: " << vidx << "\n"; display_ll(tout, d); tout << "\n";);
m_swap_cache.reset();
reset_union_cache();
delay_dealloc delay(*this);
mk_swap_core(d, r, vidx, memoize_res);
TRACE("mk_swap_bug2", tout << "mk_swap result\n"; display_ll(tout, r); tout << "\n";);
STRACE("imdd_trace", tout << "mk_swap(0x" << d << ", 0x" << r.get() << ", " << vidx << ", " << memoize_res << ");\n";);
}
imdd_manager::iterator::iterator(imdd_manager const & m, imdd const * d) {
if (d->empty()) {
m_done = true;
return;
}
m_done = false;
unsigned arity = d->get_arity();
m_iterators.resize(arity);
m_element.resize(arity);
begin_iterators(d, 0);
}
void imdd_manager::iterator::begin_iterators(imdd const * curr, unsigned start_idx) {
for (unsigned i = start_idx; i < get_arity(); i++) {
m_iterators[i] = curr->begin_children();
imdd_children::iterator & it = m_iterators[i];
SASSERT(!it.at_end());
m_element[i] = it->begin_key();
curr = it->val();
}
}
imdd_manager::iterator & imdd_manager::iterator::operator++() {
unsigned i = get_arity();
while (i > 0) {
--i;
imdd_children::iterator & it = m_iterators[i];
if (m_element[i] < it->end_key()) {
m_element[i]++;
begin_iterators(it->val(), i+1);
return *this;
}
else {
++it;
if (!it.at_end()) {
m_element[i] = it->begin_key();
begin_iterators(it->val(), i+1);
return *this;
}
}
}
m_done = true; // at end...
return *this;
}
bool imdd_manager::iterator::operator==(iterator const & it) const {
if (m_done && it.m_done)
return true;
if (m_done || it.m_done)
return false;
if (m_element.size() != it.m_element.size())
return false;
unsigned sz = m_element.size();
for (unsigned i = 0; i < sz; i++)
if (m_element[i] != it.m_element[i])
return false;
return true;
}
bool imdd_manager::is_equal_core(imdd * d1, imdd * d2) {
if (d1 == d2)
return true;
if (d1->is_memoized() && d2->is_memoized())
return false;
SASSERT(d1->get_arity() == d2->get_arity());
SASSERT(!d1->empty());
SASSERT(!d2->empty());
bool shared = d1->is_shared() && d2->is_shared();
bool r;
if (shared && m_is_equal_cache.find(d1, d2, r))
return r;
if (d1->get_arity() == 1) {
r = d1->m_children.is_equal(d2->m_children);
}
else {
imdd_children::iterator it1 = d1->begin_children();
imdd_children::iterator end1 = d1->end_children();
imdd_children::iterator it2 = d2->begin_children();
imdd_children::iterator end2 = d2->end_children();
SASSERT(it1 != end1);
SASSERT(it2 != end2);
if (it1->begin_key() != it2->begin_key()) {
r = false;
}
else {
while (true) {
if (it1 == end1) {
r = (it2 == end2);
break;
}
if (it2 == end2) {
r = (it1 == end1);
break;
}
SASSERT(it1->val() != 0 && it2->val() != 0);
if (!is_equal_core(it1->val(), it2->val())) {
r = false;
break;
}
if (it1->end_key() < it2->end_key()) {
unsigned prev_end_key = it1->end_key();
++it1;
if (it1 == end1 || it1->begin_key() != prev_end_key + 1) {
r = false;
break;
}
}
else if (it1->end_key() > it2->end_key()) {
unsigned prev_end_key = it2->end_key();
++it2;
if (it2 == end2 || it2->begin_key() != prev_end_key + 1) {
r = false;
break;
}
}
else {
SASSERT(it1->end_key() == it2->end_key());
++it1;
++it2;
}
}
}
}
if (shared)
m_is_equal_cache.insert(d1, d2, r);
return r;
}
bool imdd_manager::is_equal(imdd * d1, imdd * d2) {
if (d1 == d2)
return true;
if (d1->is_memoized() && d2->is_memoized())
return false;
if (d1->get_arity() != d2->get_arity())
return false;
if (d1->empty() && d2->empty())
return true;
if (d1->empty() || d2->empty())
return false;
SASSERT(!d1->empty());
SASSERT(!d2->empty());
m_is_equal_cache.reset();
return is_equal_core(d1, d2);
}
/**
\brief Return true if the given imdd contains the tuple (values[0], ..., values[num-1])
*/
bool imdd_manager::contains(imdd * d, unsigned num, unsigned const * values) const {
SASSERT(d->get_arity() == num);
imdd * curr = d;
for (unsigned i = 0; i < num; i++) {
imdd * child;
if (!curr->m_children.find(values[i], child))
return false;
curr = child;
}
return true;
}
inline bool overlap(unsigned b1, unsigned e1, unsigned b2, unsigned e2) {
// [b1, e1] [b2, e2]
if (e1 < b2)
return false;
// [b2, e2] [b1, e1]
if (e2 < b1)
return false;
return true;
}
bool imdd_manager::subsumes_core(imdd * d1, imdd * d2) {
if (d1 == d2)
return true;
SASSERT(d1->get_arity() == d2->get_arity());
SASSERT(!d1->empty());
SASSERT(!d2->empty());
bool shared = d1->is_shared() && d2->is_shared();
bool r;
if (shared && m_subsumes_cache.find(d1, d2, r))
return r;
imdd_children::iterator it1 = d1->begin_children();
imdd_children::iterator end1 = d1->end_children();
imdd_children::iterator it2 = d2->begin_children();
imdd_children::iterator end2 = d2->end_children();
SASSERT(it1 != end1);
SASSERT(it2 != end2);
if (it1->begin_key() > it2->begin_key()) {
r = false;
goto subsumes_end;
}
if (d1->get_arity() == 1) {
// leaf case
while(true) {
SASSERT(it1 != end1);
SASSERT(it2 != end2);
it1.move_to(it2->begin_key());
if (it1 == end1 ||
it1->begin_key() > it2->begin_key() || // missed beginning of it2 curr entry
it1->end_key() < it2->end_key() // missed end of it2 curr entry
) {
r = false;
goto subsumes_end;
}
SASSERT(it1->end_key() >= it2->end_key());
++it2;
if (it2 == end2) {
r = true;
goto subsumes_end;
}
}
}
else {
// non-leaf case
while (true) {
SASSERT(it1 != end1);
SASSERT(it2 != end2);
SASSERT(it1->val() != 0);
SASSERT(it2->val() != 0);
it1.move_to(it2->begin_key());
if (it1 == end1 ||
it1->begin_key() > it2->begin_key() // missed beginning of it2 curr entry
) {
r = false;
goto subsumes_end;
}
// the beginning of it2 is inside it1
SASSERT(it2->begin_key() >= it1->begin_key() && it2->begin_key() <= it1->end_key());
// it1: [ ][ ][ ]
// it2 [ ]
while (true) {
SASSERT(it1 != end1);
SASSERT(it2 != end2);
// there is a overlap between the current entry in it1 and the current entry in it2
SASSERT(overlap(it1->begin_key(), it1->end_key(),
it2->begin_key(), it2->end_key()));
if (!subsumes_core(it1->val(), it2->val())) {
r = false;
goto subsumes_end;
}
if (it1->end_key() >= it2->end_key()) {
++it2; // processed the whole entry in it2
break;
}
SASSERT(it1->end_key() < it2->end_key());
unsigned prev_end_key = it1->end_key();
++it1;
if (it1 == end1 || it1->begin_key() != prev_end_key + 1) {
r = false;
goto subsumes_end;
}
}
if (it2 == end2) {
r = true;
goto subsumes_end;
}
}
}
subsumes_end:
if (shared)
m_subsumes_cache.insert(d1, d2, r);
return r;
}
/**
\brief Return true is d1 is a superset of d2, or equal to d2.
*/
bool imdd_manager::subsumes(imdd * d1, imdd * d2) {
SASSERT(d1->get_arity() == d2->get_arity());
if (d1 == d2)
return true;
if (d2->empty())
return true;
if (d1->empty()) {
SASSERT(!d2->empty());
return false;
}
SASSERT(!d1->empty());
SASSERT(!d2->empty());
m_subsumes_cache.reset();
return subsumes_core(d1, d2);
}
void imdd_manager::remove_facts_dupdt(imdd_ref & d, unsigned num, unsigned const * lowers, unsigned const * uppers) {
SASSERT(d->get_arity() == num);
d = remove_facts_main(d, num, lowers, uppers, true, true);
}
void imdd_manager::remove_facts(imdd * d, imdd_ref & r, unsigned num, unsigned const * lowers, unsigned const * uppers) {
SASSERT(d->get_arity() == num);
r = remove_facts_main(d, num, lowers, uppers, false, true);
}
imdd * imdd_manager::remove_facts_main(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) {
SASSERT(d->get_arity() == num);
delay_dealloc delay(*this);
m_remove_facts_cache.reset();
return remove_facts_core(d, num, lowers, uppers, destructive, memoize_res);
}
imdd * imdd_manager::remove_facts_core(imdd * d, unsigned num, unsigned const * lowers, unsigned const * uppers, bool destructive, bool memoize_res) {
SASSERT(d->get_arity() == num && num > 0);
imdd * new_d = 0;
unsigned b = *lowers;
unsigned e = *uppers;
sbuffer<entry> to_insert, to_remove;
imdd_children::iterator it = d->m_children.find_geq(b);
imdd_children::iterator end = d->end_children();
bool is_destructive = destructive_update_at(destructive, d);
if (!is_destructive && is_cached(m_remove_facts_cache, d, new_d)) {
return new_d;
}
if (num == 1) {
new_d = remove_main(d, *lowers, *uppers, destructive, memoize_res);
if (!is_destructive) {
cache_result(m_remove_facts_cache, d, new_d);
}
return new_d;
}
if (it == end || e < it->begin_key()) {
if (!is_destructive) {
cache_result(m_remove_facts_cache, d, d);
}
return d;
}
if (!is_destructive) {
new_d = copy_main(d);
}
else {
new_d = d;
}
for (; it != end && b <= e; ++it) {
//
// remove ([b:e]::rest), [b_key:e_key] * ch) =
// { [b_key:e_key] * ch } if e < b_key
// { [b_key:e_key] * ch' } if b <= b_key <= e_key <= e
// { [b_key:e]*ch' [e+1:e_key]*ch } if b <= b_key <= e < e_key
// { [b_key:b-1]*ch [b:e]*ch', [e+1:e_key]*ch } if b_key < b <= e < e_key
// { [b_key:b-1]*ch [b:e_key]*ch' } if b_key < b <= e_key <= e
// where
// ch' = remove(rest, ch)
// assumption: remove_child retains the cases where ch is in the tree.
//
imdd_children::entry const & curr_entry = *it;
unsigned b_key = curr_entry.begin_key();
unsigned e_key = curr_entry.end_key();
imdd* curr_child = curr_entry.val();
imdd* new_curr_child = 0;
if (e < b_key) {
break;
}
new_curr_child = remove_facts_core(curr_child, num-1, lowers+1, uppers+1, destructive, memoize_res);
if (new_curr_child == curr_child) {
continue;
}
if (new_curr_child != 0) {
if (b <= b_key && e_key <= e) {
to_insert.push_back(entry(b_key, e_key, new_curr_child));
}
if (b <= b_key && e < e_key) {
to_insert.push_back(entry(b_key, e, new_curr_child));
}
if (b_key < b && e < e_key) {
to_insert.push_back(entry(b, e, new_curr_child));
}
if (b_key < b && e_key <= e) {
to_insert.push_back(entry(b, e_key, new_curr_child));
}
}
if (is_destructive) {
to_remove.push_back(entry(b, e, 0));
}
else {
remove_child(new_d, b, e);
}
b = e_key + 1;
}
for (unsigned i = 0; i < to_remove.size(); ++i) {
remove_child(new_d, to_remove[i].begin_key(), to_remove[i].end_key());
}
for (unsigned i = 0; i < to_insert.size(); ++i) {
add_child(new_d, to_insert[i].begin_key(), to_insert[i].end_key(), to_insert[i].val());
}
if (!is_destructive) {
cache_result(m_remove_facts_cache, d, new_d);
}
return new_d;
}
void imdd_manager::display(std::ostream & out, imdd const * d, unsigned_vector & intervals, bool & first) const {
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
if (d->get_arity() == 1) {
for (; it != end; ++it) {
SASSERT(it->val() == 0);
if (first) {
first = false;
}
else {
out << ",\n ";
}
out << "(";
unsigned sz = intervals.size();
for (unsigned i = 0; i < sz; i+=2) {
out << "[" << intervals[i] << ", " << intervals[i+1] << "]";
out << ", ";
}
out << "[" << it->begin_key() << ", " << it->end_key() << "])";
}
}
else {
for (; it != end; ++it) {
intervals.push_back(it->begin_key());
intervals.push_back(it->end_key());
display(out, it->val(), intervals, first);
intervals.pop_back();
intervals.pop_back();
}
}
}
void imdd_manager::display(std::ostream & out, imdd const * d) const {
unsigned_vector intervals;
bool first = true;
out << "{";
display(out, d, intervals, first);
out << "}";
}
struct display_ll_context {
typedef map<imdd *, unsigned, ptr_hash<imdd>, default_eq<imdd*> > imdd2uint;
std::ostream & m_out;
unsigned m_next_id;
imdd2uint m_map;
display_ll_context(std::ostream & out):m_out(out), m_next_id(1) {}
void display_tabs(unsigned num_tabs) {
for (unsigned i = 0; i < num_tabs; i++)
m_out << " ";
}
void display_node(unsigned num_tabs, imdd const * d) {
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
if (it == end) {
m_out << "{}";
return;
}
if (d->get_arity() == 1) {
// leaf
m_out << "{";
for (bool first = true; it != end; ++it) {
if (first)
first = false;
else
m_out << ", ";
m_out << "[" << it->begin_key() << ", " << it->end_key() << "]";
}
m_out << "}";
}
else {
m_out << "{\n";
display_tabs(num_tabs);
while (it != end) {
m_out << " [" << it->begin_key() << ", " << it->end_key() << "] -> ";
display(num_tabs+1, it->val());
++it;
if (it != end) {
m_out << "\n";
display_tabs(num_tabs);
}
else {
m_out << "}";
}
}
}
m_out << (d->is_memoized() ? "*" : "") << "$" << d->memory();
}
void display(unsigned num_tabs, imdd const * d) {
if (d == 0) {
m_out << "<NULL>";
return;
}
unsigned id;
if (m_map.find(const_cast<imdd*>(d), id)) {
m_out << "#" << id;
}
else if (d->is_shared()) {
id = m_next_id;
m_next_id++;
m_map.insert(const_cast<imdd*>(d), id);
m_out << "#" << id << ":";
display_node(num_tabs, d);
}
else {
display_node(num_tabs, d);
}
}
};
void imdd_manager::display_ll(std::ostream & out, imdd const * d) const {
display_ll_context ctx(out);
ctx.display(0, d);
}
struct addone_proc {
unsigned m_r;
addone_proc():m_r(0) {}
void operator()(imdd * d) { m_r++; }
};
/**
\brief Return the number of nodes in an IMDD.
This is *not* a constant time operation.
It is linear on the size of the IMDD
*/
unsigned imdd_manager::get_num_nodes(imdd const * d) const {
addone_proc p;
for_each(const_cast<imdd*>(d), p);
return p.m_r;
}
struct addmem_proc {
unsigned m_r;
addmem_proc():m_r(0) {}
void operator()(imdd * d) { m_r += d->memory(); }
};
/**
\brief Return the amount of memory consumed by the given IMDD.
*/
unsigned imdd_manager::memory(imdd const * d) const {
addmem_proc p;
for_each(const_cast<imdd*>(d), p);
return p.m_r;
}
/**
\brief Return number of rows the given IMDD represents.
*/
size_t imdd_manager::get_num_rows(imdd const* root) const {
obj_map<imdd const, size_t> counts;
ptr_vector<imdd const> todo;
todo.push_back(root);
while (!todo.empty()) {
imdd const* d = todo.back();
if (counts.contains(d)) {
todo.pop_back();
continue;
}
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
bool all_valid = true;
size_t count = 0, c;
for (; it != end; ++it) {
imdd const* ch = it->val();
if (!ch) {
count += (it->end_key()-it->begin_key()+1);
}
else if (counts.find(ch, c)) {
count += c*(it->end_key()-it->begin_key()+1);
}
else {
all_valid = false;
todo.push_back(ch);
}
}
if (all_valid) {
todo.pop_back();
counts.insert(d, count);
}
}
return counts.find(root);
}
imdd * imdd_manager::add_bounded_var_core(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res) {
if (d == 0) {
SASSERT(before_vidx == 0);
imdd * r = _mk_empty(1);
add_child(r, lower, upper, 0);
r = memoize_new_imdd_if(memoize_res, r);
return r;
}
imdd * r = 0;
if (is_cached(m_add_bounded_var_cache, d, r))
return r;
if (before_vidx == 0) {
imdd * r = _mk_empty(d->get_arity() + 1);
add_child(r, lower, upper, d);
r = memoize_new_imdd_if(d->is_memoized() && memoize_res, r);
return r;
}
if (destructive_update_at(destructive, d)) {
sbuffer<entry> to_insert;
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
for (; it != end; ++it) {
imdd * curr_child = it->val();
imdd * new_child = add_bounded_var_core(curr_child, before_vidx-1, lower, upper, true, memoize_res);
new_child->inc_ref(); // we will be resetting d->m_children later.
to_insert.push_back(entry(it->begin_key(), it->end_key(), new_child));
}
SASSERT(!to_insert.empty());
d->replace_children(m_sl_manager, to_insert);
return d;
}
bool new_children_memoized = true;
r = _mk_empty(d->get_arity() + 1);
imdd_children::push_back_proc push_back(m_sl_manager, r->m_children);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
SASSERT(it != end);
for (; it != end; ++it) {
imdd * curr_child = it->val();
imdd * new_child = add_bounded_var_core(curr_child, before_vidx-1, lower, upper, false, memoize_res);
push_back(it->begin_key(), it->end_key(), new_child);
if (!new_child->is_memoized())
new_children_memoized = false;
}
r = memoize_new_imdd_if(r && memoize_res && new_children_memoized, r);
cache_result(m_add_bounded_var_cache, d, r);
return r;
}
/**
\brief Add a variable (bounded by lower and upper) before the variable before_var.
That is, for each tuple (v_1, ..., v_n) in the IMDD \c d, the resultant IMDD contains the
tuples
(v_1, ..., v_{after_vidx}, lower, ..., v_n)
(v_1, ..., v_{after_vidx}, lower+1, ..., v_n)
...
(v_1, ..., v_{after_vidx}, upper, ..., v_n)
This function is mainly used to implement mk_filter.
\pre after_vidx < d->get_arity()
*/
imdd * imdd_manager::add_bounded_var_main(imdd * d, unsigned before_vidx, unsigned lower, unsigned upper, bool destructive, bool memoize_res) {
SASSERT(before_vidx <= d->get_arity());
if (d->empty())
return d;
m_add_bounded_var_cache.reset();
delay_dealloc delay(*this);
imdd * r = add_bounded_var_core(d, before_vidx, lower, upper, destructive, memoize_res);
return r;
}
filter_cache_entry * imdd_manager::mk_filter_cache_entry(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r) {
void * mem = m_filter_entries.allocate(filter_cache_entry::get_obj_size(ctx_sz));
return new (mem) filter_cache_entry(d, r, ctx_sz, ctx);
}
imdd * imdd_manager::is_mk_filter_cached(imdd * d, unsigned ctx_sz, unsigned * ctx) {
if (!d->is_shared())
return 0;
m_filter_entries.push_scope();
filter_cache_entry * tmp_entry = mk_filter_cache_entry(d, ctx_sz, ctx, 0);
filter_cache_entry * e = 0;
bool r = m_filter_cache.find(tmp_entry, e);
m_filter_entries.pop_scope();
if (!r || e->m_r->is_dead())
return 0;
return e->m_r;
}
void imdd_manager::cache_mk_filter(imdd * d, unsigned ctx_sz, unsigned * ctx, imdd * r) {
if (d->is_shared()) {
filter_cache_entry * new_entry = mk_filter_cache_entry(d, ctx_sz, ctx, r);
m_filter_cache.insert(new_entry);
}
}
void imdd_manager::init_mk_filter(unsigned arity, unsigned num_vars, unsigned * vars) {
m_filter_cache.reset();
m_filter_entries.reset();
m_filter_context.reset();
m_filter_num_vars = num_vars;
m_filter_begin_vars = vars;
m_filter_end_vars = vars + num_vars;
DEBUG_CODE({
for (unsigned i = 0; i < num_vars; i++) {
SASSERT(vars[i] < arity);
}
});
}
template<typename FilterProc>
void imdd_manager::mk_filter_dupdt_core(imdd_ref & d, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res) {
SASSERT(!d->empty());
if (!destructive_update_at(true, d)) {
mk_filter_core(d, d, vidx, num_found, proc, memoize_res);
return;
}
bool _is_filter_var = is_filter_var(vidx);
if (_is_filter_var)
num_found++;
unsigned new_vidx = vidx+1;
imdd_ref new_r(*this);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
SASSERT(it != end);
for (; it != end; ++it) {
imdd_ref new_child(*this);
it.take_curr_ownership(m_sl_manager, new_child); // take ownership of the current child.
if (!_is_filter_var) {
mk_filter_dupdt_core(new_child, new_vidx, num_found, proc, memoize_res);
add_bounded_var_dupdt(new_child, num_found, it->begin_key(), it->end_key(), memoize_res);
if (new_r == 0)
new_r = new_child;
else
mk_union_core_dupdt(new_r, new_child, memoize_res);
}
else {
m_filter_context.push_back(it->begin_key());
m_filter_context.push_back(it->end_key());
if (num_found < m_filter_num_vars) {
mk_filter_dupdt_core(new_child, new_vidx, num_found, proc, memoize_res);
}
else {
proc(m_filter_context.c_ptr(), new_child, new_child, memoize_res);
}
m_filter_context.pop_back();
m_filter_context.pop_back();
if (new_r == 0)
new_r = new_child;
else
mk_union_core_dupdt(new_r, new_child, memoize_res);
}
}
d->m_children.reset(m_sl_manager);
d = new_r;
}
template<typename FilterProc>
void imdd_manager::mk_filter_core(imdd * d, imdd_ref & r, unsigned vidx, unsigned num_found, FilterProc & proc, bool memoize_res) {
SASSERT(!d->empty());
imdd * _r = is_mk_filter_cached(d, m_filter_context.size(), m_filter_context.c_ptr());
if (_r) {
r = _r;
return;
}
bool _is_filter_var = is_filter_var(vidx);
if (_is_filter_var)
num_found++;
unsigned new_vidx = vidx+1;
imdd_ref new_r(*this);
imdd_children::iterator it = d->begin_children();
imdd_children::iterator end = d->end_children();
SASSERT(it != end);
for (; it != end; ++it) {
imdd * curr_child = it->val();
imdd_ref new_child(*this);
if (!_is_filter_var) {
mk_filter_core(curr_child, new_child, new_vidx, num_found, proc, memoize_res);
imdd_ref tmp(*this);
add_bounded_var(new_child, tmp, num_found, it->begin_key(), it->end_key(), memoize_res);
if (new_r == 0)
new_r = tmp;
else
mk_union_core(new_r, tmp, new_r, memoize_res);
}
else {
m_filter_context.push_back(it->begin_key());
m_filter_context.push_back(it->end_key());
if (num_found < m_filter_num_vars) {
mk_filter_core(curr_child, new_child, new_vidx, num_found, proc, memoize_res);
}
else {
proc(m_filter_context.c_ptr(), curr_child, new_child, memoize_res);
}
m_filter_context.pop_back();
m_filter_context.pop_back();
if (new_r == 0)
new_r = new_child;
else
mk_union_core(new_r, new_child, new_r, memoize_res);
}
}
r = new_r;
cache_mk_filter(d, m_filter_context.size(), m_filter_context.c_ptr(), r);
}
template<typename FilterProc>
void imdd_manager::mk_filter_dupdt(imdd_ref & d, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res) {
if (d->empty())
return;
unsigned arity = d->get_arity();
init_mk_filter(arity, num_vars, vars);
mk_filter_dupdt_core(d, 0, 0, proc, memoize_res);
if (d == 0)
d = _mk_empty(arity);
}
template<typename FilterProc>
void imdd_manager::mk_filter(imdd * d, imdd_ref & r, unsigned num_vars, unsigned * vars, FilterProc & proc, bool memoize_res) {
if (d->empty()) {
r = d;
return;
}
unsigned arity = d->get_arity();
init_mk_filter(arity, num_vars, vars);
mk_filter_core(d, r, 0, 0, proc, memoize_res);
if (r == 0)
r = _mk_empty(arity);
}
imdd * imdd_manager::mk_distinct_imdd(unsigned l1, unsigned u1, unsigned l2, unsigned u2, imdd * d, bool memoize_res) {
unsigned d_arity;
if (d == 0) {
d_arity = 0;
}
else {
d_arity = d->get_arity();
memoize_res = memoize_res && d->is_memoized();
}
imdd * r = _mk_empty(d_arity + 2);
imdd_children::push_back_proc push_back(m_sl_manager, r->m_children);
TRACE("mk_distinct_imdd", tout << "STARTING: " << l1 << " " << u1 << " " << l2 << " " << u2 << "\n";);
#define ADD_ENTRY(L1, U1, L2, U2) { \
TRACE("mk_distinct_imdd", tout << "[" << L1 << " " << U1 << " " << L2 << " " << U2 << "]\n";); \
imdd * new_child = _mk_empty(d_arity + 1); \
add_child(new_child, L2, U2, d); \
new_child = memoize_new_imdd_if(memoize_res, new_child); \
push_back(L1, U1, new_child);}
if (u1 < l2 || u2 < l1) {
ADD_ENTRY(l1, u1, l2, u2); // the intervals are disjoint
}
else {
if (l1 < l2) {
SASSERT(u1 >= l2);
// [l1 ...
// [l2 ...
// -->
// [l1, l2-1] X [l2, u2]
ADD_ENTRY(l1, l2-1, l2, u2);
}
unsigned l = std::max(l1, l2);
unsigned u = std::min(u1, u2);
// [l, l] X [l2, l-1] // if l-1 >= l2 (i.e., l > l2)
// [l, l] X [l+1, u2]
// [l+1, l+1] X [l2, l]
// [l+1, l+1] X [l+2, u2]
// [l+2, l+2] X [l2, l+1]
// [l+2, l+2] X [l+3, u2]
// ...
// [u, u] X [l2, u-1]
// [u, u] X [u+1, u2] // if u+1 <= u2 (i.e., u < u2)
for (unsigned i = l; i <= u; i++) {
if (i > l2 && i < u2) {
// ADD_ENTRY(i, i, l2, i-1);
// ADD_ENTRY(i, i, i+1, u2);
imdd * new_child = _mk_empty(d_arity + 1);
add_child(new_child, l2, i-1, d);
add_child(new_child, i+1, u2, d);
new_child = memoize_new_imdd_if(memoize_res, new_child);
push_back(i, i, new_child);
}
else if (i > l2) {
SASSERT(!(i < u2));
ADD_ENTRY(i, i, l2, i-1);
}
else if (i < u2) {
SASSERT(!(i > l2));
ADD_ENTRY(i, i, i+1, u2);
}
}
if (u1 > u2) {
// ... u1]
// ... u2]
// -->
// [u2+1, u1] X [l2, u2]
SASSERT(u2 >= l1);
ADD_ENTRY(u2+1, u1, l2, u2);
}
}
#undef ADD_ENTRY
r = memoize_new_imdd_if(memoize_res, r);
return r;
}
/**
\brief Auxiliary functor used to implement mk_filter_distinct
*/
struct distinct_proc {
imdd_manager & m_manager;
distinct_proc(imdd_manager & m):
m_manager(m) {
}
void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res) {
r = m_manager.mk_distinct_imdd(lowers_uppers[0], lowers_uppers[1], lowers_uppers[2], lowers_uppers[3], d, memoize_res);
}
};
void imdd_manager::mk_filter_distinct_dupdt(imdd_ref & d, unsigned v1, unsigned v2, bool memoize_res) {
unsigned vars[2] = { v1, v2 };
distinct_proc proc(*this);
mk_filter_dupdt(d, 2, vars, proc, memoize_res);
}
void imdd_manager::mk_filter_distinct(imdd * d, imdd_ref & r, unsigned v1, unsigned v2, bool memoize_res) {
unsigned vars[2] = { v1, v2 };
distinct_proc proc(*this);
mk_filter(d, r, 2, vars, proc, memoize_res);
STRACE("imdd_trace", tout << "mk_filter_distinct(0x" << d << ", 0x" << r.get() << ", " << v1 << ", " << v2 << ", " << memoize_res << ");\n";);
}
imdd * imdd_manager::mk_disequal_imdd(unsigned l1, unsigned u1, unsigned value, imdd * d, bool memoize_res) {
unsigned d_arity;
if (d == 0) {
d_arity = 0;
}
else {
d_arity = d->get_arity();
memoize_res = memoize_res && d->is_memoized();
}
imdd * r = _mk_empty(d_arity + 1);
if (value < l1 || value > u1) {
add_child(r, l1, u1, d);
}
else {
SASSERT(l1 <= value && value <= u1);
if (l1 < value) {
add_child(r, l1, value-1, d);
}
if (value < u1) {
add_child(r, value+1, u1, d);
}
}
r = memoize_new_imdd_if(memoize_res, r);
return r;
}
struct disequal_proc {
imdd_manager & m_manager;
unsigned m_value;
disequal_proc(imdd_manager & m, unsigned v):
m_manager(m),
m_value(v) {
}
void operator()(unsigned * lowers_uppers, imdd * d, imdd_ref & r, bool memoize_res) {
r = m_manager.mk_disequal_imdd(lowers_uppers[0], lowers_uppers[1], m_value, d, memoize_res);
}
};
void imdd_manager::mk_filter_disequal_dupdt(imdd_ref & d, unsigned var, unsigned value, bool memoize_res) {
unsigned vars[1] = { var };
disequal_proc proc(*this, value);
mk_filter_dupdt(d, 1, vars, proc, memoize_res);
}
void imdd_manager::mk_filter_disequal(imdd * d, imdd_ref & r, unsigned var, unsigned value, bool memoize_res) {
unsigned vars[1] = { var };
disequal_proc proc(*this, value);
mk_filter(d, r, 1, vars, proc, memoize_res);
STRACE("imdd_trace", tout << "mk_filter_disequal(0x" << d << ", 0x" << r.get() << ", " << var << ", " << value << ", " << memoize_res << ");\n";);
}
#endif