#pragma once
#include "ast/expr_substitution.h"
#include "ast/rewriter/seq_rewriter.h"
#include "ast/seq/seq_view.h"
#include "ast/seq/seq_view_witness.h"
#include "ast/rewriter/seq_range_predicate.h"
#include "ast/seq/seq_regex_live.h"
#include "ast/rewriter/guard_set.h"
#include "ast/rewriter/th_rewriter.h"
#include "util/lbool.h"
#include "util/obj_hashtable.h"
#include "util/obj_pair_hashtable.h"
#include "util/dependency.h"
#include "util/trail.h"
#include "util/statistics.h"
#include <utility>
#include <tuple>
#include <map>
#include <vector>
#include <unordered_map>
namespace seq {
class monadic {
public:
enum class orientation { forward, reversed, retry };
private:
struct config {
seq::transition_mode m_mode;
bool m_solution = true; bool m_min_core = true; unsigned m_budget_limit = 1000000; orientation m_orientation = orientation::forward;
unsigned m_split_rounds = 0;
config(seq::transition_mode mode) : m_mode(mode) {}
};
enum class bail_reason { unsupported, state_cap, budget, state_expansion, resource, nullability, guard, not_reversible, num_reasons };
struct statistics {
unsigned m_cofactor_calls = 0;
unsigned m_states = 0;
unsigned m_max_state_expansion = 0; unsigned m_split_calls = 0; unsigned m_split_rounds = 0; unsigned m_split_decided = 0; unsigned m_bails[static_cast<unsigned>(bail_reason::num_reasons)] = {};
void inc_bail(bail_reason reason) {
++m_bails[static_cast<unsigned>(reason)];
}
unsigned work_bails() const {
return m_bails[static_cast<unsigned>(bail_reason::budget)] +
m_bails[static_cast<unsigned>(bail_reason::state_expansion)];
}
};
ast_manager& m;
seq_rewriter& m_rw;
th_rewriter m_thrw; trail_stack& m_undo_trail;
expr_ref_vector m_pin; unsigned m_budget = 0; bool m_giveup = false; config m_config;
statistics m_stats;
obj_map<expr, seq::view_vector> m_solution; obj_map<expr, expr*> m_split_words; obj_pair_map<expr, expr, expr*> m_der_cache; using membership_vec = vector<std::tuple<expr_ref, expr_ref, void*>>;
membership_vec m_memberships; membership_vec m_last_search_memberships; ptr_vector<void> m_core; std::function<bool(expr *)> m_is_var; lbool m_last_result = l_undef; lbool m_last_search_result = l_undef; bool m_reversed = false; bool m_retry_disabled = false; bool m_split_disabled = false;
seq_util& u() const { return m_rw.u(); }
seq_util::rex& re() const { return m_rw.u().re; }
struct atom {
bool is_var;
expr_ref var;
expr_ref elem;
atom(ast_manager& m, bool is_var, expr* var, expr* elem) :
is_var(is_var), var(var, m), elem(elem, m) {}
};
vector<vector<atom>> m_atoms; expr_ref_vector m_regexes; ptr_vector<expr> m_vars; obj_map<expr, unsigned> m_var_idx; vector<seq::view_vector> m_groups; obj_map<expr, uint64_t> m_last_occ; unsigned m_undef_vars = 0;
struct frame {
unsigned mi, i; expr* R; unsigned vi; bool finalize; bool last_atom; unsigned next = 0; bool undef = false; };
svector<frame> m_stack;
unsigned m_pos_mi = 0; unsigned m_pos_i = 0; expr* m_pos_R = nullptr;
bool m_any_undef = false; unsigned m_search_gen = 0; typedef std::vector<seq::view::sig> group_sig;
struct group_sig_hash {
size_t operator()(group_sig const& s) const {
uint64_t h = 1469598103934665603ull;
for (auto const& p : s) {
h = (h ^ p.state) * 1099511628211ull;
h = (h ^ p.target) * 1099511628211ull;
}
return static_cast<size_t>(h);
}
};
group_sig m_sig_buf; std::unordered_map<group_sig, lbool, group_sig_hash> m_group_cache;
seq::live_states m_live_states;
seq::view_witness m_view_witness;
func_decl_ref m_rev_decl;
expr_ref der_elem(expr* r, expr* elem);
lbool nullable(expr* r);
lbool product_nonempty(expr* var, seq::view_vector const& comps, expr_ref* witness_word = nullptr);
bool parse_term(expr* term, vector<atom>& atoms);
bool reverse_regex(expr* r, expr_ref& result);
expr_ref mk_rev_var(expr* v);
expr* strip_rev_var(expr* v) const;
bool out_of_budget();
lbool decide_oriented(membership_vec const& memberships, bool reversed, unsigned budget);
unsigned work_bails() const { return m_stats.work_bails(); }
void reset_search();
lbool enumerate(membership_vec const& memberships, bool resume);
bool prepare(membership_vec const& memberships, bool reversed);
unsigned var_index(expr* v);
void start_membership(unsigned mi);
lbool advance_pos();
bool push_frame();
bool commit_next(frame& f);
lbool run_search(bool resume);
lbool group_nonempty(unsigned vi);
lbool leaf();
lbool decide(membership_vec const& memberships);
lbool decide_policy(membership_vec const& memberships, unsigned budget, bool sticky);
void split_conjuncts(expr* r, ptr_vector<expr>& out);
bool constrains_length(expr* r);
bool instantiate_word(expr* term, ptr_vector<expr>& elems, bool subst = true);
lbool model_accepts(expr* term, expr* r);
lbool materialize_recorded(expr* var, expr_ref& word, bool allow_unconstrained = true);
lbool decide_split(membership_vec const& memberships, unsigned budget, unsigned allowance);
void minimize_core(membership_vec const& memberships);
bool is_var(expr *term) const {
return m_is_var ? m_is_var(term) : is_uninterp(term);
}
public:
monadic(seq_rewriter& rw, trail_stack& undo_trail,
seq::transition_mode mode = seq::transition_mode::light_antimirov_tm) :
m(rw.m()), m_rw(rw), m_thrw(rw.m()), m_undo_trail(undo_trail),
m_pin(rw.m()), m_config(mode), m_regexes(rw.m()),
m_live_states(rw, mode, 1u << 12),
m_view_witness(undo_trail, rw, m_live_states, mode), m_rev_decl(rw.m()) {
m_view_witness.set_checkpoint([this]() {
if (!out_of_budget())
return seq::view_failure_reason::none;
return m_budget == 0 ? seq::view_failure_reason::budget :
seq::view_failure_reason::resource;
});
}
void collect_statistics(::statistics &st) const;
std::ostream& display(std::ostream& out) const;
seq::transition_mode mode() const { return m_config.m_mode; }
void set_gen_solution(bool b) { m_config.m_solution = b; }
bool gen_solution() const { return m_config.m_solution; }
obj_map<expr, seq::view_vector> const& solution() const { return m_solution; }
lbool materialize(expr* var, expr_ref& word);
lbool materialize_all(expr_substitution& model);
lbool solve(expr* term, expr* R);
void set_min_core(bool b) { m_config.m_min_core = b; }
void set_budget(unsigned b) { m_config.m_budget_limit = b; }
unsigned budget() const { return m_config.m_budget_limit; }
void set_orientation(orientation o) { m_config.m_orientation = o; }
orientation get_orientation() const { return m_config.m_orientation; }
void set_split_rounds(unsigned n) { m_config.m_split_rounds = n; }
unsigned split_rounds() const { return m_config.m_split_rounds; }
bool is_reversed() const { return m_reversed; }
void set_is_var(std::function<bool(expr *)> const &is_var) {
m_is_var = is_var;
}
void add(expr* term, expr* regex, void* d);
void set_term(void* d, expr* term);
bool can_decide_term(expr* term);
void add_lo(expr* term, unsigned lo, void* d);
void add_hi(expr* term, unsigned hi, void* d);
void add_len(expr* term, unsigned len, void* d);
lbool check();
ptr_vector<void> const& core() const { return m_core; }
class iterator {
monadic& m_engine;
membership_vec m_memberships; unsigned m_limit; unsigned m_count = 0;
unsigned m_gen = 0; bool m_started = false;
bool m_done = false;
bool m_giveup = false;
public:
iterator(monadic& engine, membership_vec const& memberships, unsigned limit);
bool next(obj_map<expr, seq::view_vector>& solution);
bool gave_up() const { return m_giveup; }
unsigned count() const { return m_count; }
};
iterator iterate(unsigned limit);
};
}