box find_CE_via_underapprox(box const & b, unordered_set<Enode*> const & forall_vars, vector<Enode*> const & vec, bool const p, SMTConfig & config) {
    box counterexample(b, forall_vars);
    if (config.nra_shrink_for_dop) {
        counterexample = shrink_for_dop(counterexample);
    }
    auto vars = counterexample.get_vars();
    unordered_set<Enode*> const var_set(vars.begin(), vars.end());
    ibex::IntervalVector & iv = counterexample.get_values();
    for (Enode * e : vec) {
        lbool polarity = p ? l_False : l_True;
        if (e->isNot()) {
            e = e->get1st();
            polarity = !polarity;
        }
        if (e->isOr() || e->isAnd()) {
            break;
        }
        nonlinear_constraint ctr(e, var_set, polarity);
        if (ctr.is_neq()) {
            break;
        }
        auto numctr = ctr.get_numctr();
        // Construct iv from box b
        if (numctr->op == ibex::CmpOp::GT || numctr->op == ibex::CmpOp::GEQ) {
            numctr->f.ibwd(ibex::Interval(0.0, POS_INFINITY), iv);
        } else if (numctr->op == ibex::CmpOp::LT || numctr->op == ibex::CmpOp::LEQ) {
            numctr->f.ibwd(ibex::Interval(NEG_INFINITY, 0.0), iv);
        } else if (numctr->op == ibex::CmpOp::EQ) {
            numctr->f.ibwd(ibex::Interval(0.0, 0.0), iv);
        } else {
            ostringstream ss;
            ss << "find_CE_via_underapprox: unknown constraint type, " << *numctr;
            throw runtime_error(ss.str());
        }
        if (iv.is_empty()) {
            // stop when counterexample is already empty;
            return counterexample;
        }
    }
    if (!iv.is_empty()) {
        iv = iv.mid();
    }
    return counterexample;
}
box find_CE_via_overapprox(box const & b, unordered_set<Enode*> const & forall_vars, vector<Enode*> const & vec, bool const p, SMTConfig & config) {
    vector<contractor> ctcs;
    box counterexample(b, forall_vars);
    if (config.nra_shrink_for_dop) {
        counterexample = shrink_for_dop(counterexample);
    }
    auto vars = counterexample.get_vars();
    unordered_set<Enode *> var_set(vars.begin(), vars.end());
    for (Enode * e : vec) {
        lbool polarity = p ? l_False : l_True;
        if (e->isNot()) {
            polarity = !polarity;
            e = e->get1st();
        }
        contractor ctc = make_contractor(e, polarity, counterexample, var_set);
        ctcs.push_back(ctc);
    }
    contractor fp = mk_contractor_fixpoint(default_strategy::term_cond, ctcs);
    random_icp icp(fp, config);
    counterexample = icp.solve(counterexample, config.nra_precision);
    return counterexample;
}