1126 lines
42 KiB
C++
1126 lines
42 KiB
C++
/*
|
|
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
Author: Leonardo de Moura
|
|
*/
|
|
#include "util/scoped_map.h"
|
|
#include "util/name_map.h"
|
|
#include "kernel/instantiate.h"
|
|
#include "kernel/abstract.h"
|
|
#include "library/util.h"
|
|
#include "library/trace.h"
|
|
#include "library/constants.h"
|
|
#include "library/cache_helper.h"
|
|
#include "library/app_builder.h"
|
|
#include "library/relation_manager.h"
|
|
|
|
namespace lean {
|
|
static void trace_fun(name const & n) {
|
|
tout() << "failed to create an '" << n << "'-application";
|
|
}
|
|
|
|
static void trace_failure(name const & n, unsigned nargs, char const * msg) {
|
|
lean_trace("app_builder",
|
|
trace_fun(n); tout() << " with " << nargs << ", " << msg << "\n";);
|
|
}
|
|
|
|
static void trace_failure(name const & n, char const * msg) {
|
|
lean_trace("app_builder",
|
|
trace_fun(n); tout() << ", " << msg << "\n";);
|
|
}
|
|
|
|
#define lean_app_builder_trace(code) lean_trace("app_builder", scope_trace_env _scope1(m_ctx.env(), m_ctx); code)
|
|
|
|
static unsigned get_nargs(unsigned mask_sz, bool const * mask) {
|
|
unsigned nargs = 0;
|
|
for (unsigned i = 0; i < mask_sz; i++) {
|
|
if (mask[i])
|
|
nargs++;
|
|
}
|
|
return nargs;
|
|
}
|
|
|
|
class app_builder_cache {
|
|
struct entry {
|
|
unsigned m_num_umeta;
|
|
unsigned m_num_emeta;
|
|
expr m_app;
|
|
list<optional<expr>> m_inst_args; // "mask" of implicit instance arguments
|
|
list<expr> m_expl_args; // metavars for explicit arguments
|
|
/*
|
|
IMPORTANT: for m_inst_args we store the arguments in reverse order.
|
|
For example, the first element in the list indicates whether the last argument
|
|
is an instance implicit argument or not. If it is not none, then the element
|
|
is the associated metavariable
|
|
|
|
m_expl_args are also stored in reverse order
|
|
*/
|
|
};
|
|
|
|
struct key {
|
|
name m_name;
|
|
unsigned m_num_expl;
|
|
unsigned m_hash;
|
|
// If nil, then the mask is composed of the last m_num_expl arguments.
|
|
// If nonnil, then the mask is NOT of the form [false*, true*]
|
|
list<bool> m_mask;
|
|
|
|
key(name const & c, unsigned n):
|
|
m_name(c), m_num_expl(n),
|
|
m_hash(::lean::hash(c.hash(), n)) {
|
|
}
|
|
|
|
key(name const & c, list<bool> const & m):
|
|
m_name(c), m_num_expl(length(m)) {
|
|
m_hash = ::lean::hash(c.hash(), m_num_expl);
|
|
m_mask = m;
|
|
for (bool b : m) {
|
|
if (b)
|
|
m_hash = ::lean::hash(m_hash, 17u);
|
|
else
|
|
m_hash = ::lean::hash(m_hash, 31u);
|
|
}
|
|
}
|
|
|
|
bool check_invariant() const {
|
|
lean_assert(empty(m_mask) || length(m_mask) == m_num_expl);
|
|
return true;
|
|
}
|
|
|
|
unsigned hash() const { return m_hash; }
|
|
friend bool operator==(key const & k1, key const & k2) {
|
|
return k1.m_name == k2.m_name && k1.m_num_expl == k2.m_num_expl && k1.m_mask == k2.m_mask;
|
|
}
|
|
};
|
|
|
|
struct key_hash_fn {
|
|
unsigned operator()(key const & k) const { return k.hash(); }
|
|
};
|
|
|
|
typedef std::unordered_map<key, entry, key_hash_fn> map;
|
|
|
|
environment m_env;
|
|
map m_map;
|
|
relation_info_getter m_rel_getter;
|
|
refl_info_getter m_refl_getter;
|
|
symm_info_getter m_symm_getter;
|
|
trans_info_getter m_trans_getter;
|
|
friend class app_builder;
|
|
public:
|
|
app_builder_cache(environment const & env):
|
|
m_env(env),
|
|
m_rel_getter(mk_relation_info_getter(env)),
|
|
m_refl_getter(mk_refl_info_getter(env)),
|
|
m_symm_getter(mk_symm_info_getter(env)),
|
|
m_trans_getter(mk_trans_info_getter(env)) {
|
|
}
|
|
|
|
environment const & env() const { return m_env; }
|
|
};
|
|
|
|
typedef cache_compatibility_helper<app_builder_cache> app_builder_cache_helper;
|
|
|
|
MK_THREAD_LOCAL_GET_DEF(app_builder_cache_helper, get_abch);
|
|
|
|
/** Return an app_builder_cache for the transparency_mode in ctx, and compatible with the environment. */
|
|
app_builder_cache & get_app_builder_cache_for(type_context const & ctx) {
|
|
return get_abch().get_cache_for(ctx);
|
|
}
|
|
|
|
/** \brief Helper for creating simple applications where some arguments are inferred using
|
|
type inference.
|
|
|
|
Example, given
|
|
rel.{l_1 l_2} : Pi (A : Type.{l_1}) (B : A -> Type.{l_2}), (Pi x : A, B x) -> (Pi x : A, B x) -> , Prop
|
|
nat : Type.{1}
|
|
real : Type.{1}
|
|
vec.{l} : Pi (A : Type.{l}) (n : nat), Type.{l1}
|
|
f g : Pi (n : nat), vec real n
|
|
then
|
|
builder.mk_app(rel, f, g)
|
|
returns the application
|
|
rel.{1 2} nat (fun n : nat, vec real n) f g
|
|
*/
|
|
class app_builder {
|
|
type_context & m_ctx;
|
|
app_builder_cache & m_cache;
|
|
typedef app_builder_cache::key key;
|
|
typedef app_builder_cache::entry entry;
|
|
|
|
levels mk_metavars(declaration const & d, buffer<expr> & mvars, buffer<optional<expr>> & inst_args) {
|
|
unsigned num_univ = d.get_num_univ_params();
|
|
buffer<level> lvls_buffer;
|
|
for (unsigned i = 0; i < num_univ; i++) {
|
|
lvls_buffer.push_back(m_ctx.mk_tmp_univ_mvar());
|
|
}
|
|
levels lvls = to_list(lvls_buffer);
|
|
expr type = m_ctx.relaxed_whnf(instantiate_type_univ_params(d, lvls));
|
|
while (is_pi(type)) {
|
|
expr mvar = m_ctx.mk_tmp_mvar(binding_domain(type));
|
|
if (binding_info(type).is_inst_implicit())
|
|
inst_args.push_back(some_expr(mvar));
|
|
else
|
|
inst_args.push_back(none_expr());
|
|
mvars.push_back(mvar);
|
|
type = m_ctx.relaxed_whnf(instantiate(binding_body(type), mvar));
|
|
}
|
|
return lvls;
|
|
}
|
|
|
|
optional<entry> get_entry(name const & c, unsigned nargs) {
|
|
key k(c, nargs);
|
|
lean_assert(k.check_invariant());
|
|
auto it = m_cache.m_map.find(k);
|
|
if (it == m_cache.m_map.end()) {
|
|
if (auto d = m_ctx.env().find(c)) {
|
|
buffer<expr> mvars;
|
|
buffer<optional<expr>> inst_args;
|
|
levels lvls = mk_metavars(*d, mvars, inst_args);
|
|
if (nargs > mvars.size())
|
|
return optional<entry>(); // insufficient number of arguments
|
|
entry e;
|
|
e.m_num_umeta = d->get_num_univ_params();
|
|
e.m_num_emeta = mvars.size();
|
|
e.m_app = ::lean::mk_app(mk_constant(c, lvls), mvars);
|
|
e.m_inst_args = reverse_to_list(inst_args.begin(), inst_args.end());
|
|
e.m_expl_args = reverse_to_list(mvars.begin() + mvars.size() - nargs, mvars.end());
|
|
m_cache.m_map.insert(mk_pair(k, e));
|
|
return optional<entry>(e);
|
|
} else {
|
|
return optional<entry>(); // unknown decl
|
|
}
|
|
} else {
|
|
return optional<entry>(it->second);
|
|
}
|
|
}
|
|
|
|
levels mk_metavars(declaration const & d, unsigned arity, buffer<expr> & mvars, buffer<optional<expr>> & inst_args) {
|
|
unsigned num_univ = d.get_num_univ_params();
|
|
buffer<level> lvls_buffer;
|
|
for (unsigned i = 0; i < num_univ; i++) {
|
|
lvls_buffer.push_back(m_ctx.mk_tmp_univ_mvar());
|
|
}
|
|
levels lvls = to_list(lvls_buffer);
|
|
expr type = instantiate_type_univ_params(d, lvls);
|
|
for (unsigned i = 0; i < arity; i++) {
|
|
type = m_ctx.relaxed_whnf(type);
|
|
if (!is_pi(type)) {
|
|
trace_failure(d.get_name(), arity, "too many arguments");
|
|
throw app_builder_exception();
|
|
}
|
|
expr mvar = m_ctx.mk_tmp_mvar(binding_domain(type));
|
|
if (binding_info(type).is_inst_implicit())
|
|
inst_args.push_back(some_expr(mvar));
|
|
else
|
|
inst_args.push_back(none_expr());
|
|
mvars.push_back(mvar);
|
|
type = instantiate(binding_body(type), mvar);
|
|
}
|
|
return lvls;
|
|
}
|
|
|
|
optional<entry> get_entry(name const & c, unsigned mask_sz, bool const * mask) {
|
|
key k(c, to_list(mask, mask+mask_sz));
|
|
lean_assert(k.check_invariant());
|
|
auto it = m_cache.m_map.find(k);
|
|
if (it == m_cache.m_map.end()) {
|
|
if (auto d = m_ctx.env().find(c)) {
|
|
buffer<expr> mvars;
|
|
buffer<optional<expr>> inst_args;
|
|
levels lvls = mk_metavars(*d, mask_sz, mvars, inst_args);
|
|
entry e;
|
|
e.m_num_umeta = d->get_num_univ_params();
|
|
e.m_num_emeta = mvars.size();
|
|
e.m_app = ::lean::mk_app(mk_constant(c, lvls), mvars);
|
|
e.m_inst_args = reverse_to_list(inst_args.begin(), inst_args.end());
|
|
list<expr> expl_args;
|
|
for (unsigned i = 0; i < mask_sz; i++) {
|
|
if (mask[i])
|
|
expl_args = cons(mvars[i], expl_args);
|
|
}
|
|
e.m_expl_args = expl_args;
|
|
m_cache.m_map.insert(mk_pair(k, e));
|
|
return optional<entry>(e);
|
|
} else {
|
|
return optional<entry>(); // unknown decl
|
|
}
|
|
} else {
|
|
return optional<entry>(it->second);
|
|
}
|
|
}
|
|
|
|
bool check_all_assigned(entry const & e) {
|
|
lean_assert(e.m_num_emeta == length(e.m_inst_args));
|
|
// recall that the flags at e.m_inst_args are stored in reverse order.
|
|
// For example, the first flag in the list indicates whether the last argument
|
|
// is an instance implicit argument or not.
|
|
unsigned i = e.m_num_emeta;
|
|
for (optional<expr> const & inst_arg : e.m_inst_args) {
|
|
lean_assert(i > 0);
|
|
--i;
|
|
if (!m_ctx.get_tmp_mvar_assignment(i)) {
|
|
if (inst_arg) {
|
|
expr type = m_ctx.instantiate_mvars(mlocal_type(*inst_arg));
|
|
if (auto v = m_ctx.mk_class_instance(type)) {
|
|
if (!m_ctx.is_def_eq(*inst_arg, *v)) {
|
|
// failed to assign instance
|
|
return false;
|
|
}
|
|
} else {
|
|
// failed to generate instance
|
|
return false;
|
|
}
|
|
} else {
|
|
// unassined metavar
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
for (unsigned i = 0; i < e.m_num_umeta; i++) {
|
|
if (!m_ctx.get_tmp_uvar_assignment(i))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void init_ctx_for(entry const & e) {
|
|
m_ctx.ensure_num_tmp_mvars(e.m_num_umeta, e.m_num_emeta);
|
|
}
|
|
|
|
void trace_unify_failure(name const & n, unsigned i, expr const & m, expr const & v) {
|
|
lean_app_builder_trace(
|
|
trace_fun(n);
|
|
tout () << ", failed to solve unification constraint for #" << (i+1)
|
|
<< " argument (" << m_ctx.instantiate_mvars(m_ctx.infer(m)) << " =?= "
|
|
<< m_ctx.instantiate_mvars(m_ctx.infer(v)) << ")\n";);
|
|
}
|
|
|
|
void trace_inst_failure(expr const & A, char const * n) {
|
|
lean_app_builder_trace(tout() << "failed to build instance of '" << n << "' for " << A << "\n";);
|
|
}
|
|
|
|
public:
|
|
app_builder(type_context & ctx):m_ctx(ctx), m_cache(get_app_builder_cache_for(ctx)) {}
|
|
|
|
level get_level(expr const & A) {
|
|
expr Type = m_ctx.relaxed_whnf(m_ctx.infer(A));
|
|
if (!is_sort(Type)) {
|
|
lean_app_builder_trace(tout() << "failed to infer universe level for type " << A << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
return sort_level(Type);
|
|
}
|
|
|
|
expr mk_app(name const & c, unsigned nargs, expr const * args) {
|
|
type_context::tmp_mode_scope scope(m_ctx);
|
|
optional<entry> e = get_entry(c, nargs);
|
|
if (!e) {
|
|
trace_failure(c, "failed to retrieve declaration");
|
|
throw app_builder_exception();
|
|
}
|
|
init_ctx_for(*e);
|
|
unsigned i = nargs;
|
|
for (auto m : e->m_expl_args) {
|
|
if (i == 0) {
|
|
trace_failure(c, "too many explicit arguments");
|
|
throw app_builder_exception();
|
|
}
|
|
--i;
|
|
if (!m_ctx.is_def_eq(m, args[i])) {
|
|
trace_unify_failure(c, i, m, args[i]);
|
|
throw app_builder_exception();
|
|
}
|
|
}
|
|
if (!check_all_assigned(*e)) {
|
|
trace_failure(c, "there are missing implicit arguments");
|
|
throw app_builder_exception();
|
|
}
|
|
return m_ctx.instantiate_mvars(e->m_app);
|
|
}
|
|
|
|
expr mk_app(name const & c, std::initializer_list<expr> const & args) {
|
|
return mk_app(c, args.size(), args.begin());
|
|
}
|
|
|
|
expr mk_app(name const & c, expr const & a1) {
|
|
return mk_app(c, {a1});
|
|
}
|
|
|
|
expr mk_app(name const & c, expr const & a1, expr const & a2) {
|
|
return mk_app(c, {a1, a2});
|
|
}
|
|
|
|
expr mk_app(name const & c, expr const & a1, expr const & a2, expr const & a3) {
|
|
return mk_app(c, {a1, a2, a3});
|
|
}
|
|
|
|
expr mk_app(name const & c, unsigned mask_sz, bool const * mask, expr const * args) {
|
|
type_context::tmp_mode_scope scope(m_ctx);
|
|
unsigned nargs = get_nargs(mask_sz, mask);
|
|
optional<entry> e = get_entry(c, mask_sz, mask);
|
|
if (!e) {
|
|
trace_failure(c, "failed to retrieve declaration");
|
|
throw app_builder_exception();
|
|
}
|
|
init_ctx_for(*e);
|
|
unsigned i = mask_sz;
|
|
unsigned j = nargs;
|
|
list<expr> it = e->m_expl_args;
|
|
while (i > 0) {
|
|
--i;
|
|
if (mask[i]) {
|
|
--j;
|
|
expr const & m = head(it);
|
|
if (!m_ctx.is_def_eq(m, args[j])) {
|
|
trace_unify_failure(c, j, m, args[j]);
|
|
throw app_builder_exception();
|
|
}
|
|
it = tail(it);
|
|
}
|
|
}
|
|
if (!check_all_assigned(*e)) {
|
|
trace_failure(c, "there are missing implicit arguments");
|
|
throw app_builder_exception();
|
|
}
|
|
return m_ctx.instantiate_mvars(e->m_app);
|
|
}
|
|
|
|
/** \brief Shortcut for mk_app(c, total_nargs, mask, expl_nargs), where
|
|
\c mask starts with total_nargs - expl_nargs false's followed by expl_nargs true's
|
|
\pre total_nargs >= expl_nargs */
|
|
expr mk_app(name const & c, unsigned total_nargs, unsigned expl_nargs, expr const * expl_args) {
|
|
lean_assert(total_nargs >= expl_nargs);
|
|
buffer<bool> mask;
|
|
mask.resize(total_nargs - expl_nargs, false);
|
|
mask.resize(total_nargs, true);
|
|
return mk_app(c, mask.size(), mask.data(), expl_args);
|
|
}
|
|
|
|
expr mk_app(name const & c, unsigned total_nargs, std::initializer_list<expr> const & args) {
|
|
return mk_app(c, total_nargs, args.size(), args.begin());
|
|
}
|
|
|
|
expr mk_app(name const & c, unsigned total_nargs, expr const & a1) {
|
|
return mk_app(c, total_nargs, {a1});
|
|
}
|
|
|
|
expr mk_app(name const & c, unsigned total_nargs, expr const & a1, expr const & a2) {
|
|
return mk_app(c, total_nargs, {a1, a2});
|
|
}
|
|
|
|
expr mk_app(name const & c, unsigned total_nargs, expr const & a1, expr const & a2, expr const & a3) {
|
|
return mk_app(c, total_nargs, {a1, a2, a3});
|
|
}
|
|
|
|
/** \brief Similar to mk_app(n, lhs, rhs), but handles eq and iff more efficiently. */
|
|
expr mk_rel(name const & n, expr const & lhs, expr const & rhs) {
|
|
if (n == get_eq_name()) {
|
|
return mk_eq(lhs, rhs);
|
|
} else if (n == get_iff_name()) {
|
|
return mk_iff(lhs, rhs);
|
|
} else if (auto info = m_cache.m_rel_getter(n)) {
|
|
buffer<bool> mask;
|
|
for (unsigned i = 0; i < info->get_arity(); i++) {
|
|
mask.push_back(i == info->get_lhs_pos() || i == info->get_rhs_pos());
|
|
}
|
|
expr args[2] = {lhs, rhs};
|
|
return mk_app(n, info->get_arity(), mask.data(), args);
|
|
} else {
|
|
// for unregistered relations assume lhs and rhs are the last two arguments.
|
|
expr args[2] = {lhs, rhs};
|
|
return mk_app(n, 2, args);
|
|
}
|
|
}
|
|
|
|
expr mk_eq(expr const & a, expr const & b) {
|
|
expr A = m_ctx.infer(a);
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app(mk_constant(get_eq_name(), {lvl}), A, a, b);
|
|
}
|
|
|
|
expr mk_iff(expr const & a, expr const & b) {
|
|
return ::lean::mk_app(mk_constant(get_iff_name()), a, b);
|
|
}
|
|
|
|
expr mk_heq(expr const & a, expr const & b) {
|
|
expr A = m_ctx.infer(a);
|
|
expr B = m_ctx.infer(b);
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app(mk_constant(get_heq_name(), {lvl}), A, a, B, b);
|
|
}
|
|
|
|
/** \brief Similar a reflexivity proof for the given relation */
|
|
expr mk_refl(name const & relname, expr const & a) {
|
|
if (relname == get_eq_name()) {
|
|
return mk_eq_refl(a);
|
|
} else if (relname == get_iff_name()) {
|
|
return mk_iff_refl(a);
|
|
} else if (relname == get_heq_name()) {
|
|
return mk_heq_refl(a);
|
|
} else if (auto info = m_cache.m_refl_getter(relname)) {
|
|
return mk_app(info->m_name, 1, &a);
|
|
} else {
|
|
lean_app_builder_trace(
|
|
tout() << "failed to build reflexivity proof, '" << relname
|
|
<< "' is not registered as a reflexive relation\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
}
|
|
expr mk_eq_refl(expr const & a) {
|
|
expr A = m_ctx.infer(a);
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app(mk_constant(get_eq_refl_name(), {lvl}), A, a);
|
|
}
|
|
expr mk_iff_refl(expr const & a) {
|
|
return ::lean::mk_app(mk_constant(get_iff_refl_name()), a);
|
|
}
|
|
expr mk_heq_refl(expr const & a) {
|
|
expr A = m_ctx.infer(a);
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app(mk_constant(get_heq_refl_name(), {lvl}), A, a);
|
|
}
|
|
|
|
/** \brief Similar a symmetry proof for the given relation */
|
|
expr mk_symm(name const & relname, expr const & H) {
|
|
if (relname == get_eq_name()) {
|
|
return mk_eq_symm(H);
|
|
} else if (relname == get_iff_name()) {
|
|
return mk_iff_symm(H);
|
|
} else if (relname == get_heq_name()) {
|
|
return mk_heq_symm(H);
|
|
} else if (auto info = m_cache.m_symm_getter(relname)) {
|
|
return mk_app(info->m_name, 1, &H);
|
|
} else {
|
|
lean_app_builder_trace(
|
|
tout() << "failed to build symmetry proof, '" << relname
|
|
<< "' is not registered as a symmetric relation\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
}
|
|
expr mk_eq_symm(expr const & H) {
|
|
if (is_app_of(H, get_eq_refl_name()))
|
|
return H;
|
|
expr p = m_ctx.relaxed_whnf(m_ctx.infer(H));
|
|
expr A, lhs, rhs;
|
|
if (!is_eq(p, A, lhs, rhs)) {
|
|
lean_app_builder_trace(tout() << "failed to build eq.symm, equality expected:\n" << p << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app(mk_constant(get_eq_symm_name(), {lvl}), A, lhs, rhs, H);
|
|
}
|
|
expr mk_eq_symm(expr const & a, expr const & b, expr const & H) {
|
|
expr A = m_ctx.infer(a);
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app(mk_constant(get_eq_symm_name(), {lvl}), A, a, b, H);
|
|
}
|
|
expr mk_iff_symm(expr const & H) {
|
|
expr p = m_ctx.infer(H);
|
|
expr lhs, rhs;
|
|
if (is_iff(p, lhs, rhs)) {
|
|
return ::lean::mk_app(mk_constant(get_iff_symm_name()), lhs, rhs, H);
|
|
} else {
|
|
return mk_app(get_iff_symm_name(), {H});
|
|
}
|
|
}
|
|
expr mk_heq_symm(expr const & H) {
|
|
expr p = m_ctx.relaxed_whnf(m_ctx.infer(H));
|
|
expr A, a, B, b;
|
|
if (!is_heq(p, A, a, B, b)) {
|
|
lean_app_builder_trace(tout() << "failed to build heq.symm, heterogeneous equality expected:\n" << p << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app({mk_constant(get_heq_symm_name(), {lvl}), A, B, a, b, H});
|
|
}
|
|
|
|
/** \brief Similar a transitivity proof for the given relation */
|
|
expr mk_trans(name const & relname, expr const & H1, expr const & H2) {
|
|
if (relname == get_eq_name()) {
|
|
return mk_eq_trans(H1, H2);
|
|
} else if (relname == get_iff_name()) {
|
|
return mk_iff_trans(H1, H2);
|
|
} else if (relname == get_heq_name()) {
|
|
return mk_heq_trans(H1, H2);
|
|
} else if (auto info = m_cache.m_trans_getter(relname, relname)) {
|
|
expr args[2] = {H1, H2};
|
|
return mk_app(info->m_name, 2, args);
|
|
} else {
|
|
lean_app_builder_trace(
|
|
tout() << "failed to build symmetry proof, '" << relname
|
|
<< "' is not registered as a transitive relation\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
}
|
|
expr mk_eq_trans(expr const & H1, expr const & H2) {
|
|
if (is_app_of(H1, get_eq_refl_name()))
|
|
return H2;
|
|
if (is_app_of(H2, get_eq_refl_name()))
|
|
return H1;
|
|
expr p1 = m_ctx.relaxed_whnf(m_ctx.infer(H1));
|
|
expr p2 = m_ctx.relaxed_whnf(m_ctx.infer(H2));
|
|
expr A, lhs1, rhs1, lhs2, rhs2;
|
|
if (!is_eq(p1, A, lhs1, rhs1) || !is_eq(p2, lhs2, rhs2)) {
|
|
lean_app_builder_trace(
|
|
tout() << "failed to build eq.trans, equality expected:\n"
|
|
<< p1 << "\n" << p2 << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app({mk_constant(get_eq_trans_name(), {lvl}), A, lhs1, rhs1, rhs2, H1, H2});
|
|
}
|
|
expr mk_eq_trans(expr const & a, expr const & b, expr const & c, expr const & H1, expr const & H2) {
|
|
if (is_app_of(H1, get_eq_refl_name()))
|
|
return H2;
|
|
if (is_app_of(H2, get_eq_refl_name()))
|
|
return H1;
|
|
expr A = m_ctx.infer(a);
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app({mk_constant(get_eq_trans_name(), {lvl}), A, a, b, c, H1, H2});
|
|
}
|
|
expr mk_iff_trans(expr const & H1, expr const & H2) {
|
|
expr p1 = m_ctx.infer(H1);
|
|
expr p2 = m_ctx.infer(H2);
|
|
expr lhs1, rhs1, lhs2, rhs2;
|
|
if (is_iff(p1, lhs1, rhs1) && is_iff(p2, lhs2, rhs2)) {
|
|
return ::lean::mk_app({mk_constant(get_iff_trans_name()), lhs1, rhs1, rhs2, H1, H2});
|
|
} else {
|
|
return mk_app(get_iff_trans_name(), {H1, H2});
|
|
}
|
|
}
|
|
expr mk_heq_trans(expr const & H1, expr const & H2) {
|
|
expr p1 = m_ctx.relaxed_whnf(m_ctx.infer(H1));
|
|
expr p2 = m_ctx.relaxed_whnf(m_ctx.infer(H2));
|
|
expr A1, a1, B1, b1, A2, a2, B2, b2;
|
|
if (!is_heq(p1, A1, a1, B1, b1) || !is_heq(p2, A2, a2, B2, b2)) {
|
|
lean_app_builder_trace(
|
|
tout() << "failed to build heq.trans, heterogeneous equality expected:\n"
|
|
<< p1 << "\n" << p2 << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level lvl = get_level(A1);
|
|
return ::lean::mk_app({mk_constant(get_heq_trans_name(), {lvl}), A1, B1, B2, a1, b1, b2, H1, H2});
|
|
}
|
|
|
|
expr mk_eq_rec(expr const & motive, expr const & H1, expr const & H2) {
|
|
if (is_constant(get_app_fn(H2), get_eq_refl_name()))
|
|
return H1;
|
|
expr p = m_ctx.relaxed_whnf(m_ctx.infer(H2));
|
|
expr A, lhs, rhs;
|
|
if (!is_eq(p, A, lhs, rhs)) {
|
|
lean_app_builder_trace(tout() << "failed to build eq.rec, equality proof expected:\n" << H2 << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level A_lvl = get_level(A);
|
|
expr mtype = m_ctx.relaxed_whnf(m_ctx.infer(motive));
|
|
if (!is_pi(mtype) || !is_sort(binding_body(mtype))) {
|
|
lean_app_builder_trace(tout() << "failed to build eq.rec, invalid motive:\n" << motive << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level l_1 = sort_level(binding_body(mtype));
|
|
name const & eqrec = get_eq_rec_name();
|
|
return ::lean::mk_app({mk_constant(eqrec, {l_1, A_lvl}), A, lhs, motive, H1, rhs, H2});
|
|
}
|
|
|
|
expr mk_eq_drec(expr const & motive, expr const & H1, expr const & H2) {
|
|
if (is_constant(get_app_fn(H2), get_eq_refl_name()))
|
|
return H1;
|
|
expr p = m_ctx.relaxed_whnf(m_ctx.infer(H2));
|
|
expr A, lhs, rhs;
|
|
if (!is_eq(p, A, lhs, rhs)) {
|
|
lean_app_builder_trace(tout() << "failed to build eq.drec, equality proof expected:\n" << H2 << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level A_lvl = get_level(A);
|
|
expr mtype = m_ctx.relaxed_whnf(m_ctx.infer(motive));
|
|
if (!is_pi(mtype) || !is_pi(binding_body(mtype)) || !is_sort(binding_body(binding_body(mtype)))) {
|
|
lean_app_builder_trace(tout() << "failed to build eq.drec, invalid motive:\n" << motive << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level l_1 = sort_level(binding_body(binding_body(mtype)));
|
|
name const & eqrec = get_eq_drec_name();
|
|
return ::lean::mk_app({mk_constant(eqrec, {l_1, A_lvl}), A, lhs, motive, H1, rhs, H2});
|
|
}
|
|
|
|
expr mk_eq_of_heq(expr const & H) {
|
|
if (is_constant(get_app_fn(H), get_heq_of_eq_name()))
|
|
return app_arg(H);
|
|
expr p = m_ctx.relaxed_whnf(m_ctx.infer(H));
|
|
expr A, a, B, b;
|
|
if (!is_heq(p, A, a, B, b)) {
|
|
lean_app_builder_trace(tout() << "failed to build eq_of_heq, heterogeneous equality proof expected:\n" << H << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app({mk_constant(get_eq_of_heq_name(), {lvl}), A, a, b, H});
|
|
}
|
|
|
|
expr mk_heq_of_eq(expr const & H) {
|
|
if (is_constant(get_app_fn(H), get_eq_of_heq_name()))
|
|
return app_arg(H);
|
|
expr p = m_ctx.relaxed_whnf(m_ctx.infer(H));
|
|
expr A, a, b;
|
|
if (!is_eq(p, A, a, b)) {
|
|
lean_app_builder_trace(tout() << "failed to build heq_of_eq equality proof expected:\n" << H << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app({mk_constant(get_heq_of_eq_name(), {lvl}), A, a, b, H});
|
|
}
|
|
|
|
/** \brief Given a reflexive relation R, and a proof H : a = b,
|
|
build a proof for (R a b) */
|
|
expr lift_from_eq(name const & R, expr const & H) {
|
|
if (R == get_eq_name())
|
|
return H;
|
|
expr H_type = m_ctx.relaxed_whnf(m_ctx.infer(H));
|
|
// H_type : @eq A a b
|
|
expr const & a = app_arg(app_fn(H_type));
|
|
expr const & A = app_arg(app_fn(app_fn(H_type)));
|
|
type_context::tmp_locals locals(m_ctx);
|
|
expr x = locals.push_local(name("A"), A);
|
|
// motive := fun x : A, a ~ x
|
|
expr motive = locals.mk_lambda(mk_rel(R, a, x));
|
|
// minor : a ~ a
|
|
expr minor = mk_refl(R, a);
|
|
return mk_eq_rec(motive, minor, H);
|
|
}
|
|
|
|
expr mk_partial_add(expr const & A) {
|
|
level lvl = get_level(A);
|
|
auto A_has_add = m_ctx.mk_class_instance(::lean::mk_app(mk_constant(get_has_add_name(), {lvl}), A));
|
|
if (!A_has_add) {
|
|
trace_inst_failure(A, "has_add");
|
|
throw app_builder_exception();
|
|
}
|
|
return ::lean::mk_app(mk_constant(get_add_name(), {lvl}), A, *A_has_add);
|
|
}
|
|
|
|
expr mk_partial_mul(expr const & A) {
|
|
level lvl = get_level(A);
|
|
auto A_has_mul = m_ctx.mk_class_instance(::lean::mk_app(mk_constant(get_has_mul_name(), {lvl}), A));
|
|
if (!A_has_mul) {
|
|
trace_inst_failure(A, "has_mul");
|
|
throw app_builder_exception();
|
|
}
|
|
return ::lean::mk_app(mk_constant(get_mul_name(), {lvl}), A, *A_has_mul);
|
|
}
|
|
|
|
expr mk_zero(expr const & A) {
|
|
level lvl = get_level(A);
|
|
auto A_has_zero = m_ctx.mk_class_instance(::lean::mk_app(mk_constant(get_has_zero_name(), {lvl}), A));
|
|
if (!A_has_zero) {
|
|
trace_inst_failure(A, "has_zero");
|
|
throw app_builder_exception();
|
|
}
|
|
return ::lean::mk_app(mk_constant(get_zero_name(), {lvl}), A, *A_has_zero);
|
|
}
|
|
|
|
expr mk_one(expr const & A) {
|
|
level lvl = get_level(A);
|
|
auto A_has_one = m_ctx.mk_class_instance(::lean::mk_app(mk_constant(get_has_one_name(), {lvl}), A));
|
|
if (!A_has_one) {
|
|
trace_inst_failure(A, "has_one");
|
|
throw app_builder_exception();
|
|
}
|
|
return ::lean::mk_app(mk_constant(get_one_name(), {lvl}), A, *A_has_one);
|
|
}
|
|
|
|
expr mk_partial_left_distrib(expr const & A) {
|
|
level lvl = get_level(A);
|
|
auto A_distrib = m_ctx.mk_class_instance(::lean::mk_app(mk_constant(get_distrib_name(), {lvl}), A));
|
|
if (!A_distrib) {
|
|
trace_inst_failure(A, "distrib");
|
|
throw app_builder_exception();
|
|
}
|
|
return ::lean::mk_app(mk_constant(get_left_distrib_name(), {lvl}), A, *A_distrib);
|
|
}
|
|
|
|
expr mk_partial_right_distrib(expr const & A) {
|
|
level lvl = get_level(A);
|
|
auto A_distrib = m_ctx.mk_class_instance(::lean::mk_app(mk_constant(get_distrib_name(), {lvl}), A));
|
|
if (!A_distrib) {
|
|
trace_inst_failure(A, "distrib");
|
|
throw app_builder_exception();
|
|
}
|
|
return ::lean::mk_app(mk_constant(get_right_distrib_name(), {lvl}), A, *A_distrib);
|
|
}
|
|
|
|
expr mk_ss_elim(expr const & A, expr const & ss_inst, expr const & old_e, expr const & new_e) {
|
|
level lvl = get_level(A);
|
|
return ::lean::mk_app(mk_constant(get_subsingleton_elim_name(), {lvl}), A, ss_inst, old_e, new_e);
|
|
}
|
|
|
|
expr mk_false_rec(expr const & c, expr const & H) {
|
|
level c_lvl = get_level(c);
|
|
return ::lean::mk_app(mk_constant(get_false_rec_name(), {c_lvl}), c, H);
|
|
}
|
|
|
|
expr mk_congr_arg(expr const & f, expr const & H) {
|
|
expr eq = m_ctx.relaxed_whnf(m_ctx.infer(H));
|
|
expr pi = m_ctx.relaxed_whnf(m_ctx.infer(f));
|
|
expr A, B, lhs, rhs;
|
|
if (!is_eq(eq, A, lhs, rhs)) {
|
|
lean_app_builder_trace(tout() << "failed to build congr_arg, equality expected:\n" << eq << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
if (!is_arrow(pi)) {
|
|
lean_app_builder_trace(tout() << "failed to build congr_arg, non-dependent function expected:\n" << pi << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
B = binding_body(pi);
|
|
level lvl_1 = get_level(A);
|
|
level lvl_2 = get_level(B);
|
|
return ::lean::mk_app({mk_constant(get_congr_arg_name(), {lvl_1, lvl_2}), A, B, lhs, rhs, f, H});
|
|
}
|
|
|
|
expr mk_eq_true_intro(expr const & H) {
|
|
expr p = m_ctx.infer(H);
|
|
return ::lean::mk_app(mk_constant(get_eq_true_intro_name()), p, H);
|
|
}
|
|
|
|
expr mk_eq_false_intro(expr const & H) {
|
|
expr not_p = m_ctx.relaxed_whnf(m_ctx.infer(H));
|
|
if (!is_pi(not_p)) {
|
|
lean_app_builder_trace(tout() << "failed to build eq_false_intro, negation expected:\n" << not_p << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
return ::lean::mk_app(mk_constant(get_eq_false_intro_name()), binding_domain(not_p), H);
|
|
}
|
|
|
|
expr mk_of_eq_true(expr const & H) {
|
|
if (is_constant(get_app_fn(H), get_eq_true_intro_name())) {
|
|
// of_eq_true (eq_true_intro H) == H
|
|
return app_arg(H);
|
|
}
|
|
expr eq = m_ctx.relaxed_whnf(m_ctx.infer(H));
|
|
expr lhs, rhs;
|
|
if (!is_eq(eq, lhs, rhs)) {
|
|
lean_app_builder_trace(tout() << "failed to build of_eq_true, equality expected:\n" << eq << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
return ::lean::mk_app(mk_constant(get_of_eq_true_name()), lhs, H);
|
|
}
|
|
|
|
expr mk_not_of_eq_false(expr const & H) {
|
|
if (is_constant(get_app_fn(H), get_eq_false_intro_name())) {
|
|
// not_of_eq_false (eq_false_intro H) == H
|
|
return app_arg(H);
|
|
}
|
|
expr eq = m_ctx.relaxed_whnf(m_ctx.infer(H));
|
|
expr lhs, rhs;
|
|
if (!is_eq(eq, lhs, rhs)) {
|
|
lean_app_builder_trace(tout() << "failed to build not_of_eq_false, equality expected:\n" << eq << "\n";);
|
|
throw app_builder_exception();
|
|
}
|
|
return ::lean::mk_app(mk_constant(get_not_of_eq_false_name()), lhs, H);
|
|
}
|
|
};
|
|
|
|
level get_level(type_context & ctx, expr const & A) {
|
|
return app_builder(ctx).get_level(A);
|
|
}
|
|
|
|
expr mk_app(type_context & ctx, name const & c, unsigned nargs, expr const * args, optional<transparency_mode> const & md) {
|
|
if (md) {
|
|
type_context::transparency_scope _s(ctx, *md);
|
|
return app_builder(ctx).mk_app(c, nargs, args);
|
|
} else if (ctx.mode() == transparency_mode::Reducible || ctx.mode() == transparency_mode::None) {
|
|
type_context::transparency_scope _s(ctx, transparency_mode::Semireducible);
|
|
return app_builder(ctx).mk_app(c, nargs, args);
|
|
} else {
|
|
return app_builder(ctx).mk_app(c, nargs, args);
|
|
}
|
|
}
|
|
|
|
expr mk_app(type_context & ctx, name const & c, unsigned mask_sz, bool const * mask, expr const * args) {
|
|
return app_builder(ctx).mk_app(c, mask_sz, mask, args);
|
|
}
|
|
|
|
expr mk_app(type_context & ctx, name const & c, unsigned total_nargs, unsigned expl_nargs, expr const * expl_args) {
|
|
return app_builder(ctx).mk_app(c, total_nargs, expl_nargs, expl_args);
|
|
}
|
|
|
|
expr mk_rel(type_context & ctx, name const & n, expr const & lhs, expr const & rhs) {
|
|
return app_builder(ctx).mk_rel(n, lhs, rhs);
|
|
}
|
|
|
|
expr mk_eq(type_context & ctx, expr const & lhs, expr const & rhs) {
|
|
return app_builder(ctx).mk_eq(lhs, rhs);
|
|
}
|
|
|
|
expr mk_iff(type_context & ctx, expr const & lhs, expr const & rhs) {
|
|
return app_builder(ctx).mk_iff(lhs, rhs);
|
|
}
|
|
|
|
expr mk_heq(type_context & ctx, expr const & lhs, expr const & rhs) {
|
|
return app_builder(ctx).mk_heq(lhs, rhs);
|
|
}
|
|
|
|
expr mk_refl(type_context & ctx, name const & relname, expr const & a) {
|
|
return app_builder(ctx).mk_refl(relname, a);
|
|
}
|
|
|
|
expr mk_eq_refl(type_context & ctx, expr const & a) {
|
|
return app_builder(ctx).mk_eq_refl(a);
|
|
}
|
|
|
|
expr mk_iff_refl(type_context & ctx, expr const & a) {
|
|
return app_builder(ctx).mk_iff_refl(a);
|
|
}
|
|
|
|
expr mk_heq_refl(type_context & ctx, expr const & a) {
|
|
return app_builder(ctx).mk_heq_refl(a);
|
|
}
|
|
|
|
expr mk_symm(type_context & ctx, name const & relname, expr const & H) {
|
|
return app_builder(ctx).mk_symm(relname, H);
|
|
}
|
|
|
|
expr mk_eq_symm(type_context & ctx, expr const & H) {
|
|
return app_builder(ctx).mk_eq_symm(H);
|
|
}
|
|
|
|
expr mk_eq_symm(type_context & ctx, expr const & a, expr const & b, expr const & H) {
|
|
return app_builder(ctx).mk_eq_symm(a, b, H);
|
|
}
|
|
|
|
expr mk_iff_symm(type_context & ctx, expr const & H) {
|
|
return app_builder(ctx).mk_iff_symm(H);
|
|
}
|
|
|
|
expr mk_heq_symm(type_context & ctx, expr const & H) {
|
|
return app_builder(ctx).mk_heq_symm(H);
|
|
}
|
|
|
|
expr mk_trans(type_context & ctx, name const & relname, expr const & H1, expr const & H2) {
|
|
return app_builder(ctx).mk_trans(relname, H1, H2);
|
|
}
|
|
|
|
expr mk_eq_trans(type_context & ctx, expr const & H1, expr const & H2) {
|
|
return app_builder(ctx).mk_eq_trans(H1, H2);
|
|
}
|
|
|
|
expr mk_eq_trans(type_context & ctx, expr const & a, expr const & b, expr const & c, expr const & H1, expr const & H2) {
|
|
return app_builder(ctx).mk_eq_trans(a, b, c, H1, H2);
|
|
}
|
|
|
|
expr mk_iff_trans(type_context & ctx, expr const & H1, expr const & H2) {
|
|
return app_builder(ctx).mk_iff_trans(H1, H2);
|
|
}
|
|
|
|
expr mk_heq_trans(type_context & ctx, expr const & H1, expr const & H2) {
|
|
return app_builder(ctx).mk_heq_trans(H1, H2);
|
|
}
|
|
|
|
expr mk_eq_rec(type_context & ctx, expr const & C, expr const & H1, expr const & H2) {
|
|
return app_builder(ctx).mk_eq_rec(C, H1, H2);
|
|
}
|
|
|
|
expr mk_eq_drec(type_context & ctx, expr const & C, expr const & H1, expr const & H2) {
|
|
return app_builder(ctx).mk_eq_drec(C, H1, H2);
|
|
}
|
|
|
|
expr mk_eq_of_heq(type_context & ctx, expr const & H) {
|
|
return app_builder(ctx).mk_eq_of_heq(H);
|
|
}
|
|
|
|
expr mk_heq_of_eq(type_context & ctx, expr const & H) {
|
|
return app_builder(ctx).mk_heq_of_eq(H);
|
|
}
|
|
|
|
expr mk_congr_arg(type_context & ctx, expr const & f, expr const & H) {
|
|
return app_builder(ctx).mk_congr_arg(f, H);
|
|
}
|
|
|
|
expr mk_congr_fun(type_context & ctx, expr const & H, expr const & a) {
|
|
// TODO(Leo): efficient version
|
|
return mk_app(ctx, get_congr_fun_name(), {H, a});
|
|
}
|
|
|
|
expr mk_congr(type_context & ctx, expr const & H1, expr const & H2) {
|
|
// TODO(Leo): efficient version
|
|
return mk_app(ctx, get_congr_name(), {H1, H2});
|
|
}
|
|
|
|
expr mk_funext(type_context & ctx, expr const & lam_pf) {
|
|
// TODO(dhs): efficient version
|
|
return mk_app(ctx, get_funext_name(), lam_pf);
|
|
}
|
|
|
|
expr lift_from_eq(type_context & ctx, name const & R, expr const & H) {
|
|
return app_builder(ctx).lift_from_eq(R, H);
|
|
}
|
|
|
|
expr mk_iff_false_intro(type_context & ctx, expr const & H) {
|
|
// TODO(Leo): implement custom version if bottleneck.
|
|
return mk_app(ctx, get_iff_false_intro_name(), {H});
|
|
}
|
|
|
|
expr mk_iff_true_intro(type_context & ctx, expr const & H) {
|
|
// TODO(Leo): implement custom version if bottleneck.
|
|
return mk_app(ctx, get_iff_true_intro_name(), {H});
|
|
}
|
|
|
|
expr mk_eq_false_intro(type_context & ctx, expr const & H) {
|
|
return app_builder(ctx).mk_eq_false_intro(H);
|
|
}
|
|
|
|
expr mk_eq_true_intro(type_context & ctx, expr const & H) {
|
|
return app_builder(ctx).mk_eq_true_intro(H);
|
|
}
|
|
|
|
expr mk_not_of_eq_false(type_context & ctx, expr const & H) {
|
|
return app_builder(ctx).mk_not_of_eq_false(H);
|
|
}
|
|
|
|
expr mk_of_eq_true(type_context & ctx, expr const & H) {
|
|
return app_builder(ctx).mk_of_eq_true(H);
|
|
}
|
|
|
|
expr mk_neq_of_not_iff(type_context & ctx, expr const & H) {
|
|
// TODO(Leo): implement custom version if bottleneck.
|
|
return mk_app(ctx, get_neq_of_not_iff_name(), {H});
|
|
}
|
|
|
|
expr mk_not_of_iff_false(type_context & ctx, expr const & H) {
|
|
if (is_constant(get_app_fn(H), get_iff_false_intro_name())) {
|
|
// not_of_iff_false (iff_false_intro H) == H
|
|
return app_arg(H);
|
|
}
|
|
// TODO(Leo): implement custom version if bottleneck.
|
|
return mk_app(ctx, get_not_of_iff_false_name(), 2, {H});
|
|
}
|
|
|
|
expr mk_of_iff_true(type_context & ctx, expr const & H) {
|
|
if (is_constant(get_app_fn(H), get_iff_true_intro_name())) {
|
|
// of_iff_true (iff_true_intro H) == H
|
|
return app_arg(H);
|
|
}
|
|
// TODO(Leo): implement custom version if bottleneck.
|
|
return mk_app(ctx, get_of_iff_true_name(), {H});
|
|
}
|
|
|
|
expr mk_false_of_true_iff_false(type_context & ctx, expr const & H) {
|
|
// TODO(Leo): implement custom version if bottleneck.
|
|
return mk_app(ctx, get_false_of_true_iff_false_name(), {H});
|
|
}
|
|
|
|
expr mk_false_of_true_eq_false(type_context & ctx, expr const & H) {
|
|
// TODO(Leo): implement custom version if bottleneck.
|
|
return mk_app(ctx, get_false_of_true_eq_false_name(), {H});
|
|
}
|
|
|
|
expr mk_not(type_context & ctx, expr const & H) {
|
|
// TODO(dhs): implement custom version if bottleneck.
|
|
return mk_app(ctx, get_not_name(), {H});
|
|
}
|
|
|
|
expr mk_absurd(type_context & ctx, expr const & Hp, expr const & Hnp, expr const & b) {
|
|
return mk_app(ctx, get_absurd_name(), {b, Hp, Hnp});
|
|
}
|
|
|
|
expr mk_partial_add(type_context & ctx, expr const & A) {
|
|
return app_builder(ctx).mk_partial_add(A);
|
|
}
|
|
|
|
expr mk_partial_mul(type_context & ctx, expr const & A) {
|
|
return app_builder(ctx).mk_partial_mul(A);
|
|
}
|
|
|
|
expr mk_zero(type_context & ctx, expr const & A) {
|
|
return app_builder(ctx).mk_zero(A);
|
|
}
|
|
|
|
expr mk_one(type_context & ctx, expr const & A) {
|
|
return app_builder(ctx).mk_one(A);
|
|
}
|
|
|
|
expr mk_partial_left_distrib(type_context & ctx, expr const & A) {
|
|
return app_builder(ctx).mk_partial_left_distrib(A);
|
|
}
|
|
|
|
expr mk_partial_right_distrib(type_context & ctx, expr const & A) {
|
|
return app_builder(ctx).mk_partial_right_distrib(A);
|
|
}
|
|
|
|
expr mk_ss_elim(type_context & ctx, expr const & A, expr const & ss_inst, expr const & old_e, expr const & new_e) {
|
|
return app_builder(ctx).mk_ss_elim(A, ss_inst, old_e, new_e);
|
|
}
|
|
|
|
expr mk_false_rec(type_context & ctx, expr const & c, expr const & H) {
|
|
return app_builder(ctx).mk_false_rec(c, H);
|
|
}
|
|
|
|
expr mk_ite(type_context & ctx, expr const & c, expr const & t, expr const & e) {
|
|
bool mask[5] = {true, false, false, true, true};
|
|
expr args[3] = {c, t, e};
|
|
return mk_app(ctx, get_ite_name(), 5, mask, args);
|
|
}
|
|
|
|
expr mk_id_locked(type_context & ctx, expr const & type, expr const & h) {
|
|
level lvl = get_level(ctx, type);
|
|
return mk_app(mk_constant(get_id_locked_name(), {lvl}), type, h);
|
|
}
|
|
|
|
static bool is_eq_trans(expr const & h, expr & h1, expr & h2) {
|
|
if (is_app_of(h, get_eq_trans_name(), 6)) {
|
|
h1 = app_arg(app_fn(h));
|
|
h2 = app_arg(h);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_propext(expr const & h, expr & h1) {
|
|
if (is_app_of(h, get_propext_name(), 3)) {
|
|
h1 = app_arg(h);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_eq_self_iff_true(expr const & h, expr & t) {
|
|
if (is_app_of(h, get_eq_self_iff_true_name(), 2)) {
|
|
t = app_arg(h);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
expr mk_eq_mpr(type_context & ctx, expr const & h1, expr const & h2) {
|
|
/*
|
|
eq.mpr (eq.trans H (propext (@eq_self_iff_true t))) h2
|
|
==>
|
|
eq.mpr H (eq.refl t)
|
|
|
|
Remark: note that (h2 : true)
|
|
*/
|
|
expr H, P, E, t;
|
|
if (is_eq_trans(h1, H, P) && is_propext(P, E) && is_eq_self_iff_true(E, t)) {
|
|
return mk_app(ctx, get_eq_mpr_name(), H, mk_eq_refl(ctx, t));
|
|
}
|
|
return mk_app(ctx, get_eq_mpr_name(), h1, h2);
|
|
}
|
|
|
|
expr mk_iff_mpr(type_context & ctx, expr const & h1, expr const & h2) {
|
|
return mk_app(ctx, get_iff_mpr_name(), h1, h2);
|
|
}
|
|
|
|
expr mk_eq_mp(type_context & ctx, expr const & h1, expr const & h2) {
|
|
return mk_app(ctx, get_eq_mp_name(), h1, h2);
|
|
}
|
|
|
|
expr mk_iff_mp(type_context & ctx, expr const & h1, expr const & h2) {
|
|
return mk_app(ctx, get_iff_mpr_name(), h1, h2);
|
|
}
|
|
|
|
void initialize_app_builder() {
|
|
register_trace_class("app_builder");
|
|
}
|
|
void finalize_app_builder() {}
|
|
}
|