mirror of
https://github.com/Z3Prover/z3
synced 2025-04-07 09:55:19 +00:00
fixes to bv-sls
This commit is contained in:
parent
d7c0e17f96
commit
bab7ca2b70
|
@ -124,6 +124,7 @@ namespace bv {
|
||||||
};
|
};
|
||||||
m_eval.init_eval(m_terms.assertions(), eval);
|
m_eval.init_eval(m_terms.assertions(), eval);
|
||||||
init_repair();
|
init_repair();
|
||||||
|
// m_engine_init = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool, app*> sls::next_to_repair() {
|
std::pair<bool, app*> sls::next_to_repair() {
|
||||||
|
@ -161,17 +162,18 @@ namespace bv {
|
||||||
lbool sls::search1() {
|
lbool sls::search1() {
|
||||||
// init and init_eval were invoked
|
// init and init_eval were invoked
|
||||||
unsigned n = 0;
|
unsigned n = 0;
|
||||||
for (; n++ < m_config.m_max_repairs && m.inc(); ) {
|
for (; n < m_config.m_max_repairs && m.inc(); ) {
|
||||||
auto [down, e] = next_to_repair();
|
auto [down, e] = next_to_repair();
|
||||||
if (!e)
|
if (!e)
|
||||||
return l_true;
|
return l_true;
|
||||||
|
|
||||||
trace_repair(down, e);
|
IF_VERBOSE(20, trace_repair(down, e));
|
||||||
|
|
||||||
++m_stats.m_moves;
|
++m_stats.m_moves;
|
||||||
|
if (down) {
|
||||||
if (down)
|
|
||||||
try_repair_down(e);
|
try_repair_down(e);
|
||||||
|
++n;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
try_repair_up(e);
|
try_repair_up(e);
|
||||||
}
|
}
|
||||||
|
@ -181,12 +183,13 @@ namespace bv {
|
||||||
lbool sls::search2() {
|
lbool sls::search2() {
|
||||||
lbool res = l_undef;
|
lbool res = l_undef;
|
||||||
if (m_stats.m_restarts == 0)
|
if (m_stats.m_restarts == 0)
|
||||||
res = m_engine();
|
res = m_engine(),
|
||||||
|
m_engine_init = true;
|
||||||
else if (m_stats.m_restarts % 1000 == 0)
|
else if (m_stats.m_restarts % 1000 == 0)
|
||||||
res = m_engine.search_loop();
|
res = m_engine.search_loop(),
|
||||||
|
m_engine_init = true;
|
||||||
if (res != l_undef)
|
if (res != l_undef)
|
||||||
m_engine_model = true;
|
m_engine_model = true;
|
||||||
m_engine_init = true;
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +206,7 @@ namespace bv {
|
||||||
if (res != l_undef)
|
if (res != l_undef)
|
||||||
break;
|
break;
|
||||||
trace();
|
trace();
|
||||||
// res = search2();
|
//res = search2();
|
||||||
if (res != l_undef)
|
if (res != l_undef)
|
||||||
break;
|
break;
|
||||||
reinit_eval();
|
reinit_eval();
|
||||||
|
@ -223,6 +226,7 @@ namespace bv {
|
||||||
VERIFY(m_eval.wval(e).commit_eval());
|
VERIFY(m_eval.wval(e).commit_eval());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IF_VERBOSE(3, verbose_stream() << "done\n");
|
||||||
for (auto p : m_terms.parents(e))
|
for (auto p : m_terms.parents(e))
|
||||||
m_repair_up.insert(p->get_id());
|
m_repair_up.insert(p->get_id());
|
||||||
|
|
||||||
|
@ -256,6 +260,7 @@ namespace bv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
IF_VERBOSE(3, verbose_stream() << "init-repair " << mk_bounded_pp(e, m) << "\n");
|
||||||
// repair was not successful, so reset the state to find a different way to repair
|
// repair was not successful, so reset the state to find a different way to repair
|
||||||
init_repair();
|
init_repair();
|
||||||
}
|
}
|
||||||
|
@ -265,7 +270,7 @@ namespace bv {
|
||||||
if (m_terms.is_assertion(e))
|
if (m_terms.is_assertion(e))
|
||||||
m_repair_roots.insert(e->get_id());
|
m_repair_roots.insert(e->get_id());
|
||||||
else if (!m_eval.repair_up(e)) {
|
else if (!m_eval.repair_up(e)) {
|
||||||
//m_repair_roots.insert(e->get_id());
|
IF_VERBOSE(2, verbose_stream() << "repair-up "; trace_repair(true, e));
|
||||||
init_repair();
|
init_repair();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -366,14 +371,14 @@ namespace bv {
|
||||||
m_engine.updt_params(q);
|
m_engine.updt_params(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sls::trace_repair(bool down, expr* e) {
|
std::ostream& sls::trace_repair(bool down, expr* e) {
|
||||||
IF_VERBOSE(20,
|
|
||||||
verbose_stream() << (down ? "d #" : "u #")
|
verbose_stream() << (down ? "d #" : "u #")
|
||||||
<< e->get_id() << ": "
|
<< e->get_id() << ": "
|
||||||
<< mk_bounded_pp(e, m, 1) << " ";
|
<< mk_bounded_pp(e, m, 1) << " ";
|
||||||
if (bv.is_bv(e)) verbose_stream() << m_eval.wval(e) << " " << (m_eval.is_fixed0(e) ? "fixed " : " ");
|
if (bv.is_bv(e)) verbose_stream() << m_eval.wval(e) << " " << (m_eval.is_fixed0(e) ? "fixed " : " ");
|
||||||
if (m.is_bool(e)) verbose_stream() << m_eval.bval0(e) << " ";
|
if (m.is_bool(e)) verbose_stream() << m_eval.bval0(e) << " ";
|
||||||
verbose_stream() << "\n");
|
verbose_stream() << "\n";
|
||||||
|
return verbose_stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sls::trace() {
|
void sls::trace() {
|
||||||
|
|
|
@ -68,7 +68,7 @@ namespace bv {
|
||||||
void reinit_eval();
|
void reinit_eval();
|
||||||
void init_repair();
|
void init_repair();
|
||||||
void trace();
|
void trace();
|
||||||
void trace_repair(bool down, expr* e);
|
std::ostream& trace_repair(bool down, expr* e);
|
||||||
|
|
||||||
indexed_uint_set m_to_repair;
|
indexed_uint_set m_to_repair;
|
||||||
void init_repair_candidates();
|
void init_repair_candidates();
|
||||||
|
|
|
@ -1351,55 +1351,18 @@ namespace bv {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sls_eval::try_repair_ashr(bvect const& e, bvval & a, bvval& b, unsigned i) {
|
bool sls_eval::try_repair_ashr(bvect const& e, bvval & a, bvval& b, unsigned i) {
|
||||||
if (true) {
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
return try_repair_ashr0(e, a, b);
|
return try_repair_ashr0(e, a, b);
|
||||||
else
|
else
|
||||||
return try_repair_ashr1(e, a, b);
|
return try_repair_ashr1(e, a, b);
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
unsigned sh = b.to_nat(b.bw);
|
|
||||||
if (sh == 0)
|
|
||||||
return a.try_set(e);
|
|
||||||
else if (sh >= b.bw) {
|
|
||||||
if (e.get(a.bw - 1))
|
|
||||||
return a.try_set_bit(a.bw - 1, true);
|
|
||||||
else
|
|
||||||
return a.try_set_bit(a.bw - 1, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// e = a >> sh
|
|
||||||
// a[bw-1:sh] = e[bw-sh-1:0]
|
|
||||||
// a[sh-1:0] = a[sh-1:0]
|
|
||||||
// ignore sign
|
|
||||||
for (unsigned i = sh; i < a.bw; ++i)
|
|
||||||
m_tmp.set(i, e.get(i - sh));
|
|
||||||
for (unsigned i = 0; i < sh; ++i)
|
|
||||||
m_tmp.set(i, a.get_bit(i));
|
|
||||||
a.clear_overflow_bits(m_tmp);
|
|
||||||
return a.try_set(m_tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// NB. blind sub-range of possible values for b
|
|
||||||
SASSERT(i == 1);
|
|
||||||
unsigned sh = m_rand(a.bw + 1);
|
|
||||||
b.set(m_tmp, sh);
|
|
||||||
return b.try_set(m_tmp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sls_eval::try_repair_lshr(bvect const& e, bvval& a, bvval& b, unsigned i) {
|
bool sls_eval::try_repair_lshr(bvect const& e, bvval& a, bvval& b, unsigned i) {
|
||||||
#if 0
|
|
||||||
return try_repair_ashr(e, a, b, i);
|
|
||||||
#else
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
return try_repair_lshr0(e, a, b);
|
return try_repair_lshr0(e, a, b);
|
||||||
else
|
else
|
||||||
return try_repair_lshr1(e, a, b);
|
return try_repair_lshr1(e, a, b);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1427,22 +1390,25 @@ namespace bv {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned sh = b.to_nat(b.bw);
|
unsigned sh = b.to_nat(b.bw);
|
||||||
if (sh == 0 && a.try_set(e))
|
if (m_rand(20) != 0) {
|
||||||
return true;
|
if (sh == 0 && a.try_set(e))
|
||||||
else if (sh >= b.bw)
|
|
||||||
return true;
|
|
||||||
else if (sh < b.bw && m_rand(20) != 0) {
|
|
||||||
// e = a >> sh
|
|
||||||
// a[bw-1:sh] = e[bw-sh-1:0]
|
|
||||||
// a[sh-1:0] = a[sh-1:0]
|
|
||||||
for (unsigned i = sh; i < a.bw; ++i)
|
|
||||||
t.set(i, e.get(i - sh));
|
|
||||||
for (unsigned i = 0; i < sh; ++i)
|
|
||||||
t.set(i, a.get_bit(i));
|
|
||||||
a.clear_overflow_bits(t);
|
|
||||||
if (a.try_set(t))
|
|
||||||
return true;
|
return true;
|
||||||
|
else if (sh >= b.bw)
|
||||||
|
return true;
|
||||||
|
else if (sh < b.bw && m_rand(20) != 0) {
|
||||||
|
// e = a >> sh
|
||||||
|
// a[bw-1:sh] = e[bw-sh-1:0]
|
||||||
|
// a[sh-1:0] = a[sh-1:0]
|
||||||
|
for (unsigned i = sh; i < a.bw; ++i)
|
||||||
|
t.set(i, e.get(i - sh));
|
||||||
|
for (unsigned i = 0; i < sh; ++i)
|
||||||
|
t.set(i, a.get_bit(i));
|
||||||
|
a.clear_overflow_bits(t);
|
||||||
|
if (a.try_set(t))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//bool r = try_repair_ashr(e, a, const_cast<bvval&>(b), 0);
|
//bool r = try_repair_ashr(e, a, const_cast<bvval&>(b), 0);
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace bv {
|
||||||
|
|
||||||
// s <=s t <=> s + K <= t + K, K = 2^{bw-1}
|
// s <=s t <=> s + K <= t + K, K = 2^{bw-1}
|
||||||
|
|
||||||
void sls_fixed::init_range(app* e, bool sign) {
|
bool sls_fixed::init_range(app* e, bool sign) {
|
||||||
expr* s, * t, * x, * y;
|
expr* s, * t, * x, * y;
|
||||||
rational a, b;
|
rational a, b;
|
||||||
unsigned idx;
|
unsigned idx;
|
||||||
|
@ -64,63 +64,116 @@ namespace bv {
|
||||||
if (bv.is_ule(e, s, t)) {
|
if (bv.is_ule(e, s, t)) {
|
||||||
get_offset(s, x, a);
|
get_offset(s, x, a);
|
||||||
get_offset(t, y, b);
|
get_offset(t, y, b);
|
||||||
init_range(x, a, y, b, sign);
|
return init_range(x, a, y, b, sign);
|
||||||
}
|
}
|
||||||
else if (bv.is_ult(e, s, t)) {
|
else if (bv.is_ult(e, s, t)) {
|
||||||
get_offset(s, x, a);
|
get_offset(s, x, a);
|
||||||
get_offset(t, y, b);
|
get_offset(t, y, b);
|
||||||
init_range(y, b, x, a, !sign);
|
return init_range(y, b, x, a, !sign);
|
||||||
}
|
}
|
||||||
else if (bv.is_uge(e, s, t)) {
|
else if (bv.is_uge(e, s, t)) {
|
||||||
get_offset(s, x, a);
|
get_offset(s, x, a);
|
||||||
get_offset(t, y, b);
|
get_offset(t, y, b);
|
||||||
init_range(y, b, x, a, sign);
|
return init_range(y, b, x, a, sign);
|
||||||
}
|
}
|
||||||
else if (bv.is_ugt(e, s, t)) {
|
else if (bv.is_ugt(e, s, t)) {
|
||||||
get_offset(s, x, a);
|
get_offset(s, x, a);
|
||||||
get_offset(t, y, b);
|
get_offset(t, y, b);
|
||||||
init_range(x, a, y, b, !sign);
|
return init_range(x, a, y, b, !sign);
|
||||||
}
|
}
|
||||||
else if (bv.is_sle(e, s, t)) {
|
else if (bv.is_sle(e, s, t)) {
|
||||||
get_offset(s, x, a);
|
get_offset(s, x, a);
|
||||||
get_offset(t, y, b);
|
get_offset(t, y, b);
|
||||||
init_range(x, a + N(s), y, b + N(s), sign);
|
return init_range(x, a + N(s), y, b + N(s), sign);
|
||||||
}
|
}
|
||||||
else if (bv.is_slt(e, s, t)) {
|
else if (bv.is_slt(e, s, t)) {
|
||||||
get_offset(s, x, a);
|
get_offset(s, x, a);
|
||||||
get_offset(t, y, b);
|
get_offset(t, y, b);
|
||||||
init_range(y, b + N(s), x, a + N(s), !sign);
|
return init_range(y, b + N(s), x, a + N(s), !sign);
|
||||||
}
|
}
|
||||||
else if (bv.is_sge(e, s, t)) {
|
else if (bv.is_sge(e, s, t)) {
|
||||||
get_offset(s, x, a);
|
get_offset(s, x, a);
|
||||||
get_offset(t, y, b);
|
get_offset(t, y, b);
|
||||||
init_range(y, b + N(s), x, a + N(s), sign);
|
return init_range(y, b + N(s), x, a + N(s), sign);
|
||||||
}
|
}
|
||||||
else if (bv.is_sgt(e, s, t)) {
|
else if (bv.is_sgt(e, s, t)) {
|
||||||
get_offset(s, x, a);
|
get_offset(s, x, a);
|
||||||
get_offset(t, y, b);
|
get_offset(t, y, b);
|
||||||
init_range(x, a + N(s), y, b + N(s), !sign);
|
return init_range(x, a + N(s), y, b + N(s), !sign);
|
||||||
}
|
}
|
||||||
else if (!sign && m.is_eq(e, s, t)) {
|
else if (m.is_eq(e, s, t)) {
|
||||||
if (bv.is_numeral(s, a))
|
if (bv.is_numeral(s, a))
|
||||||
// t - a <= 0
|
init_eq(t, a, sign);
|
||||||
init_range(t, -a, nullptr, rational(0), false);
|
|
||||||
else if (bv.is_numeral(t, a))
|
else if (bv.is_numeral(t, a))
|
||||||
init_range(s, -a, nullptr, rational(0), false);
|
init_eq(s, a, sign);
|
||||||
}
|
else
|
||||||
else if (sign && m.is_eq(e, s, t)) {
|
return false;
|
||||||
if (bv.is_numeral(s, a))
|
return true;
|
||||||
// 1 <= t - a
|
|
||||||
init_range(nullptr, rational(1), t, -a, false);
|
|
||||||
else if (bv.is_numeral(t, a))
|
|
||||||
init_range(nullptr, rational(1), s, -a, false);
|
|
||||||
}
|
}
|
||||||
else if (bv.is_bit2bool(e, s, idx)) {
|
else if (bv.is_bit2bool(e, s, idx)) {
|
||||||
auto& val = wval(s);
|
auto& val = wval(s);
|
||||||
val.try_set_bit(idx, !sign);
|
val.try_set_bit(idx, !sign);
|
||||||
val.fixed.set(idx, true);
|
val.fixed.set(idx, true);
|
||||||
val.tighten_range();
|
val.tighten_range();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sls_fixed::init_eq(expr* t, rational const& a, bool sign) {
|
||||||
|
unsigned lo, hi;
|
||||||
|
rational b(0);
|
||||||
|
// verbose_stream() << mk_bounded_pp(t, m) << " == " << a << "\n";
|
||||||
|
expr* s = nullptr;
|
||||||
|
if (sign)
|
||||||
|
// 1 <= t - a
|
||||||
|
init_range(nullptr, rational(1), t, -a, false);
|
||||||
|
else
|
||||||
|
// t - a <= 0
|
||||||
|
init_range(t, -a, nullptr, rational::zero(), false);
|
||||||
|
if (!sign && bv.is_bv_not(t, s)) {
|
||||||
|
for (unsigned i = 0; i < bv.get_bv_size(s); ++i)
|
||||||
|
if (!a.get_bit(i))
|
||||||
|
b += rational::power_of_two(i);
|
||||||
|
init_eq(s, b, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!sign && bv.is_concat(t) && to_app(t)->get_num_args() == 2) {
|
||||||
|
auto x = to_app(t)->get_arg(0);
|
||||||
|
auto y = to_app(t)->get_arg(1);
|
||||||
|
auto sz = bv.get_bv_size(y);
|
||||||
|
auto k = rational::power_of_two(sz);
|
||||||
|
init_eq(y, mod(a, k), false);
|
||||||
|
init_eq(x, div(a + k - 1, k), false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (bv.is_extract(t, lo, hi, s)) {
|
||||||
|
if (hi == lo) {
|
||||||
|
sign = sign ? a == 1 : a == 0;
|
||||||
|
auto& val = wval(s);
|
||||||
|
if (val.try_set_bit(lo, !sign))
|
||||||
|
val.fixed.set(lo, true);
|
||||||
|
val.tighten_range();
|
||||||
|
}
|
||||||
|
else if (!sign) {
|
||||||
|
auto& val = wval(s);
|
||||||
|
for (unsigned i = lo; i <= hi; ++i)
|
||||||
|
if (val.try_set_bit(i, a.get_bit(i - lo)))
|
||||||
|
val.fixed.set(i, true);
|
||||||
|
val.tighten_range();
|
||||||
|
// verbose_stream() << lo << " " << hi << " " << val << " := " << a << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sign && hi + 1 == bv.get_bv_size(s)) {
|
||||||
|
// s < 2^lo * (a + 1)
|
||||||
|
rational b = rational::power_of_two(lo) * (a + 1) - 1;
|
||||||
|
rational offset;
|
||||||
|
get_offset(s, t, offset);
|
||||||
|
// t + offset <= b
|
||||||
|
init_range(t, offset, nullptr, b, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -132,51 +185,66 @@ namespace bv {
|
||||||
// a < x + b <=> ! (x + b <= a) <=> x not in [-a, b - a [ <=> x in [b - a, -a [ a != -1
|
// a < x + b <=> ! (x + b <= a) <=> x not in [-a, b - a [ <=> x in [b - a, -a [ a != -1
|
||||||
// x + a < x + b <=> ! (x + b <= x + a) <=> x in [-a, -b [ a != b
|
// x + a < x + b <=> ! (x + b <= x + a) <=> x in [-a, -b [ a != b
|
||||||
//
|
//
|
||||||
void sls_fixed::init_range(expr* x, rational const& a, expr* y, rational const& b, bool sign) {
|
bool sls_fixed::init_range(expr* x, rational const& a, expr* y, rational const& b, bool sign) {
|
||||||
if (!x && !y)
|
if (!x && !y)
|
||||||
return;
|
return false;
|
||||||
if (!x) {
|
if (!x)
|
||||||
// a <= y + b
|
return add_range(y, a - b, -b, sign);
|
||||||
if (a == 0)
|
else if (!y)
|
||||||
return;
|
return add_range(x, -a, b - a + 1, sign);
|
||||||
auto& v = wval(y);
|
else if (x == y)
|
||||||
if (!sign)
|
return add_range(x, -a, -b, sign);
|
||||||
v.add_range(a - b, -b);
|
return false;
|
||||||
else
|
}
|
||||||
v.add_range(-b, a - b);
|
|
||||||
}
|
|
||||||
else if (!y) {
|
|
||||||
|
|
||||||
if (mod(b + 1, rational::power_of_two(bv.get_bv_size(x))) == 0)
|
bool sls_fixed::add_range(expr* e, rational lo, rational hi, bool sign) {
|
||||||
return;
|
auto& v = wval(e);
|
||||||
auto& v = wval(x);
|
lo = mod(lo, rational::power_of_two(bv.get_bv_size(e)));
|
||||||
if (!sign)
|
hi = mod(hi, rational::power_of_two(bv.get_bv_size(e)));
|
||||||
v.add_range(-a, b - a + 1);
|
if (lo == hi)
|
||||||
else
|
return false;
|
||||||
v.add_range(b - a + 1, -a);
|
if (sign)
|
||||||
}
|
std::swap(lo, hi);
|
||||||
else if (x == y) {
|
v.add_range(lo, hi);
|
||||||
if (a == b)
|
if (v.lo() == 0 && bv.is_concat(e) && to_app(e)->get_num_args() == 2) {
|
||||||
return;
|
auto x = to_app(e)->get_arg(0);
|
||||||
auto& v = wval(x);
|
auto y = to_app(e)->get_arg(1);
|
||||||
if (!sign)
|
auto sz = bv.get_bv_size(y);
|
||||||
v.add_range(-a, -b);
|
auto k = rational::power_of_two(sz);
|
||||||
else
|
lo = v.lo();
|
||||||
v.add_range(-b, -a);
|
hi = v.hi();
|
||||||
|
if (hi <= k) {
|
||||||
|
add_range(y, lo, hi, false);
|
||||||
|
init_eq(x, lo, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hi = div(hi + k - 1, k);
|
||||||
|
add_range(x, lo, hi, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sls_fixed::get_offset(expr* e, expr*& x, rational& offset) {
|
void sls_fixed::get_offset(expr* e, expr*& x, rational& offset) {
|
||||||
expr* s, * t;
|
expr* s, * t;
|
||||||
x = e;
|
x = e;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
if (bv.is_bv_add(e, s, t)) {
|
rational n;
|
||||||
if (bv.is_numeral(s, offset))
|
while (true) {
|
||||||
|
if (bv.is_bv_add(x, s, t) && bv.is_numeral(s, n)) {
|
||||||
x = t;
|
x = t;
|
||||||
else if (bv.is_numeral(t, offset))
|
offset += n;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (bv.is_bv_add(x, s, t) && bv.is_numeral(t, n)) {
|
||||||
x = s;
|
x = s;
|
||||||
|
offset += n;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (bv.is_numeral(e, offset))
|
if (bv.is_numeral(e, n))
|
||||||
|
offset += n,
|
||||||
x = nullptr;
|
x = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,11 +332,6 @@ namespace bv {
|
||||||
case OP_BADD: {
|
case OP_BADD: {
|
||||||
auto& a = wval(e->get_arg(0));
|
auto& a = wval(e->get_arg(0));
|
||||||
auto& b = wval(e->get_arg(1));
|
auto& b = wval(e->get_arg(1));
|
||||||
rational r;
|
|
||||||
if (bv.is_numeral(e->get_arg(0), r) && b.has_range())
|
|
||||||
v.add_range(r + b.lo(), r + b.hi());
|
|
||||||
else if (bv.is_numeral(e->get_arg(1), r) && a.has_range())
|
|
||||||
v.add_range(r + a.lo(), r + a.hi());
|
|
||||||
bool pfixed = true;
|
bool pfixed = true;
|
||||||
for (unsigned i = 0; i < v.bw; ++i) {
|
for (unsigned i = 0; i < v.bw; ++i) {
|
||||||
if (pfixed && a.fixed.get(i) && b.fixed.get(i))
|
if (pfixed && a.fixed.get(i) && b.fixed.get(i))
|
||||||
|
@ -283,7 +346,6 @@ namespace bv {
|
||||||
v.fixed.set(i, false);
|
v.fixed.set(i, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OP_BMUL: {
|
case OP_BMUL: {
|
||||||
|
|
|
@ -30,9 +30,11 @@ namespace bv {
|
||||||
bv_util& bv;
|
bv_util& bv;
|
||||||
|
|
||||||
void init_ranges(expr_ref_vector const& es);
|
void init_ranges(expr_ref_vector const& es);
|
||||||
void init_range(app* e, bool sign);
|
bool init_range(app* e, bool sign);
|
||||||
void init_range(expr* x, rational const& a, expr* y, rational const& b, bool sign);
|
bool init_range(expr* x, rational const& a, expr* y, rational const& b, bool sign);
|
||||||
void get_offset(expr* e, expr*& x, rational& offset);
|
void get_offset(expr* e, expr*& x, rational& offset);
|
||||||
|
bool init_eq(expr* e, rational const& v, bool sign);
|
||||||
|
bool add_range(expr* e, rational lo, rational hi, bool sign);
|
||||||
|
|
||||||
void init_fixed_basic(app* e);
|
void init_fixed_basic(app* e);
|
||||||
void init_fixed_bv(app* e);
|
void init_fixed_bv(app* e);
|
||||||
|
|
|
@ -204,139 +204,69 @@ namespace bv {
|
||||||
|
|
||||||
//
|
//
|
||||||
// largest dst <= src and dst is feasible
|
// largest dst <= src and dst is feasible
|
||||||
// set dst := src & (~fixed | bits)
|
//
|
||||||
//
|
|
||||||
// increment dst if dst < src by setting bits below msb(src & ~dst) to 1
|
|
||||||
//
|
|
||||||
// if dst < lo < hi:
|
|
||||||
// return false
|
|
||||||
// if lo < hi <= dst:
|
|
||||||
// set dst := hi - 1
|
|
||||||
// if hi <= dst < lo
|
|
||||||
// set dst := hi - 1
|
|
||||||
//
|
|
||||||
|
|
||||||
bool sls_valuation::get_at_most(bvect const& src, bvect& dst) const {
|
bool sls_valuation::get_at_most(bvect const& src, bvect& dst) const {
|
||||||
SASSERT(!has_overflow(src));
|
SASSERT(!has_overflow(src));
|
||||||
for (unsigned i = 0; i < nw; ++i)
|
src.copy_to(nw, dst);
|
||||||
dst[i] = src[i] & (~fixed[i] | m_bits[i]);
|
sup_feasible(dst);
|
||||||
|
if (in_range(dst)) {
|
||||||
//
|
SASSERT(can_set(dst));
|
||||||
// If dst < src, then find the most significant
|
return true;
|
||||||
// bit where src[idx] = 1, dst[idx] = 0
|
}
|
||||||
// set dst[j] = bits_j | ~fixed_j for j < idx
|
if (dst < m_lo && m_lo < m_hi) // dst < lo < hi
|
||||||
//
|
return false;
|
||||||
for (unsigned i = nw; i-- > 0; ) {
|
if (is_zero(m_hi))
|
||||||
if (0 != (~dst[i] & src[i])) {
|
return false;
|
||||||
auto idx = log2(~dst[i] & src[i]);
|
m_hi.copy_to(nw, dst); // hi <= dst < lo or lo < hi <= dst
|
||||||
auto mask = (1 << idx) - 1;
|
sub1(dst);
|
||||||
dst[i] = (~fixed[i] & mask) | dst[i];
|
SASSERT(can_set(dst));
|
||||||
for (unsigned j = i; j-- > 0; )
|
return true;
|
||||||
dst[j] = (~fixed[j] | m_bits[j]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SASSERT(!has_overflow(dst));
|
|
||||||
return round_down(dst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// smallest dst >= src and dst is feasible with respect to this.
|
// smallest dst >= src and dst is feasible with respect to this.
|
||||||
// set dst := (src & ~fixed) | (fixed & bits)
|
|
||||||
//
|
|
||||||
// decrement dst if dst > src by setting bits below msb to 0 unless fixed
|
|
||||||
//
|
|
||||||
// if lo < hi <= dst
|
|
||||||
// return false
|
|
||||||
// if dst < lo < hi:
|
|
||||||
// set dst := lo
|
|
||||||
// if hi <= dst < lo
|
|
||||||
// set dst := lo
|
|
||||||
//
|
|
||||||
bool sls_valuation::get_at_least(bvect const& src, bvect& dst) const {
|
bool sls_valuation::get_at_least(bvect const& src, bvect& dst) const {
|
||||||
SASSERT(!has_overflow(src));
|
SASSERT(!has_overflow(src));
|
||||||
for (unsigned i = 0; i < nw; ++i)
|
src.copy_to(nw, dst);
|
||||||
dst[i] = (~fixed[i] & src[i]) | (fixed[i] & m_bits[i]);
|
dst.set_bw(bw);
|
||||||
|
inf_feasible(dst);
|
||||||
|
if (in_range(dst)) {
|
||||||
|
SASSERT(can_set(dst));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
if (dst > m_lo)
|
||||||
// If dst > src, then find the most significant
|
return false;
|
||||||
// bit where src[idx] = 0, dst[idx] = 1
|
m_lo.copy_to(nw, dst);
|
||||||
// set dst[j] = dst[j] & fixed_j for j < idx
|
SASSERT(can_set(dst));
|
||||||
//
|
|
||||||
for (unsigned i = nw; i-- > 0; ) {
|
|
||||||
if (0 != (dst[i] & ~src[i])) {
|
|
||||||
auto idx = log2(dst[i] & ~src[i]);
|
|
||||||
auto mask = (1 << idx);
|
|
||||||
dst[i] = dst[i] & (fixed[i] | mask);
|
|
||||||
for (unsigned j = i; j-- > 0; )
|
|
||||||
dst[j] = dst[j] & fixed[j];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SASSERT(!has_overflow(dst));
|
|
||||||
return round_up(dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sls_valuation::round_up(bvect& dst) const {
|
|
||||||
if (m_lo < m_hi) {
|
|
||||||
if (m_hi <= dst)
|
|
||||||
return false;
|
|
||||||
if (m_lo > dst)
|
|
||||||
set(dst, m_lo);
|
|
||||||
}
|
|
||||||
else if (m_hi <= dst && m_lo > dst)
|
|
||||||
set(dst, m_lo);
|
|
||||||
SASSERT(!has_overflow(dst));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sls_valuation::round_down(bvect& dst) const {
|
|
||||||
if (m_lo < m_hi) {
|
|
||||||
if (m_lo > dst)
|
|
||||||
return false;
|
|
||||||
if (m_hi <= dst) {
|
|
||||||
set(dst, m_hi);
|
|
||||||
sub1(dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (m_hi <= dst && m_lo > dst) {
|
|
||||||
set(dst, m_hi);
|
|
||||||
sub1(dst);
|
|
||||||
}
|
|
||||||
SASSERT(well_formed());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sls_valuation::set_random_at_most(bvect const& src, bvect& tmp, random_gen& r) {
|
bool sls_valuation::set_random_at_most(bvect const& src, bvect& tmp, random_gen& r) {
|
||||||
if (!get_at_most(src, tmp))
|
if (!get_at_most(src, tmp))
|
||||||
return false;
|
return false;
|
||||||
if (is_zero(tmp) || (0 == r() % 2))
|
|
||||||
|
if (is_zero(tmp) || (0 != r(10)))
|
||||||
return try_set(tmp);
|
return try_set(tmp);
|
||||||
|
|
||||||
set_random_below(tmp, r);
|
|
||||||
// random value below tmp
|
// random value below tmp
|
||||||
|
set_random_below(tmp, r);
|
||||||
if (m_lo == m_hi || is_zero(m_lo) || m_lo <= tmp)
|
|
||||||
return try_set(tmp);
|
return (can_set(tmp) || get_at_most(src, tmp)) && try_set(tmp);
|
||||||
|
|
||||||
// for simplicity, bail out if we were not lucky
|
|
||||||
return get_at_most(src, tmp) && try_set(tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sls_valuation::set_random_at_least(bvect const& src, bvect& tmp, random_gen& r) {
|
bool sls_valuation::set_random_at_least(bvect const& src, bvect& tmp, random_gen& r) {
|
||||||
if (!get_at_least(src, tmp))
|
if (!get_at_least(src, tmp))
|
||||||
return false;
|
return false;
|
||||||
if (is_ones(tmp) || (0 == r() % 2))
|
|
||||||
|
if (is_ones(tmp) || (0 != r(10)))
|
||||||
return try_set(tmp);
|
return try_set(tmp);
|
||||||
|
|
||||||
// random value at least tmp
|
// random value at least tmp
|
||||||
set_random_above(tmp, r);
|
set_random_above(tmp, r);
|
||||||
|
|
||||||
if (m_lo == m_hi || is_zero(m_hi) || m_hi > tmp)
|
|
||||||
return try_set(tmp);
|
|
||||||
|
|
||||||
// for simplicity, bail out if we were not lucky
|
return (can_set(tmp) || get_at_least(src, tmp)) && try_set(tmp);
|
||||||
return get_at_least(src, tmp) && try_set(tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sls_valuation::set_random_in_range(bvect const& lo, bvect const& hi, bvect& tmp, random_gen& r) {
|
bool sls_valuation::set_random_in_range(bvect const& lo, bvect const& hi, bvect& tmp, random_gen& r) {
|
||||||
|
@ -533,7 +463,7 @@ namespace bv {
|
||||||
|
|
||||||
//
|
//
|
||||||
// new_bits != bits => ~fixed
|
// new_bits != bits => ~fixed
|
||||||
// 0 = (new_bits ^ bits) & fixed
|
// 0 = (new_bits ^ bits) & fixedf
|
||||||
// also check that new_bits are in range
|
// also check that new_bits are in range
|
||||||
//
|
//
|
||||||
bool sls_valuation::can_set(bvect const& new_bits) const {
|
bool sls_valuation::can_set(bvect const& new_bits) const {
|
||||||
|
@ -559,14 +489,16 @@ namespace bv {
|
||||||
}
|
}
|
||||||
|
|
||||||
void sls_valuation::add_range(rational l, rational h) {
|
void sls_valuation::add_range(rational l, rational h) {
|
||||||
|
return;
|
||||||
//return;
|
//verbose_stream() << *this << " " << l << " " << h << " --> \n";
|
||||||
|
|
||||||
l = mod(l, rational::power_of_two(bw));
|
l = mod(l, rational::power_of_two(bw));
|
||||||
h = mod(h, rational::power_of_two(bw));
|
h = mod(h, rational::power_of_two(bw));
|
||||||
if (h == l)
|
if (h == l)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// verbose_stream() << *this << " " << l << " " << h << " --> ";
|
||||||
|
|
||||||
if (m_lo == m_hi) {
|
if (m_lo == m_hi) {
|
||||||
set_value(m_lo, l);
|
set_value(m_lo, l);
|
||||||
set_value(m_hi, h);
|
set_value(m_hi, h);
|
||||||
|
@ -591,19 +523,25 @@ namespace bv {
|
||||||
set_value(m_lo, l);
|
set_value(m_lo, l);
|
||||||
set_value(m_hi, h);
|
set_value(m_hi, h);
|
||||||
}
|
}
|
||||||
else if (old_lo + 1 == l) {
|
else if (old_lo + 1 == l)
|
||||||
|
set_value(m_lo, l);
|
||||||
|
else if (old_hi == h + 1)
|
||||||
|
set_value(m_hi, h);
|
||||||
|
else if (old_hi == h && old_lo < l)
|
||||||
set_value(m_lo, l);
|
set_value(m_lo, l);
|
||||||
}
|
else if (old_lo == l && h < old_hi)
|
||||||
else if (old_hi == h + 1) {
|
|
||||||
set_value(m_hi, h);
|
set_value(m_hi, h);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SASSERT(!has_overflow(m_lo));
|
SASSERT(!has_overflow(m_lo));
|
||||||
SASSERT(!has_overflow(m_hi));
|
SASSERT(!has_overflow(m_hi));
|
||||||
|
|
||||||
|
//verbose_stream() << *this << " --> ";
|
||||||
|
|
||||||
tighten_range();
|
tighten_range();
|
||||||
|
|
||||||
|
//verbose_stream() << *this << "\n";
|
||||||
SASSERT(well_formed());
|
SASSERT(well_formed());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,59 +559,76 @@ namespace bv {
|
||||||
// lo + 1 = hi -> set bits = lo
|
// lo + 1 = hi -> set bits = lo
|
||||||
// lo < hi, set most significant bits based on hi
|
// lo < hi, set most significant bits based on hi
|
||||||
//
|
//
|
||||||
|
|
||||||
|
unsigned sls_valuation::diff_index(bvect const& a) const {
|
||||||
|
unsigned index = 0;
|
||||||
|
for (unsigned i = nw; i-- > 0; ) {
|
||||||
|
auto diff = fixed[i] & (m_bits[i] ^ a[i]);
|
||||||
|
if (diff != 0 && index == 0)
|
||||||
|
index = 1 + i * 8 * sizeof(digit_t) + log2(diff);
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sls_valuation::inf_feasible(bvect& a) const {
|
||||||
|
unsigned lo_index = diff_index(a);
|
||||||
|
|
||||||
|
if (lo_index != 0) {
|
||||||
|
lo_index--;
|
||||||
|
SASSERT(a.get(lo_index) != m_bits.get(lo_index));
|
||||||
|
SASSERT(fixed.get(lo_index));
|
||||||
|
for (unsigned i = 0; i <= lo_index; ++i) {
|
||||||
|
if (!fixed.get(i))
|
||||||
|
a.set(i, false);
|
||||||
|
else if (fixed.get(i))
|
||||||
|
a.set(i, m_bits.get(i));
|
||||||
|
}
|
||||||
|
if (!a.get(lo_index)) {
|
||||||
|
for (unsigned i = lo_index + 1; i < bw; ++i)
|
||||||
|
if (!fixed.get(i) && !a.get(i)) {
|
||||||
|
a.set(i, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sls_valuation::sup_feasible(bvect& a) const {
|
||||||
|
unsigned hi_index = diff_index(a);
|
||||||
|
if (hi_index != 0) {
|
||||||
|
hi_index--;
|
||||||
|
SASSERT(a.get(hi_index) != m_bits.get(hi_index));
|
||||||
|
SASSERT(fixed.get(hi_index));
|
||||||
|
for (unsigned i = 0; i <= hi_index; ++i) {
|
||||||
|
if (!fixed.get(i))
|
||||||
|
a.set(i, true);
|
||||||
|
else if (fixed.get(i))
|
||||||
|
a.set(i, m_bits.get(i));
|
||||||
|
}
|
||||||
|
if (a.get(hi_index)) {
|
||||||
|
for (unsigned i = hi_index + 1; i < bw; ++i)
|
||||||
|
if (!fixed.get(i) && a.get(i)) {
|
||||||
|
a.set(i, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void sls_valuation::tighten_range() {
|
void sls_valuation::tighten_range() {
|
||||||
|
|
||||||
if (m_lo == m_hi)
|
if (m_lo == m_hi)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
inf_feasible(m_lo);
|
||||||
|
|
||||||
bvect hi1(nw);
|
bvect hi1(nw);
|
||||||
hi1.set_bw(bw);
|
hi1.set_bw(bw);
|
||||||
m_hi.copy_to(nw, hi1);
|
m_hi.copy_to(nw, hi1);
|
||||||
sub1(hi1);
|
sub1(hi1);
|
||||||
unsigned lo_index = 0, hi_index = 0;
|
sup_feasible(hi1);
|
||||||
for (unsigned i = nw; i-- > 0; ) {
|
add1(hi1);
|
||||||
auto lo_diff = (fixed[i] & (m_bits[i] ^ m_lo[i]));
|
hi1.copy_to(nw, m_hi);
|
||||||
if (lo_diff != 0 && lo_index == 0)
|
|
||||||
lo_index = 1 + i * 8 * sizeof(digit_t) + log2(lo_diff);
|
|
||||||
auto hi_diff = (fixed[i] & (m_bits[i] ^ hi1[i]));
|
|
||||||
if (hi_diff != 0 && hi_index == 0)
|
|
||||||
hi_index = 1 + i * 8 * sizeof(digit_t) + log2(hi_diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lo_index != 0) {
|
|
||||||
lo_index--;
|
|
||||||
SASSERT(m_lo.get(lo_index) != m_bits.get(lo_index));
|
|
||||||
SASSERT(fixed.get(lo_index));
|
|
||||||
for (unsigned i = 0; i <= lo_index; ++i) {
|
|
||||||
if (!fixed.get(i))
|
|
||||||
m_lo.set(i, false);
|
|
||||||
else if (fixed.get(i))
|
|
||||||
m_lo.set(i, m_bits.get(i));
|
|
||||||
}
|
|
||||||
for (unsigned i = lo_index + 1; i < bw; ++i)
|
|
||||||
if (!fixed.get(i) && !m_lo.get(i)) {
|
|
||||||
m_lo.set(i, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hi_index != 0) {
|
|
||||||
hi_index--;
|
|
||||||
SASSERT(hi1.get(hi_index) != m_bits.get(hi_index));
|
|
||||||
SASSERT(fixed.get(hi_index));
|
|
||||||
for (unsigned i = 0; i <= hi_index; ++i) {
|
|
||||||
if (!fixed.get(i))
|
|
||||||
hi1.set(i, true);
|
|
||||||
else if (fixed.get(i))
|
|
||||||
hi1.set(i, m_bits.get(i));
|
|
||||||
}
|
|
||||||
for (unsigned i = hi_index + 1; i < bw; ++i)
|
|
||||||
if (!fixed.get(i) && hi1.get(i)) {
|
|
||||||
hi1.set(i, false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
add1(hi1);
|
|
||||||
hi1.copy_to(nw, m_hi);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_range() && !in_range(m_bits))
|
if (has_range() && !in_range(m_bits))
|
||||||
m_bits = m_lo;
|
m_bits = m_lo;
|
||||||
|
|
|
@ -113,8 +113,6 @@ namespace bv {
|
||||||
unsigned m_signed_prefix = 0;
|
unsigned m_signed_prefix = 0;
|
||||||
|
|
||||||
unsigned mask;
|
unsigned mask;
|
||||||
bool round_up(bvect& dst) const;
|
|
||||||
bool round_down(bvect& dst) const;
|
|
||||||
|
|
||||||
void repair_sign_bits(bvect& dst) const;
|
void repair_sign_bits(bvect& dst) const;
|
||||||
|
|
||||||
|
@ -141,9 +139,11 @@ namespace bv {
|
||||||
SASSERT(in_range(m_bits));
|
SASSERT(in_range(m_bits));
|
||||||
if (fixed.get(i) && get_bit(i) != b)
|
if (fixed.get(i) && get_bit(i) != b)
|
||||||
return false;
|
return false;
|
||||||
|
m_bits.set(i, b);
|
||||||
eval.set(i, b);
|
eval.set(i, b);
|
||||||
if (in_range(m_bits))
|
if (in_range(m_bits))
|
||||||
return true;
|
return true;
|
||||||
|
m_bits.set(i, !b);
|
||||||
eval.set(i, !b);
|
eval.set(i, !b);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,9 @@ namespace bv {
|
||||||
rational lo() const { return m_lo.get_value(nw); }
|
rational lo() const { return m_lo.get_value(nw); }
|
||||||
rational hi() const { return m_hi.get_value(nw); }
|
rational hi() const { return m_hi.get_value(nw); }
|
||||||
|
|
||||||
|
unsigned diff_index(bvect const& a) const;
|
||||||
|
void inf_feasible(bvect& a) const;
|
||||||
|
void sup_feasible(bvect& a) const;
|
||||||
|
|
||||||
void get(bvect& dst) const;
|
void get(bvect& dst) const;
|
||||||
void add_range(rational lo, rational hi);
|
void add_range(rational lo, rational hi);
|
||||||
|
|
|
@ -175,8 +175,8 @@ public:
|
||||||
m_st.reset();
|
m_st.reset();
|
||||||
m_sls->collect_statistics(m_st);
|
m_sls->collect_statistics(m_st);
|
||||||
report_tactic_progress("Number of flips:", m_sls->get_num_moves());
|
report_tactic_progress("Number of flips:", m_sls->get_num_moves());
|
||||||
IF_VERBOSE(20, verbose_stream() << res << "\n");
|
IF_VERBOSE(10, verbose_stream() << res << "\n");
|
||||||
IF_VERBOSE(20, m_sls->display(verbose_stream()));
|
IF_VERBOSE(10, m_sls->display(verbose_stream()));
|
||||||
|
|
||||||
if (res == l_true) {
|
if (res == l_true) {
|
||||||
if (g->models_enabled()) {
|
if (g->models_enabled()) {
|
||||||
|
|
Loading…
Reference in a new issue