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/terms.hh"
26 #include "gringo/logger.hh"
27 #include <cstring>
28 
29 namespace Gringo {
30 
31 // {{{1 TheoryOpDef
32 
TheoryOpDef(Location const & loc,String op,unsigned priority,TheoryOperatorType type)33 TheoryOpDef::TheoryOpDef(Location const &loc, String op, unsigned priority, TheoryOperatorType type)
34 : loc_(loc)
35 , op_(op)
36 , priority_(priority)
37 , type_(type) { }
38 
39 TheoryOpDef::TheoryOpDef(TheoryOpDef &&) = default;
40 
op() const41 String TheoryOpDef::op() const {
42     return op_;
43 }
44 
key() const45 TheoryOpDef::Key TheoryOpDef::key() const {
46     return {op_, type_ == TheoryOperatorType::Unary};
47 }
48 
49 TheoryOpDef::~TheoryOpDef() noexcept = default;
50 
51 TheoryOpDef &TheoryOpDef::operator=(TheoryOpDef &&) = default;
52 
loc() const53 Location const &TheoryOpDef::loc() const {
54     return loc_;
55 }
56 
print(std::ostream & out) const57 void TheoryOpDef::print(std::ostream &out) const {
58     out << op_ << " :" << priority_ << "," << type_;
59 }
60 
priority() const61 unsigned TheoryOpDef::priority() const {
62     return priority_;
63 }
64 
type() const65 TheoryOperatorType TheoryOpDef::type() const {
66     return type_;
67 }
68 
69 // {{{1 TheoryTermDef
70 
TheoryTermDef(Location const & loc,String name)71 TheoryTermDef::TheoryTermDef(Location const &loc, String name)
72 : loc_(loc)
73 , name_(name) { }
74 
75 TheoryTermDef::TheoryTermDef(TheoryTermDef &&) = default;
76 
addOpDef(TheoryOpDef && def,Logger & log)77 void TheoryTermDef::addOpDef(TheoryOpDef &&def, Logger &log) {
78     auto it = opDefs_.find(def.key());
79     if (it == opDefs_.end()) {
80         opDefs_.push(std::move(def));
81     }
82     else {
83         GRINGO_REPORT(log, Warnings::RuntimeError)
84             << def.loc() << ": error: redefinition of theory operator:" << "\n"
85             << "  " << def.op() << "\n"
86             << it->loc() << ": note: operator first defined here\n";
87     }
88 }
89 
name() const90 String TheoryTermDef::name() const {
91     return name_;
92 }
93 
loc() const94 Location const &TheoryTermDef::loc() const {
95     return loc_;
96 }
97 
98 TheoryTermDef::~TheoryTermDef() noexcept = default;
99 
100 TheoryTermDef &TheoryTermDef::operator=(TheoryTermDef &&) = default;
101 
print(std::ostream & out) const102 void TheoryTermDef::print(std::ostream &out) const {
103     out << name_ << "{";
104     print_comma(out, opDefs_, ",");
105     out << "}";
106 }
107 
getPrioAndAssoc(String op) const108 std::pair<unsigned, bool> TheoryTermDef::getPrioAndAssoc(String op) const {
109     auto ret = opDefs_.find(TheoryOpDef::Key{op, false});
110     if (ret != opDefs_.end()) {
111         return {ret->priority(), ret->type() == TheoryOperatorType::BinaryLeft};
112     }
113     else {
114         return {0, true};
115     }
116 }
117 
hasOp(String op,bool unary) const118 bool TheoryTermDef::hasOp(String op, bool unary) const {
119     return opDefs_.find(TheoryOpDef::Key{op, unary}) != opDefs_.end();
120 }
121 
getPrio(String op,bool unary) const122 unsigned TheoryTermDef::getPrio(String op, bool unary) const {
123     auto ret = opDefs_.find(TheoryOpDef::Key{op, unary});
124     if (ret != opDefs_.end()) {
125         return ret->priority();
126     }
127     else {
128         return 0;
129     }
130 }
131 
132 // {{{1 TheoryAtomDef
133 
TheoryAtomDef(Location const & loc,String name,unsigned arity,String elemDef,TheoryAtomType type)134 TheoryAtomDef::TheoryAtomDef(Location const &loc, String name, unsigned arity, String elemDef, TheoryAtomType type)
135 : TheoryAtomDef(loc, name, arity, elemDef, type, {}, "") { }
136 
TheoryAtomDef(Location const & loc,String name,unsigned arity,String elemDef,TheoryAtomType type,StringVec && ops,String guardDef)137 TheoryAtomDef::TheoryAtomDef(Location const &loc, String name, unsigned arity, String elemDef, TheoryAtomType type, StringVec &&ops, String guardDef)
138 : loc_(loc)
139 , sig_(name, arity, false)
140 , elemDef_(elemDef)
141 , guardDef_(guardDef)
142 , ops_(std::move(ops))
143 , type_(type) { }
144 
145 TheoryAtomDef::TheoryAtomDef(TheoryAtomDef &&) = default;
146 
sig() const147 Sig TheoryAtomDef::sig() const {
148     return sig_;
149 }
150 
hasGuard() const151 bool TheoryAtomDef::hasGuard() const {
152     return !ops_.empty();
153 }
154 
type() const155 TheoryAtomType TheoryAtomDef::type() const {
156     return type_;
157 }
158 
loc() const159 Location const &TheoryAtomDef::loc() const {
160     return loc_;
161 }
162 
ops() const163 StringVec const &TheoryAtomDef::ops() const {
164     return ops_;
165 }
166 
elemDef() const167 String TheoryAtomDef::elemDef() const {
168     return elemDef_;
169 }
170 
guardDef() const171 String TheoryAtomDef::guardDef() const {
172     assert(hasGuard());
173     return guardDef_;
174 }
175 
176 TheoryAtomDef::~TheoryAtomDef() noexcept = default;
177 
178 TheoryAtomDef &TheoryAtomDef::operator=(TheoryAtomDef &&) = default;
179 
print(std::ostream & out) const180 void TheoryAtomDef::print(std::ostream &out) const {
181     out << "&" << sig_.name() << "/" << sig_.arity() << ":" << elemDef_;
182     if (hasGuard()) {
183         out << ",{";
184         print_comma(out, ops_, ",");
185         out << "}," << guardDef_;
186     }
187     out << "," << type_;
188 }
189 
190 // {{{1 TheoryDef
191 
TheoryDef(Location const & loc,String name)192 TheoryDef::TheoryDef(Location const &loc, String name)
193 : loc_(loc)
194 , name_(name) { }
195 
196 TheoryDef::TheoryDef(TheoryDef &&) = default;
197 
name() const198 String TheoryDef::name() const {
199     return name_;
200 }
201 
loc() const202 Location const &TheoryDef::loc() const {
203     return loc_;
204 }
205 
addAtomDef(TheoryAtomDef && def,Logger & log)206 void TheoryDef::addAtomDef(TheoryAtomDef &&def, Logger &log) {
207     auto it = atomDefs_.find(def.sig());
208     if (it == atomDefs_.end()) {
209         atomDefs_.push(std::move(def));
210     }
211     else {
212         GRINGO_REPORT(log, Warnings::RuntimeError)
213             << def.loc() << ": error: redefinition of theory atom:" << "\n"
214             << "  " << def.sig() << "\n"
215             << it->loc() << ": note: atom first defined here\n";
216     }
217 }
218 
addTermDef(TheoryTermDef && def,Logger & log)219 void TheoryDef::addTermDef(TheoryTermDef &&def, Logger &log) {
220     auto it = termDefs_.find(def.name());
221     if (it == termDefs_.end()) {
222         termDefs_.push(std::move(def));
223     }
224     else {
225         GRINGO_REPORT(log, Warnings::RuntimeError)
226             << def.loc() << ": error: redefinition of theory term:" << "\n"
227             << "  " << def.name() << "\n"
228             << it->loc() << ": note: term first defined term\n";
229     }
230 }
231 
232 TheoryDef::~TheoryDef() noexcept = default;
233 
234 TheoryDef &TheoryDef::operator=(TheoryDef &&) = default;
235 
print(std::ostream & out) const236 void TheoryDef::print(std::ostream &out) const {
237     out << "#theory " << name_ << "{";
238     if (!atomDefs_.empty() || !termDefs_.empty()) {
239         out << "\n";
240     }
241     bool sep = false;
242     for (auto &def : termDefs_) {
243         if (sep) { out << ";\n"; }
244         else     { sep = true; }
245         out << "  " << def;
246     }
247     for (auto &def : atomDefs_) {
248         if (sep) { out << ";\n"; }
249         else     { sep = true; }
250         out << "  " << def;
251     }
252     if (sep) {
253         out << "\n";
254     }
255     out << "}.";
256 }
257 
getAtomDef(Sig sig) const258 TheoryAtomDef const *TheoryDef::getAtomDef(Sig sig) const {
259     auto ret = atomDefs_.find(sig);
260     return ret != atomDefs_.end() ? &*ret : nullptr;
261 }
262 
getTermDef(String name) const263 TheoryTermDef const *TheoryDef::getTermDef(String name) const {
264     auto ret = termDefs_.find(name);
265     return ret != termDefs_.end() ? &*ret : nullptr;
266 }
267 
atomDefs() const268 TheoryAtomDefs const &TheoryDef::atomDefs() const {
269     return atomDefs_;
270 }
271 
272 // }}}1
273 
274 // {{{1 definition of CSPRelTerm
275 
276 CSPRelTerm::CSPRelTerm(CSPRelTerm &&) = default;
277 CSPRelTerm &CSPRelTerm::operator=(CSPRelTerm &&) = default;
278 
CSPRelTerm(Relation rel,CSPAddTerm && term)279 CSPRelTerm::CSPRelTerm(Relation rel, CSPAddTerm &&term)
280     : rel(rel)
281     , term(std::move(term)) { }
282 
collect(VarTermBoundVec & vars) const283 void CSPRelTerm::collect(VarTermBoundVec &vars) const                            { term.collect(vars); }
collect(VarTermSet & vars) const284 void CSPRelTerm::collect(VarTermSet &vars) const                                 { term.collect(vars); }
replace(Defines & x)285 void CSPRelTerm::replace(Defines &x)                                             { term.replace(x); }
operator ==(CSPRelTerm const & x) const286 bool CSPRelTerm::operator==(CSPRelTerm const &x) const                           { return rel == x.rel && term == x.term; }
simplify(SimplifyState & state,Logger & log)287 bool CSPRelTerm::simplify(SimplifyState &state, Logger &log)             { return term.simplify(state, log); }
rewriteArithmetics(Term::ArithmeticsMap & arith,AuxGen & auxGen)288 void CSPRelTerm::rewriteArithmetics(Term::ArithmeticsMap &arith, AuxGen &auxGen) { term.rewriteArithmetics(arith, auxGen); }
hash() const289 size_t CSPRelTerm::hash() const                                                  { return get_value_hash(size_t(rel), term); }
hasPool() const290 bool CSPRelTerm::hasPool() const                                                 { return term.hasPool(); }
unpool() const291 std::vector<CSPRelTerm> CSPRelTerm::unpool() const {
292     std::vector<CSPRelTerm> ret;
293     for (auto &x : term.unpool()) { ret.emplace_back(rel, std::move(x)); }
294     return ret;
295 }
~CSPRelTerm()296 CSPRelTerm::~CSPRelTerm() { }
operator <<(std::ostream & out,CSPRelTerm const & x)297 std::ostream &operator<<(std::ostream &out, CSPRelTerm const &x) {
298     out << "$" << x.rel << x.term;
299     return out;
300 }
operator ()(CSPRelTerm const & x) const301 CSPRelTerm clone<CSPRelTerm>::operator()(CSPRelTerm const &x) const {
302     return CSPRelTerm(x.rel, get_clone(x.term));
303 }
304 
305 // {{{1 definition of CSPAddTerm
306 
307 CSPAddTerm::CSPAddTerm(CSPAddTerm &&) = default;
308 CSPAddTerm &CSPAddTerm::operator=(CSPAddTerm &&) = default;
309 
CSPAddTerm(CSPMulTerm && x)310 CSPAddTerm::CSPAddTerm(CSPMulTerm &&x) { terms.emplace_back(std::move(x)); }
CSPAddTerm(Terms && terms)311 CSPAddTerm::CSPAddTerm(Terms &&terms) : terms(std::move(terms)) { }
312 
append(CSPMulTerm && x)313 void CSPAddTerm::append(CSPMulTerm &&x) { terms.emplace_back(std::move(x)); }
314 
collect(VarTermBoundVec & vars) const315 void CSPAddTerm::collect(VarTermBoundVec &vars) const {
316     for (auto &x : terms) { x.collect(vars); }
317 }
collect(VarTermSet & vars) const318 void CSPAddTerm::collect(VarTermSet &vars) const {
319     for (auto &x : terms) { x.collect(vars); }
320 }
replace(Defines & x)321 void CSPAddTerm::replace(Defines &x) {
322     for (auto &y : terms) { y.replace(x); }
323 }
operator ==(CSPAddTerm const & x) const324 bool CSPAddTerm::operator==(CSPAddTerm const &x) const { return is_value_equal_to(terms, x.terms); }
simplify(SimplifyState & state,Logger & log)325 bool CSPAddTerm::simplify(SimplifyState &state, Logger &log) {
326     for (auto &y : terms) {
327         if (!y.simplify(state, log)) { return false; }
328     }
329     return true;
330 }
rewriteArithmetics(Term::ArithmeticsMap & arith,AuxGen & auxGen)331 void CSPAddTerm::rewriteArithmetics(Term::ArithmeticsMap &arith, AuxGen &auxGen) {
332     for (auto &x : terms) { x.rewriteArithmetics(arith, auxGen); }
333 }
hash() const334 size_t CSPAddTerm::hash() const { return get_value_hash(terms); }
hasPool() const335 bool CSPAddTerm::hasPool() const {
336     for (auto &y : terms) {
337         if (y.hasPool()) { return true; }
338     }
339     return false;
340 }
unpool() const341 std::vector<CSPAddTerm> CSPAddTerm::unpool() const {
342     std::vector<CSPAddTerm> x;
343     auto f = [&](Terms &&args) { x.emplace_back(std::move(args)); };
344     Term::unpool(terms.begin(), terms.end(), std::bind(&CSPMulTerm::unpool, std::placeholders::_1), f);
345     return x;
346 }
toGround(CSPGroundLit & ground,bool invert,Logger & log) const347 void CSPAddTerm::toGround(CSPGroundLit &ground, bool invert, Logger &log) const {
348     bool undefined = false;
349     for (auto &x : terms) {
350         int coe = x.coe->toNum(undefined, log);
351         if (coe != 0) {
352             if (x.var) { std::get<1>(ground).emplace_back(invert ? -coe : coe, x.var->eval(undefined, log)); }
353             else       { std::get<2>(ground) = eval(invert ? BinOp::ADD : BinOp::SUB, std::get<2>(ground), coe); }
354         }
355     }
356     assert(!undefined);
357 }
358 
checkEval(Logger & log) const359 bool CSPAddTerm::checkEval(Logger &log) const {
360     for (auto &x : terms) {
361         bool undefined = false;
362         x.coe->toNum(undefined, log);
363         if (undefined) { return false; }
364         if (x.var) {
365             x.var->eval(undefined, log);
366             if (undefined) { return false; }
367         }
368     }
369     return true;
370 }
371 
~CSPAddTerm()372 CSPAddTerm::~CSPAddTerm() { }
operator <<(std::ostream & out,CSPAddTerm const & x)373 std::ostream &operator<<(std::ostream &out, CSPAddTerm const &x) {
374     print_comma(out, x.terms, "$+");
375     return out;
376 }
operator ()(CSPAddTerm const & x) const377 CSPAddTerm clone<CSPAddTerm>::operator()(CSPAddTerm const &x) const {
378     return CSPAddTerm(get_clone(x.terms));
379 }
380 
381 // {{{1 definition of CSPMulTerm
382 
383 CSPMulTerm::CSPMulTerm(CSPMulTerm &&) = default;
384 CSPMulTerm &CSPMulTerm::operator=(CSPMulTerm &&) = default;
385 
CSPMulTerm(UTerm && var,UTerm && coe)386 CSPMulTerm::CSPMulTerm(UTerm &&var, UTerm &&coe)
387     : var(std::move(var))
388     , coe(std::move(coe)) { }
389 
collect(VarTermBoundVec & vars) const390 void CSPMulTerm::collect(VarTermBoundVec &vars) const {
391     if (var) { var->collect(vars, false); }
392     coe->collect(vars, false);
393 }
collect(VarTermSet & vars) const394 void CSPMulTerm::collect(VarTermSet &vars) const {
395     if (var) { var->collect(vars); }
396     coe->collect(vars);
397 }
replace(Defines & x)398 void CSPMulTerm::replace(Defines &x) {
399     if (var) { Term::replace(var, var->replace(x, true)); }
400     Term::replace(coe, coe->replace(x, true));
401 }
operator ==(CSPMulTerm const & x) const402 bool CSPMulTerm::operator==(CSPMulTerm const &x) const {
403     if (var && x.var) { return is_value_equal_to(var, x.var) && is_value_equal_to(coe, x.coe); }
404     else { return !var && !x.var && is_value_equal_to(coe, x.coe); }
405 }
simplify(SimplifyState & state,Logger & log)406 bool CSPMulTerm::simplify(SimplifyState &state, Logger &log) {
407     if (var && var->simplify(state, false, false, log).update(var, false).undefined()) { return false;}
408     return !coe->simplify(state, false, false, log).update(coe, false).undefined();
409 }
rewriteArithmetics(Term::ArithmeticsMap & arith,AuxGen & auxGen)410 void CSPMulTerm::rewriteArithmetics(Term::ArithmeticsMap &arith, AuxGen &auxGen) {
411     if (var) { Term::replace(var, var->rewriteArithmetics(arith, auxGen)); }
412     Term::replace(coe, coe->rewriteArithmetics(arith, auxGen));
413 }
hash() const414 size_t CSPMulTerm::hash() const {
415     if (var) { return get_value_hash(var, coe); }
416     else     { return get_value_hash(coe); }
417 }
hasPool() const418 bool CSPMulTerm::hasPool() const {
419     return (var && var->hasPool()) || coe->hasPool();
420 }
unpool() const421 std::vector<CSPMulTerm> CSPMulTerm::unpool() const {
422     std::vector<CSPMulTerm> value;
423     if (var) {
424         auto f = [&](UTerm &&a, UTerm &&b) { value.emplace_back(std::move(a), std::move(b)); };
425         Term::unpool(var, coe, Gringo::unpool, Gringo::unpool, f);
426     }
427     else {
428         for (auto &x : Gringo::unpool(coe)) { value.emplace_back(nullptr, std::move(x)); }
429     }
430     return value;
431 }
~CSPMulTerm()432 CSPMulTerm::~CSPMulTerm() { }
operator <<(std::ostream & out,CSPMulTerm const & x)433 std::ostream &operator<<(std::ostream &out, CSPMulTerm const &x) {
434     out << *x.coe;
435     if (x.var) { out << "$*$" << *x.var; };
436     return out;
437 }
operator ()(CSPMulTerm const & x) const438 CSPMulTerm clone<CSPMulTerm>::operator()(CSPMulTerm const &x) const {
439     return CSPMulTerm(x.var ? get_clone(x.var) : nullptr, get_clone(x.coe));
440 }
441 
442 // }}}1
443 
444 } // namspace Gringo
445 
446