Normalize level expressions
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
e220d7c525
commit
7b00561a94
6 changed files with 159 additions and 45 deletions
|
@ -53,10 +53,10 @@ struct environment::imp {
|
|||
return d != uninit && (k < 0 || (k >= 0 && d >= static_cast<unsigned>(k)));
|
||||
}
|
||||
case level_kind::Lift: return is_ge(lift_of(l1), l2, sub(k, lift_offset(l1)));
|
||||
case level_kind::Max: return is_ge(max_level1(l1), l2, k) || is_ge(max_level2(l1), l2, k);
|
||||
case level_kind::Max: return std::any_of(max_begin_levels(l1), max_end_levels(l1), [&](level const & l) { return is_ge(l, l2, k); });
|
||||
}
|
||||
case level_kind::Lift: return is_ge(l1, lift_of(l2), add(k, lift_offset(l2)));
|
||||
case level_kind::Max: return is_ge(l1, max_level1(l2), k) && is_ge(l1, max_level2(l2), k);
|
||||
case level_kind::Max: return std::all_of(max_begin_levels(l2), max_end_levels(l2), [&](level const & l) { return is_ge(l1, l, k); });
|
||||
}
|
||||
lean_unreachable();
|
||||
return false;
|
||||
|
@ -103,9 +103,9 @@ struct environment::imp {
|
|||
|
||||
void add_constraints(uvar v1, level const & l, unsigned k) {
|
||||
switch (kind(l)) {
|
||||
case level_kind::UVar: add_constraint(v1, uvar_idx(l), k); return;
|
||||
case level_kind::Lift: add_constraints(v1, lift_of(l), add(k, lift_offset(l))); return;
|
||||
case level_kind::Max: add_constraints(v1, max_level1(l), k); add_constraints(v1, max_level2(l), k); return;
|
||||
case level_kind::UVar: add_constraint(v1, uvar_idx(l), k); return;
|
||||
case level_kind::Lift: add_constraints(v1, lift_of(l), add(k, lift_offset(l))); return;
|
||||
case level_kind::Max: std::for_each(max_begin_levels(l), max_end_levels(l), [&](level const & l1) { add_constraints(v1, l1, k); }); return;
|
||||
}
|
||||
lean_unreachable();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
|||
|
||||
Author: Leonardo de Moura
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include "level.h"
|
||||
#include "buffer.h"
|
||||
#include "rc.h"
|
||||
#include "debug.h"
|
||||
#include "hash.h"
|
||||
|
@ -27,9 +29,18 @@ struct level_lift : public level_cell {
|
|||
level_lift(level const & l, unsigned k):level_cell(level_kind::Lift), m_l(l), m_k(k) {}
|
||||
};
|
||||
struct level_max : public level_cell {
|
||||
level m_l1;
|
||||
level m_l2;
|
||||
level_max(level const & l1, level const & l2):level_cell(level_kind::Max), m_l1(l1), m_l2(l2) {}
|
||||
unsigned m_size;
|
||||
level m_levels[0];
|
||||
level_max(unsigned sz, level const * ls):level_cell(level_kind::Max), m_size(sz) {
|
||||
for (unsigned i = 0; i < m_size; i++)
|
||||
new (m_levels + i) level(ls[i]);
|
||||
}
|
||||
~level_max() {
|
||||
for (unsigned i = 0; i < m_size; i++)
|
||||
(m_levels+i)->~level();
|
||||
}
|
||||
level const * begin_levels() const { return m_levels; }
|
||||
level const * end_levels() const { return m_levels + m_size; }
|
||||
};
|
||||
level_uvar * to_uvar(level_cell * c) { return static_cast<level_uvar*>(c); }
|
||||
level_lift * to_lift(level_cell * c) { return static_cast<level_lift*>(c); }
|
||||
|
@ -38,13 +49,13 @@ void level_cell::dealloc() {
|
|||
switch (m_kind) {
|
||||
case level_kind::UVar: delete to_uvar(this); break;
|
||||
case level_kind::Lift: delete to_lift(this); break;
|
||||
case level_kind::Max: delete to_max(this); break;
|
||||
case level_kind::Max: static_cast<level_max*>(this)->~level_max(); delete[] reinterpret_cast<char*>(this); break;
|
||||
}
|
||||
}
|
||||
level::level(): m_ptr(new level_uvar(name("bot"), 0)) { lean_assert(uvar_name(*this) == name("bot")); }
|
||||
level::level(name const & n, uvar u): m_ptr(new level_uvar(n, u)) {}
|
||||
level::level(level const & l, unsigned k): m_ptr(new level_lift(l, k)) {}
|
||||
level::level(level const & l1, level const & l2):m_ptr(new level_max(l1, l2)) {}
|
||||
level::level(): m_ptr(new level_uvar(name("bot"), 0)) { lean_assert(uvar_name(*this) == name("bot")); }
|
||||
level::level(name const & n, uvar u): m_ptr(new level_uvar(n, u)) {}
|
||||
level::level(level const & l, unsigned k):m_ptr(new level_lift(l, k)) { lean_assert(is_uvar(l)); }
|
||||
level::level(level_cell * ptr): m_ptr(ptr) { lean_assert(m_ptr->get_rc() == 1); }
|
||||
level::level(level const & s):
|
||||
m_ptr(s.m_ptr) {
|
||||
if (m_ptr)
|
||||
|
@ -58,22 +69,89 @@ level::~level() {
|
|||
if (m_ptr)
|
||||
m_ptr->dec_ref();
|
||||
}
|
||||
level to_level(level_cell * c) { return level(c); }
|
||||
level_cell * to_cell(level const & l) { return l.m_ptr; }
|
||||
level_max * to_max(level const & l) { return to_max(to_cell(l)); }
|
||||
unsigned level::hash() const {
|
||||
switch (m_ptr->m_kind) {
|
||||
case level_kind::UVar: return to_uvar(m_ptr)->m_name.hash();
|
||||
case level_kind::Lift: return ::lean::hash(to_lift(m_ptr)->m_l.hash(), to_lift(m_ptr)->m_k);
|
||||
case level_kind::Max: return ::lean::hash(to_max(m_ptr)->m_l1.hash(), to_max(m_ptr)->m_l2.hash());
|
||||
case level_kind::Max: return ::lean::hash(to_max(m_ptr)->m_size, [&](unsigned i) { return to_max(m_ptr)->m_levels[i].hash(); });
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
level max_core(unsigned sz, level const * ls) {
|
||||
char * mem = new char[sizeof(level_max) + sz*sizeof(level)];
|
||||
return to_level(new (mem) level_max(sz, ls));
|
||||
}
|
||||
|
||||
level const & _lift_of(level const & l) { return is_lift(l) ? lift_of(l) : l; }
|
||||
unsigned _lift_offset(level const & l) { return is_lift(l) ? lift_offset(l) : 0; }
|
||||
|
||||
void push_back(buffer<level> & ls, level const & l) {
|
||||
for (unsigned i = 0; i < ls.size(); i++) {
|
||||
if (_lift_of(ls[i]) == _lift_of(l)) {
|
||||
if (_lift_offset(ls[i]) < _lift_offset(l))
|
||||
ls[i] = l;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ls.push_back(l);
|
||||
}
|
||||
|
||||
level max_core(unsigned sz1, level const * ls1, unsigned sz2, level const * ls2) {
|
||||
buffer<level> new_lvls;
|
||||
std::for_each(ls1, ls1 + sz1, [&](level const & l) { push_back(new_lvls, l); });
|
||||
std::for_each(ls2, ls2 + sz2, [&](level const & l) { push_back(new_lvls, l); });
|
||||
if (new_lvls.size() == 1)
|
||||
return new_lvls[0];
|
||||
else
|
||||
return max_core(new_lvls.size(), new_lvls.data());
|
||||
}
|
||||
|
||||
level max_core(level const & l1, level const & l2) { return max_core(1, &l1, 1, &l2); }
|
||||
level max_core(level const & l1, level_max * l2) { return max_core(1, &l1, l2->m_size, l2->m_levels); }
|
||||
level max_core(level_max * l1, level const & l2) { return max_core(l1->m_size, l1->m_levels, 1, &l2); }
|
||||
level max_core(level_max * l1, level_max * l2) { return max_core(l1->m_size, l1->m_levels, l2->m_size, l2->m_levels); }
|
||||
|
||||
level max(level const & l1, level const & l2) {
|
||||
if (is_max(l1)) {
|
||||
if (is_max(l2))
|
||||
return max_core(to_max(l1), to_max(l1));
|
||||
else
|
||||
return max_core(to_max(l1), l2);
|
||||
}
|
||||
else {
|
||||
if (is_max(l2))
|
||||
return max_core(l1, to_max(l2));
|
||||
else
|
||||
return max_core(l1, l2);
|
||||
}
|
||||
}
|
||||
|
||||
level operator+(level const & l, unsigned k) {
|
||||
switch (kind(l)) {
|
||||
case level_kind::UVar: return level(l, k);
|
||||
case level_kind::Lift: return level(lift_of(l), lift_offset(l) + k);
|
||||
case level_kind::Max: {
|
||||
std::cout << l << "\n";
|
||||
buffer<level> new_lvls;
|
||||
for (unsigned i = 0; i < max_size(l); i++)
|
||||
new_lvls.push_back(max_level(l, i) + k);
|
||||
return max_core(new_lvls.size(), new_lvls.data());
|
||||
}}
|
||||
lean_unreachable();
|
||||
return level();
|
||||
}
|
||||
|
||||
level_kind kind (level const & l) { lean_assert(l.m_ptr); return l.m_ptr->m_kind; }
|
||||
name const & uvar_name (level const & l) { lean_assert(is_uvar(l)); return static_cast<level_uvar*>(l.m_ptr)->m_name; }
|
||||
uvar uvar_idx (level const & l) { lean_assert(is_uvar(l)); return static_cast<level_uvar*>(l.m_ptr)->m_uvar; }
|
||||
level const & lift_of (level const & l) { lean_assert(is_lift(l)); return static_cast<level_lift*>(l.m_ptr)->m_l; }
|
||||
unsigned lift_offset(level const & l) { lean_assert(is_lift(l)); return static_cast<level_lift*>(l.m_ptr)->m_k; }
|
||||
level const & max_level1 (level const & l) { lean_assert(is_max(l)); return static_cast<level_max*>(l.m_ptr)->m_l1; }
|
||||
level const & max_level2 (level const & l) { lean_assert(is_max(l)); return static_cast<level_max*>(l.m_ptr)->m_l2; }
|
||||
unsigned max_size (level const & l) { lean_assert(is_max(l)); return static_cast<level_max*>(l.m_ptr)->m_size; }
|
||||
level const & max_level (level const & l, unsigned i) { lean_assert(is_max(l)); return static_cast<level_max*>(l.m_ptr)->m_levels[i]; }
|
||||
|
||||
level & level::operator=(level const & l) { LEAN_COPY_REF(level, l); }
|
||||
level & level::operator=(level&& l) { LEAN_MOVE_REF(level, l); }
|
||||
|
@ -81,9 +159,17 @@ level & level::operator=(level&& l) { LEAN_MOVE_REF(level, l); }
|
|||
bool operator==(level const & l1, level const & l2) {
|
||||
if (kind(l1) != kind(l2)) return false;
|
||||
switch (kind(l1)) {
|
||||
case level_kind::UVar: return uvar_idx(l1) == uvar_idx(l2);
|
||||
case level_kind::Lift: return lift_of(l1) == lift_of(l2) && lift_offset(l1) == lift_offset(l2);
|
||||
case level_kind::Max: return max_level1(l1) == max_level1(l2) && max_level2(l1) == max_level2(l2);
|
||||
case level_kind::UVar: return uvar_idx(l1) == uvar_idx(l2);
|
||||
case level_kind::Lift: return lift_of(l1) == lift_of(l2) && lift_offset(l1) == lift_offset(l2);
|
||||
case level_kind::Max:
|
||||
if (max_size(l1) == max_size(l2)) {
|
||||
for (unsigned i = 0; i < max_size(l1); i++)
|
||||
if (max_level(l1, i) != max_level(l2, i))
|
||||
return false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
lean_unreachable();
|
||||
return false;
|
||||
|
@ -91,20 +177,21 @@ bool operator==(level const & l1, level const & l2) {
|
|||
|
||||
std::ostream & operator<<(std::ostream & out, level const & l) {
|
||||
switch (kind(l)) {
|
||||
case level_kind::UVar: out << uvar_name(l); return out;
|
||||
case level_kind::Lift: out << lift_of(l) << "+" << lift_offset(l); return out;
|
||||
case level_kind::Max: out << "(max " << max_level1(l) << " " << max_level2(l) << ")"; return out;
|
||||
case level_kind::UVar: out << uvar_name(l); return out;
|
||||
case level_kind::Lift: out << lift_of(l) << "+" << lift_offset(l); return out;
|
||||
case level_kind::Max:
|
||||
out << "(max";
|
||||
for (unsigned i = 0; i < max_size(l); i++)
|
||||
out << " " << max_level(l, i);
|
||||
out << ")";
|
||||
return out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
level max(std::initializer_list<level> const & l) {
|
||||
lean_assert(l.size() >= 1);
|
||||
auto it = l.begin();
|
||||
level r = *it;
|
||||
++it;
|
||||
for (; it != l.end(); ++it)
|
||||
r = max(r, *it);
|
||||
return r;
|
||||
if (l.size() == 1)
|
||||
return *(l.begin());
|
||||
return max_core(l.size(), l.begin());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,13 +21,14 @@ class level {
|
|||
level_cell * m_ptr;
|
||||
/** \brief Private constructor used by the environment to create a new universe variable named \c n with internal id \c u. */
|
||||
level(name const & n, uvar u);
|
||||
level(level const & l, unsigned k);
|
||||
level(level_cell * ptr);
|
||||
friend level to_level(level_cell * c);
|
||||
friend level_cell * to_cell(level const & l);
|
||||
friend level operator+(level const & l, unsigned k);
|
||||
public:
|
||||
/** \brief Universe 0 */
|
||||
level();
|
||||
/** \brief Lift universe l by k (l + k) */
|
||||
level(level const & l, unsigned k);
|
||||
/** \brief New level that is >= max(l1,l2) */
|
||||
level(level const & l1, level const & l2);
|
||||
level(level const & l);
|
||||
level(level&& s);
|
||||
~level();
|
||||
|
@ -39,8 +40,8 @@ public:
|
|||
friend uvar uvar_idx (level const & l);
|
||||
friend level const & lift_of (level const & l);
|
||||
friend unsigned lift_offset(level const & l);
|
||||
friend level const & max_level1 (level const & l);
|
||||
friend level const & max_level2 (level const & l);
|
||||
friend unsigned max_size (level const & l);
|
||||
friend level const & max_level (level const & l, unsigned i);
|
||||
|
||||
level & operator=(level const & l);
|
||||
level & operator=(level&& l);
|
||||
|
@ -51,10 +52,15 @@ public:
|
|||
|
||||
friend std::ostream & operator<<(std::ostream & out, level const & l);
|
||||
};
|
||||
inline level max(level const & l1, level const & l2) { return level(l1, l2); }
|
||||
level max(std::initializer_list<level> const & l);
|
||||
inline level operator+(level const & l, unsigned k) { return level(l, k); }
|
||||
|
||||
level max(level const & l1, level const & l2);
|
||||
level max(std::initializer_list<level> const & l);
|
||||
level operator+(level const & l, unsigned k);
|
||||
|
||||
inline bool is_uvar(level const & l) { return kind(l) == level_kind::UVar; }
|
||||
inline bool is_lift(level const & l) { return kind(l) == level_kind::Lift; }
|
||||
inline bool is_max (level const & l) { return kind(l) == level_kind::Max; }
|
||||
|
||||
inline level const * max_begin_levels(level const & l) { return &max_level(l, 0); }
|
||||
inline level const * max_end_levels(level const & l) { return max_begin_levels(l) + max_size(l); }
|
||||
}
|
||||
|
|
|
@ -41,10 +41,10 @@ public:
|
|||
unsigned get_var_idx() const { lean_assert(is_bounded_var()); return m_bvar; }
|
||||
};
|
||||
|
||||
value_kind kind(value const & v) { return v.kind(); }
|
||||
expr const & to_expr(value const & v) { return v.get_expr(); }
|
||||
stack const & stack_of(value const & v) { return v.get_ctx(); }
|
||||
unsigned to_bvar(value const & v) { return v.get_var_idx(); }
|
||||
value_kind kind(value const & v) { return v.kind(); }
|
||||
expr const & to_expr(value const & v) { return v.get_expr(); }
|
||||
stack const & stack_of(value const & v) { return v.get_ctx(); }
|
||||
unsigned to_bvar(value const & v) { return v.get_var_idx(); }
|
||||
|
||||
stack extend(stack const & s, value const & v) { return cons(v, s); }
|
||||
|
||||
|
|
|
@ -36,9 +36,9 @@ static void tst2() {
|
|||
static void tst3() {
|
||||
environment env;
|
||||
level l1 = env.define_uvar("l1", level());
|
||||
level l2 = env.define_uvar("l2", level(l1, (1<<30) + 1024));
|
||||
level l2 = env.define_uvar("l2", l1 + ((1<<30) + 1024));
|
||||
try {
|
||||
level l3 = env.define_uvar("l3", level(l2, 1<<30));
|
||||
level l3 = env.define_uvar("l3", l2 + (1<<30));
|
||||
lean_unreachable();
|
||||
}
|
||||
catch (exception ex) {
|
||||
|
@ -74,11 +74,27 @@ static void tst4() {
|
|||
env.display_uvars(std::cout);
|
||||
}
|
||||
|
||||
static void tst5() {
|
||||
environment env;
|
||||
level l1 = env.define_uvar("l1", level() + 1);
|
||||
level l2 = env.define_uvar("l2", level() + 1);
|
||||
std::cout << max(l1, l1) << "\n";
|
||||
lean_assert(max(l1, l1) == l1);
|
||||
lean_assert(max(l1+1, l1+1) == l1+1);
|
||||
std::cout << max(l1, l1+1) << "\n";
|
||||
std::cout << max(l2, max(l1, l1+1)) << "\n";
|
||||
lean_assert(max(l1, l1+1) == l1+1);
|
||||
lean_assert(max(l2, max(l1, l1+1)) == max(l2, l1+1));
|
||||
std::cout << max(l1, max(l2, l1+1)) << "\n";
|
||||
lean_assert(max(l1, max(l2, l1+1)) == max(l1+1, l2));
|
||||
}
|
||||
|
||||
int main() {
|
||||
continue_on_violation(true);
|
||||
// continue_on_violation(true);
|
||||
tst1();
|
||||
tst2();
|
||||
tst3();
|
||||
tst4();
|
||||
tst5();
|
||||
return has_violations() ? 1 : 0;
|
||||
}
|
||||
|
|
|
@ -94,6 +94,11 @@ public:
|
|||
m_pos--;
|
||||
}
|
||||
|
||||
void append(unsigned sz, T const * elems) {
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
push_back(elems[i]);
|
||||
}
|
||||
|
||||
void resize(unsigned nsz, T const & elem=T()) {
|
||||
unsigned sz = size();
|
||||
if (nsz > sz) {
|
||||
|
|
Loading…
Reference in a new issue