1 // {{{ MIT License
2 
3 // Copyright 2017 Roland Kaminski
4 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to
7 // deal in the Software without restriction, including without limitation the
8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 // sell copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 // IN THE SOFTWARE.
22 
23 // }}}
24 
25 #include "gringo/term.hh"
26 #include "gringo/logger.hh"
27 #include "gringo/graph.hh"
28 #include <cmath>
29 
30 namespace Gringo {
31 
32 // {{{ definition of Defines
33 
34 Defines::Defines() = default;
35 
36 Defines::Defines(Defines &&) = default;
37 
defs() const38 Defines::DefMap const &Defines::defs() const {
39     return defs_;
40 }
41 
add(Location const & loc,String name,UTerm && value,bool defaultDef,Logger & log)42 void Defines::add(Location const &loc, String name, UTerm &&value, bool defaultDef, Logger &log) {
43     auto it = defs_.find(name);
44     if (it == defs_.end())                           { defs_.emplace(name, make_tuple(defaultDef, loc, std::move(value))); }
45     else if (std::get<0>(it->second) && !defaultDef) { it->second = make_tuple(defaultDef, loc, std::move(value)); }
46     else if (std::get<0>(it->second) || !defaultDef) {
47         GRINGO_REPORT(log, Warnings::RuntimeError)
48             << loc << ": error: redefinition of constant:\n"
49             << "  #const " << name << "=" << *value << ".\n"
50             << std::get<1>(it->second) << ": note: constant also defined here\n";
51     }
52 }
53 
init(Logger & log)54 void Defines::init(Logger &log) {
55     using DefineGraph = Graph<Defines::DefMap::iterator>;
56     using NodeMap     = std::unordered_map<String, DefineGraph::Node*>;
57 
58     DefineGraph graph;
59     NodeMap nodes;
60     for (auto it = defs_.begin(), end = defs_.end(); it != end; ++it) {
61         nodes.emplace(it->first, &graph.insertNode(it));
62     }
63     for (auto &x : nodes) {
64         Term::VarSet vals;
65         std::get<2>(x.second->data->second)->collectIds(vals);
66         for (auto &y : vals) {
67             auto it = nodes.find(y);
68             if (it != nodes.end()) { x.second->insertEdge(*it->second); }
69         }
70     }
71     for (auto &scc : graph.tarjan()) {
72         if (scc.size() > 1) {
73             std::ostringstream msg;
74             msg
75                 << std::get<1>(scc.back()->data->second) << ": error: cyclic constant definition:\n"
76                 << "  #const " << scc.back()->data->first << "=" << *std::get<2>(scc.back()->data->second) << ".\n";
77             scc.pop_back();
78             for (auto &x : scc) {
79                 msg
80                     << std::get<1>(x->data->second) << ": note: cycle involves definition:\n"
81                     << "  #const " << x->data->first << "=" << *std::get<2>(x->data->second) << ".\n";
82             }
83             GRINGO_REPORT(log, Warnings::RuntimeError) << msg.str();
84         }
85         for (auto &x : scc) { Term::replace(std::get<2>(x->data->second), std::get<2>(x->data->second)->replace(*this, true)); }
86     }
87 }
88 
empty() const89 bool Defines::empty() const { return defs_.empty(); }
90 
apply(Symbol x,Symbol & retVal,UTerm & retTerm,bool replace)91 void Defines::apply(Symbol x, Symbol &retVal, UTerm &retTerm, bool replace) {
92     if (x.type() == SymbolType::Fun) {
93         if (x.sig().arity() > 0) {
94             SymVec args;
95             bool changed = true;
96             for (std::size_t i = 0, e = x.args().size; i != e; ++i) {
97                 UTerm rt;
98                 args.emplace_back();
99                 apply(x.args()[i], args.back(), rt, true);
100                 if (rt) {
101                     Location loc{rt->loc()};
102                     UTermVec tArgs;
103                     args.pop_back();
104                     for (auto &y : args) { tArgs.emplace_back(make_locatable<ValTerm>(rt->loc(), y)); }
105                     tArgs.emplace_back(std::move(rt));
106                     for (++i; i != e; ++i) {
107                         Symbol rv;
108                         tArgs.emplace_back();
109                         apply(x.args()[i], rv, tArgs.back(), true);
110                         if (!tArgs.back()) {
111                             if (rv.type() == SymbolType::Special) { rv = x.args()[i]; }
112                             tArgs.back() = make_locatable<ValTerm>(loc, rv);
113                         }
114                     }
115                     retTerm = make_locatable<FunctionTerm>(loc, x.name(), std::move(tArgs));
116                     return;
117                 }
118                 else if (args.back().type() == SymbolType::Special) { args.back() = x.args()[i]; }
119                 else                                           { changed = true; }
120             }
121             if (changed) { retVal = Symbol::createFun(x.name(), Potassco::toSpan(args)); }
122         }
123         else if (replace) {
124             auto it(defs_.find(x.name()));
125             if (it != defs_.end()) {
126                 retVal = std::get<2>(it->second)->isEDB();
127                 if (retVal.type() == SymbolType::Special) {
128                     retTerm = get_clone(std::get<2>(it->second));
129                 }
130             }
131         }
132     }
133 }
134 
~Defines()135 Defines::~Defines() { }
136 
137 // }}}
138 
139 // {{{ definition of GRef
140 
GRef(UTerm && name)141 GRef::GRef(UTerm &&name)
142     : type(EMPTY)
143     , name(std::move(name))
144     , value(Symbol::createNum(0))
145     , term(0) { }
146 
operator bool() const147 GRef::operator bool() const { return type != EMPTY; }
148 
reset()149 void GRef::reset() { type = EMPTY; }
150 
operator =(Symbol const & x)151 GRef &GRef::operator=(Symbol const &x) {
152     type  = VALUE;
153     value = x;
154     return *this;
155 }
156 
operator =(GTerm & x)157 GRef &GRef::operator=(GTerm &x) {
158     type = TERM;
159     term = &x;
160     return *this;
161 }
162 
occurs(GRef & x) const163 bool GRef::occurs(GRef &x) const {
164     switch (type) {
165         case VALUE: { return false; }
166         case TERM:  { return term->occurs(x); }
167         case EMPTY: { return this == &x; }
168     }
169     assert(false);
170     return false;
171 }
172 
match(Symbol const & x)173 bool GRef::match(Symbol const &x) {
174     switch (type) {
175         case VALUE: { return value == x; }
176         case TERM:  { return term->match(x); }
177         case EMPTY: { assert(false); }
178     }
179     return false;
180 }
181 
182 template <class T>
unify(T & x)183 bool GRef::unify(T &x) {
184 switch (type) {
185     case VALUE: { return x.match(value); }
186     case TERM:  { return term->unify(x); }
187     case EMPTY: { assert(false); }
188 }
189 return false;
190 
191 }
192 
193 // }}}
194 
195 // {{{1 definition of GValTerm
196 
GValTerm(Symbol val)197 GValTerm::GValTerm(Symbol val) : val(val) { }
198 
operator ==(GTerm const & x) const199 bool GValTerm::operator==(GTerm const &x) const {
200     auto t = dynamic_cast<GValTerm const*>(&x);
201     return t && val == t->val;
202 }
203 
hash() const204 size_t GValTerm::hash() const { return get_value_hash(typeid(GValTerm).hash_code(), val); }
205 
print(std::ostream & out) const206 void GValTerm::print(std::ostream &out) const { out << val; }
207 
sig() const208 Sig GValTerm::sig() const { return val.sig(); }
209 
eval() const210 GTerm::EvalResult GValTerm::eval() const { return EvalResult(true, val); }
211 
occurs(GRef &) const212 bool GValTerm::occurs(GRef &) const { return false; }
213 
reset()214 void GValTerm::reset() { }
215 
match(Symbol const & x)216 bool GValTerm::match(Symbol const &x) { return val == x; }
217 
unify(GTerm & x)218 bool GValTerm::unify(GTerm &x) { return x.match(val); }
219 
unify(GFunctionTerm & x)220 bool GValTerm::unify(GFunctionTerm &x) { return x.match(val); }
221 
unify(GLinearTerm & x)222 bool GValTerm::unify(GLinearTerm &x) { return x.match(val); }
223 
unify(GVarTerm & x)224 bool GValTerm::unify(GVarTerm &x) { return x.match(val); }
225 
~GValTerm()226 GValTerm::~GValTerm() { }
227 
228 // {{{1 definition of GFunctionTerm
229 
GFunctionTerm(String name,UGTermVec && args)230 GFunctionTerm::GFunctionTerm(String name, UGTermVec &&args) : sign(false), name(name), args(std::move(args)) { }
231 
232 // Note: uses structural comparisson of names of variable terms (VarTerm/LinearTerm)
operator ==(GTerm const & x) const233 bool GFunctionTerm::operator==(GTerm const &x) const {
234     auto t = dynamic_cast<GFunctionTerm const*>(&x);
235     return t && sig() == x.sig() && is_value_equal_to(args, t->args);
236 }
237 
hash() const238 size_t GFunctionTerm::hash() const { return get_value_hash(typeid(GFunctionTerm).hash_code(), sig(), args); }
239 
print(std::ostream & out) const240 void GFunctionTerm::print(std::ostream &out) const {
241     if ((sig()).sign()) {
242         out << "-";
243     }
244     out << name;
245     out << "(";
246     print_comma(out, args, ",", [](std::ostream &out, UGTerm const &x) { out << *x; });
247     out << ")";
248 }
249 
sig() const250 Sig GFunctionTerm::sig() const { return Sig(name, numeric_cast<uint32_t>(args.size()), sign); }
251 
eval() const252 GTerm::EvalResult GFunctionTerm::eval() const { return EvalResult(false, Symbol()); }
253 
occurs(GRef & x) const254 bool GFunctionTerm::occurs(GRef &x) const {
255     for (auto &y : args) {
256         if (y->occurs(x)) { return true; }
257     }
258     return false;
259 }
260 
reset()261 void GFunctionTerm::reset() {
262     for (auto &y : args) { y->reset(); }
263 }
264 
match(Symbol const & x)265 bool GFunctionTerm::match(Symbol const &x) {
266     if (x.type() != SymbolType::Fun || sig() != x.sig()) { return false; }
267     else {
268         auto i = 0;
269         for (auto &y : args) {
270             if (!y->match(x.args()[i++])) { return false; }
271         }
272         return true;
273     }
274 }
275 
unify(GTerm & x)276 bool GFunctionTerm::unify(GTerm &x) { return x.unify(*this); }
277 
unify(GFunctionTerm & x)278 bool GFunctionTerm::unify(GFunctionTerm &x) {
279     if (sig() != x.sig()) { return false; }
280     else {
281         for (auto it = args.begin(), jt = x.args.begin(), ie = args.end(); it != ie; ++it, ++jt) {
282             if (!(*it)->unify(**jt)) { return false; }
283         }
284         return true;
285     }
286 }
287 
unify(GLinearTerm &)288 bool GFunctionTerm::unify(GLinearTerm &) { return false; }
289 
unify(GVarTerm & x)290 bool GFunctionTerm::unify(GVarTerm &x) {
291     if (*x.ref) { return x.ref->unify(*this); }
292     else if (!occurs(*x.ref)) {
293         *x.ref = *this;
294         return true;
295     }
296     else { return false; }
297 }
298 
~GFunctionTerm()299 GFunctionTerm::~GFunctionTerm() { }
300 
301 // {{{1 definition of GLinearTerm
302 
GLinearTerm(SGRef ref,int m,int n)303 GLinearTerm::GLinearTerm(SGRef ref, int m, int n) : ref(ref), m(m), n(n) { assert(ref); }
304 
operator ==(GTerm const & x) const305 bool GLinearTerm::operator==(GTerm const &x) const {
306     auto t = dynamic_cast<GLinearTerm const*>(&x);
307     return t && *ref->name == *t->ref->name && m == t->m && n == t->n;
308 }
309 
hash() const310 size_t GLinearTerm::hash() const   { return get_value_hash(typeid(GLinearTerm).hash_code(), ref->name, m, n); }
311 
print(std::ostream & out) const312 void GLinearTerm::print(std::ostream &out) const { out << "(" << m << "*" << *ref->name << "+" << n << ")"; }
313 
sig() const314 Sig GLinearTerm::sig() const   { throw std::logic_error("must not be called"); }
315 
eval() const316 GTerm::EvalResult GLinearTerm::eval() const   { return EvalResult(false, Symbol()); }
317 
occurs(GRef & x) const318 bool GLinearTerm::occurs(GRef &x) const { return ref->occurs(x); }
319 
reset()320 void GLinearTerm::reset() { ref->reset(); }
321 
match(Symbol const & x)322 bool GLinearTerm::match(Symbol const &x) {
323     if (x.type() != SymbolType::Num) { return false; }
324     else {
325         int y = x.num();
326         y-= n;
327         if (y % m != 0) { return false; }
328         else {
329             y /= m;
330             if (*ref) { return ref->match(Symbol::createNum(y)); }
331             else {
332                 *ref = Symbol::createNum(y);
333                 return true;
334             }
335         }
336     }
337 }
338 
unify(GTerm & x)339 bool GLinearTerm::unify(GTerm &x)   { return x.unify(*this); }
340 
unify(GFunctionTerm & x)341 bool GVarTerm::unify(GFunctionTerm &x) {
342     if (*ref) { return ref->unify(x); }
343     else if (!x.occurs(*ref)) {
344         *ref = x;
345         return true;
346     }
347     else { return false; }
348 }
349 
unify(GLinearTerm &)350 bool GLinearTerm::unify(GLinearTerm &) {
351     // Note: more could be done but this would be somewhat involved
352     //       because this would require rational numbers
353     //       as of now this simply unifies too much
354     return true;
355 }
356 
unify(GFunctionTerm &)357 bool GLinearTerm::unify(GFunctionTerm &) { return false; }
358 
unify(GVarTerm & x)359 bool GLinearTerm::unify(GVarTerm &x) {
360     if (*x.ref) { return x.ref->unify(*this); }
361     else {
362         // see not at: GLinearTerm::unify(GLinearTerm &x)
363         return true;
364     }
365 }
366 
~GLinearTerm()367 GLinearTerm::~GLinearTerm() { }
368 
369 // {{{1 definition of GVarTerm
370 
GVarTerm(SGRef ref)371 GVarTerm::GVarTerm(SGRef ref) : ref(ref) { assert(ref); }
372 
operator ==(GTerm const & x) const373 bool GVarTerm::operator==(GTerm const &x) const {
374     auto t = dynamic_cast<GVarTerm const*>(&x);
375     return t && *ref->name == *t->ref->name;
376 }
377 
hash() const378 size_t GVarTerm::hash() const      { return get_value_hash(typeid(GVarTerm).hash_code(), ref->name); }
379 
print(std::ostream & out) const380 void GVarTerm::print(std::ostream &out) const    { out << *ref->name; }
381 
sig() const382 Sig GVarTerm::sig() const      { throw std::logic_error("must not be called"); }
383 
eval() const384 GTerm::EvalResult GVarTerm::eval() const      { return EvalResult(false, Symbol()); }
385 
occurs(GRef & x) const386 bool GVarTerm::occurs(GRef &x) const { return ref->occurs(x); }
387 
reset()388 void GVarTerm::reset()    { ref->reset(); }
389 
match(Symbol const & x)390 bool GVarTerm::match(Symbol const &x) {
391     if (*ref) { return ref->match(x); }
392     else {
393         *ref = x;
394         return true;
395     }
396 }
397 
unify(GTerm & x)398 bool GVarTerm::unify(GTerm &x)      { return x.unify(*this); }
399 
unify(GLinearTerm & x)400 bool GVarTerm::unify(GLinearTerm &x) {
401     if (*ref) { return ref->unify(x); }
402     else {
403         // see note at: GLinearTerm::unify(GLinearTerm &x)
404         return true;
405     }
406 }
407 
unify(GVarTerm & x)408 bool GVarTerm::unify(GVarTerm &x) {
409     if (*ref)        { return ref->unify(x); }
410     else if (*x.ref) { return x.ref->unify(*this); }
411     else if (ref->name != x.ref->name) {
412         *ref = x;
413         return true;
414     }
415     else { return true; }
416 }
417 
~GVarTerm()418 GVarTerm::~GVarTerm() { }
419 
420 // }}}1
421 
422 // {{{1 definition of operator<< for BinOp and UnOp
423 
eval(UnOp op,int x)424 int eval(UnOp op, int x) {
425     switch (op) {
426         case UnOp::NEG: { return -x; }
427         case UnOp::ABS: { return std::abs(x); }
428         case UnOp::NOT: { return ~x; }
429     }
430     assert(false);
431     return 0;
432 }
433 
operator <<(std::ostream & out,UnOp op)434 std::ostream &operator<<(std::ostream &out, UnOp op) {
435     switch (op) {
436         case UnOp::ABS: { out << "#abs"; break; }
437         case UnOp::NOT: { out << "~"; break; }
438         case UnOp::NEG: { out << "-"; break; }
439     }
440     return out;
441 }
442 
443 namespace {
ipow(int a,int b)444 inline int ipow(int a, int b) {
445     if (b < 0) { return 0; }
446     else {
447         int r = 1;
448         while (b > 0) {
449             if(b & 1) { r *= a; }
450             b >>= 1;
451             a *= a;
452         }
453         return r;
454     }
455 }
456 }
457 
eval(BinOp op,int x,int y)458 int eval(BinOp op, int x, int y) {
459     switch (op) {
460         case BinOp::XOR: { return x ^ y; }
461         case BinOp::OR:  { return x | y; }
462         case BinOp::AND: { return x & y; }
463         case BinOp::ADD: { return x + y; }
464         case BinOp::SUB: { return x - y; }
465         case BinOp::MUL: { return x * y; }
466         case BinOp::MOD: {
467             assert(y != 0 && "must be checked before call");
468             return x % y;
469         }
470         case BinOp::POW: { return ipow(x, y); }
471         case BinOp::DIV: {
472             assert(y != 0 && "must be checked before call");
473             return x / y;
474         }
475     }
476     assert(false);
477     return 0;
478 }
479 
toNum(bool & undefined,Logger & log)480 int Term::toNum(bool &undefined, Logger &log) {
481     bool undefined_arg = false;
482     Symbol y(eval(undefined_arg, log));
483     if (y.type() == SymbolType::Num) {
484         undefined = undefined || undefined_arg;
485         return y.num();
486     }
487     else if (!undefined_arg) {
488         GRINGO_REPORT(log, Warnings::OperationUndefined)
489             << loc() << ": info: number expected:\n"
490             << "  " << *this << "\n";
491     }
492     undefined = true;
493     return 0;
494 }
495 
operator <<(std::ostream & out,BinOp op)496 std::ostream &operator<<(std::ostream &out, BinOp op) {
497     switch (op) {
498         case BinOp::AND: { out << "&"; break; }
499         case BinOp::OR:  { out << "?"; break; }
500         case BinOp::XOR: { out << "^"; break; }
501         case BinOp::POW: { out << "**"; break; }
502         case BinOp::ADD: { out << "+"; break; }
503         case BinOp::SUB: { out << "-"; break; }
504         case BinOp::MUL: { out << "*"; break; }
505         case BinOp::DIV: { out << "/"; break; }
506         case BinOp::MOD: { out << "\\"; break; }
507     }
508     return out;
509 }
510 
511 // {{{1 definition of Term and auxiliary functions
512 
513 namespace {
514 
wrap(UTerm && x)515 UTerm wrap(UTerm &&x) {
516     UTermVec args;
517     args.emplace_back(std::move(x));
518     return make_locatable<FunctionTerm>(args.front()->loc(), "#b", std::move(args));
519 }
520 
521 } // namespace
522 
unpool(UTerm const & x)523 UTermVec unpool(UTerm const &x) {
524     UTermVec pool;
525     x->unpool(pool);
526     return pool;
527 }
528 
_newRef(RenameMap & names,Term::ReferenceMap & refs) const529 SGRef Term::_newRef(RenameMap &names, Term::ReferenceMap &refs) const {
530     UTerm x(renameVars(names));
531     auto &ref = refs[x.get()];
532     if (!ref) { ref = std::make_shared<GRef>(std::move(x)); }
533     return ref;
534 }
535 
gterm() const536 UGTerm Term::gterm() const {
537     RenameMap names;
538     ReferenceMap refs;
539     return gterm(names, refs);
540 }
541 
542 
gfunterm(RenameMap &,ReferenceMap &) const543 UGFunTerm Term::gfunterm(RenameMap &, ReferenceMap &) const {
544     return nullptr;
545 }
546 
collect(VarTermSet & x) const547 void Term::collect(VarTermSet &x) const {
548     VarTermBoundVec vars;
549     collect(vars, false);
550     for (auto &y : vars) { x.emplace(*y.first); }
551 }
552 
553 
isZero(Logger & log) const554 bool Term::isZero(Logger &log) const {
555     bool undefined = false;
556     return getInvertibility() == Term::CONSTANT && eval(undefined, log) == Symbol::createNum(0);
557 }
558 
bind(VarSet & bound)559 bool Term::bind(VarSet &bound) {
560     VarTermBoundVec occs;
561     collect(occs, false);
562     bool ret = false;
563     for (auto &x : occs) {
564         if ((x.first->bindRef = bound.insert(x.first->name).second)) { ret = true; }
565     }
566     return ret;
567 }
568 
insert(ArithmeticsMap & arith,AuxGen & auxGen,UTerm && term,bool eq)569 UTerm Term::insert(ArithmeticsMap &arith, AuxGen &auxGen, UTerm &&term, bool eq) {
570     unsigned level = term->getLevel();
571     if (arith[level]->find(term) == arith[level]->end()) { level = numeric_cast<unsigned>(arith.size() - 1); }
572     auto ret = arith[level]->emplace(std::move(term), nullptr);
573     if (ret.second) { ret.first->second = auxGen.uniqueVar(ret.first->first->loc(), level, "#Arith"); }
574     if (eq) {
575         auto ret2 = arith[level]->emplace(get_clone(ret.first->second), nullptr);
576         if (ret2.second) { ret2.first->second = get_clone(ret.first->first); }
577     }
578     return get_clone(ret.first->second);
579 }
580 
581 // {{{1 definition of AuxGen
582 
uniqueName(char const * prefix)583 String AuxGen::uniqueName(char const *prefix) {
584     return String((prefix + std::to_string((*auxNum)++)).c_str());
585 }
586 
uniqueVar(Location const & loc,unsigned level,const char * prefix)587 UTerm AuxGen::uniqueVar(Location const &loc, unsigned level, const char *prefix) {
588     return make_locatable<VarTerm>(loc, uniqueName(prefix), std::make_shared<Symbol>(), level);
589 }
590 
591 // {{{1 definition of SimplifyState
592 
createScript(Location const & loc,String name,UTermVec && args,bool arith)593 SimplifyState::SimplifyRet SimplifyState::createScript(Location const &loc, String name, UTermVec &&args, bool arith) {
594     scripts.emplace_back(gen.uniqueVar(loc, level, "#Script"), name, std::move(args));
595     if (arith) {
596         return make_locatable<LinearTerm>(loc, static_cast<VarTerm&>(*std::get<0>(scripts.back())), 1, 0);
597     }
598     else {
599         return UTerm{std::get<0>(scripts.back())->clone()};
600     }
601 }
602 
createDots(Location const & loc,UTerm && left,UTerm && right)603 std::unique_ptr<LinearTerm> SimplifyState::createDots(Location const &loc, UTerm &&left, UTerm &&right) {
604     dots.emplace_back(gen.uniqueVar(loc, level, "#Range"), std::move(left), std::move(right));
605     return make_locatable<LinearTerm>(loc, static_cast<VarTerm&>(*std::get<0>(dots.back())), 1, 0);
606 }
607 
608 
609 // {{{1 definition of SimplifyState::SimplifyRet
610 
SimplifyRet(SimplifyRet && x)611 SimplifyState::SimplifyRet::SimplifyRet(SimplifyRet &&x) : type(x.type) {
612     switch(type) {
613         case LINEAR:
614         case REPLACE:   { x.type = UNTOUCHED; }
615         case UNTOUCHED: {
616             term = x.term;
617             break;
618         }
619         case UNDEFINED:
620         case CONSTANT: {
621             val = x.val;
622             break;
623         }
624     }
625 }
626 //! Reference to untouched term.
SimplifyRet(Term & x,bool project)627 SimplifyState::SimplifyRet::SimplifyRet(Term &x, bool project) : type(UNTOUCHED), project(project), term(&x) { }
628 //! Indicate replacement with linear term.
SimplifyRet(std::unique_ptr<LinearTerm> && x)629 SimplifyState::SimplifyRet::SimplifyRet(std::unique_ptr<LinearTerm> &&x) : type(LINEAR), project(false), term(x.release()) { }
630 //! Indicate replacement with arbitrary term.
SimplifyRet(UTerm && x)631 SimplifyState::SimplifyRet::SimplifyRet(UTerm &&x) : type(REPLACE), project(false), term(x.release()) { }
632 //! Indicate replacement with value.
SimplifyRet(Symbol const & x)633 SimplifyState::SimplifyRet::SimplifyRet(Symbol const &x) : type(CONSTANT), project(false), val(x) { }
SimplifyRet()634 SimplifyState::SimplifyRet::SimplifyRet() : type(UNDEFINED), project(false) { }
notNumeric() const635 bool SimplifyState::SimplifyRet::notNumeric() const {
636     switch (type) {
637         case UNDEFINED: { return true; }
638         case LINEAR:    { return false; }
639         case CONSTANT:  { return val.type() != SymbolType::Num; }
640         case REPLACE:
641         case UNTOUCHED: { return term->isNotNumeric(); }
642     }
643     assert(false);
644     return false;
645 }
constant() const646 bool SimplifyState::SimplifyRet::constant() const  { return type == CONSTANT; }
isZero() const647 bool SimplifyState::SimplifyRet::isZero() const    { return constant() && val.type() == SymbolType::Num && val.num() == 0; }
lin()648 LinearTerm &SimplifyState::SimplifyRet::lin()      { return static_cast<LinearTerm&>(*term); }
update(UTerm & x,bool arith)649 SimplifyState::SimplifyRet &SimplifyState::SimplifyRet::update(UTerm &x, bool arith) {
650     switch (type) {
651         case CONSTANT: {
652             x = make_locatable<ValTerm>(x->loc(), val);
653             return *this;
654         }
655         case LINEAR: {
656             // we do not need a trivial linear if its context already requires
657             // integers
658             if (arith && lin().m == 1 && lin().n == 0) {
659                 type = UNTOUCHED;
660                 x = std::move(lin().var);
661                 delete term;
662                 return *this;
663             }
664         }
665         case REPLACE:  {
666             type = UNTOUCHED;
667             x.reset(term);
668             return *this;
669         }
670         case UNDEFINED:
671         case UNTOUCHED: { return *this; }
672     }
673     throw std::logic_error("SimplifyState::SimplifyRet::update: must not happen");
674 }
undefined() const675 bool SimplifyState::SimplifyRet::undefined() const {
676     return type == UNDEFINED;
677 }
notFunction() const678 bool SimplifyState::SimplifyRet::notFunction() const {
679     switch (type) {
680         case UNDEFINED: { return true; }
681         case LINEAR:    { return true; }
682         case CONSTANT:  { return val.type() != SymbolType::Fun; }
683         case REPLACE:
684         case UNTOUCHED: { return term->isNotFunction(); }
685     }
686     assert(false);
687     return false;
688 }
~SimplifyRet()689 SimplifyState::SimplifyRet::~SimplifyRet() {
690     if (type == LINEAR || type == REPLACE) { delete term; }
691 }
692 
693 // }}}1
694 
695 // {{{1 definition of PoolTerm
696 
PoolTerm(UTermVec && terms)697 PoolTerm::PoolTerm(UTermVec &&terms)
698     : args(std::move(terms)) { }
699 
rename(String)700 void PoolTerm::rename(String) {
701     throw std::logic_error("must not be called");
702 }
703 
getLevel() const704 unsigned PoolTerm::getLevel() const {
705     unsigned level = 0;
706     for (auto &x : args) { level = std::max(x->getLevel(), level); }
707     return level;
708 }
709 
projectScore() const710 unsigned PoolTerm::projectScore() const {
711     throw std::logic_error("Term::projectScore must be called after Term::unpool");
712 }
713 
isNotNumeric() const714 bool PoolTerm::isNotNumeric() const { return false; }
isNotFunction() const715 bool PoolTerm::isNotFunction() const { return false; }
716 
getInvertibility() const717 Term::Invertibility PoolTerm::getInvertibility() const     { return Term::NOT_INVERTIBLE; }
718 
print(std::ostream & out) const719 void PoolTerm::print(std::ostream &out) const    { print_comma(out, args, ";", [](std::ostream &out, UTerm const &y) { out << *y; }); }
720 
simplify(SimplifyState &,bool,bool,Logger &)721 Term::SimplifyRet PoolTerm::simplify(SimplifyState &, bool, bool, Logger &) {
722     throw std::logic_error("Term::simplify must be called after Term::unpool");
723 }
724 
project(bool,AuxGen &)725 Term::ProjectRet PoolTerm::project(bool, AuxGen &) {
726     throw std::logic_error("Term::project must be called after Term::unpool");
727 }
728 
hasVar() const729 bool PoolTerm::hasVar() const {
730     for (auto &x : args) {
731         if (x->hasVar()) { return true; }
732     }
733     return false;
734 }
735 
hasPool() const736 bool PoolTerm::hasPool() const   { return true; }
737 
collect(VarTermBoundVec & vars,bool bound) const738 void PoolTerm::collect(VarTermBoundVec &vars, bool bound) const {
739     for (auto &y : args) { y->collect(vars, bound); }
740 }
741 
collect(VarSet & vars,unsigned minLevel,unsigned maxLevel) const742 void PoolTerm::collect(VarSet &vars, unsigned minLevel , unsigned maxLevel) const {
743     for (auto &y : args) { y->collect(vars, minLevel, maxLevel); }
744 }
745 
eval(bool &,Logger &) const746 Symbol PoolTerm::eval(bool &, Logger &) const { throw std::logic_error("Term::unpool must be called before Term::eval"); }
747 
match(Symbol const &) const748 bool PoolTerm::match(Symbol const &) const { throw std::logic_error("Term::unpool must be called before Term::match"); }
749 
unpool(UTermVec & x) const750 void PoolTerm::unpool(UTermVec &x) const {
751     for (auto &t : args) { t->unpool(x); }
752 }
753 
rewriteArithmetics(Term::ArithmeticsMap &,AuxGen &,bool)754 UTerm PoolTerm::rewriteArithmetics(Term::ArithmeticsMap &, AuxGen &, bool) {
755     throw std::logic_error("Term::rewriteArithmetics must be called before Term::rewriteArithmetics");
756 }
757 
operator ==(Term const & x) const758 bool PoolTerm::operator==(Term const &x) const {
759     auto t = dynamic_cast<PoolTerm const*>(&x);
760     return t && is_value_equal_to(args, t->args);
761 }
762 
hash() const763 size_t PoolTerm::hash() const {
764     return get_value_hash(typeid(PoolTerm).hash_code(), args);
765 }
766 
clone() const767 PoolTerm *PoolTerm::clone() const {
768     return make_locatable<PoolTerm>(loc(), get_clone(args)).release();
769 }
770 
getSig() const771 Sig PoolTerm::getSig() const { assert(false); throw std::logic_error("Term::getSig must not be called on PoolTerm"); }
772 
renameVars(RenameMap & names) const773 UTerm PoolTerm::renameVars(RenameMap &names) const {
774     UTermVec args;
775     for (auto &x : this->args) { args.emplace_back(x->renameVars(names)); }
776     return make_locatable<PoolTerm>(loc(), std::move(args));
777 }
778 
gterm(RenameMap & names,ReferenceMap & refs) const779 UGTerm PoolTerm::gterm(RenameMap &names, ReferenceMap &refs) const   { return gringo_make_unique<GVarTerm>(_newRef(names, refs)); }
780 
collectIds(VarSet & x) const781 void PoolTerm::collectIds(VarSet &x) const {
782     for (auto &y : args) { y->collectIds(x); }
783 }
784 
replace(Defines & x,bool replace)785 UTerm PoolTerm::replace(Defines &x, bool replace) {
786     for (auto &y : args) { Term::replace(y, y->replace(x, replace)); }
787     return nullptr;
788 }
789 
estimate(double,VarSet const &) const790 double PoolTerm::estimate(double, VarSet const &) const {
791     return 0;
792 }
793 
isEDB() const794 Symbol PoolTerm::isEDB() const { return {}; }
795 
isAtom() const796 bool PoolTerm::isAtom() const {
797     for (auto &x : args) {
798         if (!x->isAtom()) { return false; }
799     }
800     return true;
801 }
802 
~PoolTerm()803 PoolTerm::~PoolTerm() { }
804 
805 // {{{1 definition of ValTerm
806 
ValTerm(Symbol value)807 ValTerm::ValTerm(Symbol value)
808     : value(value) { }
809 
projectScore() const810 unsigned ValTerm::projectScore() const {
811     return 0;
812 }
813 
rename(String x)814 void ValTerm::rename(String x) {
815     value = Symbol::createId(x);
816 }
817 
getLevel() const818 unsigned ValTerm::getLevel() const {
819     return 0;
820 }
821 
isNotNumeric() const822 bool ValTerm::isNotNumeric() const { return value.type() != SymbolType::Num; }
isNotFunction() const823 bool ValTerm::isNotFunction() const { return value.type() != SymbolType::Fun; }
824 
getInvertibility() const825 Term::Invertibility ValTerm::getInvertibility() const      { return Term::CONSTANT; }
826 
print(std::ostream & out) const827 void ValTerm::print(std::ostream &out) const     { out << value; }
828 
simplify(SimplifyState &,bool,bool,Logger &)829 Term::SimplifyRet ValTerm::simplify(SimplifyState &, bool, bool, Logger &) { return {value}; }
830 
project(bool rename,AuxGen &)831 Term::ProjectRet ValTerm::project(bool rename, AuxGen &) {
832     assert(!rename); (void)rename;
833     return std::make_tuple(nullptr, UTerm(clone()), UTerm(clone()));
834 }
835 
hasVar() const836 bool ValTerm::hasVar() const {
837     return false;
838 }
839 
hasPool() const840 bool ValTerm::hasPool() const { return false; }
841 
collect(VarTermBoundVec &,bool) const842 void ValTerm::collect(VarTermBoundVec &, bool) const { }
843 
collect(VarSet &,unsigned,unsigned) const844 void ValTerm::collect(VarSet &, unsigned, unsigned) const { }
845 
eval(bool &,Logger &) const846 Symbol ValTerm::eval(bool &, Logger &) const { return value; }
847 
match(Symbol const & x) const848 bool ValTerm::match(Symbol const &x) const { return value == x; }
849 
unpool(UTermVec & x) const850 void ValTerm::unpool(UTermVec &x) const {
851     x.emplace_back(UTerm(clone()));
852 }
853 
rewriteArithmetics(Term::ArithmeticsMap &,AuxGen &,bool)854 UTerm ValTerm::rewriteArithmetics(Term::ArithmeticsMap &, AuxGen &, bool) { return nullptr; }
855 
operator ==(Term const & x) const856 bool ValTerm::operator==(Term const &x) const {
857     auto t = dynamic_cast<ValTerm const*>(&x);
858     return t && value == t->value;
859 }
860 
hash() const861 size_t ValTerm::hash() const {
862     return get_value_hash(typeid(ValTerm).hash_code(), value);
863 }
864 
clone() const865 ValTerm *ValTerm::clone() const {
866     return make_locatable<ValTerm>(loc(), value).release();
867 }
868 
getSig() const869 Sig ValTerm::getSig() const {
870     if (value.type() == SymbolType::Fun) { return value.sig(); }
871     else {
872         throw std::logic_error("Term::getSig must not be called on ValTerm");
873     }
874 }
875 
renameVars(RenameMap &) const876 UTerm ValTerm::renameVars(RenameMap &) const { return UTerm(clone()); }
877 
gterm(RenameMap &,ReferenceMap &) const878 UGTerm ValTerm::gterm(RenameMap &, ReferenceMap &) const { return gringo_make_unique<GValTerm>(value); }
879 
collectIds(VarSet & x) const880 void ValTerm::collectIds(VarSet &x) const {
881     if (value.type() == SymbolType::Fun && value.sig().arity() == 0) { x.emplace(value.name()); }
882 }
883 
replace(Defines & x,bool replace)884 UTerm ValTerm::replace(Defines &x, bool replace) {
885     Symbol retVal;
886     UTerm retTerm;
887     x.apply(value, retVal, retTerm, replace);
888     if (retVal.type() != SymbolType::Special) { value = retVal; }
889     else                                 { return retTerm; }
890     return nullptr;
891 }
892 
estimate(double,VarSet const &) const893 double ValTerm::estimate(double, VarSet const &) const {
894     return 0;
895 }
896 
isEDB() const897 Symbol ValTerm::isEDB() const { return value; }
898 
isAtom() const899 bool ValTerm::isAtom() const {
900     return value.type() == SymbolType::Fun;
901 }
902 
~ValTerm()903 ValTerm::~ValTerm() { }
904 
905 // {{{1 definition of VarTerm
906 
VarTerm(String name,SVal ref,unsigned level,bool bindRef)907 VarTerm::VarTerm(String name, SVal ref, unsigned level, bool bindRef)
908     : name(name)
909     , ref(name == "_" ? std::make_shared<Symbol>() : ref)
910     , bindRef(bindRef)
911     , level(level) { assert(ref || name == "_"); }
912 
projectScore() const913 unsigned VarTerm::projectScore() const {
914     return 0;
915 }
916 
rename(String)917 void VarTerm::rename(String) {
918     throw std::logic_error("must not be called");
919 }
920 
getLevel() const921 unsigned VarTerm::getLevel() const {
922     return level;
923 }
924 
isNotNumeric() const925 bool VarTerm::isNotNumeric() const { return false; }
isNotFunction() const926 bool VarTerm::isNotFunction() const { return false; }
927 
getInvertibility() const928 Term::Invertibility VarTerm::getInvertibility() const { return Term::INVERTIBLE; }
929 
print(std::ostream & out) const930 void VarTerm::print(std::ostream &out) const { out << name; }
931 
simplify(SimplifyState & state,bool positional,bool arithmetic,Logger &)932 Term::SimplifyRet VarTerm::simplify(SimplifyState &state, bool positional, bool arithmetic, Logger &) {
933     if (name == "_") {
934         if (positional) { return {*this, true}; }
935         else { name = state.gen.uniqueName("#Anon"); }
936     }
937     if (arithmetic) { return {make_locatable<LinearTerm>(loc(), *this, 1, 0)}; }
938     else            { return {*this, false}; }
939 }
940 
project(bool rename,AuxGen & auxGen)941 Term::ProjectRet VarTerm::project(bool rename, AuxGen &auxGen) {
942     assert(!rename); (void)rename;
943     if (name == "_") {
944         UTerm r(make_locatable<ValTerm>(loc(), Symbol::createId("#p")));
945         UTerm x(r->clone());
946         UTerm y(auxGen.uniqueVar(loc(), 0, "#P"));
947         return std::make_tuple(std::move(r), std::move(x), std::move(y));
948     }
949     else {
950         UTerm y(auxGen.uniqueVar(loc(), 0, "#X"));
951         UTerm x(wrap(UTerm(y->clone())));
952         return std::make_tuple(wrap(UTerm(clone())), std::move(x), std::move(y));
953     }
954 }
955 
hasVar() const956 bool VarTerm::hasVar() const { return true; }
957 
hasPool() const958 bool VarTerm::hasPool() const    { return false; }
959 
collect(VarTermBoundVec & vars,bool bound) const960 void VarTerm::collect(VarTermBoundVec &vars, bool bound) const {
961     vars.emplace_back(const_cast<VarTerm*>(this), bound);
962 }
963 
collect(VarSet & vars,unsigned minLevel,unsigned maxLevel) const964 void VarTerm::collect(VarSet &vars, unsigned minLevel , unsigned maxLevel) const {
965     if (minLevel <= level && level <= maxLevel) { vars.emplace(name); }
966 }
967 
eval(bool &,Logger &) const968 Symbol VarTerm::eval(bool &, Logger &) const { return *ref; }
969 
match(Symbol const & x) const970 bool VarTerm::match(Symbol const &x) const {
971     if (bindRef) {
972         *ref = x;
973         return true;
974     }
975     else { return x == *ref; }
976 }
977 
unpool(UTermVec & x) const978 void VarTerm::unpool(UTermVec &x) const { x.emplace_back(UTerm(clone())); }
979 
rewriteArithmetics(Term::ArithmeticsMap &,AuxGen &,bool)980 UTerm VarTerm::rewriteArithmetics(Term::ArithmeticsMap &, AuxGen &, bool) { return nullptr; }
981 
operator ==(Term const & x) const982 bool VarTerm::operator==(Term const &x) const {
983     auto t = dynamic_cast<VarTerm const*>(&x);
984     return t && name == t->name && level == t->level && (name != "_" || t == this);
985 }
986 
hash() const987 size_t VarTerm::hash() const {
988     // NOTE: in principle ref and level could be used as identity
989     //       if not for the way gterms are constructed
990     //       which create arbitrary references
991     return get_value_hash(typeid(VarTerm).hash_code(), name, level);
992 }
993 
clone() const994 VarTerm *VarTerm::clone() const {
995     return make_locatable<VarTerm>(loc(), name, ref, level, bindRef).release();
996 }
997 
getSig() const998 Sig VarTerm::getSig() const { throw std::logic_error("Term::getSig must not be called on VarTerm"); }
999 
renameVars(RenameMap & names) const1000 UTerm VarTerm::renameVars(RenameMap &names) const {
1001     auto ret(names.emplace(name, std::make_pair(name, nullptr)));
1002     if (ret.second) {
1003         ret.first->second.first  = ((bindRef ? "X" : "Y") + std::to_string(names.size() - 1)).c_str();
1004         ret.first->second.second = std::make_shared<Symbol>();
1005     }
1006     return make_locatable<VarTerm>(loc(), ret.first->second.first, ret.first->second.second, level, bindRef);
1007 }
1008 
gterm(RenameMap & names,ReferenceMap & refs) const1009 UGTerm VarTerm::gterm(RenameMap &names, ReferenceMap &refs) const { return gringo_make_unique<GVarTerm>(_newRef(names, refs)); }
1010 
collectIds(VarSet &) const1011 void VarTerm::collectIds(VarSet &) const { }
1012 
replace(Defines &,bool)1013 UTerm VarTerm::replace(Defines &, bool) { return nullptr; }
1014 
estimate(double size,VarSet const & bound) const1015 double VarTerm::estimate(double size, VarSet const &bound) const {
1016     return bound.find(name) == bound.end() ? size : 0.0;
1017 }
1018 
isEDB() const1019 Symbol VarTerm::isEDB() const { return {}; }
1020 
~VarTerm()1021 VarTerm::~VarTerm() { }
1022 
1023 // {{{1 definition of LinearTerm
1024 
LinearTerm(VarTerm const & var,int m,int n)1025 LinearTerm::LinearTerm(VarTerm const &var, int m, int n)
1026     : var(static_cast<VarTerm*>(var.clone()))
1027     , m(m)
1028     , n(n) { }
1029 
LinearTerm(UVarTerm && var,int m,int n)1030 LinearTerm::LinearTerm(UVarTerm &&var, int m, int n)
1031     : var(std::move(var))
1032     , m(m)
1033     , n(n) { }
1034 
projectScore() const1035 unsigned LinearTerm::projectScore() const {
1036     return 0;
1037 }
1038 
rename(String)1039 void LinearTerm::rename(String) {
1040     throw std::logic_error("must not be called");
1041 }
1042 
hasVar() const1043 bool LinearTerm::hasVar() const { return true; }
1044 
hasPool() const1045 bool LinearTerm::hasPool() const { return false; }
1046 
collect(VarTermBoundVec & vars,bool bound) const1047 void LinearTerm::collect(VarTermBoundVec &vars, bool bound) const {
1048     var->collect(vars, bound);
1049 }
1050 
collect(VarSet & vars,unsigned minLevel,unsigned maxLevel) const1051 void LinearTerm::collect(VarSet &vars, unsigned minLevel , unsigned maxLevel) const {
1052     var->collect(vars, minLevel, maxLevel);
1053 }
1054 
getLevel() const1055 unsigned LinearTerm::getLevel() const {
1056     return var->getLevel();
1057 }
1058 
isNotNumeric() const1059 bool LinearTerm::isNotNumeric() const   { return false; }
isNotFunction() const1060 bool LinearTerm::isNotFunction() const   { return true; }
1061 
getInvertibility() const1062 Term::Invertibility LinearTerm::getInvertibility() const   { return Term::INVERTIBLE; }
1063 
print(std::ostream & out) const1064 void LinearTerm::print(std::ostream &out) const  {
1065     if (m == 1) {
1066         out << "(" << *var << "+" << n << ")";
1067     }
1068     else if (n == 0) {
1069         out << "(" << m << "*" << *var << ")";
1070     }
1071     else {
1072         out << "(" << m << "*" << *var << "+" << n << ")";
1073     }
1074 }
1075 
simplify(SimplifyState &,bool,bool,Logger &)1076 Term::SimplifyRet LinearTerm::simplify(SimplifyState &, bool, bool, Logger &) { return {*this, false}; }
1077 
project(bool rename,AuxGen & auxGen)1078 Term::ProjectRet LinearTerm::project(bool rename, AuxGen &auxGen) {
1079     assert(!rename); (void)rename;
1080     UTerm y(auxGen.uniqueVar(loc(), 0, "#X"));
1081     UTerm x(wrap(UTerm(y->clone())));
1082     return std::make_tuple(wrap(make_locatable<LinearTerm>(loc(), std::move(var), m, n)), std::move(x), std::move(y));
1083 }
1084 
eval(bool & undefined,Logger & log) const1085 Symbol LinearTerm::eval(bool &undefined, Logger &log) const {
1086     bool undefined_arg = false;
1087     Symbol value = var->eval(undefined_arg, log);
1088     if (value.type() == SymbolType::Num) {
1089         undefined = undefined || undefined_arg;
1090         return Symbol::createNum(m * value.num() + n);
1091     }
1092     else if (!undefined_arg) {
1093         GRINGO_REPORT(log, Warnings::OperationUndefined)
1094             << loc() << ": info: operation undefined:\n"
1095             << "  " << *this << "\n";
1096     }
1097     undefined = true;
1098     return Symbol::createNum(0);
1099 }
1100 
match(Symbol const & x) const1101 bool LinearTerm::match(Symbol const &x) const {
1102     if (x.type() == SymbolType::Num) {
1103         assert(m != 0);
1104         int c(x.num() - n);
1105         if (c % m == 0) { return var->match(Symbol::createNum(c/m)); }
1106     }
1107     return false;
1108 }
1109 
unpool(UTermVec & x) const1110 void LinearTerm::unpool(UTermVec &x) const {
1111     x.emplace_back(UTerm(clone()));
1112 }
1113 
rewriteArithmetics(Term::ArithmeticsMap & arith,AuxGen & auxGen,bool forceDefined)1114 UTerm LinearTerm::rewriteArithmetics(Term::ArithmeticsMap &arith, AuxGen &auxGen, bool forceDefined) {
1115     if (forceDefined) {
1116         return Term::insert(arith, auxGen, make_locatable<LinearTerm>(loc(), *var, m, n), true);
1117     }
1118     return nullptr;
1119 }
1120 
operator ==(Term const & x) const1121 bool LinearTerm::operator==(Term const &x) const {
1122     auto t = dynamic_cast<LinearTerm const*>(&x);
1123     return t && m == t->m && n == t->n && is_value_equal_to(var, t->var);
1124 }
1125 
hash() const1126 size_t LinearTerm::hash() const {
1127     return get_value_hash(typeid(LinearTerm).hash_code(), m, n, var->hash());
1128 }
1129 
clone() const1130 LinearTerm *LinearTerm::clone() const {
1131     return make_locatable<LinearTerm>(loc(), *var, m, n).release();
1132 }
1133 
getSig() const1134 Sig LinearTerm::getSig() const   { throw std::logic_error("Term::getSig must not be called on LinearTerm"); }
1135 
renameVars(RenameMap & names) const1136 UTerm LinearTerm::renameVars(RenameMap &names) const {
1137     return make_locatable<LinearTerm>(loc(), UVarTerm(static_cast<VarTerm*>(var->renameVars(names).release())), m, n);
1138 }
1139 
gterm(RenameMap & names,ReferenceMap & refs) const1140 UGTerm LinearTerm::gterm(RenameMap &names, ReferenceMap &refs) const { return gringo_make_unique<GLinearTerm>(var->_newRef(names, refs), m, n); }
1141 
collectIds(VarSet &) const1142 void LinearTerm::collectIds(VarSet &) const {
1143 }
1144 
replace(Defines &,bool)1145 UTerm LinearTerm::replace(Defines &, bool) {
1146     return nullptr;
1147 }
1148 
estimate(double size,VarSet const & bound) const1149 double LinearTerm::estimate(double size, VarSet const &bound) const {
1150     return var->estimate(size, bound);
1151 }
1152 
isEDB() const1153 Symbol LinearTerm::isEDB() const { return {}; }
1154 
~LinearTerm()1155 LinearTerm::~LinearTerm() { }
1156 
1157 // {{{1 definition of UnOpTerm
1158 
1159 // TODO: NEG has to be handled specially now - maybe I should add a new kind of term?
1160 
UnOpTerm(UnOp op,UTerm && arg)1161 UnOpTerm::UnOpTerm(UnOp op, UTerm &&arg)
1162     : op(op)
1163     , arg(std::move(arg)) { }
1164 
projectScore() const1165 unsigned UnOpTerm::projectScore() const {
1166     return arg->projectScore();
1167 }
1168 
rename(String)1169 void UnOpTerm::rename(String) { throw std::logic_error("must not be called"); }
1170 
getLevel() const1171 unsigned UnOpTerm::getLevel() const { return arg->getLevel(); }
1172 
isNotNumeric() const1173 bool UnOpTerm::isNotNumeric() const { return false; }
isNotFunction() const1174 bool UnOpTerm::isNotFunction() const { return op != UnOp::NEG; }
1175 
getInvertibility() const1176 Term::Invertibility UnOpTerm::getInvertibility() const {
1177     return op == UnOp::NEG ? Term::INVERTIBLE : Term::NOT_INVERTIBLE;
1178 }
1179 
print(std::ostream & out) const1180 void UnOpTerm::print(std::ostream &out) const {
1181     if (op == UnOp::ABS) {
1182         out << "|" << *arg << "|";
1183     }
1184     else {
1185         // TODO: parenthesis are problematic if I want to use this term for predicates
1186         out << "(" << op << *arg << ")";
1187     }
1188 }
1189 
simplify(SimplifyState & state,bool,bool arithmetic,Logger & log)1190 Term::SimplifyRet UnOpTerm::simplify(SimplifyState &state, bool, bool arithmetic, Logger &log) {
1191     bool multiNeg = !arithmetic && op == UnOp::NEG;
1192     SimplifyRet ret(arg->simplify(state, false, !multiNeg, log));
1193     if (ret.undefined()) {
1194         return {};
1195     }
1196     else if ((multiNeg && ret.notNumeric() && ret.notFunction()) || (!multiNeg && ret.notNumeric())) {
1197         GRINGO_REPORT(log, Warnings::OperationUndefined)
1198             << loc() << ": info: operation undefined:\n"
1199             << "  " << *this << "\n";
1200         return {};
1201     }
1202     switch (ret.type) {
1203         case SimplifyRet::CONSTANT: {
1204             if (ret.val.type() == SymbolType::Num) {
1205                 return {Symbol::createNum(Gringo::eval(op, ret.val.num()))};
1206             }
1207             else {
1208                 assert(ret.val.type() == SymbolType::Fun);
1209                 return {ret.val.flipSign()};
1210             }
1211         }
1212         case SimplifyRet::LINEAR: {
1213             if (op == UnOp::NEG) {
1214                 ret.lin().m *= -1;
1215                 ret.lin().n *= -1;
1216                 return ret;
1217             }
1218         }
1219         default: {
1220             ret.update(arg, !multiNeg);
1221             return {*this, false};
1222         }
1223     }
1224 }
project(bool rename,AuxGen & auxGen)1225 Term::ProjectRet UnOpTerm::project(bool rename, AuxGen &auxGen) {
1226     assert(!rename); (void)rename;
1227     UTerm y(auxGen.uniqueVar(loc(), 0, "#X"));
1228     UTerm x(wrap(UTerm(y->clone())));
1229     return std::make_tuple(wrap(make_locatable<UnOpTerm>(loc(), op, std::move(arg))), std::move(x), std::move(y));
1230 }
hasVar() const1231 bool UnOpTerm::hasVar() const {
1232     return arg->hasVar();
1233 }
hasPool() const1234 bool UnOpTerm::hasPool() const   { return arg->hasPool(); }
collect(VarTermBoundVec & vars,bool bound) const1235 void UnOpTerm::collect(VarTermBoundVec &vars, bool bound) const {
1236     arg->collect(vars, bound && op == UnOp::NEG);
1237 }
collect(VarSet & vars,unsigned minLevel,unsigned maxLevel) const1238 void UnOpTerm::collect(VarSet &vars, unsigned minLevel , unsigned maxLevel) const {
1239     arg->collect(vars, minLevel, maxLevel);
1240 }
eval(bool & undefined,Logger & log) const1241 Symbol UnOpTerm::eval(bool &undefined, Logger &log) const {
1242     bool undefined_arg = false;
1243     Symbol value = arg->eval(undefined_arg, log);
1244     if (value.type() == SymbolType::Num) {
1245         undefined = undefined || undefined_arg;
1246         int num = value.num();
1247         switch (op) {
1248             case UnOp::NEG: { return Symbol::createNum(-num); }
1249             case UnOp::ABS: { return Symbol::createNum(std::abs(num)); }
1250             case UnOp::NOT: { return Symbol::createNum(~num); }
1251         }
1252         assert(false);
1253         return Symbol::createNum(0);
1254     }
1255     else if (op == UnOp::NEG && value.type() == SymbolType::Fun) {
1256         undefined = undefined || undefined_arg;
1257         return value.flipSign();
1258     }
1259     else if (!undefined_arg) {
1260         GRINGO_REPORT(log, Warnings::OperationUndefined)
1261             << loc() << ": info: operation undefined:\n"
1262             << "  " << *this << "\n";
1263     }
1264     undefined = true;
1265     return Symbol::createNum(0);
1266 }
match(Symbol const & x) const1267 bool UnOpTerm::match(Symbol const &x) const  {
1268     if (op != UnOp::NEG) {
1269         throw std::logic_error("Term::rewriteArithmetics must be called before Term::match");
1270     }
1271     if (x.type() == SymbolType::Num) {
1272         return arg->match(Symbol::createNum(-x.num()));
1273     }
1274     else if (x.type() == SymbolType::Fun) {
1275         return arg->match(x.flipSign());
1276     }
1277     return false;
1278 }
unpool(UTermVec & x) const1279 void UnOpTerm::unpool(UTermVec &x) const {
1280     auto f = [&](UTerm &&y) { x.emplace_back(make_locatable<UnOpTerm>(loc(), op, std::move(y))); };
1281     Term::unpool(arg, Gringo::unpool, f);
1282 }
rewriteArithmetics(Term::ArithmeticsMap & arith,AuxGen & auxGen,bool forceDefined)1283 UTerm UnOpTerm::rewriteArithmetics(Term::ArithmeticsMap &arith, AuxGen &auxGen, bool forceDefined) {
1284     if (!forceDefined && op == UnOp::NEG) {
1285         Term::replace(arg, arg->rewriteArithmetics(arith, auxGen, false));
1286         return nullptr;
1287     }
1288     else {
1289         return Term::insert(arith, auxGen, make_locatable<UnOpTerm>(loc(), op, std::move(arg)), forceDefined && op == UnOp::NEG);
1290     }
1291 }
operator ==(Term const & x) const1292 bool UnOpTerm::operator==(Term const &x) const {
1293     auto t = dynamic_cast<UnOpTerm const*>(&x);
1294     return t && op == t->op && is_value_equal_to(arg, t->arg);
1295 }
hash() const1296 size_t UnOpTerm::hash() const {
1297     return get_value_hash(typeid(UnOpTerm).hash_code(), size_t(op), arg);
1298 }
clone() const1299 UnOpTerm *UnOpTerm::clone() const {
1300     return make_locatable<UnOpTerm>(loc(), op, get_clone(arg)).release();
1301 }
getSig() const1302 Sig UnOpTerm::getSig() const {
1303     if (op == UnOp::NEG) { return arg->getSig().flipSign(); }
1304     throw std::logic_error("Term::getSig must not be called on UnOpTerm");
1305 }
renameVars(RenameMap & names) const1306 UTerm UnOpTerm::renameVars(RenameMap &names) const { return make_locatable<UnOpTerm>(loc(), op, arg->renameVars(names)); }
gterm(RenameMap & names,ReferenceMap & refs) const1307 UGTerm UnOpTerm::gterm(RenameMap &names, ReferenceMap &refs) const {
1308     if (op == UnOp::NEG) {
1309         UGFunTerm fun(arg->gfunterm(names, refs));
1310         if (fun) {
1311             fun->sign = !fun->sign;
1312             return std::move(fun);
1313         }
1314     }
1315     return gringo_make_unique<GVarTerm>(_newRef(names, refs));
1316 }
1317 
gfunterm(RenameMap & names,ReferenceMap & refs) const1318 UGFunTerm UnOpTerm::gfunterm(RenameMap &names, ReferenceMap &refs) const {
1319     if (op != UnOp::NEG) { return nullptr; }
1320     UGFunTerm fun(arg->gfunterm(names, refs));
1321     if (!fun) { return nullptr; }
1322     fun->sign = ! fun->sign;
1323     return fun;
1324 }
1325 
collectIds(VarSet & x) const1326 void UnOpTerm::collectIds(VarSet &x) const {
1327     arg->collectIds(x);
1328 }
replace(Defines & x,bool)1329 UTerm UnOpTerm::replace(Defines &x, bool) {
1330     Term::replace(arg, arg->replace(x, true));
1331     return nullptr;
1332 }
estimate(double,VarSet const &) const1333 double UnOpTerm::estimate(double, VarSet const &) const {
1334     return 0;
1335 }
isEDB() const1336 Symbol UnOpTerm::isEDB() const { return {}; }
isAtom() const1337 bool UnOpTerm::isAtom() const {
1338     return op == UnOp::NEG && arg->isAtom();
1339 }
~UnOpTerm()1340 UnOpTerm::~UnOpTerm() { }
1341 
1342 // {{{1 definition of BinOpTerm
1343 
BinOpTerm(BinOp op,UTerm && left,UTerm && right)1344 BinOpTerm::BinOpTerm(BinOp op, UTerm &&left, UTerm &&right)
1345     : op(op)
1346     , left(std::move(left))
1347     , right(std::move(right)) { }
1348 
projectScore() const1349 unsigned BinOpTerm::projectScore() const {
1350     return left->projectScore() + right->projectScore();
1351 }
1352 
rename(String)1353 void BinOpTerm::rename(String) {
1354     throw std::logic_error("must not be called");
1355 }
1356 
getLevel() const1357 unsigned BinOpTerm::getLevel() const {
1358     return std::max(left->getLevel(), right->getLevel());
1359 }
1360 
isNotNumeric() const1361 bool BinOpTerm::isNotNumeric() const    { return false; }
isNotFunction() const1362 bool BinOpTerm::isNotFunction() const    { return true; }
1363 
getInvertibility() const1364 Term::Invertibility BinOpTerm::getInvertibility() const    { return Term::NOT_INVERTIBLE; }
1365 
print(std::ostream & out) const1366 void BinOpTerm::print(std::ostream &out) const {
1367     out << "(" << *left << op << *right << ")";
1368 }
1369 
simplify(SimplifyState & state,bool,bool,Logger & log)1370 Term::SimplifyRet BinOpTerm::simplify(SimplifyState &state, bool, bool, Logger &log) {
1371     auto retLeft(left->simplify(state, false, true, log));
1372     auto retRight(right->simplify(state, false, true, log));
1373     if (retLeft.undefined() || retRight.undefined()) {
1374         return {};
1375     }
1376     else if (retLeft.notNumeric() || retRight.notNumeric() || ((op == BinOp::DIV || op == BinOp::MOD) && retRight.isZero())) {
1377         retLeft.update(left, true); retRight.update(right, true);
1378         GRINGO_REPORT(log, Warnings::OperationUndefined)
1379             << loc() << ": info: operation undefined:\n"
1380             << "  " << *this << "\n";
1381         return {};
1382     }
1383     else if (op == BinOp::MUL && (retLeft.isZero() || retRight.isZero())) {
1384         // NOTE: keep binary operation untouched
1385     }
1386     else if (retLeft.type == SimplifyRet::CONSTANT && retRight.type == SimplifyRet::CONSTANT) {
1387         auto left  = retLeft.val.num();
1388         auto right = retRight.val.num();
1389         if (op == BinOp::POW && left == 0 && right < 0) {
1390             GRINGO_REPORT(log, Warnings::OperationUndefined)
1391                 << loc() << ": info: operation undefined:\n"
1392                 << "  " << *this << "\n";
1393             return {};
1394         }
1395         return {Symbol::createNum(Gringo::eval(op, left, right))};
1396     }
1397     else if (retLeft.type == SimplifyRet::CONSTANT && retRight.type == SimplifyRet::LINEAR) {
1398         if (op == BinOp::ADD) {
1399             retRight.lin().n += retLeft.val.num();
1400             return retRight;
1401         }
1402         else if (op == BinOp::SUB) {
1403             retRight.lin().n = retLeft.val.num() - retRight.lin().n;
1404             retRight.lin().m = -retRight.lin().m;
1405             return retRight;
1406         }
1407         else if (op == BinOp::MUL) {
1408             retRight.lin().n *= retLeft.val.num();
1409             retRight.lin().m *= retLeft.val.num();
1410             return retRight;
1411         }
1412     }
1413     else if (retLeft.type == SimplifyRet::LINEAR && retRight.type == SimplifyRet::CONSTANT) {
1414         if (op == BinOp::ADD) {
1415             retLeft.lin().n += retRight.val.num();
1416             return retLeft;
1417         }
1418         else if (op == BinOp::SUB) {
1419             retLeft.lin().n-= retRight.val.num();
1420             return retLeft;
1421         }
1422         else if (op == BinOp::MUL) {
1423             retLeft.lin().n *= retRight.val.num();
1424             retLeft.lin().m *= retRight.val.num();
1425             return retLeft;
1426         }
1427     }
1428     retLeft.update(left, true);
1429     retRight.update(right, true);
1430     return {*this, false};
1431 }
1432 
project(bool rename,AuxGen & auxGen)1433 Term::ProjectRet BinOpTerm::project(bool rename, AuxGen &auxGen) {
1434     assert(!rename); (void)rename;
1435     UTerm y(auxGen.uniqueVar(loc(), 0, "#X"));
1436     UTerm x(wrap(UTerm(y->clone())));
1437     return std::make_tuple(wrap(make_locatable<BinOpTerm>(loc(), op, std::move(left), std::move(right))), std::move(x), std::move(y));
1438 }
1439 
hasVar() const1440 bool BinOpTerm::hasVar() const {
1441     return left->hasVar() || right->hasVar();
1442 }
1443 
hasPool() const1444 bool BinOpTerm::hasPool() const  { return left->hasPool() || right->hasPool(); }
1445 
collect(VarTermBoundVec & vars,bool) const1446 void BinOpTerm::collect(VarTermBoundVec &vars, bool) const {
1447     left->collect(vars, false);
1448     right->collect(vars, false);
1449 }
1450 
collect(VarSet & vars,unsigned minLevel,unsigned maxLevel) const1451 void BinOpTerm::collect(VarSet &vars, unsigned minLevel , unsigned maxLevel) const {
1452     left->collect(vars, minLevel, maxLevel);
1453     right->collect(vars, minLevel, maxLevel);
1454 }
1455 
eval(bool & undefined,Logger & log) const1456 Symbol BinOpTerm::eval(bool &undefined, Logger &log) const {
1457     bool undefined_arg = false;
1458     Symbol l(left->eval(undefined_arg, log));
1459     Symbol r(right->eval(undefined_arg, log));
1460     bool defined =
1461         l.type() == SymbolType::Num &&
1462         r.type() == SymbolType::Num &&
1463         ((op != BinOp::DIV && op != BinOp::MOD) || r.num() != 0) &&
1464         (op != BinOp::POW || l.num() != 0 || r.num() >= 0);
1465     if (defined) {
1466         undefined = undefined || undefined_arg;
1467         return Symbol::createNum(Gringo::eval(op, l.num(), r.num()));
1468     }
1469     else if (!undefined_arg) {
1470         GRINGO_REPORT(log, Warnings::OperationUndefined)
1471             << loc() << ": info: operation undefined:\n"
1472             << "  " << *this << "\n";
1473     }
1474     undefined = true;
1475     return Symbol::createNum(0);
1476 }
1477 
match(Symbol const &) const1478 bool BinOpTerm::match(Symbol const &) const { throw std::logic_error("Term::rewriteArithmetics must be called before Term::match"); }
1479 
unpool(UTermVec & x) const1480 void BinOpTerm::unpool(UTermVec &x) const {
1481     auto f = [&](UTerm &&l, UTerm &&r) { x.emplace_back(make_locatable<BinOpTerm>(loc(), op, std::move(l), std::move(r))); };
1482     Term::unpool(left, right, Gringo::unpool, Gringo::unpool, f);
1483 }
1484 
rewriteArithmetics(Term::ArithmeticsMap & arith,AuxGen & auxGen,bool)1485 UTerm BinOpTerm::rewriteArithmetics(Term::ArithmeticsMap &arith, AuxGen &auxGen, bool) {
1486     return Term::insert(arith, auxGen, make_locatable<BinOpTerm>(loc(), op, std::move(left), std::move(right)));
1487 }
1488 
operator ==(Term const & x) const1489 bool BinOpTerm::operator==(Term const &x) const {
1490     auto t = dynamic_cast<BinOpTerm const*>(&x);
1491     return t && op == t->op && is_value_equal_to(left, t->left) && is_value_equal_to(right, t->right);
1492 }
1493 
hash() const1494 size_t BinOpTerm::hash() const {
1495     return get_value_hash(typeid(BinOpTerm).hash_code(), size_t(op), left, right);
1496 }
1497 
clone() const1498 BinOpTerm *BinOpTerm::clone() const {
1499     return make_locatable<BinOpTerm>(loc(), op, get_clone(left), get_clone(right)).release();
1500 }
1501 
getSig() const1502 Sig BinOpTerm::getSig() const    { throw std::logic_error("Term::getSig must not be called on DotsTerm"); }
1503 
renameVars(RenameMap & names) const1504 UTerm BinOpTerm::renameVars(RenameMap &names) const {
1505     UTerm term(left->renameVars(names));
1506     return make_locatable<BinOpTerm>(loc(), op, std::move(term), right->renameVars(names));
1507 }
1508 
gterm(RenameMap & names,ReferenceMap & refs) const1509 UGTerm BinOpTerm::gterm(RenameMap &names, ReferenceMap &refs) const  { return gringo_make_unique<GVarTerm>(_newRef(names, refs)); }
1510 
collectIds(VarSet & x) const1511 void BinOpTerm::collectIds(VarSet &x) const {
1512     left->collectIds(x);
1513     right->collectIds(x);
1514 }
1515 
replace(Defines & x,bool)1516 UTerm BinOpTerm::replace(Defines &x, bool) {
1517     Term::replace(left, left->replace(x, true));
1518     Term::replace(right, right->replace(x, true));
1519     return nullptr;
1520 }
1521 
estimate(double,VarSet const &) const1522 double BinOpTerm::estimate(double, VarSet const &) const {
1523     return 0;
1524 }
1525 
isEDB() const1526 Symbol BinOpTerm::isEDB() const { return {}; }
1527 
~BinOpTerm()1528 BinOpTerm::~BinOpTerm() { }
1529 
1530 // {{{1 definition of DotsTerm
1531 
DotsTerm(UTerm && left,UTerm && right)1532 DotsTerm::DotsTerm(UTerm &&left, UTerm &&right)
1533     : left(std::move(left))
1534     , right(std::move(right)) { }
1535 
projectScore() const1536 unsigned DotsTerm::projectScore() const {
1537     return 2;
1538 }
1539 
rename(String)1540 void DotsTerm::rename(String) {
1541     throw std::logic_error("must not be called");
1542 }
getLevel() const1543 unsigned DotsTerm::getLevel() const {
1544     return std::max(left->getLevel(), right->getLevel());
1545 }
1546 
isNotNumeric() const1547 bool DotsTerm::isNotNumeric() const     { return false; }
isNotFunction() const1548 bool DotsTerm::isNotFunction() const     { return true; }
1549 
getInvertibility() const1550 Term::Invertibility DotsTerm::getInvertibility() const     { return Term::NOT_INVERTIBLE; }
1551 
print(std::ostream & out) const1552 void DotsTerm::print(std::ostream &out) const {
1553     out << "(" << *left << ".." << *right << ")";
1554 }
1555 
simplify(SimplifyState & state,bool,bool,Logger & log)1556 Term::SimplifyRet DotsTerm::simplify(SimplifyState &state, bool, bool, Logger &log) {
1557     if (!left->simplify(state, false, false, log).update(left, true).undefined() && !right->simplify(state, false, false, log).update(right, true).undefined()) {
1558         return { state.createDots(loc(), std::move(left), std::move(right)) };
1559     }
1560     else {
1561         return {};
1562     }
1563 }
1564 
project(bool,AuxGen &)1565 Term::ProjectRet DotsTerm::project(bool, AuxGen &) {
1566     throw std::logic_error("Term::project must be called after Term::simplify");
1567 }
1568 
hasVar() const1569 bool DotsTerm::hasVar() const {
1570     return left->hasVar() || right->hasVar();
1571 }
1572 
hasPool() const1573 bool DotsTerm::hasPool() const   { return left->hasPool() || right->hasPool(); }
1574 
collect(VarTermBoundVec & vars,bool) const1575 void DotsTerm::collect(VarTermBoundVec &vars, bool) const {
1576     left->collect(vars, false);
1577     right->collect(vars, false);
1578 }
1579 
collect(VarSet & vars,unsigned minLevel,unsigned maxLevel) const1580 void DotsTerm::collect(VarSet &vars, unsigned minLevel , unsigned maxLevel) const {
1581     left->collect(vars, minLevel, maxLevel);
1582     right->collect(vars, minLevel, maxLevel);
1583 }
1584 
eval(bool &,Logger &) const1585 Symbol DotsTerm::eval(bool &, Logger &) const { throw std::logic_error("Term::rewriteDots must be called before Term::eval"); }
1586 
match(Symbol const &) const1587 bool DotsTerm::match(Symbol const &) const  { throw std::logic_error("Term::rewriteDots must be called before Term::match"); }
1588 
unpool(UTermVec & x) const1589 void DotsTerm::unpool(UTermVec &x) const {
1590     auto f = [&](UTerm &&l, UTerm &&r) { x.emplace_back(make_locatable<DotsTerm>(loc(), std::move(l), std::move(r))); };
1591     Term::unpool(left, right, Gringo::unpool, Gringo::unpool, f);
1592 }
1593 
rewriteArithmetics(Term::ArithmeticsMap &,AuxGen &,bool)1594 UTerm DotsTerm::rewriteArithmetics(Term::ArithmeticsMap &, AuxGen &, bool) {
1595     throw std::logic_error("Term::rewriteDots must be called before Term::rewriteArithmetics");
1596 }
1597 
operator ==(Term const &) const1598 bool DotsTerm::operator==(Term const &) const {
1599     // Note: each DotsTerm is associated to a unique variable
1600     return false;
1601 }
1602 
hash() const1603 size_t DotsTerm::hash() const {
1604     return get_value_hash(typeid(DotsTerm).hash_code(), left, right);
1605 }
1606 
clone() const1607 DotsTerm *DotsTerm::clone() const {
1608     return make_locatable<DotsTerm>(loc(), get_clone(left), get_clone(right)).release();
1609 }
1610 
getSig() const1611 Sig DotsTerm::getSig() const     { throw std::logic_error("Term::getSig must not be called on LuaTerm"); }
1612 
renameVars(RenameMap & names) const1613 UTerm DotsTerm::renameVars(RenameMap &names) const {
1614     UTerm term(left->renameVars(names));
1615     return make_locatable<DotsTerm>(loc(), std::move(term), right->renameVars(names));
1616 }
1617 
gterm(RenameMap & names,ReferenceMap & refs) const1618 UGTerm DotsTerm::gterm(RenameMap &names, ReferenceMap &refs) const   { return gringo_make_unique<GVarTerm>(_newRef(names, refs)); }
1619 
collectIds(VarSet & x) const1620 void DotsTerm::collectIds(VarSet &x) const {
1621     left->collectIds(x);
1622     right->collectIds(x);
1623 }
1624 
replace(Defines & x,bool)1625 UTerm DotsTerm::replace(Defines &x, bool) {
1626     Term::replace(left, left->replace(x, true));
1627     Term::replace(right, right->replace(x, true));
1628     return nullptr;
1629 }
1630 
estimate(double,VarSet const &) const1631 double DotsTerm::estimate(double, VarSet const &) const {
1632     return 0;
1633 }
1634 
isEDB() const1635 Symbol DotsTerm::isEDB() const { return {}; }
1636 
~DotsTerm()1637 DotsTerm::~DotsTerm() { }
1638 
1639 // {{{1 definition of LuaTerm
1640 
LuaTerm(String name,UTermVec && args)1641 LuaTerm::LuaTerm(String name, UTermVec &&args)
1642     : name(name)
1643     , args(std::move(args)) { }
1644 
projectScore() const1645 unsigned LuaTerm::projectScore() const {
1646     return 2;
1647 }
1648 
rename(String)1649 void LuaTerm::rename(String) {
1650     throw std::logic_error("must not be called");
1651 }
1652 
getLevel() const1653 unsigned LuaTerm::getLevel() const {
1654     unsigned level = 0;
1655     for (auto &x : args) { level = std::max(x->getLevel(), level); }
1656     return level;
1657 }
1658 
isNotNumeric() const1659 bool LuaTerm::isNotNumeric() const { return false; }
isNotFunction() const1660 bool LuaTerm::isNotFunction() const { return false; }
1661 
getInvertibility() const1662 Term::Invertibility LuaTerm::getInvertibility() const      { return Term::NOT_INVERTIBLE; }
1663 
print(std::ostream & out) const1664 void LuaTerm::print(std::ostream &out) const {
1665     out << "@" << name << "(";
1666     print_comma(out, args, ",", [](std::ostream &out, UTerm const &y) { out << *y; });
1667     out << ")";
1668 }
1669 
simplify(SimplifyState & state,bool,bool arith,Logger & log)1670 Term::SimplifyRet LuaTerm::simplify(SimplifyState &state, bool, bool arith, Logger &log) {
1671     for (auto &arg : args) {
1672         if (arg->simplify(state, false, false, log).update(arg, false).undefined()) {
1673             return {};
1674         }
1675     }
1676     return state.createScript(loc(), std::move(name), std::move(args), arith);
1677 }
1678 
project(bool rename,AuxGen & auxGen)1679 Term::ProjectRet LuaTerm::project(bool rename, AuxGen &auxGen) {
1680     assert(!rename); (void)rename;
1681     UTerm y(auxGen.uniqueVar(loc(), 0, "#X"));
1682     UTerm x(wrap(UTerm(y->clone())));
1683     return std::make_tuple(make_locatable<LuaTerm>(loc(), name, std::move(args)), std::move(x), std::move(y));
1684 }
1685 
hasVar() const1686 bool LuaTerm::hasVar() const {
1687     for (auto &x : args) {
1688         if (x->hasVar()) { return true; }
1689     }
1690     return false;
1691 }
1692 
hasPool() const1693 bool LuaTerm::hasPool() const {
1694     for (auto &x : args) { if (x->hasPool()) { return true; } }
1695     return false;
1696 }
1697 
collect(VarTermBoundVec & vars,bool) const1698 void LuaTerm::collect(VarTermBoundVec &vars, bool) const {
1699     for (auto &y : args) { y->collect(vars, false); }
1700 }
1701 
collect(VarSet & vars,unsigned minLevel,unsigned maxLevel) const1702 void LuaTerm::collect(VarSet &vars, unsigned minLevel , unsigned maxLevel) const {
1703     for (auto &y : args) { y->collect(vars, minLevel, maxLevel); }
1704 }
1705 
eval(bool &,Logger &) const1706 Symbol LuaTerm::eval(bool &, Logger &) const { throw std::logic_error("Term::simplify must be called before Term::eval"); }
1707 
match(Symbol const &) const1708 bool LuaTerm::match(Symbol const &) const   { throw std::logic_error("Term::rewriteArithmetics must be called before Term::match"); }
1709 
unpool(UTermVec & x) const1710 void LuaTerm::unpool(UTermVec &x) const {
1711     auto f = [&](UTermVec &&args) { x.emplace_back(make_locatable<LuaTerm>(loc(), name, std::move(args))); };
1712     Term::unpool(args.begin(), args.end(), Gringo::unpool, f);
1713 }
1714 
rewriteArithmetics(Term::ArithmeticsMap & arith,AuxGen & auxGen,bool)1715 UTerm LuaTerm::rewriteArithmetics(Term::ArithmeticsMap &arith, AuxGen &auxGen, bool) {
1716     return Term::insert(arith, auxGen, make_locatable<LuaTerm>(loc(), name, std::move(args)));
1717 }
1718 
operator ==(Term const & x) const1719 bool LuaTerm::operator==(Term const &x) const {
1720     auto t = dynamic_cast<LuaTerm const*>(&x);
1721     return t && name == t->name && is_value_equal_to(args, t->args);
1722 }
1723 
hash() const1724 size_t LuaTerm::hash() const {
1725     return get_value_hash(typeid(LuaTerm).hash_code(), name, args);
1726 }
1727 
clone() const1728 LuaTerm *LuaTerm::clone() const {
1729     return make_locatable<LuaTerm>(loc(), name, get_clone(args)).release();
1730 }
1731 
getSig() const1732 Sig LuaTerm::getSig() const { return Sig(name, numeric_cast<uint32_t>(args.size()), false); }
1733 
renameVars(RenameMap & names) const1734 UTerm LuaTerm::renameVars(RenameMap &names) const {
1735     UTermVec args;
1736     for (auto &x : this->args) { args.emplace_back(x->renameVars(names)); }
1737     return make_locatable<LuaTerm>(loc(), name, std::move(args));
1738 }
1739 
gterm(RenameMap & names,ReferenceMap & refs) const1740 UGTerm LuaTerm::gterm(RenameMap &names, ReferenceMap &refs) const    { return gringo_make_unique<GVarTerm>(_newRef(names, refs)); }
1741 
collectIds(VarSet & x) const1742 void LuaTerm::collectIds(VarSet &x) const {
1743     for (auto &y : args) { y->collectIds(x); }
1744 }
1745 
replace(Defines & x,bool)1746 UTerm LuaTerm::replace(Defines &x, bool) {
1747     for (auto &y : args) { Term::replace(y, y->replace(x, true)); }
1748     return nullptr;
1749 }
1750 
estimate(double,VarSet const &) const1751 double LuaTerm::estimate(double, VarSet const &) const {
1752     return 0;
1753 }
1754 
isEDB() const1755 Symbol LuaTerm::isEDB() const { return {}; }
1756 
~LuaTerm()1757 LuaTerm::~LuaTerm() { }
1758 
1759 // {{{1 definition of FunctionTerm
1760 
FunctionTerm(String name,UTermVec && args)1761 FunctionTerm::FunctionTerm(String name, UTermVec &&args)
1762     : name(name)
1763     , args(std::move(args)) { }
1764 
projectScore() const1765 unsigned FunctionTerm::projectScore() const {
1766     unsigned ret = 0;
1767     for (auto &x : args) { ret += x->projectScore(); }
1768     return ret;
1769 }
1770 
rename(String x)1771 void FunctionTerm::rename(String x) {
1772     name = x;
1773 }
1774 
getLevel() const1775 unsigned FunctionTerm::getLevel() const {
1776     unsigned level = 0;
1777     for (auto &x : args) { level = std::max(x->getLevel(), level); }
1778     return level;
1779 }
1780 
isNotNumeric() const1781 bool FunctionTerm::isNotNumeric() const { return true; }
isNotFunction() const1782 bool FunctionTerm::isNotFunction() const { return false; }
1783 
getInvertibility() const1784 Term::Invertibility FunctionTerm::getInvertibility() const { return Term::INVERTIBLE; }
1785 
print(std::ostream & out) const1786 void FunctionTerm::print(std::ostream &out) const {
1787     out << name << "(";
1788     print_comma(out, args, ",", [](std::ostream &out, UTerm const &y) { out << *y; });
1789     if (name == "" && args.size() == 1) {
1790         out << ",";
1791     }
1792     out << ")";
1793 }
1794 
simplify(SimplifyState & state,bool positional,bool,Logger & log)1795 Term::SimplifyRet FunctionTerm::simplify(SimplifyState &state, bool positional, bool, Logger &log) {
1796     bool constant  = true;
1797     bool projected = false;
1798     for (auto &arg : args) {
1799         auto ret(arg->simplify(state, positional, false, log));
1800         if (ret.undefined()) {
1801             return {};
1802         }
1803         constant  = constant  && ret.constant();
1804         projected = projected || ret.project;
1805         ret.update(arg, false);
1806     }
1807     if (constant) {
1808         bool undefined = false;
1809         return {eval(undefined, log)};
1810     }
1811     else { return {*this, projected}; }
1812 }
1813 
project(bool rename,AuxGen & auxGen)1814 Term::ProjectRet FunctionTerm::project(bool rename, AuxGen &auxGen) {
1815     UTermVec argsProjected;
1816     UTermVec argsProject;
1817     for (auto &arg : args) {
1818         auto ret(arg->project(false, auxGen));
1819         Term::replace(arg, std::move(std::get<0>(ret)));
1820         argsProjected.emplace_back(std::move(std::get<1>(ret)));
1821         argsProject.emplace_back(std::move(std::get<2>(ret)));
1822     }
1823     String oldName = name;
1824     if (rename) { name = String((std::string("#p_") + name.c_str()).c_str()); }
1825     return std::make_tuple(
1826             nullptr,
1827             make_locatable<FunctionTerm>(loc(), name, std::move(argsProjected)),
1828             make_locatable<FunctionTerm>(loc(), oldName, std::move(argsProject)));
1829 }
1830 
hasVar() const1831 bool FunctionTerm::hasVar() const {
1832     for (auto &x : args) {
1833         if (x->hasVar()) { return true; }
1834     }
1835     return false;
1836 }
1837 
hasPool() const1838 bool FunctionTerm::hasPool() const {
1839     for (auto &x : args) { if (x->hasPool()) { return true; } }
1840     return false;
1841 }
1842 
collect(VarTermBoundVec & vars,bool bound) const1843 void FunctionTerm::collect(VarTermBoundVec &vars, bool bound) const {
1844     for (auto &y : args) { y->collect(vars, bound); }
1845 }
1846 
collect(VarSet & vars,unsigned minLevel,unsigned maxLevel) const1847 void FunctionTerm::collect(VarSet &vars, unsigned minLevel , unsigned maxLevel) const {
1848     for (auto &y : args) { y->collect(vars, minLevel, maxLevel); }
1849 }
1850 
eval(bool & undefined,Logger & log) const1851 Symbol FunctionTerm::eval(bool &undefined, Logger &log) const {
1852     cache.clear();
1853     for (auto &term : args) { cache.emplace_back(term->eval(undefined, log)); }
1854     return Symbol::createFun(name, Potassco::toSpan(cache));
1855 }
1856 
match(Symbol const & x) const1857 bool FunctionTerm::match(Symbol const &x) const {
1858     if (x.type() == SymbolType::Fun) {
1859         Sig s(x.sig());
1860         if (!s.sign() && s.name() == name && s.arity() == args.size()) {
1861             auto i = 0;
1862             for (auto &term : args) {
1863                 if (!term->match(x.args()[i++])) { return false; }
1864             }
1865             return true;
1866         }
1867     }
1868     return false;
1869 }
1870 
unpool(UTermVec & x) const1871 void FunctionTerm::unpool(UTermVec &x) const {
1872     auto f = [&](UTermVec &&args) { x.emplace_back(make_locatable<FunctionTerm>(loc(), name, std::move(args))); };
1873     Term::unpool(args.begin(), args.end(), Gringo::unpool, f);
1874 }
1875 
rewriteArithmetics(Term::ArithmeticsMap & arith,AuxGen & auxGen,bool forceDefined)1876 UTerm FunctionTerm::rewriteArithmetics(Term::ArithmeticsMap &arith, AuxGen &auxGen, bool forceDefined) {
1877     for (auto &arg : args) { Term::replace(arg, arg->rewriteArithmetics(arith, auxGen, forceDefined)); }
1878     return nullptr;
1879 }
1880 
operator ==(Term const & x) const1881 bool FunctionTerm::operator==(Term const &x) const {
1882     auto t = dynamic_cast<FunctionTerm const*>(&x);
1883     return t && name == t->name && is_value_equal_to(args, t->args);
1884 }
1885 
hash() const1886 size_t FunctionTerm::hash() const {
1887     return get_value_hash(typeid(FunctionTerm).hash_code(), name, args);
1888 }
1889 
clone() const1890 FunctionTerm *FunctionTerm::clone() const {
1891     return make_locatable<FunctionTerm>(loc(), name, get_clone(args)).release();
1892 }
1893 
getSig() const1894 Sig FunctionTerm::getSig() const { return Sig(name, numeric_cast<uint32_t>(args.size()), false); }
1895 
renameVars(RenameMap & names) const1896 UTerm FunctionTerm::renameVars(RenameMap &names) const {
1897     UTermVec args;
1898     for (auto &x : this->args) { args.emplace_back(x->renameVars(names)); }
1899     return make_locatable<FunctionTerm>(loc(), name, std::move(args));
1900 }
1901 
gterm(RenameMap & names,ReferenceMap & refs) const1902 UGTerm FunctionTerm::gterm(RenameMap &names, ReferenceMap &refs) const {
1903     return gfunterm(names, refs);
1904 }
1905 
gfunterm(RenameMap & names,ReferenceMap & refs) const1906 UGFunTerm FunctionTerm::gfunterm(RenameMap &names, ReferenceMap &refs) const {
1907     UGTermVec args;
1908     for (auto &x : this->args) { args.emplace_back(x->gterm(names, refs)); }
1909     return gringo_make_unique<GFunctionTerm>(name, std::move(args));
1910 }
1911 
collectIds(VarSet & x) const1912 void FunctionTerm::collectIds(VarSet &x) const {
1913     for (auto &y : args) { y->collectIds(x); }
1914 }
1915 
replace(Defines & x,bool)1916 UTerm FunctionTerm::replace(Defines &x, bool) {
1917     for (auto &y : args) { Term::replace(y, y->replace(x, true)); }
1918     return nullptr;
1919 }
1920 
estimate(double size,VarSet const & bound) const1921 double FunctionTerm::estimate(double size, VarSet const &bound) const {
1922     double ret = 0.0;
1923     if (!args.empty()) {
1924         double root = std::max(1.0, std::pow((name.empty() ? size : size/2.0), 1.0/args.size()));
1925         for (auto &x : args) { ret += x->estimate(root, bound); }
1926         ret /= args.size();
1927     }
1928     return ret;
1929 }
1930 
isEDB() const1931 Symbol FunctionTerm::isEDB() const {
1932     cache.clear();
1933     for (auto &x : args) {
1934         cache.emplace_back(x->isEDB());
1935         if (cache.back().type() == SymbolType::Special) { return {}; }
1936     }
1937     return Symbol::createFun(name, Potassco::toSpan(cache));
1938 }
1939 
isAtom() const1940 bool FunctionTerm::isAtom() const {
1941     return true;
1942 }
1943 
~FunctionTerm()1944 FunctionTerm::~FunctionTerm() { }
1945 
1946 // }}}1
1947 
1948 } // namespace Gringo
1949