#pragma once
#include "ast/ff_decl_plugin.h"
#include "ast/rewriter/rewriter_types.h"
#include "util/common_msgs.h"
class ff_domain_analysis {
struct summary {
expr* variable = nullptr;
unsigned degree = 0;
rational zero{0}, one{0};
bool valid = true;
};
ast_manager& m;
ff_util ff;
obj_map<expr, summary> cache;
expr_ref_vector pins;
summary const& analyze(expr* root) {
ptr_vector<expr> pending;
pending.push_back(root);
while (!pending.empty()) {
if (!m.inc())
throw rewriter_exception(Z3_CANCELED_MSG);
expr* e = pending.back();
if (cache.contains(e)) {
pending.pop_back();
continue;
}
summary value;
rational c;
if (!is_app(e))
value.valid = false;
else if (!ff.is_interp(e)) {
value.variable = e;
value.degree = 1;
value.one = rational(1);
}
else if (ff.is_numeral(e, c))
value.zero = value.one = c;
else if (!ff.is_add(e) && !ff.is_mul(e) && !ff.is_neg(e))
value.valid = false;
else {
bool ready = true;
for (expr* arg : *to_app(e))
if (!cache.contains(arg)) {
pending.push_back(arg);
ready = false;
}
if (!ready)
continue;
bool mul = ff.is_mul(e);
if (mul)
value.zero = value.one = rational(1);
rational const& p = ff.modulus(e->get_sort());
for (expr* arg : *to_app(e)) {
auto const& child = cache.find(arg);
if (!child.valid || (value.variable && child.variable && value.variable != child.variable)) {
value.valid = false;
break;
}
if (child.variable)
value.variable = child.variable;
value.degree = mul ? value.degree + child.degree : std::max(value.degree, child.degree);
if (value.degree > 2) {
value.valid = false;
break;
}
value.zero = mod(mul ? value.zero * child.zero : value.zero + child.zero, p);
value.one = mod(mul ? value.one * child.one : value.one + child.one, p);
}
if (ff.is_neg(e)) {
value.zero = mod(-value.zero, p);
value.one = mod(-value.one, p);
}
}
cache.insert(e, value);
pins.push_back(e);
}
return cache.find(root);
}
public:
explicit ff_domain_analysis(ast_manager& m) : m(m), ff(m), pins(m) {}
unsigned cache_size() const { return cache.size(); }
expr* variable(expr* formula) {
expr *a, *b;
if (!m.is_eq(formula, a, b) || !ff.is_ff(a))
return nullptr;
if ((!ff.is_interp(a) && ff.is_numeral(b)) || (!ff.is_interp(b) && ff.is_numeral(a)))
return nullptr;
auto lhs = analyze(a);
auto rhs = analyze(b);
if (!lhs.valid || !rhs.valid || (lhs.variable && rhs.variable && lhs.variable != rhs.variable))
return nullptr;
return std::max(lhs.degree, rhs.degree) == 2 && lhs.zero == rhs.zero && lhs.one == rhs.one ?
(lhs.variable ? lhs.variable : rhs.variable) : nullptr;
}
};