z3-z3-4.13.0.src.math.subpaving.subpaving_t.h Maven / Gradle / Ivy
The newest version!
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
subpaving_t.h
Abstract:
Subpaving template for non-linear arithmetic.
Author:
Leonardo de Moura (leonardo) 2012-07-31.
Revision History:
--*/
#pragma once
#include
#include "util/tptr.h"
#include "util/small_object_allocator.h"
#include "util/chashtable.h"
#include "util/parray.h"
#include "math/interval/interval.h"
#include "util/scoped_numeral_vector.h"
#include "math/subpaving/subpaving_types.h"
#include "util/params.h"
#include "util/statistics.h"
#include "util/lbool.h"
#include "util/id_gen.h"
#include "util/rlimit.h"
#ifdef _MSC_VER
#pragma warning(disable : 4200)
#pragma warning(disable : 4355)
#endif
namespace subpaving {
template
class context_t {
public:
typedef typename C::numeral_manager numeral_manager;
typedef typename numeral_manager::numeral numeral;
/**
\brief Inequalities used to encode a problem.
*/
class ineq {
friend class context_t;
var m_x;
numeral m_val;
unsigned m_ref_count:30;
unsigned m_lower:1;
unsigned m_open:1;
public:
var x() const { return m_x; }
numeral const & value() const { return m_val; }
bool is_lower() const { return m_lower; }
bool is_open() const { return m_open; }
void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc());
struct lt_var_proc { bool operator()(ineq const * a, ineq const * b) const { return a->m_x < b->m_x; } };
};
class node;
class constraint {
public:
enum kind {
CLAUSE, MONOMIAL, POLYNOMIAL
// TODO: add SIN, COS, TAN, ...
};
protected:
kind m_kind;
uint64_t m_timestamp;
public:
constraint(kind k):m_kind(k), m_timestamp(0) {}
kind get_kind() const { return m_kind; }
// Return the timestamp of the last propagation visit
uint64_t timestamp() const { return m_timestamp; }
// Reset propagation visit time
void set_visited(uint64_t ts) { m_timestamp = ts; }
};
/**
\brief Clauses in the problem description and lemmas learned during paving.
*/
class clause : public constraint {
friend class context_t;
unsigned m_size; //!< Number of atoms in the clause.
unsigned m_lemma:1; //!< True if it is a learned clause.
unsigned m_watched:1; //!< True if it we are watching this clause. All non-lemmas are watched.
unsigned m_num_jst:30; //!< Number of times it is used to justify some bound.
ineq * m_atoms[0];
static unsigned get_obj_size(unsigned sz) { return sizeof(clause) + sz*sizeof(ineq*); }
public:
clause():constraint(constraint::CLAUSE) {}
unsigned size() const { return m_size; }
bool watched() const { return m_watched; }
ineq * operator[](unsigned i) const { SASSERT(i < size()); return m_atoms[i]; }
void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc());
};
class justification {
void * m_data;
public:
enum kind {
AXIOM = 0,
ASSUMPTION,
CLAUSE,
VAR_DEF
};
justification(bool axiom = true) {
m_data = axiom ? reinterpret_cast(static_cast(AXIOM)) : reinterpret_cast(static_cast(ASSUMPTION));
}
justification(justification const & source) { m_data = source.m_data; }
explicit justification(clause * c) { m_data = TAG(void*, c, CLAUSE); }
explicit justification(var x) { m_data = BOXTAGINT(void*, x, VAR_DEF); }
kind get_kind() const { return static_cast(GET_TAG(m_data)); }
bool is_clause() const { return get_kind() == CLAUSE; }
bool is_axiom() const { return get_kind() == AXIOM; }
bool is_assumption() const { return get_kind() == ASSUMPTION; }
bool is_var_def() const { return get_kind() == VAR_DEF; }
clause * get_clause() const {
SASSERT(is_clause());
return UNTAG(clause*, m_data);
}
var get_var() const {
SASSERT(is_var_def());
return UNBOXINT(m_data);
}
bool operator==(justification const & other) const { return m_data == other.m_data; }
bool operator!=(justification const & other) const { return !operator==(other); }
};
class bound {
friend class context_t;
numeral m_val;
unsigned m_x:29;
unsigned m_lower:1;
unsigned m_open:1;
unsigned m_mark:1;
uint64_t m_timestamp;
bound * m_prev;
justification m_jst;
void set_timestamp(uint64_t ts) { m_timestamp = ts; }
public:
var x() const { return static_cast(m_x); }
numeral const & value() const { return m_val; }
numeral & value() { return m_val; }
bool is_lower() const { return m_lower; }
bool is_open() const { return m_open; }
uint64_t timestamp() const { return m_timestamp; }
bound * prev() const { return m_prev; }
justification jst() const { return m_jst; }
void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc());
};
struct bound_array_config {
typedef context_t value_manager;
typedef small_object_allocator allocator;
typedef bound * value;
static const bool ref_count = false;
static const bool preserve_roots = true;
static const unsigned max_trail_sz = 16;
static const unsigned factor = 2;
};
// auxiliary declarations for parray_manager
void dec_ref(bound *) {}
void inc_ref(bound *) {}
typedef parray_manager bound_array_manager;
typedef typename bound_array_manager::ref bound_array;
/**
\brief Node in the context_t.
*/
class node {
bound_array_manager & m_bm;
bound_array m_lowers;
bound_array m_uppers;
var m_conflict;
unsigned m_id;
unsigned m_depth;
bound * m_trail;
node * m_parent; //!< parent node
node * m_first_child;
node * m_next_sibling;
// Doubly linked list of leaves to be processed
node * m_prev;
node * m_next;
public:
node(context_t & s, unsigned id);
node(node * parent, unsigned id);
// return unique identifier.
unsigned id() const { return m_id; }
bound_array_manager & bm() const { return m_bm; }
bound_array & lowers() { return m_lowers; }
bound_array & uppers() { return m_uppers; }
bool inconsistent() const { return m_conflict != null_var; }
void set_conflict(var x) { SASSERT(!inconsistent()); m_conflict = x; }
bound * trail_stack() const { return m_trail; }
bound * parent_trail_stack() const { return m_parent == nullptr ? nullptr : m_parent->m_trail; }
bound * lower(var x) const { return bm().get(m_lowers, x); }
bound * upper(var x) const { return bm().get(m_uppers, x); }
node * parent() const { return m_parent; }
node * first_child() const { return m_first_child; }
node * next_sibling() const { return m_next_sibling; }
node * prev() const { return m_prev; }
node * next() const { return m_next; }
/**
\brief Return true if x is unbounded in this node
*/
bool is_unbounded(var x) const { return lower(x) == nullptr && upper(x) == nullptr; }
void push(bound * b);
void set_first_child(node * n) { m_first_child = n; }
void set_next_sibling(node * n) { m_next_sibling = n; }
void set_next(node * n) { m_next = n; }
void set_prev(node * n) { m_prev = n; }
unsigned depth() const { return m_depth; }
};
/**
\brief Intervals are just temporary place holders.
The pavers maintain bounds.
*/
struct interval {
bool m_constant; // Flag: constant intervals are pairs
// constant intervals
node * m_node;
var m_x;
// mutable intervals
numeral m_l_val;
bool m_l_inf;
bool m_l_open;
numeral m_u_val;
bool m_u_inf;
bool m_u_open;
interval():m_constant(false) {}
void set_constant(node * n, var x) {
m_constant = true;
m_node = n;
m_x = x;
}
void set_mutable() { m_constant = false; }
};
class interval_config {
public:
typedef typename C::numeral_manager numeral_manager;
typedef typename numeral_manager::numeral numeral;
typedef typename context_t::interval interval;
private:
numeral_manager & m_manager;
public:
interval_config(numeral_manager & m):m_manager(m) {}
numeral_manager & m() const { return m_manager; }
void round_to_minus_inf() { C::round_to_minus_inf(m()); }
void round_to_plus_inf() { C::round_to_plus_inf(m()); }
void set_rounding(bool to_plus_inf) { C::set_rounding(m(), to_plus_inf); }
numeral const & lower(interval const & a) const {
if (a.m_constant) {
bound * b = a.m_node->lower(a.m_x);
return b == nullptr ? a.m_l_val /* don't care */ : b->value();
}
return a.m_l_val;
}
numeral const & upper(interval const & a) const {
if (a.m_constant) {
bound * b = a.m_node->upper(a.m_x);
return b == nullptr ? a.m_u_val /* don't care */ : b->value();
}
return a.m_u_val;
}
numeral & lower(interval & a) { SASSERT(!a.m_constant); return a.m_l_val; }
numeral & upper(interval & a) { SASSERT(!a.m_constant); return a.m_u_val; }
bool lower_is_inf(interval const & a) const { return a.m_constant ? a.m_node->lower(a.m_x) == nullptr : a.m_l_inf; }
bool upper_is_inf(interval const & a) const { return a.m_constant ? a.m_node->upper(a.m_x) == nullptr : a.m_u_inf; }
bool lower_is_open(interval const & a) const {
if (a.m_constant) {
bound * b = a.m_node->lower(a.m_x);
return b == nullptr || b->is_open();
}
return a.m_l_open;
}
bool upper_is_open(interval const & a) const {
if (a.m_constant) {
bound * b = a.m_node->upper(a.m_x);
return b == nullptr || b->is_open();
}
return a.m_u_open;
}
// Setters
void set_lower(interval & a, numeral const & n) { SASSERT(!a.m_constant); m().set(a.m_l_val, n); }
void set_upper(interval & a, numeral const & n) { SASSERT(!a.m_constant); m().set(a.m_u_val, n); }
void set_lower_is_open(interval & a, bool v) { SASSERT(!a.m_constant); a.m_l_open = v; }
void set_upper_is_open(interval & a, bool v) { SASSERT(!a.m_constant); a.m_u_open = v; }
void set_lower_is_inf(interval & a, bool v) { SASSERT(!a.m_constant); a.m_l_inf = v; }
void set_upper_is_inf(interval & a, bool v) { SASSERT(!a.m_constant); a.m_u_inf = v; }
};
typedef ::interval_manager interval_manager;
class definition : public constraint {
public:
definition(typename constraint::kind k):constraint(k) {}
};
class monomial : public definition {
friend class context_t;
unsigned m_size;
power m_powers[0];
monomial(unsigned sz, power const * pws);
static unsigned get_obj_size(unsigned sz) { return sizeof(monomial) + sz*sizeof(power); }
public:
unsigned size() const { return m_size; }
power const & get_power(unsigned idx) const { SASSERT(idx < size()); return m_powers[idx]; }
power const * get_powers() const { return m_powers; }
var x(unsigned idx) const { return get_power(idx).x(); }
unsigned degree(unsigned idx) const { return get_power(idx).degree(); }
void display(std::ostream & out, display_var_proc const & proc = display_var_proc(), bool use_star = false) const;
};
class polynomial : public definition {
friend class context_t;
unsigned m_size;
numeral m_c;
numeral * m_as;
var * m_xs;
static unsigned get_obj_size(unsigned sz) { return sizeof(polynomial) + sz*sizeof(numeral) + sz*sizeof(var); }
public:
polynomial():definition(constraint::POLYNOMIAL) {}
unsigned size() const { return m_size; }
numeral const & a(unsigned i) const { return m_as[i]; }
var x(unsigned i) const { return m_xs[i]; }
var const * xs() const { return m_xs; }
numeral const * as() const { return m_as; }
numeral const & c() const { return m_c; }
void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc(), bool use_star = false) const;
};
/**
\brief Watched element (aka occurrence) can be:
- A clause
- A definition (i.e., a variable)
Remark: we cannot use the two watched literal approach since we process multiple nodes.
*/
class watched {
public:
enum kind { CLAUSE=0, DEFINITION };
private:
void * m_data;
public:
watched():m_data(nullptr) {}
explicit watched(var x) { m_data = BOXTAGINT(void*, x, DEFINITION); }
explicit watched(clause * c) { m_data = TAG(void*, c, CLAUSE); }
kind get_kind() const { return static_cast(GET_TAG(m_data)); }
bool is_clause() const { return get_kind() != DEFINITION; }
bool is_definition() const { return get_kind() == DEFINITION; }
clause * get_clause() const { SASSERT(is_clause()); return UNTAG(clause*, m_data); }
var get_var() const { SASSERT(is_definition()); return UNBOXINT(m_data); }
bool operator==(watched const & other) const { return m_data == other.m_data; }
bool operator!=(watched const & other) const { return !operator==(other); }
};
/**
\brief Abstract functor for selecting the next leaf node to be explored.
*/
class node_selector {
context_t * m_ctx;
public:
node_selector(context_t * ctx):m_ctx(ctx) {}
virtual ~node_selector() = default;
context_t * ctx() const { return m_ctx; }
// Return the next leaf node to be processed.
// Front and back are the first and last nodes in the doubly linked list of
// leaf nodes.
// Remark: new nodes are always inserted in the front of the list.
virtual node * operator()(node * front, node * back) = 0;
};
/**
\brief Abstract functor for selecting the next variable to branch.
*/
class var_selector {
context_t * m_ctx;
public:
var_selector(context_t * ctx):m_ctx(ctx) {}
virtual ~var_selector() = default;
context_t * ctx() const { return m_ctx; }
// Return the next variable to branch.
virtual var operator()(node * n) = 0;
// -----------------------------------
//
// Event handlers
//
// -----------------------------------
// Invoked when a new variable is created.
virtual void new_var_eh(var x) {}
// Invoked when node n is created
virtual void new_node_eh(node * n) {}
// Invoked before deleting node n.
virtual void del_node_eh(node * n) {}
// Invoked when variable x is used during conflict resolution.
virtual void used_var_eh(node * n, var x) {}
};
class node_splitter;
friend class node_splitter;
/**
\brief Abstract functor for creating children for node n by branching on a given variable.
*/
class node_splitter {
context_t * m_ctx;
public:
node_splitter(context_t * ctx):m_ctx(ctx) {}
virtual ~node_splitter() = default;
context_t * ctx() const { return m_ctx; }
node * mk_node(node * p) { return ctx()->mk_node(p); }
bound * mk_decided_bound(var x, numeral const & val, bool lower, bool open, node * n) {
return ctx()->mk_bound(x, val, lower, open, n, justification());
}
/**
\brief Create children nodes for n by splitting on x.
\pre n is a leaf. The interval for x in n has more than one element.
*/
virtual void operator()(node * n, var x) = 0;
};
/**
\brief Return most recent splitting var for node n.
*/
var splitting_var(node * n) const;
/**
\brief Return true if x is a definition.
*/
bool is_definition(var x) const { return m_defs[x] != 0; }
typedef svector watch_list;
typedef _scoped_numeral_vector scoped_numeral_vector;
private:
reslimit& m_limit;
C m_c;
bool m_arith_failed; //!< True if the arithmetic module produced an exception.
bool m_own_allocator;
small_object_allocator * m_allocator;
bound_array_manager m_bm;
interval_manager m_im;
scoped_numeral_vector m_num_buffer;
bool_vector m_is_int;
ptr_vector m_defs;
vector m_wlist;
ptr_vector m_unit_clauses;
ptr_vector m_clauses;
ptr_vector m_lemmas;
id_gen m_node_id_gen;
uint64_t m_timestamp;
node * m_root;
// m_leaf_head is the head of a doubly linked list of leaf nodes to be processed.
node * m_leaf_head;
node * m_leaf_tail;
var m_conflict;
ptr_vector m_queue;
unsigned m_qhead;
display_var_proc m_default_display_proc;
display_var_proc * m_display_proc;
scoped_ptr m_node_selector;
scoped_ptr m_var_selector;
scoped_ptr m_node_splitter;
svector m_pws;
// Configuration
numeral m_epsilon; //!< If upper - lower < epsilon, then new bound is not propagated.
bool m_zero_epsilon;
numeral m_max_bound; //!< Bounds > m_max and < -m_max are not propagated
numeral m_minus_max_bound; //!< -m_max_bound
numeral m_nth_root_prec; //!< precision for computing the nth root
unsigned m_max_depth; //!< Maximum depth
unsigned m_max_nodes; //!< Maximum number of nodes in the tree
unsigned long long m_max_memory; // in bytes
// Counters
unsigned m_num_nodes;
// Statistics
unsigned m_num_conflicts;
unsigned m_num_mk_bounds;
unsigned m_num_splits;
unsigned m_num_visited;
// Temporary
numeral m_tmp1, m_tmp2, m_tmp3;
interval m_i_tmp1, m_i_tmp2, m_i_tmp3;
friend class node;
void set_arith_failed() { m_arith_failed = true; }
void checkpoint();
bound_array_manager & bm() { return m_bm; }
interval_manager & im() { return m_im; }
small_object_allocator & allocator() const { return *m_allocator; }
bound * mk_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst);
void del_bound(bound * b);
// Create a new bound and add it to the propagation queue.
void propagate_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst);
bool is_int(monomial const * m) const;
bool is_int(polynomial const * p) const;
bool is_monomial(var x) const { return m_defs[x] != 0 && m_defs[x]->get_kind() == constraint::MONOMIAL; }
monomial * get_monomial(var x) const { SASSERT(is_monomial(x)); return static_cast(m_defs[x]); }
bool is_polynomial(var x) const { return m_defs[x] != 0 && m_defs[x]->get_kind() == constraint::POLYNOMIAL; }
polynomial * get_polynomial(var x) const { SASSERT(is_polynomial(x)); return static_cast(m_defs[x]); }
static void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc, var x, numeral & k, bool lower, bool open);
void display(std::ostream & out, var x) const;
void display_definition(std::ostream & out, definition const * d, bool use_star = false) const;
void display(std::ostream & out, constraint * a, bool use_star = false) const;
void display(std::ostream & out, bound * b) const;
void display(std::ostream & out, ineq * a) const;
void display_params(std::ostream & out) const;
void add_unit_clause(ineq * a, bool axiom);
// Remark: Not all lemmas need to be watched. Some of them can be used to justify clauses only.
void add_clause_core(unsigned sz, ineq * const * atoms, bool lemma, bool watched);
void del_clause(clause * cls);
node * mk_node(node * parent = nullptr);
void del_node(node * n);
void del_nodes();
void del(interval & a);
void del_clauses(ptr_vector & cs);
void del_unit_clauses();
void del_clauses();
void del_monomial(monomial * m);
void del_sum(polynomial * p);
void del_definitions();
/**
\brief Insert n in the beginning of the doubly linked list of leaves.
\pre n is a leaf, and it is not already in the list.
*/
void push_front(node * n);
/**
\brief Insert n in the end of the doubly linked list of leaves.
\pre n is a leaf, and it is not already in the list.
*/
void push_back(node * n);
/**
\brief Remove n from the doubly linked list of leaves.
\pre n is a leaf, and it is in the list.
*/
void remove_from_leaf_dlist(node * n);
/**
\brief Remove all nodes from the leaf dlist.
*/
void reset_leaf_dlist();
/**
\brief Add all leaves back to the leaf dlist.
*/
void rebuild_leaf_dlist(node * n);
// -----------------------------------
//
// Propagation
//
// -----------------------------------
/**
\brief Return true if the given node is in an inconsistent state.
*/
bool inconsistent(node * n) const { return n->inconsistent(); }
/**
\brief Set a conflict produced by the bounds of x at the given node.
*/
void set_conflict(var x, node * n);
/**
\brief Return true if bound b may propagate a new bound using constraint c at node n.
*/
bool may_propagate(bound * b, constraint * c, node * n);
/**
\brief Normalize bound if x is integer.
Examples:
x < 2 --> x <= 1
x <= 2.3 --> x <= 2
*/
void normalize_bound(var x, numeral & val, bool lower, bool & open);
/**
\brief Return true if (x, k, lower, open) is a relevant new bound at node n.
That is, it improves the current bound, and satisfies m_epsilon and m_max_bound.
*/
bool relevant_new_bound(var x, numeral const & k, bool lower, bool open, node * n);
/**
\brief Return true if the lower and upper bounds of x are 0 at node n.
*/
bool is_zero(var x, node * n) const;
/**
\brief Return true if upper bound of x is 0 at node n.
*/
bool is_upper_zero(var x, node * n) const;
/**
\brief Return true if lower and upper bounds of x are conflicting at node n. That is, upper(x) < lower(x)
*/
bool conflicting_bounds(var x, node * n) const;
/**
\brief Return true if x is unbounded at node n.
*/
bool is_unbounded(var x, node * n) const { return n->is_unbounded(x); }
/**
\brief Return true if b is the most recent lower/upper bound for variable b->x() at node n.
*/
bool most_recent(bound * b, node * n) const;
/**
\brief Add most recent bounds of node n into the propagation queue.
That is, all bounds b s.t. b is in the trail of n, but not in the tail of parent(n), and most_recent(b, n).
*/
void add_recent_bounds(node * n);
/**
\brief Propagate new bounds at node n using get_monomial(x)
\pre is_monomial(x)
*/
void propagate_monomial(var x, node * n);
void propagate_monomial_upward(var x, node * n);
void propagate_monomial_downward(var x, node * n, unsigned i);
/**
\brief Propagate new bounds at node n using get_polynomial(x)
\pre is_polynomial(x)
*/
void propagate_polynomial(var x, node * n);
// Propagate a new bound for y using the polynomial associated with x. x may be equal to y.
void propagate_polynomial(var x, node * n, var y);
/**
\brief Propagate new bounds at node n using clause c.
*/
void propagate_clause(clause * c, node * n);
/**
\brief Return the truth value of inequaliy t at node n.
*/
lbool value(ineq * t, node * n);
/**
\brief Propagate new bounds at node n using the definition of variable x.
\pre is_definition(x)
*/
void propagate_def(var x, node * n);
/**
\brief Propagate constraints in b->x()'s watch list.
*/
void propagate(node * n, bound * b);
/**
\brief Perform bound propagation at node n.
*/
void propagate(node * n);
/**
\brief Try to propagate at node n using all definitions.
*/
void propagate_all_definitions(node * n);
// -----------------------------------
//
// Main
//
// -----------------------------------
void init();
/**
\brief Assert unit clauses in the node n.
*/
void assert_units(node * n);
// -----------------------------------
//
// Debugging support
//
// -----------------------------------
/**
\brief Return true if b is a bound for node n.
*/
bool is_bound_of(bound * b, node * n) const;
/**
\brief Check the consistency of the doubly linked list of leaves.
*/
bool check_leaf_dlist() const;
/**
\brief Check paving tree structure.
*/
bool check_tree() const;
/**
\brief Check main invariants.
*/
bool check_invariant() const;
public:
context_t(reslimit& lim, C const & c, params_ref const & p, small_object_allocator * a);
~context_t();
/**
\brief Return true if the arithmetic module failed.
*/
bool arith_failed() const { return m_arith_failed; }
numeral_manager & nm() const { return m_c.m(); }
unsigned num_vars() const { return m_is_int.size(); }
bool is_int(var x) const { SASSERT(x < num_vars()); return m_is_int[x]; }
/**
\brief Create a new variable.
*/
var mk_var(bool is_int);
/**
\brief Create the monomial xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1].
The result is a variable y s.t. y = xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1].
\pre for all i \in [0, sz-1] : ks[i] > 0
\pre sz > 0
*/
var mk_monomial(unsigned sz, power const * pws);
/**
\brief Create the sum c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1].
The result is a variable y s.t. y = c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1].
\pre sz > 0
\pre for all i \in [0, sz-1] : as[i] != 0
*/
var mk_sum(numeral const & c, unsigned sz, numeral const * as, var const * xs);
/**
\brief Create an inequality.
*/
ineq * mk_ineq(var x, numeral const & k, bool lower, bool open);
void inc_ref(ineq * a);
void dec_ref(ineq * a);
/**
\brief Assert the clause atoms[0] \/ ... \/ atoms[sz-1]
\pre sz > 1
*/
void add_clause(unsigned sz, ineq * const * atoms) { add_clause_core(sz, atoms, false, true); }
/**
\brief Assert a constraint of one of the forms: x < k, x > k, x <= k, x >= k.
If axiom == true, then the constraint is not tracked in proofs.
*/
void add_ineq(var x, numeral const & k, bool lower, bool open, bool axiom);
/**
\brief Store in the given vector all leaves of the paving tree.
*/
void collect_leaves(ptr_vector & leaves) const;
/**
\brief Display constraints asserted in the subpaving.
*/
void display_constraints(std::ostream & out, bool use_star = false) const;
/**
\brief Display bounds for each leaf of the tree.
*/
void display_bounds(std::ostream & out) const;
void display_bounds(std::ostream & out, node * n) const;
void set_display_proc(display_var_proc * p) { m_display_proc = p; }
void updt_params(params_ref const & p);
static void collect_param_descrs(param_descrs & d);
void reset_statistics();
void collect_statistics(statistics & st) const;
void operator()();
};
};