1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.

1648 lines
52 KiB
C++

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Guido Tack <guido.tack@monash.edu>
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <minizinc/flat_exp.hh>
#include <list>
namespace MiniZinc {
ASTString op_to_builtin(Expression* op_lhs, Expression* op_rhs, BinOpType bot) {
std::string builtin;
if (op_rhs->type().isint()) {
switch (bot) {
case BOT_PLUS:
return constants().ids.int_.plus;
case BOT_MINUS:
return constants().ids.int_.minus;
case BOT_MULT:
return constants().ids.int_.times;
case BOT_POW:
return constants().ids.pow;
case BOT_IDIV:
return constants().ids.int_.div;
case BOT_MOD:
return constants().ids.int_.mod;
case BOT_LE:
return constants().ids.int_.lt;
case BOT_LQ:
return constants().ids.int_.le;
case BOT_GR:
return constants().ids.int_.gt;
case BOT_GQ:
return constants().ids.int_.ge;
case BOT_EQ:
return constants().ids.int_.eq;
case BOT_NQ:
return constants().ids.int_.ne;
default:
throw InternalError("not yet implemented");
}
} else if (op_rhs->type().isbool()) {
if (bot == BOT_EQ || bot == BOT_EQUIV) {
return constants().ids.bool_eq;
}
builtin = "bool_";
} else if (op_rhs->type().isSet()) {
builtin = "set_";
} else if (op_rhs->type().isfloat()) {
switch (bot) {
case BOT_PLUS:
return constants().ids.float_.plus;
case BOT_MINUS:
return constants().ids.float_.minus;
case BOT_MULT:
return constants().ids.float_.times;
case BOT_POW:
return constants().ids.pow;
case BOT_DIV:
return constants().ids.float_.div;
case BOT_MOD:
return constants().ids.float_.mod;
case BOT_LE:
return constants().ids.float_.lt;
case BOT_LQ:
return constants().ids.float_.le;
case BOT_GR:
return constants().ids.float_.gt;
case BOT_GQ:
return constants().ids.float_.ge;
case BOT_EQ:
return constants().ids.float_.eq;
case BOT_NQ:
return constants().ids.float_.ne;
default:
throw InternalError("not yet implemented");
}
} else if (op_rhs->type().isOpt() && (bot == BOT_EQUIV || bot == BOT_EQ)) {
/// TODO: extend to all option type operators
switch (op_lhs->type().bt()) {
case Type::BT_BOOL:
return constants().ids.bool_eq;
case Type::BT_FLOAT:
return constants().ids.float_.eq;
case Type::BT_INT:
if (op_lhs->type().st() == Type::ST_PLAIN) {
return constants().ids.int_.eq;
} else {
return constants().ids.set_eq;
}
default:
throw InternalError("not yet implemented");
}
} else {
throw InternalError("Operator not yet implemented");
}
switch (bot) {
case BOT_PLUS:
return builtin + "plus";
case BOT_MINUS:
return builtin + "minus";
case BOT_MULT:
return builtin + "times";
case BOT_DIV:
case BOT_IDIV:
return builtin + "div";
case BOT_MOD:
return builtin + "mod";
case BOT_LE:
return builtin + "lt";
case BOT_LQ:
return builtin + "le";
case BOT_GR:
return builtin + "gt";
case BOT_GQ:
return builtin + "ge";
case BOT_EQ:
return builtin + "eq";
case BOT_NQ:
return builtin + "ne";
case BOT_IN:
return constants().ids.set_in;
case BOT_SUBSET:
return builtin + "subset";
case BOT_SUPERSET:
return builtin + "superset";
case BOT_UNION:
return builtin + "union";
case BOT_DIFF:
return builtin + "diff";
case BOT_SYMDIFF:
return builtin + "symdiff";
case BOT_INTERSECT:
return builtin + "intersect";
case BOT_PLUSPLUS:
case BOT_DOTDOT:
throw InternalError("not yet implemented");
case BOT_EQUIV:
return builtin + "eq";
case BOT_IMPL:
return builtin + "le";
case BOT_RIMPL:
return builtin + "ge";
case BOT_OR:
return builtin + "or";
case BOT_AND:
return builtin + "and";
case BOT_XOR:
return constants().ids.bool_xor;
default:
assert(false);
return ASTString();
}
}
ASTString op_to_id(BinOpType bot) {
switch (bot) {
case BOT_PLUS:
return ASTString("'+'");
case BOT_MINUS:
return ASTString("'-'");
case BOT_MULT:
return ASTString("'*'");
case BOT_DIV:
return ASTString("'/'");
case BOT_IDIV:
return ASTString("'div'");
case BOT_MOD:
return ASTString("'mod'");
case BOT_LE:
return ASTString("'<'");
case BOT_LQ:
return ASTString("'<='");
case BOT_GR:
return ASTString("'>'");
case BOT_GQ:
return ASTString("'>='");
case BOT_EQ:
return ASTString("'='");
case BOT_NQ:
return ASTString("'!='");
case BOT_IN:
return ASTString("'in'");
case BOT_SUBSET:
return ASTString("'subset'");
case BOT_SUPERSET:
return ASTString("'superset'");
case BOT_UNION:
return ASTString("'union'");
case BOT_DIFF:
return ASTString("'diff'");
case BOT_SYMDIFF:
return ASTString("'symdiff'");
case BOT_INTERSECT:
return ASTString("'intersect'");
case BOT_PLUSPLUS:
return ASTString("'++'");
case BOT_DOTDOT:
return ASTString("'..'");
case BOT_EQUIV:
return ASTString("'<->'");
case BOT_IMPL:
return ASTString("'->'");
case BOT_RIMPL:
return ASTString("'<-'");
case BOT_OR:
return ASTString("'\\/'");
case BOT_AND:
return ASTString("'/\\'");
case BOT_XOR:
return ASTString("'xor'");
default:
assert(false);
return ASTString("");
}
}
bool is_reverse_map(BinOp* e) { return e->ann().contains(constants().ann.is_reverse_map); }
template <class Lit>
void collect_linexps(EnvI& env, typename LinearTraits<Lit>::Val in_c, Expression* exp,
std::vector<typename LinearTraits<Lit>::Val>& coeffs,
std::vector<KeepAlive>& vars, typename LinearTraits<Lit>::Val& constval) {
typedef typename LinearTraits<Lit>::Val Val;
struct StackItem {
Expression* e;
Val c;
StackItem(Expression* e0, Val c0) : e(e0), c(c0) {}
};
std::vector<StackItem> stack;
stack.push_back(StackItem(exp, in_c));
while (!stack.empty()) {
Expression* e = stack.back().e;
Val c = stack.back().c;
stack.pop_back();
if (e == nullptr) {
continue;
}
if (e->type().isPar()) {
constval += c * LinearTraits<Lit>::eval(env, e);
} else if (Lit* l = e->dynamicCast<Lit>()) {
constval += c * l->v();
} else if (auto* bo = e->dynamicCast<BinOp>()) {
switch (bo->op()) {
case BOT_PLUS:
stack.push_back(StackItem(bo->lhs(), c));
stack.push_back(StackItem(bo->rhs(), c));
break;
case BOT_MINUS:
stack.push_back(StackItem(bo->lhs(), c));
stack.push_back(StackItem(bo->rhs(), -c));
break;
case BOT_MULT:
if (bo->lhs()->type().isPar()) {
stack.push_back(StackItem(bo->rhs(), c * LinearTraits<Lit>::eval(env, bo->lhs())));
} else if (bo->rhs()->type().isPar()) {
stack.push_back(StackItem(bo->lhs(), c * LinearTraits<Lit>::eval(env, bo->rhs())));
} else {
coeffs.push_back(c);
vars.emplace_back(e);
}
break;
case BOT_DIV:
if (bo->rhs()->isa<FloatLit>() && bo->rhs()->cast<FloatLit>()->v() == 1.0) {
stack.push_back(StackItem(bo->lhs(), c));
} else {
coeffs.push_back(c);
vars.emplace_back(e);
}
break;
case BOT_IDIV:
if (bo->rhs()->isa<IntLit>() && bo->rhs()->cast<IntLit>()->v() == 1) {
stack.push_back(StackItem(bo->lhs(), c));
} else {
coeffs.push_back(c);
vars.emplace_back(e);
}
break;
default:
coeffs.push_back(c);
vars.emplace_back(e);
break;
}
// } else if (Call* call = e->dynamicCast<Call>()) {
// /// TODO! Handle sum, lin_exp (maybe not that important?)
} else {
coeffs.push_back(c);
vars.emplace_back(e);
}
}
}
template <class Lit>
KeepAlive mklinexp(EnvI& env, typename LinearTraits<Lit>::Val c0,
typename LinearTraits<Lit>::Val c1, Expression* e0, Expression* e1) {
typedef typename LinearTraits<Lit>::Val Val;
GCLock lock;
std::vector<Val> coeffs;
std::vector<KeepAlive> vars;
Val constval = 0;
collect_linexps<Lit>(env, c0, e0, coeffs, vars, constval);
collect_linexps<Lit>(env, c1, e1, coeffs, vars, constval);
simplify_lin<Lit>(coeffs, vars, constval);
KeepAlive ka;
if (coeffs.empty()) {
ka = LinearTraits<Lit>::newLit(constval);
} else if (coeffs.size() == 1 && coeffs[0] == 1 && constval == 0) {
ka = vars[0];
} else {
std::vector<Expression*> coeffs_e(coeffs.size());
for (auto i = static_cast<unsigned int>(coeffs.size()); i--;) {
if (!LinearTraits<Lit>::finite(coeffs[i])) {
throw FlatteningError(
env, e0->loc(),
"unbounded coefficient in linear expression."
" Make sure variables involved in non-linear/logical expressions have finite bounds"
" in their definition or via constraints");
}
coeffs_e[i] = LinearTraits<Lit>::newLit(coeffs[i]);
}
std::vector<Expression*> vars_e(vars.size());
for (auto i = static_cast<unsigned int>(vars.size()); i--;) {
vars_e[i] = vars[i]();
}
std::vector<Expression*> args(3);
args[0] = new ArrayLit(e0->loc(), coeffs_e);
Type t = coeffs_e[0]->type();
t.dim(1);
args[0]->type(t);
args[1] = new ArrayLit(e0->loc(), vars_e);
Type tt = vars_e[0]->type();
tt.dim(1);
args[1]->type(tt);
args[2] = LinearTraits<Lit>::newLit(constval);
Call* c = new Call(e0->loc().introduce(), constants().ids.lin_exp, args);
add_path_annotation(env, c);
tt = args[1]->type();
tt.dim(0);
c->decl(env.model->matchFn(env, c, false));
if (c->decl() == nullptr) {
throw FlatteningError(env, c->loc(), "cannot find matching declaration");
}
c->type(c->decl()->rtype(env, args, false));
ka = c;
}
assert(ka());
return ka;
}
Call* aggregate_and_or_ops(EnvI& env, BinOp* bo, bool negateArgs, BinOpType bot) {
assert(bot == BOT_AND || bot == BOT_OR);
BinOpType negbot = (bot == BOT_AND ? BOT_OR : BOT_AND);
typedef std::pair<Expression*, bool> arg_literal;
typedef std::list<arg_literal> arg_literal_l;
arg_literal_l bo_args({arg_literal(bo->lhs(), !negateArgs), arg_literal(bo->rhs(), !negateArgs)});
std::vector<Expression*> output_pos;
std::vector<Expression*> output_neg;
auto i = bo_args.begin();
while (i != bo_args.end()) {
auto* bo_arg = i->first->dynamicCast<BinOp>();
UnOp* uo_arg = i->first->dynamicCast<UnOp>();
bool positive = i->second;
if ((bo_arg != nullptr) && positive && bo_arg->op() == bot) {
i->first = bo_arg->lhs();
i++;
bo_args.insert(i, arg_literal(bo_arg->rhs(), true));
i--;
i--;
} else if ((bo_arg != nullptr) && !positive && bo_arg->op() == negbot) {
i->first = bo_arg->lhs();
i++;
bo_args.insert(i, arg_literal(bo_arg->rhs(), false));
i--;
i--;
} else if ((uo_arg != nullptr) && !positive && uo_arg->op() == UOT_NOT) {
i->first = uo_arg->e();
i->second = true;
} else if (bot == BOT_OR && (uo_arg != nullptr) && positive && uo_arg->op() == UOT_NOT) {
output_neg.push_back(uo_arg->e());
i++;
} else {
if (positive) {
output_pos.push_back(i->first);
} else {
output_neg.push_back(i->first);
}
i++;
}
}
Call* c;
std::vector<Expression*> c_args(1);
if (bot == BOT_AND) {
for (auto& i : output_neg) {
UnOp* neg_arg = new UnOp(i->loc(), UOT_NOT, i);
neg_arg->type(i->type());
output_pos.push_back(neg_arg);
}
auto* al = new ArrayLit(bo->loc().introduce(), output_pos);
Type al_t = bo->type();
al_t.dim(1);
al->type(al_t);
env.annotateFromCallStack(al);
c_args[0] = al;
c = new Call(bo->loc().introduce(),
bot == BOT_AND ? constants().ids.forall : constants().ids.exists, c_args);
} else {
auto* al_pos = new ArrayLit(bo->loc().introduce(), output_pos);
Type al_t = bo->type();
al_t.dim(1);
al_pos->type(al_t);
env.annotateFromCallStack(al_pos);
c_args[0] = al_pos;
if (!output_neg.empty()) {
auto* al_neg = new ArrayLit(bo->loc().introduce(), output_neg);
al_neg->type(al_t);
env.annotateFromCallStack(al_neg);
c_args.push_back(al_neg);
}
c = new Call(bo->loc().introduce(),
output_neg.empty() ? constants().ids.exists : constants().ids.clause, c_args);
}
c->decl(env.model->matchFn(env, c, false));
assert(c->decl());
Type t = c->decl()->rtype(env, c_args, false);
t.cv(bo->type().cv());
c->type(t);
return c;
}
/// Return a lin_exp or id if \a e is a lin_exp or id
template <class Lit>
Expression* get_linexp(Expression* e) {
for (;;) {
if (e && e->eid() == Expression::E_ID && e != constants().absent) {
if (e->cast<Id>()->decl()->e()) {
e = e->cast<Id>()->decl()->e();
} else {
break;
}
} else {
break;
}
}
if (e && (e->isa<Id>() || e->isa<Lit>() ||
(e->isa<Call>() && e->cast<Call>()->id() == constants().ids.lin_exp))) {
return e;
}
return nullptr;
}
template <class Lit>
void flatten_linexp_binop(EnvI& env, const Ctx& ctx, VarDecl* r, VarDecl* b, EE& ret,
Expression* le0, Expression* le1, BinOpType& bot, bool doubleNeg,
std::vector<EE>& ees, std::vector<KeepAlive>& args, ASTString& callid) {
typedef typename LinearTraits<Lit>::Val Val;
std::vector<Val> coeffv;
std::vector<KeepAlive> alv;
Val d = 0;
Expression* le[2] = {le0, le1};
// Assign linear expression directly if one side is an Id.
Id* assignTo = nullptr;
if (bot == BOT_EQ && ctx.b == C_ROOT) {
if (le0->isa<Id>()) {
assignTo = le0->cast<Id>();
} else if (le1->isa<Id>()) {
assignTo = le1->cast<Id>();
}
}
for (unsigned int i = 0; i < 2; i++) {
Val sign = (i == 0 ? 1 : -1);
if (Lit* l = le[i]->dynamicCast<Lit>()) {
try {
d += sign * l->v();
} catch (ArithmeticError& e) {
throw EvalError(env, l->loc(), e.msg());
}
} else if (le[i]->isa<Id>()) {
coeffv.push_back(sign);
alv.emplace_back(le[i]);
} else if (Call* sc = le[i]->dynamicCast<Call>()) {
GCLock lock;
ArrayLit* sc_coeff = eval_array_lit(env, sc->arg(0));
ArrayLit* sc_al = eval_array_lit(env, sc->arg(1));
try {
d += sign * LinearTraits<Lit>::eval(env, sc->arg(2));
for (unsigned int j = 0; j < sc_coeff->size(); j++) {
coeffv.push_back(sign * LinearTraits<Lit>::eval(env, (*sc_coeff)[j]));
alv.emplace_back((*sc_al)[j]);
}
} catch (ArithmeticError& e) {
throw EvalError(env, sc->loc(), e.msg());
}
} else {
throw EvalError(env, le[i]->loc(),
"Internal error, unexpected expression inside linear expression");
}
}
simplify_lin<Lit>(coeffv, alv, d);
if (coeffv.empty()) {
bool result;
switch (bot) {
case BOT_LE:
result = (0 < -d);
break;
case BOT_LQ:
result = (0 <= -d);
break;
case BOT_GR:
result = (0 > -d);
break;
case BOT_GQ:
result = (0 >= -d);
break;
case BOT_EQ:
result = (0 == -d);
break;
case BOT_NQ:
result = (0 != -d);
break;
default:
assert(false);
break;
}
if (doubleNeg) {
result = !result;
}
ees[2].b = constants().boollit(result);
ret.r = conj(env, r, ctx, ees);
return;
}
if (coeffv.size() == 1 && std::abs(coeffv[0]) == 1) {
if (coeffv[0] == -1) {
switch (bot) {
case BOT_LE:
bot = BOT_GR;
break;
case BOT_LQ:
bot = BOT_GQ;
break;
case BOT_GR:
bot = BOT_LE;
break;
case BOT_GQ:
bot = BOT_LQ;
break;
default:
break;
}
} else {
d = -d;
}
typename LinearTraits<Lit>::Bounds ib = LinearTraits<Lit>::computeBounds(env, alv[0]());
if (ib.valid) {
bool failed = false;
bool subsumed = false;
switch (bot) {
case BOT_LE:
subsumed = ib.u < d;
failed = ib.l >= d;
break;
case BOT_LQ:
subsumed = ib.u <= d;
failed = ib.l > d;
break;
case BOT_GR:
subsumed = ib.l > d;
failed = ib.u <= d;
break;
case BOT_GQ:
subsumed = ib.l >= d;
failed = ib.u < d;
break;
case BOT_EQ:
subsumed = ib.l == d && ib.u == d;
failed = ib.u < d || ib.l > d;
break;
case BOT_NQ:
subsumed = ib.u < d || ib.l > d;
failed = ib.l == d && ib.u == d;
break;
default:
break;
}
if (doubleNeg) {
std::swap(subsumed, failed);
}
if (subsumed) {
ees[2].b = constants().literalTrue;
ret.r = conj(env, r, ctx, ees);
return;
}
if (failed) {
ees[2].b = constants().literalFalse;
ret.r = conj(env, r, ctx, ees);
return;
}
}
if (ctx.b == C_ROOT && alv[0]()->isa<Id>() && bot == BOT_EQ) {
GCLock lock;
VarDecl* vd = alv[0]()->cast<Id>()->decl();
if (vd->ti()->domain()) {
typename LinearTraits<Lit>::Domain domain =
LinearTraits<Lit>::evalDomain(env, vd->ti()->domain());
if (LinearTraits<Lit>::domainContains(domain, d)) {
if (!LinearTraits<Lit>::domainEquals(domain, d)) {
set_computed_domain(env, vd, LinearTraits<Lit>::newDomain(d), false);
}
ret.r = bind(env, ctx, r, constants().literalTrue);
} else {
ret.r = bind(env, ctx, r, constants().literalFalse);
}
} else {
set_computed_domain(env, vd, LinearTraits<Lit>::newDomain(d), false);
ret.r = bind(env, ctx, r, constants().literalTrue);
}
} else {
GCLock lock;
Expression* e0;
Expression* e1;
BinOpType old_bot = bot;
Val old_d = d;
switch (bot) {
case BOT_LE:
e0 = alv[0]();
if (e0->type().isint()) {
d--;
bot = BOT_LQ;
}
e1 = LinearTraits<Lit>::newLit(d);
break;
case BOT_GR:
e1 = alv[0]();
if (e1->type().isint()) {
d++;
bot = BOT_LQ;
} else {
bot = BOT_LE;
}
e0 = LinearTraits<Lit>::newLit(d);
break;
case BOT_GQ:
e0 = LinearTraits<Lit>::newLit(d);
e1 = alv[0]();
bot = BOT_LQ;
break;
default:
e0 = alv[0]();
e1 = LinearTraits<Lit>::newLit(d);
}
if (ctx.b == C_ROOT && alv[0]()->isa<Id>() && !env.hasReverseMapper(alv[0]()->cast<Id>()) &&
alv[0]()->cast<Id>()->decl()->ti()->domain()) {
VarDecl* vd = alv[0]()->cast<Id>()->decl();
typename LinearTraits<Lit>::Domain domain =
LinearTraits<Lit>::evalDomain(env, vd->ti()->domain());
typename LinearTraits<Lit>::Domain ndomain =
LinearTraits<Lit>::limitDomain(old_bot, domain, old_d);
if (domain && ndomain) {
if (LinearTraits<Lit>::domainEmpty(ndomain)) {
ret.r = bind(env, ctx, r, constants().literalFalse);
return;
}
if (!LinearTraits<Lit>::domainEquals(domain, ndomain)) {
ret.r = bind(env, ctx, r, constants().literalTrue);
set_computed_domain(env, vd, LinearTraits<Lit>::newDomain(ndomain), false);
if (r == constants().varTrue) {
auto* bo = new BinOp(Location().introduce(), e0, bot, e1);
bo->type(Type::varbool());
std::vector<Expression*> boargs(2);
boargs[0] = e0;
boargs[1] = e1;
Call* c = new Call(Location(), op_to_builtin(e0, e1, bot), boargs);
c->type(Type::varbool());
c->decl(env.model->matchFn(env, c, false));
auto it = env.cseMapFind(c);
if (it != env.cseMapEnd()) {
if (Id* ident = it->second.r()->template dynamicCast<Id>()) {
bind(env, Ctx(), ident->decl(), constants().literalTrue);
it->second.r = constants().literalTrue;
}
if (Id* ident = it->second.b()->template dynamicCast<Id>()) {
bind(env, Ctx(), ident->decl(), constants().literalTrue);
it->second.b = constants().literalTrue;
}
}
}
}
return;
}
}
args.emplace_back(e0);
args.emplace_back(e1);
}
} else if (bot == BOT_EQ && coeffv.size() == 2 && coeffv[0] == -coeffv[1] && d == 0) {
Id* id0 = alv[0]()->cast<Id>();
Id* id1 = alv[1]()->cast<Id>();
if (ctx.b == C_ROOT && r == constants().varTrue &&
(id0->decl()->e() == nullptr || id1->decl()->e() == nullptr)) {
if (id0->decl()->e()) {
(void)bind(env, ctx, id1->decl(), id0);
} else {
(void)bind(env, ctx, id0->decl(), id1);
}
} else {
callid = LinearTraits<Lit>::id_eq();
args.emplace_back(alv[0]());
args.emplace_back(alv[1]());
}
} else {
GCLock lock;
if (assignTo != nullptr) {
Val resultCoeff = 0;
typename LinearTraits<Lit>::Bounds bounds(d, d, true);
for (auto i = static_cast<unsigned int>(coeffv.size()); i--;) {
if (alv[i]() == assignTo) {
resultCoeff = coeffv[i];
continue;
}
typename LinearTraits<Lit>::Bounds bound = LinearTraits<Lit>::computeBounds(env, alv[i]());
if (bound.valid && LinearTraits<Lit>::finite(bound)) {
if (coeffv[i] > 0) {
bounds.l += coeffv[i] * bound.l;
bounds.u += coeffv[i] * bound.u;
} else {
bounds.l += coeffv[i] * bound.u;
bounds.u += coeffv[i] * bound.l;
}
} else {
bounds.valid = false;
break;
}
}
if (bounds.valid && resultCoeff != 0) {
if (resultCoeff < 0) {
bounds.l = LinearTraits<Lit>::floorDiv(bounds.l, -resultCoeff);
bounds.u = LinearTraits<Lit>::ceilDiv(bounds.u, -resultCoeff);
} else {
Val bl = bounds.l;
bounds.l = LinearTraits<Lit>::ceilDiv(bounds.u, -resultCoeff);
bounds.u = LinearTraits<Lit>::floorDiv(bl, -resultCoeff);
}
VarDecl* vd = assignTo->decl();
if (vd->ti()->domain()) {
typename LinearTraits<Lit>::Domain domain =
LinearTraits<Lit>::evalDomain(env, vd->ti()->domain());
if (LinearTraits<Lit>::domainIntersects(domain, bounds.l, bounds.u)) {
typename LinearTraits<Lit>::Domain new_domain =
LinearTraits<Lit>::intersectDomain(domain, bounds.l, bounds.u);
if (!LinearTraits<Lit>::domainEquals(domain, new_domain)) {
set_computed_domain(env, vd, LinearTraits<Lit>::newDomain(new_domain), false);
}
} else {
ret.r = bind(env, ctx, r, constants().literalFalse);
}
} else {
set_computed_domain(env, vd, LinearTraits<Lit>::newDomain(bounds.l, bounds.u), true);
}
}
}
int coeff_sign;
LinearTraits<Lit>::constructLinBuiltin(bot, callid, coeff_sign, d);
std::vector<Expression*> coeff_ev(coeffv.size());
for (auto i = static_cast<unsigned int>(coeff_ev.size()); i--;) {
coeff_ev[i] = LinearTraits<Lit>::newLit(coeff_sign * coeffv[i]);
}
auto* ncoeff = new ArrayLit(Location().introduce(), coeff_ev);
Type t = coeff_ev[0]->type();
t.dim(1);
ncoeff->type(t);
args.emplace_back(ncoeff);
std::vector<Expression*> alv_e(alv.size());
Type tt = alv[0]()->type();
tt.dim(1);
for (auto i = static_cast<unsigned int>(alv.size()); i--;) {
if (alv[i]()->type().isvar()) {
tt.ti(Type::TI_VAR);
}
alv_e[i] = alv[i]();
}
auto* nal = new ArrayLit(Location().introduce(), alv_e);
nal->type(tt);
args.emplace_back(nal);
Lit* il = LinearTraits<Lit>::newLit(-d);
args.push_back(il);
}
}
EE flatten_binop(EnvI& env, const Ctx& input_ctx, Expression* e, VarDecl* r, VarDecl* b) {
Ctx ctx = input_ctx;
CallStackItem _csi(env, e);
EE ret;
auto* bo = e->cast<BinOp>();
if (is_reverse_map(bo)) {
CallArgItem cai(env);
Id* id = bo->lhs()->dynamicCast<Id>();
if (id == nullptr) {
throw EvalError(env, bo->lhs()->loc(), "Reverse mappers are only defined for identifiers");
}
if (bo->op() != BOT_EQ && bo->op() != BOT_EQUIV) {
throw EvalError(env, bo->loc(), "Reverse mappers have to use `=` as the operator");
}
Call* c = bo->rhs()->dynamicCast<Call>();
if (c == nullptr) {
throw EvalError(env, bo->rhs()->loc(), "Reverse mappers require call on right hand side");
}
std::vector<Expression*> args(c->argCount());
for (unsigned int i = 0; i < c->argCount(); i++) {
Id* idi = c->arg(i)->dynamicCast<Id>();
if (idi == nullptr) {
throw EvalError(env, c->arg(i)->loc(),
"Reverse mapper calls require identifiers as arguments");
}
EE ee = flat_exp(env, Ctx(), idi, nullptr, constants().varTrue);
args[i] = ee.r();
}
EE ee = flat_exp(env, Ctx(), id, nullptr, constants().varTrue);
GCLock lock;
Call* revMap = new Call(Location().introduce(), c->id(), args);
args.push_back(ee.r());
Call* keepAlive = new Call(Location().introduce(), constants().varRedef->id(), args);
keepAlive->type(Type::varbool());
keepAlive->decl(constants().varRedef);
ret = flat_exp(env, Ctx(), keepAlive, constants().varTrue, constants().varTrue);
if (ee.r()->isa<Id>()) {
env.reverseMappers.insert(ee.r()->cast<Id>(), revMap);
}
return ret;
}
if ((bo->op() == BOT_EQ || bo->op() == BOT_EQUIV) &&
(bo->lhs() == constants().absent || bo->rhs() == constants().absent)) {
GCLock lock;
std::vector<Expression*> args(1);
args[0] = bo->lhs() == constants().absent ? bo->rhs() : bo->lhs();
if (args[0] != constants().absent) {
Call* cr = new Call(bo->loc().introduce(), "absent", args);
cr->decl(env.model->matchFn(env, cr, false));
cr->type(cr->decl()->rtype(env, args, false));
ret = flat_exp(env, ctx, cr, r, b);
} else {
ret.b = bind(env, Ctx(), b, constants().literalTrue);
ret.r = bind(env, ctx, r, constants().literalTrue);
}
return ret;
}
Ctx ctx0 = ctx;
ctx0.neg = false;
Ctx ctx1 = ctx;
ctx1.neg = false;
BinOpType bot = bo->op();
if (bo->lhs()->type().isbool()) {
switch (bot) {
case BOT_EQ:
bot = BOT_EQUIV;
break;
case BOT_NQ:
bot = BOT_XOR;
break;
case BOT_LQ:
bot = BOT_IMPL;
break;
case BOT_GQ:
bot = BOT_RIMPL;
break;
default:
break;
}
}
bool negArgs = false;
bool doubleNeg = false;
if (ctx.neg) {
switch (bot) {
case BOT_AND:
ctx.b = -ctx.b;
ctx.neg = false;
negArgs = true;
bot = BOT_OR;
break;
case BOT_OR:
ctx.b = -ctx.b;
ctx.neg = false;
negArgs = true;
bot = BOT_AND;
break;
default:
break;
}
}
Expression* boe0 = bo->lhs();
Expression* boe1 = bo->rhs();
bool isBuiltin = bo->decl() == nullptr || bo->decl()->e() == nullptr;
switch (bot) {
case BOT_AND:
if (isBuiltin) {
if (r == constants().varTrue) {
Ctx nctx;
nctx.neg = negArgs;
nctx.b = negArgs ? C_NEG : C_ROOT;
std::vector<Expression*> todo;
todo.push_back(boe1);
todo.push_back(boe0);
while (!todo.empty()) {
Expression* e_todo = todo.back();
todo.pop_back();
auto* e_bo = e_todo->dynamicCast<BinOp>();
if ((e_bo != nullptr) && e_bo->op() == (negArgs ? BOT_OR : BOT_AND)) {
todo.push_back(e_bo->rhs());
todo.push_back(e_bo->lhs());
} else {
(void)flat_exp(env, nctx, e_todo, constants().varTrue, constants().varTrue);
}
}
ret.r = bind(env, ctx, r, constants().literalTrue);
break;
}
GC::lock();
Call* c = aggregate_and_or_ops(env, bo, negArgs, bot);
KeepAlive ka(c);
GC::unlock();
ret = flat_exp(env, ctx, c, r, b);
if (Id* id = ret.r()->dynamicCast<Id>()) {
add_ctx_ann(id->decl(), ctx.b);
}
break;
}
case BOT_OR:
if (isBuiltin) {
GC::lock();
Call* c = aggregate_and_or_ops(env, bo, negArgs, bot);
KeepAlive ka(c);
GC::unlock();
ret = flat_exp(env, ctx, c, r, b);
if (Id* id = ret.r()->dynamicCast<Id>()) {
add_ctx_ann(id->decl(), ctx.b);
}
break;
}
case BOT_PLUS:
if (isBuiltin) {
KeepAlive ka;
if (boe0->type().isint()) {
ka = mklinexp<IntLit>(env, 1, 1, boe0, boe1);
} else {
ka = mklinexp<FloatLit>(env, 1.0, 1.0, boe0, boe1);
}
ret = flat_exp(env, ctx, ka(), r, b);
break;
}
case BOT_MINUS:
if (isBuiltin) {
KeepAlive ka;
if (boe0->type().isint()) {
ka = mklinexp<IntLit>(env, 1, -1, boe0, boe1);
} else {
ka = mklinexp<FloatLit>(env, 1.0, -1.0, boe0, boe1);
}
ret = flat_exp(env, ctx, ka(), r, b);
break;
}
case BOT_MULT:
case BOT_IDIV:
case BOT_MOD:
case BOT_POW:
case BOT_DIV:
case BOT_UNION:
case BOT_DIFF:
case BOT_SYMDIFF:
case BOT_INTERSECT:
case BOT_DOTDOT: {
assert(!ctx0.neg);
assert(!ctx1.neg);
EE e0 = flat_exp(env, ctx0, boe0, nullptr, b);
EE e1 = flat_exp(env, ctx1, boe1, nullptr, b);
if (e0.r()->type().isPar() && e1.r()->type().isPar()) {
GCLock lock;
auto* parbo = new BinOp(bo->loc(), e0.r(), bo->op(), e1.r());
std::vector<Expression*> args(2);
args[0] = e0.r();
args[1] = e1.r();
FunctionI* fi = env.model->matchFn(env, bo->opToString(), args, false);
parbo->decl(fi);
Type tt = fi->rtype(env, {e0.r()->type(), e1.r()->type()}, false);
assert(tt.isPar());
parbo->type(tt);
try {
Expression* res = eval_par(env, parbo);
assert(!res->type().isunknown());
ret.r = bind(env, ctx, r, res);
std::vector<EE> ees(2);
ees[0].b = e0.b;
ees[1].b = e1.b;
ret.b = conj(env, b, Ctx(), ees);
} catch (ResultUndefinedError&) {
ret.r = create_dummy_value(env, e->type());
ret.b = bind(env, Ctx(), b, constants().literalFalse);
}
break;
}
if (isBuiltin && bot == BOT_MULT) {
Expression* e0r = e0.r();
Expression* e1r = e1.r();
if (e0r->type().isPar()) {
std::swap(e0r, e1r);
}
if (e1r->type().isPar() && e1r->type().isint()) {
IntVal coeff = eval_int(env, e1r);
KeepAlive ka = mklinexp<IntLit>(env, coeff, 0, e0r, nullptr);
ret = flat_exp(env, ctx, ka(), r, b);
break;
}
if (e1r->type().isPar() && e1r->type().isfloat()) {
FloatVal coeff = eval_float(env, e1r);
KeepAlive ka = mklinexp<FloatLit>(env, coeff, 0.0, e0r, nullptr);
ret = flat_exp(env, ctx, ka(), r, b);
break;
}
} else if (isBuiltin && (bot == BOT_DIV || bot == BOT_IDIV)) {
Expression* e0r = e0.r();
Expression* e1r = e1.r();
if (e1r->type().isPar() && e1r->type().isint()) {
IntVal coeff = eval_int(env, e1r);
if (coeff == 1) {
ret = flat_exp(env, ctx, e0r, r, b);
break;
}
} else if (e1r->type().isPar() && e1r->type().isfloat()) {
FloatVal coeff = eval_float(env, e1r);
if (coeff == 1.0) {
ret = flat_exp(env, ctx, e0r, r, b);
break;
}
KeepAlive ka = mklinexp<FloatLit>(env, 1.0 / coeff, 0.0, e0r, nullptr);
ret = flat_exp(env, ctx, ka(), r, b);
break;
}
}
GC::lock();
std::vector<Expression*> args(2);
args[0] = e0.r();
args[1] = e1.r();
Call* cc;
if (!isBuiltin) {
cc = new Call(bo->loc().introduce(), bo->opToString(), args);
} else {
cc = new Call(bo->loc().introduce(), op_to_builtin(args[0], args[1], bot), args);
}
cc->type(bo->type());
EnvI::CSEMap::iterator cit;
if ((cit = env.cseMapFind(cc)) != env.cseMapEnd()) {
ret.b = bind(env, Ctx(), b, env.ignorePartial ? constants().literalTrue : cit->second.b());
ret.r = bind(env, ctx, r, cit->second.r());
} else {
if (FunctionI* fi = env.model->matchFn(env, cc->id(), args, false)) {
assert(cc->type() == fi->rtype(env, args, false));
cc->decl(fi);
cc->type(cc->decl()->rtype(env, args, false));
KeepAlive ka(cc);
GC::unlock();
EE ee = flat_exp(env, ctx, cc, r, nullptr);
GC::lock();
ret.r = ee.r;
std::vector<EE> ees(3);
ees[0].b = e0.b;
ees[1].b = e1.b;
ees[2].b = ee.b;
ret.b = conj(env, b, Ctx(), ees);
} else {
add_path_annotation(env, cc);
ret.r = bind(env, ctx, r, cc);
std::vector<EE> ees(2);
ees[0].b = e0.b;
ees[1].b = e1.b;
ret.b = conj(env, b, Ctx(), ees);
if (!ctx.neg) {
env.cseMapInsert(cc, ret);
}
}
}
}
GC::unlock();
break;
case BOT_RIMPL: {
std::swap(boe0, boe1);
}
// fall through
case BOT_IMPL: {
if (ctx.b == C_ROOT && r == constants().varTrue && boe0->type().isPar()) {
bool bval;
{
GCLock lock;
bval = eval_bool(env, boe0);
}
if (bval) {
Ctx nctx = ctx;
nctx.neg = negArgs;
nctx.b = negArgs ? C_NEG : C_ROOT;
ret = flat_exp(env, nctx, boe1, constants().varTrue, constants().varTrue);
} else {
Ctx nctx = ctx;
nctx.neg = negArgs;
nctx.b = negArgs ? C_NEG : C_ROOT;
ret = flat_exp(env, nctx, constants().literalTrue, constants().varTrue,
constants().varTrue);
}
break;
}
if (ctx.b == C_ROOT && r == constants().varTrue && boe1->type().isPar()) {
bool bval;
{
GCLock lock;
bval = eval_bool(env, boe1);
}
if (bval) {
Ctx nctx = ctx;
nctx.neg = negArgs;
nctx.b = negArgs ? C_NEG : C_ROOT;
ret = flat_exp(env, nctx, constants().literalTrue, constants().varTrue,
constants().varTrue);
break;
}
Ctx nctx = ctx;
nctx.neg = !negArgs;
nctx.b = !negArgs ? C_NEG : C_ROOT;
ret = flat_exp(env, nctx, boe0, constants().varTrue, constants().varTrue);
break;
}
GC::lock();
std::vector<Expression*> args;
ASTString id;
if (ctx.neg) {
std::vector<Expression*> bo_args(2);
bo_args[0] = boe0;
bo_args[1] = new UnOp(bo->loc(), UOT_NOT, boe1);
bo_args[1]->type(boe1->type());
id = constants().ids.forall;
args.push_back(new ArrayLit(bo->loc(), bo_args));
args[0]->type(Type::varbool(1));
} else {
std::vector<Expression*> clause_pos(1);
clause_pos[0] = boe1;
std::vector<Expression*> clause_neg(1);
clause_neg[0] = boe0;
args.push_back(new ArrayLit(boe1->loc().introduce(), clause_pos));
Type t0 = boe1->type();
t0.dim(1);
args[0]->type(t0);
args.push_back(new ArrayLit(boe0->loc().introduce(), clause_neg));
Type t1 = boe0->type();
t1.dim(1);
args[1]->type(t1);
id = constants().ids.clause;
}
ctx.neg = false;
Call* c = new Call(bo->loc().introduce(), id, args);
c->decl(env.model->matchFn(env, c, false));
if (c->decl() == nullptr) {
throw FlatteningError(env, c->loc(), "cannot find matching declaration");
}
c->type(c->decl()->rtype(env, args, false));
KeepAlive ka(c);
GC::unlock();
ret = flat_exp(env, ctx, c, r, b);
if (Id* id = ret.r()->dynamicCast<Id>()) {
add_ctx_ann(id->decl(), ctx.b);
}
} break;
case BOT_EQUIV:
if (ctx.neg) {
ctx.neg = false;
ctx.b = -ctx.b;
bot = BOT_XOR;
ctx0.b = ctx1.b = C_MIX;
goto flatten_bool_op;
}
if (!boe1->type().isOpt() && istrue(env, boe0)) {
return flat_exp(env, ctx, boe1, r, b);
}
if (!boe0->type().isOpt() && istrue(env, boe1)) {
return flat_exp(env, ctx, boe0, r, b);
}
if (!boe0->type().isOpt() && !boe1->type().isOpt() && (r != nullptr) &&
r == constants().varTrue) {
if (boe1->type().isPar() || boe1->isa<Id>()) {
std::swap(boe0, boe1);
}
if (istrue(env, boe0)) {
return flat_exp(env, ctx1, boe1, r, b);
}
if (isfalse(env, boe0)) {
ctx1.neg = true;
ctx1.b = -ctx1.b;
return flat_exp(env, ctx1, boe1, r, b);
}
ctx0.b = C_MIX;
EE e0 = flat_exp(env, ctx0, boe0, nullptr, nullptr);
if (istrue(env, e0.r())) {
return flat_exp(env, ctx1, boe1, r, b);
}
if (isfalse(env, e0.r())) {
ctx1.neg = true;
ctx1.b = -ctx1.b;
return flat_exp(env, ctx1, boe1, r, b);
}
Id* id = e0.r()->cast<Id>();
ctx1.b = C_MIX;
(void)flat_exp(env, ctx1, boe1, id->decl(), constants().varTrue);
add_ctx_ann(id->decl(), ctx1.b);
ret.b = bind(env, Ctx(), b, constants().literalTrue);
ret.r = bind(env, Ctx(), r, constants().literalTrue);
break;
}
ctx0.b = ctx1.b = C_MIX;
goto flatten_bool_op;
case BOT_XOR:
if (ctx.neg) {
ctx.neg = false;
ctx.b = -ctx.b;
bot = BOT_EQUIV;
}
ctx0.b = ctx1.b = C_MIX;
goto flatten_bool_op;
case BOT_LE:
if (ctx.neg) {
doubleNeg = true;
bot = BOT_GQ;
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = +ctx0.b;
ctx1.b = -ctx1.b;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = +ctx0.b;
ctx1.i = -ctx1.b;
}
} else {
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = -ctx0.b;
ctx1.b = +ctx1.b;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = -ctx0.b;
ctx1.i = +ctx1.b;
}
}
goto flatten_bool_op;
case BOT_LQ:
if (ctx.neg) {
doubleNeg = true;
bot = BOT_GR;
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = +ctx0.b;
ctx1.b = -ctx1.b;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = +ctx0.b;
ctx1.i = -ctx1.b;
}
} else {
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = -ctx0.b;
ctx1.b = +ctx1.b;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = -ctx0.b;
ctx1.i = +ctx1.b;
}
}
goto flatten_bool_op;
case BOT_GR:
if (ctx.neg) {
doubleNeg = true;
bot = BOT_LQ;
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = -ctx0.b;
ctx1.b = +ctx1.b;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = -ctx0.b;
ctx1.i = +ctx1.b;
}
} else {
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = +ctx0.b;
ctx1.b = -ctx1.b;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = +ctx0.b;
ctx1.i = -ctx1.b;
}
}
goto flatten_bool_op;
case BOT_GQ:
if (ctx.neg) {
doubleNeg = true;
bot = BOT_LE;
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = -ctx0.b;
ctx1.b = +ctx1.b;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = -ctx0.b;
ctx1.i = +ctx1.b;
}
} else {
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = +ctx0.b;
ctx1.b = -ctx1.b;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = +ctx0.b;
ctx1.i = -ctx1.b;
}
}
goto flatten_bool_op;
case BOT_EQ:
if (ctx.neg) {
doubleNeg = true;
bot = BOT_NQ;
}
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = ctx1.b = C_MIX;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = ctx1.i = C_MIX;
}
goto flatten_bool_op;
case BOT_NQ:
if (ctx.neg) {
doubleNeg = true;
bot = BOT_EQ;
}
if (boe0->type().bt() == Type::BT_BOOL) {
ctx0.b = ctx1.b = C_MIX;
} else if (boe0->type().bt() == Type::BT_INT) {
ctx0.i = ctx1.i = C_MIX;
}
goto flatten_bool_op;
case BOT_IN:
case BOT_SUBSET:
case BOT_SUPERSET:
ctx0.i = ctx1.i = C_MIX;
flatten_bool_op : {
bool inRootCtx = (ctx0.b == ctx1.b && ctx0.b == C_ROOT && b == constants().varTrue);
EE e0 = flat_exp(env, ctx0, boe0, nullptr, inRootCtx ? b : nullptr);
EE e1 = flat_exp(env, ctx1, boe1, nullptr, inRootCtx ? b : nullptr);
ret.b = bind(env, Ctx(), b, constants().literalTrue);
std::vector<EE> ees(3);
ees[0].b = e0.b;
ees[1].b = e1.b;
if (isfalse(env, e0.b()) || isfalse(env, e1.b())) {
ees.resize(2);
ret.r = conj(env, r, ctx, ees);
break;
}
if (e0.r()->type().isPar() && e1.r()->type().isPar()) {
GCLock lock;
auto* bo_par = new BinOp(e->loc(), e0.r(), bot, e1.r());
std::vector<Expression*> args({e0.r(), e1.r()});
bo_par->decl(env.model->matchFn(env, bo_par->opToString(), args, false));
if (bo_par->decl() == nullptr) {
throw FlatteningError(env, bo_par->loc(), "cannot find matching declaration");
}
bo_par->type(Type::parbool());
bool bo_val = eval_bool(env, bo_par);
if (doubleNeg) {
bo_val = !bo_val;
}
ees[2].b = constants().boollit(bo_val);
ret.r = conj(env, r, ctx, ees);
break;
}
if (e0.r()->type().bt() == Type::BT_INT && e1.r()->type().isPar() && e0.r()->isa<Id>() &&
(bot == BOT_IN || bot == BOT_SUBSET)) {
VarDecl* vd = e0.r()->cast<Id>()->decl();
Id* ident = vd->id();
if (ctx.b == C_ROOT && r == constants().varTrue) {
if (vd->ti()->domain() == nullptr) {
vd->ti()->domain(e1.r());
} else {
GCLock lock;
IntSetVal* newdom = eval_intset(env, e1.r());
while (ident != nullptr) {
bool changeDom = false;
if (ident->decl()->ti()->domain() != nullptr) {
IntSetVal* domain = eval_intset(env, ident->decl()->ti()->domain());
IntSetRanges dr(domain);
IntSetRanges ibr(newdom);
Ranges::Inter<IntVal, IntSetRanges, IntSetRanges> i(dr, ibr);
IntSetVal* newibv = IntSetVal::ai(i);
if (domain->card() != newibv->card()) {
newdom = newibv;
changeDom = true;
}
} else {
changeDom = true;
}
if (ident->type().st() == Type::ST_PLAIN && newdom->size() == 0) {
env.fail();
} else if (changeDom) {
set_computed_domain(env, ident->decl(), new SetLit(Location().introduce(), newdom),
false);
if (ident->decl()->e() == nullptr && newdom->min() == newdom->max() &&
!vd->type().isSet()) {
ident->decl()->e(IntLit::a(newdom->min()));
}
}
ident =
ident->decl()->e() != nullptr ? ident->decl()->e()->dynamicCast<Id>() : nullptr;
}
}
ret.r = bind(env, ctx, r, constants().literalTrue);
break;
}
if (vd->ti()->domain() != nullptr) {
// check if current domain is already subsumed or falsified by this constraint
GCLock lock;
IntSetVal* check_dom = eval_intset(env, e1.r());
IntSetVal* domain = eval_intset(env, ident->decl()->ti()->domain());
{
IntSetRanges cdr(check_dom);
IntSetRanges dr(domain);
if (Ranges::subset(dr, cdr)) {
// the constraint is subsumed
ret.r = bind(env, ctx, r, constants().literalTrue);
break;
}
}
if (vd->type().st() == Type::ST_PLAIN) {
// only for var int (for var set of int, subset can never fail because of the domain)
IntSetRanges cdr(check_dom);
IntSetRanges dr(domain);
if (Ranges::disjoint(cdr, dr)) {
// the constraint is false
ret.r = bind(env, ctx, r, constants().literalFalse);
break;
}
}
}
}
std::vector<KeepAlive> args;
ASTString callid;
Expression* le0 = nullptr;
Expression* le1 = nullptr;
if (boe0->type().isint() && !boe0->type().isOpt() && bot != BOT_IN) {
le0 = get_linexp<IntLit>(e0.r());
} else if (boe0->type().isfloat() && !boe0->type().isOpt() && bot != BOT_IN) {
le0 = get_linexp<FloatLit>(e0.r());
}
if (le0 != nullptr) {
if (boe0->type().isint() && boe1->type().isint() && !boe1->type().isOpt()) {
le1 = get_linexp<IntLit>(e1.r());
} else if (boe0->type().isfloat() && boe1->type().isfloat() && !boe1->type().isOpt()) {
le1 = get_linexp<FloatLit>(e1.r());
}
}
if (le1 != nullptr) {
if (boe0->type().isint()) {
flatten_linexp_binop<IntLit>(env, ctx, r, b, ret, le0, le1, bot, doubleNeg, ees, args,
callid);
} else {
flatten_linexp_binop<FloatLit>(env, ctx, r, b, ret, le0, le1, bot, doubleNeg, ees, args,
callid);
}
} else {
switch (bot) {
case BOT_GR:
std::swap(e0, e1);
bot = BOT_LE;
break;
case BOT_GQ:
std::swap(e0, e1);
bot = BOT_LQ;
break;
default:
break;
}
args.push_back(e0.r);
args.push_back(e1.r);
}
if (!args.empty()) {
GC::lock();
bool idIsOp = false;
if (callid == "") {
assert(args.size() == 2);
if (!isBuiltin) {
callid = op_to_id(bot);
idIsOp = true;
} else {
callid = op_to_builtin(args[0](), args[1](), bot);
}
}
std::vector<Expression*> args_e(args.size());
for (auto i = static_cast<unsigned int>(args.size()); (i--) != 0U;) {
args_e[i] = args[i]();
}
Call* cc = new Call(e->loc().introduce(), callid, args_e);
cc->decl(env.model->matchFn(env, cc->id(), args_e, false));
if (cc->decl() == nullptr) {
throw FlatteningError(env, cc->loc(), "cannot find matching declaration");
}
if (idIsOp && cc->decl()->e() == nullptr) {
// This is in fact a built-in operator, but we only found out after
// constructing the call
cc = new Call(e->loc().introduce(), op_to_builtin(args[0](), args[1](), bot), args_e);
cc->decl(env.model->matchFn(env, cc->id(), args_e, false));
if (cc->decl() == nullptr) {
throw FlatteningError(env, cc->loc(), "cannot find matching declaration");
}
}
cc->type(cc->decl()->rtype(env, args_e, false));
// add defines_var annotation if applicable
Id* assignTo = nullptr;
if (bot == BOT_EQ && ctx.b == C_ROOT) {
if ((le0 != nullptr) && le0->isa<Id>()) {
assignTo = le0->cast<Id>();
} else if ((le1 != nullptr) && le1->isa<Id>()) {
assignTo = le1->cast<Id>();
}
if (assignTo != nullptr) {
make_defined_var(assignTo->decl()->flat(), cc);
}
}
auto cit = env.cseMapFind(cc);
if (cit != env.cseMapEnd()) {
ees[2].b = cit->second.r();
if (doubleNeg) {
Type t = ees[2].b()->type();
ees[2].b = new UnOp(Location().introduce(), UOT_NOT, ees[2].b());
ees[2].b()->type(t);
}
if (Id* id = ees[2].b()->dynamicCast<Id>()) {
add_ctx_ann(id->decl(), ctx.b);
}
ret.r = conj(env, r, ctx, ees);
GC::unlock();
} else {
bool singleExp = true;
for (auto& ee : ees) {
if (!istrue(env, ee.b())) {
singleExp = false;
break;
}
}
KeepAlive ka(cc);
GC::unlock();
if (singleExp) {
if (doubleNeg) {
ctx.b = -ctx.b;
ctx.neg = !ctx.neg;
}
ret.r = flat_exp(env, ctx, cc, r, nullptr).r;
} else {
ees[2].b = flat_exp(env, Ctx(), cc, nullptr, nullptr).r;
if (doubleNeg) {
GCLock lock;
Type t = ees[2].b()->type();
ees[2].b = new UnOp(Location().introduce(), UOT_NOT, ees[2].b());
ees[2].b()->type(t);
}
if (Id* id = ees[2].b()->dynamicCast<Id>()) {
add_ctx_ann(id->decl(), ctx.b);
}
ret.r = conj(env, r, ctx, ees);
}
}
} else {
ret.r = conj(env, r, ctx, ees);
}
} break;
case BOT_PLUSPLUS: {
std::vector<EE> ee(2);
EE eev = flat_exp(env, ctx, boe0, nullptr, nullptr);
ee[0] = eev;
ArrayLit* al;
if (eev.r()->isa<ArrayLit>()) {
al = eev.r()->cast<ArrayLit>();
} else {
Id* id = eev.r()->cast<Id>();
if (id->decl() == nullptr) {
throw InternalError("undefined identifier");
}
if (id->decl()->e() == nullptr) {
throw InternalError("array without initialiser not supported");
}
al = follow_id(id)->cast<ArrayLit>();
}
ArrayLit* al0 = al;
eev = flat_exp(env, ctx, boe1, nullptr, nullptr);
ee[1] = eev;
if (eev.r()->isa<ArrayLit>()) {
al = eev.r()->cast<ArrayLit>();
} else {
Id* id = eev.r()->cast<Id>();
if (id->decl() == nullptr) {
throw InternalError("undefined identifier");
}
if (id->decl()->e() == nullptr) {
throw InternalError("array without initialiser not supported");
}
al = follow_id(id)->cast<ArrayLit>();
}
ArrayLit* al1 = al;
std::vector<Expression*> v(al0->size() + al1->size());
for (unsigned int i = al0->size(); (i--) != 0U;) {
v[i] = (*al0)[i];
}
for (unsigned int i = al1->size(); (i--) != 0U;) {
v[al0->size() + i] = (*al1)[i];
}
GCLock lock;
auto* alret = new ArrayLit(e->loc(), v);
alret->type(e->type());
ret.b = conj(env, b, Ctx(), ee);
ret.r = bind(env, ctx, r, alret);
} break;
}
return ret;
}
} // namespace MiniZinc