/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Guido Tack */ /* 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 namespace MiniZinc { EE flatten_id(EnvI& env, const Ctx& ctx, Expression* e, VarDecl* r, VarDecl* b, bool doNotFollowChains) { CallStackItem _csi(env, e); EE ret; Id* id = e->cast(); if (id->decl() == nullptr) { if (id->type().isAnn()) { ret.b = bind(env, Ctx(), b, constants().literalTrue); ret.r = bind(env, ctx, r, e); return ret; } throw FlatteningError(env, e->loc(), "undefined identifier"); } if (!doNotFollowChains) { Expression* id_f = follow_id_to_decl(id); if (id_f == constants().absent) { ret.b = bind(env, Ctx(), b, constants().literalTrue); ret.r = bind(env, ctx, r, id_f); } else { id = id_f->cast()->id(); } } if (ctx.neg && id->type().dim() > 0) { if (id->type().dim() > 1) { throw InternalError("multi-dim arrays in negative positions not supported yet"); } KeepAlive ka; { GCLock lock; std::vector gen_id(1); gen_id[0] = new VarDecl(id->loc(), new TypeInst(id->loc(), Type::parint()), env.genId(), IntLit::a(0)); /// TODO: support arbitrary dimensions std::vector idxsetargs(1); idxsetargs[0] = id; Call* idxset = new Call(id->loc().introduce(), "index_set", idxsetargs); idxset->decl(env.model->matchFn(env, idxset, false)); idxset->type(idxset->decl()->rtype(env, idxsetargs, false)); Generator gen(gen_id, idxset, nullptr); std::vector idx(1); Generators gens; gens.g.push_back(gen); UnOp* aanot = new UnOp(id->loc(), UOT_NOT, nullptr); auto* cp = new Comprehension(id->loc(), aanot, gens, false); Id* bodyidx = cp->decl(0, 0)->id(); idx[0] = bodyidx; auto* aa = new ArrayAccess(id->loc(), id, idx); aanot->e(aa); Type tt = id->type(); tt.dim(0); aa->type(tt); aanot->type(aa->type()); cp->type(id->type()); ka = cp; } Ctx nctx = ctx; nctx.neg = false; ret = flat_exp(env, nctx, ka(), r, b); } else { GCLock lock; VarDecl* vd = id->decl()->flat(); Expression* rete = nullptr; if (vd == nullptr) { if (id->decl()->e() == nullptr || id->decl()->e()->type().isAnn() || id->decl()->e()->type().isvar() || id->decl()->e()->type().cv() || id->decl()->e()->type().dim() > 0) { // New top-level id, need to copy into env.m vd = flat_exp(env, Ctx(), id->decl(), nullptr, constants().varTrue).r()->cast()->decl(); } else { vd = id->decl(); } } ret.b = bind(env, Ctx(), b, constants().literalTrue); if (vd->e() != nullptr) { if (vd->e()->type().isPar() && vd->e()->type().dim() == 0) { rete = eval_par(env, vd->e()); if (vd->toplevel() && (vd->ti()->domain() != nullptr) && !vd->ti()->computedDomain()) { // need to check if domain includes RHS value if (vd->type() == Type::varbool()) { if (!Expression::equal(rete, vd->ti()->domain())) { env.fail(); } vd->ti()->domain(rete); } else if (vd->type() == Type::varint()) { IntSetVal* isv = eval_intset(env, vd->ti()->domain()); IntVal v = eval_int(env, rete); if (!isv->contains(v)) { env.fail(); } vd->ti()->domain(new SetLit(Location().introduce(), IntSetVal::a(v, v))); } else if (vd->type() == Type::varfloat()) { FloatSetVal* fsv = eval_floatset(env, vd->ti()->domain()); FloatVal v = eval_float(env, rete); if (!fsv->contains(v)) { env.fail(); } vd->ti()->domain(new SetLit(Location().introduce(), FloatSetVal::a(v, v))); } else if (vd->type() == Type::varsetint()) { IntSetVal* isv = eval_intset(env, vd->ti()->domain()); IntSetVal* v = eval_intset(env, rete); IntSetRanges isv_r(isv); IntSetRanges v_r(v); if (!Ranges::subset(v_r, isv_r)) { env.fail(); } vd->ti()->domain(new SetLit(Location().introduce(), v)); } // If we made it to here, the new domain is equal to the RHS vd->ti()->setComputedDomain(true); } } else if (vd->e()->isa()) { rete = vd->e(); } } else if (vd->ti()->ranges().size() == 0 && (vd->ti()->domain() != nullptr) && vd->type().st() == Type::ST_PLAIN && vd->type().ot() == Type::OT_PRESENT) { if (vd->type().bt() == Type::BT_BOOL) { rete = vd->ti()->domain(); } else if (vd->type().bt() == Type::BT_INT && vd->ti()->domain()->isa() && (vd->ti()->domain()->cast()->isv() != nullptr) && vd->ti()->domain()->cast()->isv()->card() == 1) { rete = IntLit::a(vd->ti()->domain()->cast()->isv()->min()); } } else if (vd->ti()->ranges().size() > 0) { // create fresh variables and array literal std::vector > dims; IntVal asize = 1; for (unsigned int i = 0; i < vd->ti()->ranges().size(); i++) { TypeInst* ti = vd->ti()->ranges()[i]; if (ti->domain() == nullptr) { throw FlatteningError(env, ti->loc(), "array dimensions unknown"); } IntSetVal* isv = eval_intset(env, ti->domain()); if (isv->size() == 0) { dims.emplace_back(1, 0); asize = 0; } else { if (isv->size() != 1) { throw FlatteningError(env, ti->loc(), "invalid array index set"); } asize *= (isv->max(0) - isv->min(0) + 1); dims.emplace_back(static_cast(isv->min(0).toInt()), static_cast(isv->max(0).toInt())); } } Type tt = vd->ti()->type(); tt.dim(0); if (asize > Constants::max_array_size) { std::ostringstream oss; oss << "array size (" << asize << ") exceeds maximum allowed size (" << Constants::max_array_size << ")"; throw FlatteningError(env, vd->loc(), oss.str()); } std::vector elems(static_cast(asize.toInt())); for (int i = 0; i < static_cast(asize.toInt()); i++) { CallStackItem csi(env, IntLit::a(i)); auto* vti = new TypeInst(Location().introduce(), tt, vd->ti()->domain()); VarDecl* nvd = new_vardecl(env, Ctx(), vti, nullptr, vd, nullptr); elems[i] = nvd->id(); } // After introducing variables for each array element, the original domain can be // set to "computed" (since it is a consequence of the individual variable domains) vd->ti()->setComputedDomain(true); auto* al = new ArrayLit(Location().introduce(), elems, dims); al->type(vd->type()); vd->e(al); env.voAddExp(vd); EE ee; ee.r = vd; env.cseMapInsert(vd->e(), ee); } if (rete == nullptr) { if (!vd->toplevel()) { // create new VarDecl in toplevel, if decl doesnt exist yet auto it = env.cseMapFind(vd->e()); if (it == env.cseMapEnd()) { Expression* vde = follow_id(vd->e()); ArrayLit* vdea = vde != nullptr ? vde->dynamicCast() : nullptr; if ((vdea != nullptr) && vdea->size() == 0) { // Do not create names for empty arrays but return array literal directly rete = vdea; } else { VarDecl* nvd = new_vardecl(env, ctx, eval_typeinst(env, ctx, vd), nullptr, vd, nullptr); if (vd->e() != nullptr) { (void)flat_exp(env, Ctx(), vd->e(), nvd, constants().varTrue); } vd = nvd; EE ee(vd, nullptr); if (vd->e() != nullptr) { env.cseMapInsert(vd->e(), ee); } } } else { if (it->second.r()->isa()) { vd = it->second.r()->cast(); } else { rete = it->second.r(); } } } if (rete == nullptr) { if (id->type().bt() == Type::BT_ANN && (vd->e() != nullptr)) { rete = vd->e(); } else { auto* vda = vd->dynamicCast(); if ((vda != nullptr) && vda->size() == 0) { // Do not create names for empty arrays but return array literal directly rete = vda; } else { rete = vd->id(); } } } } ret.r = bind(env, ctx, r, rete); } return ret; } EE flatten_id(EnvI& env, const Ctx& ctx, Expression* e, VarDecl* r, VarDecl* b) { return flatten_id(env, ctx, e, r, b, false); } } // namespace MiniZinc