/*++ Copyright (c) 2006 Microsoft Corporation Module Name: total_order.h Abstract: Author: Leonardo de Moura (leonardo) 2010-07-01. Revision History: --*/ #ifndef _TOTAL_ORDER_H_ #define _TOTAL_ORDER_H_ #include"util.h" #include"small_object_allocator.h" #include"map.h" #include"uint_map.h" #include"trace.h" /** \brief An object for maintaining a total-order on sets of T values. \c Map should be a class implementing the map API for T to void *. */ template class total_order { struct cell { cell * m_next; cell * m_prev; uint64 m_val; T m_data; }; small_object_allocator * m_allocator; bool m_own_allocator; Map m_map; cell m_base; unsigned m_size; cell * base() const { return const_cast(&m_base); } void init_base() { m_base.m_next = &m_base; m_base.m_prev = &m_base; m_base.m_val = 0; } uint64 v(cell * a) const { return a->m_val; } uint64 vb(cell * a) const { return v(a) - v(base()); } uint64 vbn(cell * a) const { return a->m_next == base() ? UINT64_MAX : vb(a->m_next); } cell * mk_cell(T const & a) { SASSERT(!m_map.contains(a)); cell * c = reinterpret_cast(m_allocator->allocate(sizeof(cell))); m_map.insert(a, c); c->m_data = a; m_size++; return c; } void del_cell(cell * c) { #ifdef Z3DEBUG T d = c->m_data; #endif m_map.erase(c->m_data); m_allocator->deallocate(sizeof(cell), c); m_size--; SASSERT(!m_map.contains(d)); } cell * to_cell(T const & a) const { void * r; #ifdef Z3DEBUG bool ok = #endif m_map.find(a, r); SASSERT(ok); return reinterpret_cast(r); } void _insert_after(cell * a, cell * b) { uint64 vb_a = vb(a); uint64 vbn_a = vbn(a); SASSERT(vb_a < vbn_a); if (vbn_a < 2 || (vb_a > vbn_a - 2)) { TRACE("total_order", tout << "relabeling...\n"; tout << "\n";); uint64 v0 = v(a); unsigned sz = size(); uint64 ideal_gap = UINT64_MAX / sz; uint64 goal_gap = ideal_gap / 32; cell * c = a->m_next->m_next; unsigned j = 2; uint64 curr_gap = (v(c) - v0) / j; while (j < sz && curr_gap < goal_gap) { j++; c = c->m_next; curr_gap = (v(c) - v0) / j; } TRACE("total_order", tout << "j: " << j << " curr_gap: " << curr_gap << " sz: " << sz << "\n";); if (j == sz) curr_gap = ideal_gap; c = a->m_next; uint64 inc = curr_gap; for (unsigned i = 0; i < j; i++) { c->m_val = v0 + inc; c = c->m_next; inc += curr_gap; } CASSERT("total_order", check_invariant()); vb_a = vb(a); vbn_a = vbn(a); } SASSERT(vb_a <= vbn_a - 2); uint64 vb_b = vb_a + ((vbn_a - vb_a)/2); SASSERT(vb_b > vb_a); SASSERT(vb_b < vbn_a); b->m_val = vb_b + v(base()); b->m_next = a->m_next; a->m_next->m_prev = b; b->m_prev = a; a->m_next = b; SASSERT(vb(a) < vb(b)); CASSERT("total_order", check_invariant()); } void insert_core(cell * a) { _insert_after(base(), a); } void remove_cell(cell * a) { SASSERT(a != base()); cell * p = a->m_prev; cell * n = a->m_next; p->m_next = n; n->m_prev = p; } void move_after(cell * a, cell * b) { if (a->m_next == b) return; remove_cell(b); _insert_after(a, b); } void move_beginning(cell * b) { if (base()->m_next == b) return; // already in the beginning remove_cell(b); insert_core(b); } void erase(cell * a) { remove_cell(a); del_cell(a); } public: total_order(): m_allocator(alloc(small_object_allocator)), m_own_allocator(true), m_size(0) { init_base(); } total_order(Map const & m): m_allocator(alloc(small_object_allocator)), m_own_allocator(true), m_map(m), m_size(0) { init_base(); } total_order(small_object_allocator * allocator): m_allocator(allocator), m_own_allocator(false), m_size(0) { init_base(); } total_order(small_object_allocator * allocator, Map const & m): m_allocator(allocator), m_own_allocator(false), m_map(m), m_size(0) { init_base(); } ~total_order() { cell * curr = base()->m_next; while (curr != base()) { cell * c = curr; curr = curr->m_next; del_cell(c); } if (m_own_allocator) dealloc(m_allocator); } /** \brief Return true if \c a is in the total order. */ bool contains(T const & a) const { return m_map.contains(a); } /** \brief Insert \c a in the beginning of the total order. \pre \c a must not be an element of the total order. */ void insert(T const & a) { SASSERT(!contains(a)); insert_core(mk_cell(a)); } /** \brief Insert \c b after \c a in the total order. \pre \c a is an element of the total order. \pre \c b must not be an element of the total order. */ void insert_after(T const & a, T const & b) { SASSERT(contains(a)); SASSERT(!contains(b)); _insert_after(to_cell(a), mk_cell(b)); SASSERT(lt(a, b)); } /** \brief Move \c a to the beginning of the total order. \pre \c a is an element of the total order. */ void move_beginning(T const & a) { SASSERT(contains(a)); move_beginning(to_cell(a)); } /** \brief Move \b after \c a in the total order. \pre \c a is an element of the total order. \pre \c b is an element of the total order. \pre \c a must be different from \c b. */ void move_after(T const & a, T const & b) { SASSERT(contains(a)); SASSERT(contains(b)); move_after(to_cell(a), to_cell(b)); SASSERT(lt(a, b)); } /** \brief Remove \c a from the total order. \pre \c a is an element of the total order. */ void erase(T const & a) { SASSERT(contains(a)); erase(to_cell(a)); } /** \brief Return true if \c a is less than \c b in the total order. */ bool lt(T const & a, T const & b) const { SASSERT(contains(a)); SASSERT(contains(b)); return vb(to_cell(a)) < vb(to_cell(b)); } /** \brief Return true if \c a is greater than \c b in the total order. */ bool gt(T const & a, T const & b) const { SASSERT(contains(a)); SASSERT(contains(b)); return vb(to_cell(a)) > vb(to_cell(b)); } /** \brief Return true if the total order is empty. */ bool empty() const { return base()->m_next == base(); } /** \brief Return the number of elements in the total order. */ unsigned size() const { return m_size; } /** \brief Return the first element of the total order. */ T const & first() const { SASSERT(!empty()); return base()->m_next->m_data; } /** \brief Return true if \c a has a successor in the total order. \pre \c a is an element of the total order. */ bool has_next(T const & a) const { SASSERT(contains(a)); return to_cell(a)->m_next != base(); } /** \brief Return the successor of \c a in the total order. \pre \c a is an element of the total order. \pre has_next(a) */ T const & next(T const & a) const { SASSERT(contains(a)); SASSERT(has_next(a)); return to_cell(a)->m_next->m_data; } /** \brief Return true if \c a has a predecessor in the total order. \pre \c a is an element of the total order. */ bool has_pred(T const & a) const { SASSERT(contains(a)); return to_cell(a)->m_prev != base(); } /** \brief Return the predecessor of \c a in the total order. \pre \c a is an element of the total order. \pre has_pred(a) */ T const & pred(T const & a) const { SASSERT(has_pred(a)); return to_cell(a)->m_prev->m_data; } /** \brief Display the elements of the total order in increasing order. \remark For debugging purposes. */ void display(std::ostream & out) const { cell * curr = base()->m_next; bool first = true; while (curr != base()) { if (first) first = false; else out << " "; out << curr->m_data; curr = curr->m_next; } } void display_detail(std::ostream & out) const { cell * curr = base()->m_next; bool first = true; while (curr != base()) { if (first) first = false; else out << " "; out << curr->m_data << ":" << curr->m_val; curr = curr->m_next; } } #ifdef Z3DEBUG bool check_invariant() const { cell * curr = base()->m_next; while (curr != base()) { SASSERT(curr->m_next == base() || vb(curr) < vb(curr->m_next)); curr = curr->m_next; } return true; } #endif }; typedef total_order > int_total_order; /** \brief A total order that uses vectors to implement a mapping from unsigned to void *. */ typedef total_order > uint_total_order; template std::ostream & operator<<(std::ostream & out, total_order const & to) { to.display(out); return out; } #endif /* _TOTAL_ORDER_H_ */