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