1 //
2 // Copyright (c) 2006-2017 Benjamin Kaufmann
3 //
4 // This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to
8 // deal in the Software without restriction, including without limitation the
9 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 // IN THE SOFTWARE.
23 //
24 #include <clasp/parser.h>
25 #include <clasp/minimize_constraint.h>
26 #include <clasp/solver.h>
27 #include <potassco/theory_data.h>
28 #include <potassco/aspif.h>
29 #include <potassco/smodels.h>
30 #include <potassco/string_convert.h>
31 #include "lpcompare.h"
32 #include "catch.hpp"
33 namespace Clasp { namespace Test {
34 
35 template <class Api>
parse(Api & api,std::istream & str,const ParserOptions & opts=ParserOptions ())36 static bool parse(Api& api, std::istream& str, const ParserOptions& opts = ParserOptions()) {
37 	ProgramParser& p = api.parser();
38 	return p.accept(str, opts) && p.parse();
39 }
clear(VarVec & vec)40 VarVec& clear(VarVec& vec) { vec.clear(); return vec; }
operator ,(VarVec & vec,int val)41 static VarVec& operator, (VarVec& vec, int val) {
42 	vec.push_back(val);
43 	return vec;
44 }
45 static struct Empty {
46 	template <class T>
operator Potassco::Span<T>Clasp::Test::Empty47 	operator Potassco::Span<T> () const { return Potassco::toSpan<T>(); }
48 } empty;
49 
50 #define NOT_EMPTY(X,...)
51 #define SPAN(VEC,...)      NOT_EMPTY(__VA_ARGS__) Potassco::toSpan((clear(VEC) , __VA_ARGS__))
52 
53 using Potassco::SmodelsOutput;
54 using Potassco::AspifOutput;
55 namespace {
56 struct Input {
57 	std::stringstream prg;
toSmodelsClasp::Test::__anon65a961950111::Input58 	void toSmodels(const char* txt, Var falseAt = 0) {
59 		prg.clear();
60 		prg.str("");
61 		SmodelsOutput sm(prg, true, falseAt);
62 		Potassco::AspifTextInput x(&sm);
63 		std::stringstream temp; temp << txt;
64 		REQUIRE(Potassco::readProgram(temp, x, 0) == 0);
65 	}
toAspifClasp::Test::__anon65a961950111::Input66 	void toAspif(const char* txt) {
67 		prg.clear();
68 		prg.str("");
69 		AspifOutput aspif(prg);
70 		Potassco::AspifTextInput x(&aspif);
71 		std::stringstream temp; temp << txt;
72 		REQUIRE(Potassco::readProgram(temp, x, 0) == 0);
73 	}
operator std::stringstream&Clasp::Test::__anon65a961950111::Input74 	operator std::stringstream& () { return prg; }
75 };
76 }
77 TEST_CASE("Smodels parser", "[parser][asp]") {
78 	SharedContext     ctx;
79 	Input             in;
80 	Asp::LogicProgram api;
81 	api.start(ctx, Asp::LogicProgram::AspOptions().noScc());
82 	SECTION("testEmptySmodels") {
83 		in.toSmodels("");
84 		REQUIRE(parse(api, in));
85 		api.endProgram();
86 		std::stringstream empty;
87 		REQUIRE(compareSmodels(empty, api));
88 	}
89 	SECTION("testSingleFact") {
90 		in.toSmodels("x1.");
91 		REQUIRE(parse(api, in));
92 		api.endProgram();
93 		REQUIRE(0u == ctx.numVars());
94 	}
95 	SECTION("testComputeStatementAssumptions") {
96 		in.toSmodels(
97 			"x1 :- not x2.\n"
98 			"x2 :- not x3.\n"
99 			"x3 :- not x4.\n"
100 			"x4 :- not x3.\n"
101 			":- x2.\n", 2);
102 		REQUIRE(parse(api, in));
103 		api.endProgram() && ctx.endInit();
104 		REQUIRE(ctx.master()->isTrue(api.getLiteral(3)));
105 		REQUIRE(ctx.master()->isTrue(api.getLiteral(1)));
106 		REQUIRE(ctx.master()->isFalse(api.getLiteral(2)));
107 		REQUIRE(ctx.master()->isFalse(api.getLiteral(4)));
108 	}
109 	SECTION("testTransformSimpleConstraintRule") {
110 		in.toSmodels(
111 			"x1 :- 2{not x3, x2, x4}.\n"
112 			"x2 :- not x3.\n"
113 			"x3 :- not x2.\n"
114 			"x4 :- not x3.\n");
115 		api.setExtendedRuleMode(Asp::LogicProgram::mode_transform_weight);
116 		REQUIRE(parse(api, in));
117 		REQUIRE(api.endProgram());
118 		REQUIRE(api.getLiteral(2) == api.getLiteral(1));
119 		REQUIRE(api.getLiteral(4) == api.getLiteral(1));
120 		in.toSmodels(
121 			"x1 :- not x3.\n"
122 			"x3 :-not x1.\n"
123 			"x2 :- x1.\n"
124 			"x4 :- x1.\n");
125 		REQUIRE(compareSmodels(in, api));
126 	}
127 	SECTION("testTransformSimpleWeightRule") {
128 		in.toSmodels(
129 			"x1 :- 2 {x2=1, not x3=2, x4=3}.\n"
130 			"x2 :- not x3.\n"
131 			"x3 :- not x2.\n"
132 			"x4 :-not x3.");
133 		api.setExtendedRuleMode(Asp::LogicProgram::mode_transform_weight);
134 		REQUIRE(parse(api, in));
135 		REQUIRE(api.endProgram());
136 		REQUIRE(1u == ctx.numVars());
137 		in.toSmodels(
138 			"x1 :- not x3.\n"
139 			"x3 :- not x1.\n"
140 			"x2 :- x1.\n"
141 			"x4 :- x1.\n");
142 		REQUIRE(compareSmodels(in, api));
143 	}
144 	SECTION("testTransformSimpleChoiceRule") {
145 		in.toSmodels("{x1;x2;x3} :- x2, not x3.\n"
146 			"x2 :- not x1.\n"
147 			"x3 :- not x2.");
148 		api.setExtendedRuleMode(Asp::LogicProgram::mode_transform_choice);
149 		REQUIRE(parse(api, in));
150 		REQUIRE(api.endProgram());
151 		in.toSmodels("x2. x4.");
152 		REQUIRE(compareSmodels(in, api));
153 	}
154 	SECTION("testSimpleConstraintRule") {
155 		in.toSmodels("x1 :- 2 {x3, x2, x4}.\n"
156 			"x2 :- not x3.\n"
157 			"x3 :- not x2.\n"
158 			"x4 :- not x3.");
159 		REQUIRE(parse(api, in));
160 		REQUIRE(api.endProgram());
161 		REQUIRE(2u == ctx.numVars());
162 		in.toSmodels("x1 :- 2 {x3, x2 = 2}.\n"
163 			"x2 :- not x3.\n"
164 			"x3 :- not x2.\n"
165 			"x4 :- x2.");
166 		REQUIRE(compareSmodels(in, api));
167 	}
168 	SECTION("testSimpleWeightRule") {
169 		in.toSmodels("x1 :- 2 {x3 = 2, x2 = 1, x4 = 3}. % but (x4 = 3 -> x4 = 2).\n"
170 			"x2 :- not x3.\n"
171 			"x3 :- not x2.\n"
172 			"x4 :- not x3.");
173 		REQUIRE(parse(api, in));
174 		REQUIRE(api.endProgram());
175 		REQUIRE(2u == ctx.numVars());
176 		in.toSmodels("x1 :- 2 {x3 = 2, x2 = 3}.\n"
177 			"x2 :- not x3.\n"
178 			"x3 :- not x2.\n"
179 			"x4 :- x2.");
180 		REQUIRE(compareSmodels(in, api));
181 	}
182 	SECTION("testSimpleChoiceRule") {
183 		in.toSmodels("{x1;x2;x3} :- x2, not x3.\n"
184 			"x2 :- not x1.\n"
185 			"x3 :- not x2.\n");
186 		REQUIRE(parse(api, in));
187 		REQUIRE(api.endProgram());
188 		in.toSmodels("x2.");
189 		REQUIRE(compareSmodels(in, api));
190 	}
191 	SECTION("testMinimizePriority") {
192 		in.toSmodels("{x1;x2;x3;x4}.\n"
193 			"#minimize{not x1, not x2}.\n"
194 			"#minimize{x3, x4}.");
195 		REQUIRE(parse(api, in));
196 		REQUIRE(api.endProgram());
197 		SharedMinimizeData* m1 = ctx.minimize();
198 		REQUIRE(m1->numRules() == 2);
199 		REQUIRE(std::find(m1->lits, m1->lits + 2, WeightLiteral(api.getLiteral(3), 0)) != m1->lits + 2);
200 		REQUIRE(std::find(m1->lits, m1->lits + 2, WeightLiteral(api.getLiteral(4), 0)) != m1->lits + 2);
201 		REQUIRE(std::find(m1->lits + 2, m1->lits + 4, WeightLiteral(~api.getLiteral(1), 1)) != m1->lits + 4);
202 		REQUIRE(std::find(m1->lits + 2, m1->lits + 4, WeightLiteral(~api.getLiteral(2), 1)) != m1->lits + 4);
203 	}
204 	SECTION("testEdgeDirectives") {
205 		in.toSmodels("{x1;x2}.\n"
206 			"#output _edge(0,1)  : x1.\n"
207 			"#output _acyc_1_1_0 : x2.");
208 		REQUIRE(parse(api, in, ParserOptions().enableAcycEdges()));
209 		REQUIRE((api.endProgram() && ctx.endInit()));
210 		REQUIRE(uint32(2) == ctx.stats().acycEdges);
211 	}
212 	SECTION("testHeuristicDirectives") {
213 		in.toSmodels("{x1;x2;x3}.\n"
214 			"#output _heuristic(a,true,1) : x1.\n"
215 			"#output _heuristic(_heuristic(a,true,1),true,1) : x2.\n"
216 			"#output a : x3.");
217 		REQUIRE(parse(api, in, ParserOptions().enableHeuristic()));
218 		REQUIRE((api.endProgram() && ctx.endInit()));
219 		REQUIRE(ctx.heuristic.size() == 2);
220 	}
221 	SECTION("testSimpleIncremental") {
222 		in.toSmodels(
223 			"#incremental.\n"
224 			"{x1;x2} :-x3.\n"
225 			"#external x3.\n"
226 			"#step.\n"
227 			"#external x3.[release]\n"
228 			"{x3}.");
229 		api.updateProgram();
230 		ProgramParser& p = api.parser();
231 		REQUIRE(p.accept(in));
232 		REQUIRE(p.parse());
233 		REQUIRE(api.endProgram());
234 		REQUIRE(p.more());
235 		REQUIRE(api.stats.rules[0].sum() == 1);
236 		REQUIRE(api.isExternal(3));
237 		api.updateProgram();
238 		REQUIRE(p.parse());
239 		REQUIRE(!p.more());
240 		REQUIRE(api.stats.rules[0].sum() == 1);
241 		REQUIRE(!api.isExternal(3));
242 	}
243 	SECTION("testIncrementalMinimize") {
244 		in.toSmodels(
245 			"#incremental.\n"
246 			"{x1; x2}.\n"
247 			"#minimize{not x1, not x2}.\n"
248 			"#step."
249 			"#minimize{x1, x2}.\n");
250 		api.updateProgram();
251 		ProgramParser& p = api.parser();
252 		REQUIRE(p.accept(in));
253 		REQUIRE(p.parse());
254 		REQUIRE(api.endProgram());
255 		SharedMinimizeData* m1 = ctx.minimize();
256 		m1->share();
257 		REQUIRE(std::find(m1->lits, m1->lits + 2, WeightLiteral(~api.getLiteral(1), 1)) != m1->lits + 2);
258 		REQUIRE(std::find(m1->lits, m1->lits + 2, WeightLiteral(~api.getLiteral(2), 1)) != m1->lits + 2);
259 
260 		REQUIRE(p.more());
261 		api.updateProgram();
262 		REQUIRE(p.parse());
263 		REQUIRE(!p.more());
264 		REQUIRE(api.endProgram());
265 		SharedMinimizeData* m2 = ctx.minimize();
266 		REQUIRE(m1 != m2);
267 		REQUIRE(isSentinel(m2->lits[2].first));
268 		REQUIRE(std::find(m2->lits, m2->lits + 2, WeightLiteral(api.getLiteral(1), 1)) != m2->lits + 2);
269 		REQUIRE(std::find(m2->lits, m2->lits + 2, WeightLiteral(api.getLiteral(2), 1)) != m2->lits + 2);
270 		m1->release();
271 	}
272 };
273 
sameProgram(Asp::LogicProgram & a,std::stringstream & prg)274 static bool sameProgram(Asp::LogicProgram& a, std::stringstream& prg) {
275 	std::stringstream out;
276 	AspParser::write(a, out, AspParser::format_aspif);
277 	prg.clear();
278 	prg.seekg(0);
279 	return compareProgram(prg, out);
280 }
281 
282 TEST_CASE("Aspif parser", "[parser][asp]") {
283 	SharedContext     ctx;
284 	Input             in;
285 	Asp::LogicProgram api;
286 	api.start(ctx, Asp::LogicProgram::AspOptions().noScc());
287 	SECTION("testEmptyProgram") {
288 		in.toAspif("");
289 		REQUIRE(parse(api, in));
290 		REQUIRE(api.endProgram());
291 		REQUIRE(api.stats.rules[0].sum() == 0);
292 	}
293 	SECTION("testSingleFact") {
294 		in.toAspif("x1.");
295 		REQUIRE(parse(api, in));
296 		REQUIRE(api.endProgram());
297 		REQUIRE(api.stats.rules[0].sum() == 1);
298 		REQUIRE(api.getLiteral(1) == lit_true());
299 	}
300 	SECTION("testSimpleRule") {
301 		in.toAspif("x1 :- x3, not x2.");
302 		REQUIRE(parse(api, in));
303 		REQUIRE(api.stats.rules[0].sum() == 1);
304 		REQUIRE(api.numAtoms() == 3);
305 		REQUIRE(api.numBodies() == 1);
306 		REQUIRE(api.getBody(0)->size() == 2);
307 		REQUIRE(api.getBody(0)->goal(0) == posLit(3));
308 		REQUIRE(api.getBody(0)->goal(1) == negLit(2));
309 		REQUIRE(sameProgram(api, in));
310 	}
311 	SECTION("testInvalidAtom") {
312 		in.toAspif(POTASSCO_FORMAT("x%u :- not x2, x3.", varMax));
313 		REQUIRE_THROWS_AS(parse(api, in), std::logic_error);
314 	}
315 	SECTION("testInvalidLiteral") {
316 		in.toAspif(POTASSCO_FORMAT("x1 :- not x%u, x3.", varMax));
317 		REQUIRE_THROWS_AS(parse(api, in), std::logic_error);
318 	}
319 	SECTION("testIntegrityConstraint") {
320 		in.toAspif(":- not x1, x2.");
321 		REQUIRE(parse(api, in));
322 		REQUIRE(api.stats.rules[0].sum() == 1);
323 		REQUIRE(api.numAtoms() == 2);
324 		REQUIRE(api.numBodies() == 1);
325 		REQUIRE(api.getBody(0)->size() == 2);
326 		REQUIRE(api.getBody(0)->goal(0) == posLit(2));
327 		REQUIRE(api.getBody(0)->goal(1) == negLit(1));
328 		REQUIRE(api.getBody(0)->value() == value_false);
329 	}
330 	SECTION("testDisjunctiveRule") {
331 		in.toAspif("x1|x2.");
332 		REQUIRE(parse(api, in));
333 		REQUIRE(api.stats.rules[0].sum() == 1);
334 		REQUIRE(api.stats.disjunctions[0] == 1);
335 		REQUIRE(api.numAtoms() == 2);
336 		REQUIRE(sameProgram(api, in));
337 	}
338 	SECTION("testChoiceRule") {
339 		in.toAspif("{x1;x2;x3}.");
340 		REQUIRE(parse(api, in));
341 		REQUIRE(api.stats.rules[0].sum() == 1);
342 		REQUIRE(api.stats.rules[0][Asp::RuleStats::Choice] == 1);
343 		REQUIRE(api.numAtoms() == 3);
344 		REQUIRE(sameProgram(api, in));
345 	}
346 	SECTION("testWeightRule") {
347 		in.toAspif(
348 			"x1 :- 2{x2, x3, not x4}.\n"
349 			"x5 :- 4{x2 = 2, x3, not x4 = 3}.\n");
350 		REQUIRE(parse(api, in));
351 		REQUIRE(api.stats.rules[0].sum() == 2);
352 		REQUIRE(api.stats.bodies[0][Asp::Body_t::Sum] == 1);
353 		REQUIRE(api.stats.bodies[0][Asp::Body_t::Count] == 1);
354 		REQUIRE(api.numBodies() == 2);
355 		REQUIRE((api.getBody(0)->bound() == 2 && !api.getBody(0)->hasWeights()));
356 		REQUIRE((api.getBody(1)->bound() == 4 && api.getBody(1)->hasWeights()));
357 		REQUIRE(sameProgram(api, in));
358 	}
359 	SECTION("testDisjunctiveWeightRule") {
360 		in.toAspif("x1|x2 :- 2{x3, x4, not x5}.");
361 		REQUIRE(parse(api, in));
362 		REQUIRE(sameProgram(api, in));
363 	}
364 	SECTION("testChoiceWeightRule") {
365 		in.toAspif("{x1;x2} :- 2{x3, x4, not x5}.");
366 		REQUIRE(parse(api, in));
367 		REQUIRE(sameProgram(api, in));
368 	}
369 	SECTION("testInvalidNegativeWeightInWeightRule") {
370 		in.toAspif("x1 :- 2 {x2 = -1, not x3 = 1, x4}.");
371 		REQUIRE_THROWS_AS(parse(api, in), std::logic_error);
372 	}
373 	SECTION("testInvalidHeadInWeightRule") {
374 		in.prg
375 			<< "asp 1 0 0"
376 			<< "\n1 0 1 0 1 2 3 2 1 -3 1 4 1"
377 			<< "\n0\n";
378 		REQUIRE_THROWS_AS(parse(api, in), std::logic_error);
379 	}
380 	SECTION("testNegativeBoundInWeightRule") {
381 		in.toAspif("x1 :- -1 {x2, not x3, x4}.");
382 		REQUIRE(parse(api, in));
383 		REQUIRE(api.stats.rules[0].sum() == 1);
384 		REQUIRE(api.stats.bodies[0][Asp::Body_t::Sum] == 0);
385 	}
386 	SECTION("testMinimizeRule") {
387 		in.toAspif(
388 			"{x1;x2;x3}.\n"
389 			"#minimize{x1 = 2, x2, not x3 = 4}.");
390 		REQUIRE(parse(api, in));
391 		REQUIRE(api.stats.rules[0][Asp::RuleStats::Minimize] == 1);
392 		REQUIRE(sameProgram(api, in));
393 	}
394 	SECTION("testMinimizeRuleMergePriority") {
395 		in.toAspif(
396 			"#minimize{x1 = 2, x2, not x3 = 4}@1.\n"
397 			"#minimize{x4 = 2, x2 = 2, x3 = 4}@1.");
398 		REQUIRE(parse(api, in));
399 		REQUIRE(api.stats.rules[0].sum() == 1);
400 		REQUIRE(api.stats.rules[0][Asp::RuleStats::Minimize] == 1);
401 	}
402 	SECTION("testMinimizeRuleWithNegativeWeights") {
403 		in.toAspif("#minimize{x4 = -2, x2 = -1, x3 = 4}.");
404 		REQUIRE(parse(api, in));
405 		REQUIRE(api.stats.rules[0].sum() == 1);
406 		REQUIRE(api.endProgram());
407 		std::stringstream exp("6 0 2 2 4 2 2 1 ");
408 		REQUIRE(findSmodels(exp, api));
409 	}
410 	SECTION("testIncremental") {
411 		in.toAspif("#incremental.\n"
412 			"{x1;x2}.\n"
413 			"#minimize{x1=2, x2}.\n"
414 			"#step.\n"
415 			"{x3}.\n"
416 			"#minimize{x3=4}.\n");
417 
418 		REQUIRE(parse(api, in));
419 		REQUIRE(api.stats.rules[0].sum() == 2);
420 		REQUIRE(api.endProgram());
421 
422 		ProgramParser& p = api.parser();
423 		REQUIRE(p.more());
424 		api.updateProgram();
425 		REQUIRE(p.parse());
426 		REQUIRE_FALSE(p.more());
427 		REQUIRE(api.stats.rules[0].sum() > 0);
428 		// minimize rule was merged
429 		REQUIRE(ctx.minimize()->numRules() == 1);
430 	}
431 	SECTION("testIncrementalExternal") {
432 		in.toAspif("#incremental."
433 			"x1 :- x2.\n"
434 			"#external x2. [true]\n"
435 			"#step.\n"
436 			"#external x2. [false]\n"
437 			"#step.\n"
438 			"#external x2. [release]\n");
439 
440 		REQUIRE(parse(api, in));
441 		REQUIRE(api.endProgram());
442 		LitVec assume;
443 		api.getAssumptions(assume);
444 		REQUIRE((assume.size() == 1 && assume[0] == api.getLiteral(2)));
445 
446 		ProgramParser& p = api.parser();
447 		REQUIRE(p.more());
448 		api.updateProgram();
449 		REQUIRE(p.parse());
450 
451 		REQUIRE(api.endProgram());
452 		assume.clear();
453 		api.getAssumptions(assume);
454 		REQUIRE((assume.size() == 1 && assume[0] == ~api.getLiteral(2)));
455 
456 		REQUIRE(p.more());
457 		api.updateProgram();
458 		REQUIRE(p.parse());
459 		REQUIRE(!p.more());
460 		REQUIRE(api.endProgram());
461 		assume.clear();
462 		api.getAssumptions(assume);
463 		REQUIRE(assume.empty());
464 		ctx.endInit();
465 		REQUIRE(ctx.master()->isFalse(api.getLiteral(2)));
466 	}
467 	SECTION("testSimpleEdgeDirective") {
468 		in.toAspif("{x1;x2}."
469 			"#edge (0,1) : x1.\n"
470 			"#edge (1,0) : x2.\n");
471 		REQUIRE(parse(api, in));
472 		REQUIRE((api.endProgram() && ctx.endInit()));
473 		REQUIRE(ctx.stats().acycEdges == 2);
474 		REQUIRE(sameProgram(api, in));
475 	}
476 	SECTION("testComplexEdgeDirective") {
477 		in.toAspif("{x1;x2;x3;x4}."
478 			"#edge (0,1) : x1, not x2.\n"
479 			"#edge (1,0) : x3, not x4.\n");
480 		REQUIRE(parse(api, in));
481 		REQUIRE((api.endProgram() && ctx.endInit()));
482 		REQUIRE(ctx.stats().acycEdges == 2);
483 		REQUIRE(sameProgram(api, in));
484 	}
485 	SECTION("testHeuristicDirective") {
486 		in.toAspif("{x1;x2;x3;x4}."
487 			"#heuristic x1. [-1@1,sign]\n"
488 			"#heuristic x1 : x3, not x4. [1@1,factor]");
489 		REQUIRE(parse(api, in));
490 		REQUIRE((api.endProgram() && ctx.endInit()));
491 		REQUIRE(ctx.heuristic.size() == 2);
492 		REQUIRE(ctx.heuristic.begin()->type() == DomModType::Sign);
493 		REQUIRE((ctx.heuristic.begin() + 1)->type() == DomModType::Factor);
494 		REQUIRE(ctx.heuristic.begin()->var() == (ctx.heuristic.begin() + 1)->var());
495 		REQUIRE(sameProgram(api, in));
496 	}
497 
498 	SECTION("testOutputDirective") {
499 		in.toAspif("{x1;x2}."
500 			"#output fact.\n"
501 			"#output conj : x1, x2.");
502 		REQUIRE(parse(api, in));
503 		REQUIRE((api.endProgram() && ctx.endInit()));
504 		REQUIRE(ctx.output.size() == 2);
505 		REQUIRE(ctx.output.numFacts() == 1);
506 		REQUIRE(sameProgram(api, in));
507 	}
508 	SECTION("testAssumptionDirective") {
509 		in.toAspif("{x1;x2}."
510 			"#assume{not x2, x1}.");
511 		REQUIRE(parse(api, in));
512 		REQUIRE(api.endProgram());
513 		LitVec assume;
514 		api.getAssumptions(assume);
515 		REQUIRE(assume.size() == 2);
516 		REQUIRE(std::find(assume.begin(), assume.end(), ~api.getLiteral(2)) != assume.end());
517 		REQUIRE(std::find(assume.begin(), assume.end(), api.getLiteral(1)) != assume.end());
518 		REQUIRE(sameProgram(api, in));
519 	}
520 	SECTION("testProjectionDirective") {
521 		in.toAspif("{x1;x2;x3;x4}."
522 			"#output a : x1.\n"
523 			"#output b : x2.\n"
524 			"#output c : x3.\n"
525 			"#output d : x4.\n"
526 			"#project{x1, x3}.");
527 		REQUIRE(parse(api, in));
528 		REQUIRE(api.endProgram());
529 		REQUIRE(ctx.output.size() == 4);
530 		Literal a = api.getLiteral(1);
531 		Literal b = api.getLiteral(2);
532 		Literal c = api.getLiteral(3);
533 		Literal d = api.getLiteral(4);
534 		OutputTable::lit_iterator proj_begin = ctx.output.proj_begin(), proj_end = ctx.output.proj_end();
535 		REQUIRE(std::find(proj_begin, proj_end, a) != proj_end);
536 		REQUIRE_FALSE(std::find(proj_begin, proj_end, b) != proj_end);
537 		REQUIRE(std::find(proj_begin, proj_end, c) != proj_end);
538 		REQUIRE_FALSE(std::find(proj_begin, proj_end, d) != proj_end);
539 		REQUIRE(sameProgram(api, in));
540 	}
541 	SECTION("testEmptyProjectionDirective") {
542 		in.toAspif("{x1;x2;x3;x4}."
543 			"#project.");
544 		REQUIRE(parse(api, in));
545 		REQUIRE(api.endProgram());
546 		REQUIRE(ctx.output.projectMode() == ProjectMode_t::Explicit);
547 		REQUIRE(sameProgram(api, in));
548 	}
549 	SECTION("testIgnoreCommentDirective") {
550 		in.prg
551 			<< "asp 1 0 0"
552 			<< "\n" << Potassco::Directive_t::Comment << " Ignore me!"
553 			<< "\n0\n";
554 		REQUIRE(parse(api, in));
555 	}
556 	SECTION("testReadTheoryAtoms") {
557 		AspifOutput aspif(in.prg);
558 		aspif.initProgram(false);
559 		aspif.beginStep();
560 		aspif.theoryTerm(0, 1);                     // (number 1)
561 		aspif.theoryTerm(1, 2);                     // (number 2)
562 		aspif.theoryTerm(2, 3);                     // (number 3)
563 		aspif.theoryTerm(3, 4);                     // (number 4)
564 		aspif.theoryTerm(4, Potassco::toSpan("x")); // (string x)
565 		aspif.theoryTerm(5, Potassco::toSpan("z")); // (string z)
566 		aspif.theoryTerm(6, Potassco::toSpan("+")); // (string +)
567 		aspif.theoryTerm(7, Potassco::toSpan("*")); // (string *)
568 		VarVec ids;
569 		aspif.theoryTerm(8,  4, SPAN(ids, 0));         // (function x(1))
570 		aspif.theoryTerm(9,  4, SPAN(ids, 1));         // (function x(2))
571 		aspif.theoryTerm(10, 4, SPAN(ids, 2));         // (function x(3))
572 		aspif.theoryTerm(11, 7, SPAN(ids, 0, 8));      // (term 1*x(1))
573 		aspif.theoryTerm(12, 7, SPAN(ids, 1, 9));      // (term 2*x(2))
574 		aspif.theoryTerm(13, 7, SPAN(ids, 2, 10));     // (term 3*x(3))
575 		aspif.theoryTerm(14, 7, SPAN(ids, 3, 5));      // (term 3*x(3))
576 
577 		aspif.theoryElement(0, SPAN(ids, 11), empty);      // (element 1*x(1):)
578 		aspif.theoryElement(1, SPAN(ids, 12), empty);      // (element 2*x(2):)
579 		aspif.theoryElement(2, SPAN(ids, 13), empty);      // (element 3*x(3):)
580 		aspif.theoryElement(3, SPAN(ids, 14), empty);      // (element 4*z:)
581 		aspif.theoryTerm(15, Potassco::toSpan("sum"));    // (string sum)
582 		aspif.theoryTerm(16, Potassco::toSpan(">="));     // (string >=)
583 		aspif.theoryTerm(17, 42);                         // (number 42)
584 		aspif.theoryAtom(1, 15, SPAN(ids, 0, 1, 2, 3), 16, 17); // (&sum { 1*x(1); 2*x(2); 3*x(3); 4*z     } >= 42)
585 		aspif.endStep();
586 		REQUIRE(parse(api, in));
587 
588 		Potassco::TheoryData& t = api.theoryData();
589 		REQUIRE(t.numAtoms() == 1);
590 
591 		const Potassco::TheoryAtom& a = **t.begin();
592 		REQUIRE(a.atom() == 1);
593 		REQUIRE(std::strcmp(t.getTerm(a.term()).symbol(), "sum") == 0);
594 		REQUIRE(a.size() == 4);
595 		for (Potassco::TheoryAtom::iterator it = a.begin(), end = a.end(); it != end; ++it) {
596 			const Potassco::TheoryElement& e = t.getElement(*it);
597 			REQUIRE(e.size() == 1);
598 			REQUIRE(Literal::fromId(e.condition()) == lit_true());
599 			REQUIRE(t.getTerm(*e.begin()).type() == Potassco::Theory_t::Compound);
600 		}
601 		REQUIRE(std::strcmp(t.getTerm(*a.guard()).symbol(), ">=") == 0);
602 		REQUIRE(t.getTerm(*a.rhs()).number() == 42);
603 
604 
605 		struct BuildStr : public Potassco::TheoryAtomStringBuilder {
getConditionClasp::Test::BuildStr606 			virtual Potassco::LitSpan getCondition(Potassco::Id_t) const { return Potassco::toSpan<Potassco::Lit_t>(); }
getNameClasp::Test::BuildStr607 			virtual std::string       getName(Potassco::Atom_t)    const { return ""; }
608 		} builder;
609 
610 		std::string n = builder.toString(t, a);
611 		REQUIRE(n == "&sum{1 * x(1); 2 * x(2); 3 * x(3); 4 * z} >= 42");
612 	}
613 
614 	SECTION("testWriteTheoryAtoms") {
615 		VarVec ids;
616 		AspifOutput aspif(in.prg);
617 		aspif.initProgram(true);
618 		aspif.beginStep();
619 		aspif.rule(Potassco::Head_t::Choice, SPAN(ids, 1), empty);
620 		aspif.theoryTerm(0, 1);                      // (number 1)
621 		aspif.theoryTerm(1, Potassco::toSpan("x"));  // (string x)
622 		aspif.theoryTerm(3, Potassco::toSpan("foo"));// (string foo)
623 		aspif.theoryTerm(2, 1, SPAN(ids, 0));         // (function x(1))
624 		aspif.theoryElement(0, SPAN(ids, 2), empty);  // (element x(1):)
625 		aspif.theoryAtom(1, 3, SPAN(ids, 0));         // (&foo { x(1); })
626 		aspif.endStep();
627 		std::stringstream step1;
628 		step1 << in.prg.str();
629 		aspif.beginStep();
630 		aspif.rule(Potassco::Head_t::Choice, SPAN(ids, 2), empty);
631 		aspif.theoryAtom(2, 3, SPAN(ids, 0));              // (&foo { x(1); })
632 		aspif.endStep();
633 		REQUIRE(parse(api, in));
634 		REQUIRE((api.endProgram() && api.theoryData().numAtoms() == 1));
635 		std::stringstream out;
636 		AspParser::write(api, out);
637 		REQUIRE(findProgram(step1, out));
638 		ProgramParser& p = api.parser();
639 		REQUIRE(p.more());
640 		api.updateProgram();
641 		REQUIRE(api.theoryData().numAtoms() == 0);
642 		REQUIRE(p.parse());
643 		REQUIRE((api.endProgram() && api.theoryData().numAtoms() == 1));
644 		AspParser::write(api, out);
645 	}
646 	SECTION("testTheoryElementWithCondition") {
647 		VarVec ids;
648 		AspifOutput aspif(in.prg);
649 		aspif.initProgram(false);
650 		aspif.beginStep();
651 		aspif.rule(Potassco::Head_t::Choice, SPAN(ids, 1, 2), empty);
652 		aspif.theoryTerm(0, 1); // (number 1)
653 		aspif.theoryTerm(1, Potassco::toSpan("foo"));
654 		Potassco::Lit_t lits[2] = {1, -2};
655 		aspif.theoryElement(0, SPAN(ids, 0), Potassco::toSpan(lits, 2));
656 		aspif.theoryAtom(0, 1, SPAN(ids, 0));
657 		aspif.endStep();
658 		REQUIRE(parse(api, in));
659 		in.prg.clear();
660 		in.prg.seekg(0);
661 		Potassco::TheoryData& t = api.theoryData();
662 		REQUIRE(t.numAtoms() == 1);
663 		const Potassco::TheoryAtom& a = **t.begin();
664 		REQUIRE(a.size() == 1);
665 		const Potassco::TheoryElement& e = t.getElement(*a.begin());
666 		REQUIRE(e.condition() != 0);
667 		Potassco::LitVec cond;
668 		api.extractCondition(e.condition(), cond);
669 		REQUIRE(cond.size() == 2);
670 		std::stringstream out;
671 		REQUIRE(api.endProgram());
672 		AspParser::write(api, out);
673 		REQUIRE(findProgram(out, in));
674 	}
675 }
676 
677 TEST_CASE("Dimacs parser", "[parser][sat]") {
678 	SharedContext ctx;
679 	SatBuilder    api;
680 	api.startProgram(ctx);
681 	std::stringstream prg;
682 	SECTION("testDimacs") {
683 		prg << "c simple test case\n"
684 		    << "p cnf 4 3\n"
685 		    << "1 2 0\n"
686 		    << "3 4 0\n"
687 		    << "-1 -2 -3 -4 0\n";
688 		REQUIRE((parse(api, prg) && api.endProgram()));
689 		REQUIRE(ctx.numVars() == 4);
690 		REQUIRE(ctx.numConstraints() == 3);
691 	}
692 
693 	SECTION("testDimacsDontAddTaut") {
694 		prg << "c simple test case\n"
695 		    << "p cnf 4 4\n"
696 		    << "1 2 0\n"
697 		    << "3 4 0\n"
698 		    << "-1 -2 -3 -4 0\n"
699 		    << "1 -2 -3 2 0\n";
700 		REQUIRE((parse(api, prg) && api.endProgram()));
701 		REQUIRE(ctx.numVars() == 4);
702 		REQUIRE(ctx.numConstraints() == 3);
703 	}
704 
705 	SECTION("testDimacsDontAddDupLit") {
706 		prg << "c simple test case\n"
707 		    << "p cnf 2 4\n"
708 		    << "1 2 2 1 0\n"
709 		    << "1 2 1 2 0\n"
710 		    << "-1 -2 -1 0\n"
711 		    << "-2 -1 -2 0\n";
712 		REQUIRE((parse(api, prg) && api.endProgram()));
713 		REQUIRE(ctx.numVars() == 2);
714 		REQUIRE(ctx.numConstraints() == 4);
715 		REQUIRE(ctx.numBinary() == 4);
716 	}
717 
718 	SECTION("testDimacsBadVars") {
719 		prg << "p cnf 1 1\n"
720 		    << "1 2 0\n";
721 		REQUIRE_THROWS_AS(parse(api, prg), std::logic_error);
722 	}
723 
724 	SECTION("testWcnf") {
725 		prg << "c comments Weighted Max-SAT\n"
726 		    << "p wcnf 3 5\n"
727 		    << "10 1 -2 0\n"
728 		    << "3 -1 2 -3 0\n"
729 		    << "8 -3 2 0\n"
730 		    << "5 1 3 0\n"
731 		    << "2 3 0\n";
732 		REQUIRE((parse(api, prg) && api.endProgram()));
733 		REQUIRE(ctx.numVars() == 7);
734 		REQUIRE(ctx.output.size() == 3);
735 		REQUIRE(ctx.numConstraints() == 4);
736 
737 		SharedMinimizeData* wLits = ctx.minimize();
738 		REQUIRE(wLits->numRules() == 1);
739 		REQUIRE(wLits->lits[0].second == 10);
740 		REQUIRE(wLits->lits[1].second == 8);
741 		REQUIRE(wLits->lits[2].second == 5);
742 		REQUIRE(wLits->lits[3].second == 3);
743 	}
744 
745 	SECTION("testPartialWcnf") {
746 		prg << "c comments Weigthed Partial Max-SAT\n"
747 		    << "p wcnf 4 5 16\n"
748 		    << "16 1 -2 4 0\n"
749 		    << "16 -1 -2 3 0\n"
750 		    << "8 -2 -4 0\n"
751 		    << "4 -3 2 0\n"
752 		    << "1 1 3 0\n";
753 		REQUIRE((parse(api, prg) && api.endProgram()));
754 		REQUIRE(ctx.numVars() == 7);
755 		REQUIRE(ctx.output.size() == 4);
756 		REQUIRE(ctx.numConstraints() == 5);
757 		SharedMinimizeData* wLits = ctx.minimize();;
758 		REQUIRE(wLits->numRules() == 1);
759 		REQUIRE(wLits->lits[0].second == 8);
760 		REQUIRE(wLits->lits[1].second == 4);
761 		REQUIRE(wLits->lits[2].second == 1);
762 	}
763 	SECTION("testDimacsExtSupportsGraph") {
764 		prg
765 			<< "p cnf 4 3\n"
766 			<< "c graph 2\n"
767 			<< "c node 1 1\n"
768 			<< "c node 2 1\n"
769 			<< "c arc 1 1 2\n"
770 			<< "c arc 2 2 1\n"
771 			<< "c endgraph\n"
772 			<< "1 2 0\n"
773 			<< "3 4 0\n"
774 			<< "-1 -2 -3 -4 0\n";
775 		REQUIRE((parse(api, prg, ParserOptions().enableAcycEdges()) && api.endProgram() && ctx.endInit()));
776 		REQUIRE(uint32(2) == ctx.stats().acycEdges);
777 	}
778 	SECTION("testDimacsExtSupportsCostFunc") {
779 		prg
780 			<< "p cnf 4 3\n"
781 			<< "c minweight 1 2 2 4 -3 1 -4 2 0\n"
782 			<< "1 2 0\n"
783 			<< "3 4 0\n"
784 			<< "-1 -2 -3 -4 0\n";
785 		REQUIRE((parse(api, prg, ParserOptions().enableMinimize()) && api.endProgram()));
786 		REQUIRE(ctx.hasMinimize());
787 		SharedMinimizeData* wLits = ctx.minimize();;
788 		REQUIRE(wLits->numRules() == 1);
789 		REQUIRE(wLits->lits[0] == WeightLiteral(posLit(2), 4));
790 		REQUIRE(wLits->lits[1] == WeightLiteral(posLit(1), 2));
791 		REQUIRE(wLits->lits[2] == WeightLiteral(negLit(4), 2));
792 		REQUIRE(wLits->lits[3] == WeightLiteral(negLit(3), 1));
793 	}
794 	SECTION("testDimacsExtSupportsProject") {
795 		prg
796 			<< "p cnf 4 3\n"
797 			<< "c project 1 4\n"
798 			<< "1 2 0\n"
799 			<< "3 4 0\n"
800 			<< "-1 -2 -3 -4 0\n";
801 		REQUIRE((parse(api, prg, ParserOptions().enableProject()) && api.endProgram()));
802 		REQUIRE(ctx.output.projectMode() == ProjectMode_t::Explicit);
803 		REQUIRE(std::find(ctx.output.proj_begin(), ctx.output.proj_end(), posLit(1)) != ctx.output.proj_end());
804 		REQUIRE(std::find(ctx.output.proj_begin(), ctx.output.proj_end(), posLit(4)) != ctx.output.proj_end());
805 	}
806 	SECTION("testDimacsExtSupportsHeuristic") {
807 		prg
808 			<< "p cnf 4 0\n"
809 			<< "c heuristic 4 1 1 0 0\n"
810 			<< "c heuristic 5 2 1 0 0\n"
811 			<< "c heuristic 5 3 1 0 -1\n";
812 		REQUIRE((parse(api, prg, ParserOptions().enableHeuristic()) && api.endProgram()));
813 		REQUIRE(ctx.heuristic.size() == 3);
814 		DomainTable::iterator it, end = ctx.heuristic.end();
815 		for (it = ctx.heuristic.begin(); it != end && it->var() != 3; ++it) { ;  }
816 		REQUIRE(it != end);
817 		REQUIRE(it->bias() == 1);
818 		REQUIRE(it->prio() == 0);
819 		REQUIRE(it->type() == Potassco::Heuristic_t::False);
820 		REQUIRE(it->cond() == negLit(1));
821 	}
822 	SECTION("testDimacsExtSupportsAssumptions") {
823 		prg
824 			<< "p cnf 4 0\n"
825 			<< "c assume 1 -2 3\n";
826 		REQUIRE((parse(api, prg, ParserOptions().enableAssume()) && api.endProgram()));
827 		LitVec ass;
828 		api.getAssumptions(ass);
829 		REQUIRE(ass.size() == 3);
830 		REQUIRE(ass[0] == posLit(1));
831 		REQUIRE(ass[1] == negLit(2));
832 		REQUIRE(ass[2] == posLit(3));
833 	}
834 	SECTION("testDimacsExtSupportsOutputRange") {
835 		prg
836 			<< "p cnf 4 0\n"
837 			<< "c output range 2 3\n";
838 		REQUIRE((parse(api, prg, ParserOptions().enableOutput()) && api.endProgram()));
839 		REQUIRE(*ctx.output.vars_begin()   == 2);
840 		REQUIRE(*(ctx.output.vars_end()-1) == 3);
841 	}
842 	SECTION("testDimacsExtSupportsOutputTable") {
843 		prg
844 			<< "p cnf 4 0\n"
845 			<< "c output 1  var(1)      \n"
846 			<< "c output -2 not_var(2)  \n"
847 			<< "c 0\n";
848 		REQUIRE((parse(api, prg, ParserOptions().enableOutput()) && api.endProgram()));
849 		REQUIRE(ctx.output.vars_begin() == ctx.output.vars_end());
850 		REQUIRE(ctx.output.size() == 2);
851 		for (OutputTable::pred_iterator it = ctx.output.pred_begin(), end = ctx.output.pred_end(); it != end; ++it) {
852 			REQUIRE((
853 				(it->name.c_str() == std::string("var(1)") && it->cond == posLit(1))
854 				||
855 				(it->name.c_str() == std::string("not_var(2)") && it->cond == negLit(2))
856 				));
857 		}
858 	}
859 }
860 
861 TEST_CASE("Opb parser", "[parser][pb]") {
862 	SharedContext ctx;
863 	PBBuilder     api;
864 	api.startProgram(ctx);
865 	std::stringstream prg;
866 
867 	SECTION("testBadVar") {
868 		prg << "* #variable= 1 #constraint= 1\n+1 x2 >= 1;\n";
869 		REQUIRE_THROWS_AS(parse(api, prg), std::logic_error);
870 	}
871 	SECTION("testBadVarInGraph") {
872 		prg << "* #variable= 1 #constraint= 1\n"
873 		    << "* graph 2\n"
874 		    << "* arc 1 1 2\n"
875 		    << "* arc 2 2 1\n"
876 		    << "* endgraph\n";
877 		REQUIRE_THROWS_AS(parse(api, prg, ParserOptions().enableAcycEdges()), std::logic_error);
878 	}
879 	SECTION("testWBO") {
880 		prg << "* #variable= 1 #constraint= 2 #soft= 2 mincost= 2 maxcost= 3 sumcost= 5\n"
881 		    << "soft: 6 ;\n"
882 		    << "[2] +1 x1 >= 1 ;\n"
883 		    << "[3] -1 x1 >= 0 ;\n";
884 		REQUIRE((parse(api, prg) && api.endProgram()));
885 		REQUIRE(ctx.numVars() == 3);
886 		REQUIRE((ctx.numConstraints() == 0 || ctx.numConstraints() == 2));
887 		REQUIRE(ctx.output.size() == 1);
888 		SumVec bound;
889 		api.getWeakBounds(bound);
890 		REQUIRE((bound.size() == 1 && bound[0] == 5));
891 		SharedMinimizeData* wLits = ctx.minimize();
892 		REQUIRE((wLits && wLits->numRules() == 1));
893 		REQUIRE(wLits->adjust(0) == 2);
894 	}
895 
896 	SECTION("testNLC") {
897 		prg << "* #variable= 5 #constraint= 4 #product= 5 sizeproduct= 13\n"
898 		    << "1 x1 +4 x1 ~x2 -2 x5 >=1;\n"
899 		    << "-1 x1 +4 x2 -2 x5 >= 3;\n"
900 		    << "10 x4 +4 x3 >= 10;\n"
901 		    << "2 x2 x3 +3 x4 ~x5 +2 ~x1 x2 +3 ~x1 x2 x3 ~x4 ~x5 >= 1 ;\n";
902 		REQUIRE((parse(api, prg) && api.endProgram()));
903 		REQUIRE(ctx.numVars() == 10);
904 		REQUIRE(ctx.numConstraints() >= 4);
905 		REQUIRE(ctx.output.size() == 5);
906 	}
907 
908 	SECTION("testNLCUnsorted") {
909 		prg << "* #variable= 4 #constraint= 2 #product= 2 sizeproduct= 8\n"
910 		    << "1 x1 +1 x2 x1 >=1;\n"
911 		    << "1 x1 +1 x2 x3 x4 ~x4 x2 x3 >=1;\n";
912 		REQUIRE((parse(api, prg) && api.endProgram()));
913 		REQUIRE(ctx.numVars() == 6);
914 		REQUIRE(ctx.master()->isTrue(posLit(1)));
915 		REQUIRE(ctx.master()->isFalse(posLit(6)));
916 	}
917 
918 	SECTION("testPBEqualityBug") {
919 		prg << "* #variable= 4 #constraint= 2\n"
920 		    << "+1 x1 = 1;\n"
921 		    << "+1 x1 +1 x2 +1 x3 +1 x4 = 1;\n";
922 		REQUIRE((parse(api, prg) && api.endProgram()));
923 		REQUIRE(ctx.master()->isTrue(posLit(1)));
924 		REQUIRE(ctx.master()->isFalse(posLit(2)));
925 		REQUIRE(ctx.master()->isFalse(posLit(3)));
926 		REQUIRE(ctx.master()->isFalse(posLit(4)));
927 	}
928 	SECTION("testPBProject") {
929 		prg << "* #variable= 6 #constraint= 0\n"
930 			<< "* project x1 x2\n"
931 			<< "* project x4\n";
932 		REQUIRE((parse(api, prg, ParserOptions().enableProject()) && api.endProgram()));
933 		REQUIRE(ctx.output.projectMode() == ProjectMode_t::Explicit);
934 		REQUIRE(std::distance(ctx.output.proj_begin(), ctx.output.proj_end()) == 3u);
935 	}
936 	SECTION("testPBAssume") {
937 		prg << "* #variable= 6 #constraint= 0\n"
938 			  << "* assume x1 -x5\n";
939 		REQUIRE((parse(api, prg, ParserOptions().enableAssume()) && api.endProgram()));
940 		LitVec ass;
941 		api.getAssumptions(ass);
942 		REQUIRE(ass.size() == 2);
943 		REQUIRE(ass[0] == posLit(1));
944 		REQUIRE(ass[1] == negLit(5));
945 	}
946 	SECTION("testPBOutput") {
947 		prg << "* #variable= 6 #constraint= 0\n"
948 			<< "* output range x2 x4\n";
949 		REQUIRE((parse(api, prg, ParserOptions().enableOutput()) && api.endProgram()));
950 		REQUIRE(*ctx.output.vars_begin()  == 2);
951 		REQUIRE(*(ctx.output.vars_end()-1)== 4);
952 	}
953 	SECTION("testPBOutputTable") {
954 		prg << "* #variable= 6 #constraint= 0\n"
955 		  << "* output x1 var(1)\n"
956 		  << "* output -x2 not_var(2)\n"
957 		  << "* 0\n";
958 		REQUIRE((parse(api, prg, ParserOptions().enableOutput()) && api.endProgram()));
959 		REQUIRE(ctx.output.vars_begin() == ctx.output.vars_end());
960 		REQUIRE(ctx.output.size() == 2);
961 		for (OutputTable::pred_iterator it = ctx.output.pred_begin(), end = ctx.output.pred_end(); it != end; ++it) {
962 			REQUIRE((
963 				(it->name.c_str() == std::string("var(1)") && it->cond == posLit(1))
964 				||
965 				(it->name.c_str() == std::string("not_var(2)") && it->cond == negLit(2))
966 				));
967 		}
968 	}
969 }
970 } }
971