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/asp_preprocessor.h>
25 #include <clasp/logic_program.h>
26 #include <clasp/shared_context.h>
27 namespace Clasp { namespace Asp {
28 /////////////////////////////////////////////////////////////////////////////////////////
29 // simple preprocessing
30 //
31 // Simplifies the program by computing max consequences.
32 // Then assign variables to non-trivial supported bodies and atoms.
33 /////////////////////////////////////////////////////////////////////////////////////////
preprocessSimple()34 bool Preprocessor::preprocessSimple() {
35 	if (!prg_->propagate(true)) { return false; }
36 	uint32 startVar = prg_->ctx()->numVars() + 1;
37 	// start with initially supported bodies
38 	VarVec& supported = prg_->getSupportedBodies(true);
39 	VarVec  unitBodies;
40 	for (VarVec::size_type i = 0; i != supported.size(); ++i) {
41 		// set up body
42 		PrgBody* b = prg_->getBody(supported[i]);
43 		if (!b->simplify(*prg_, false)) { return false; }
44 		if (b->var() < startVar) {
45 			if  (b->size() != 1) { b->assignVar(*prg_); }
46 			else                 { unitBodies.push_back(supported[i]); }
47 		}
48 		// add all heads of b to the "upper"-closure and
49 		// remove any false/removed atoms from head
50 		if (!addHeadsToUpper(b) || !b->simplifyHeads(*prg_, true)) {
51 			return false;
52 		}
53 	}
54 	for (VarVec::const_iterator it = unitBodies.begin(), end = unitBodies.end(); it != end; ++it) {
55 		prg_->getBody(*it)->assignVar(*prg_);
56 	}
57 	return prg_->propagate();
58 }
59 
addHeadToUpper(PrgHead * head,PrgEdge support)60 bool Preprocessor::addHeadToUpper(PrgHead* head, PrgEdge support) {
61 	assert(head->relevant() && !head->inUpper());
62 	head->simplifySupports(*prg_, false);
63 	head->assignVar(*prg_, support, eq());
64 	head->clearSupports();
65 	head->setInUpper(true);
66 	if (head->isAtom()) {
67 		return propagateAtomVar(static_cast<PrgAtom*>(head), support);
68 	}
69 	// add all unseen atoms of disjunction to upper
70 	PrgDisj* d = static_cast<PrgDisj*>(head);
71 	support    = PrgEdge::newEdge(*d, PrgEdge::Choice);
72 	bool ok    = true;
73 	for (PrgDisj::atom_iterator it = d->begin(), end = d->end(); it != end && ok; ++it) {
74 		PrgAtom* at = prg_->getAtom(*it);
75 		if (!at->relevant()) { continue; }
76 		if (!at->inUpper())  { ok = addHeadToUpper(at, support); }
77 		at->addSupport(support);
78 	}
79 	return ok;
80 }
81 /////////////////////////////////////////////////////////////////////////////////////////
82 // equivalence preprocessing
83 //
84 // Computes max consequences and minimizes the number of necessary variables
85 // by computing equivalence-classes.
86 /////////////////////////////////////////////////////////////////////////////////////////
preprocessEq(uint32 maxIters)87 bool Preprocessor::preprocessEq(uint32 maxIters) {
88 	uint32 startVar = prg_->ctx()->numVars();
89 	ValueRep res    = value_true;
90 	pass_           = 0;
91 	maxPass_        = maxIters;
92 	HeadRange atoms = HeadRange(prg_->atom_begin() + prg_->startAtom(), prg_->atom_end());
93 	bodyInfo_.resize( prg_->numBodies() + 1 );
94 	do {
95 		if (++pass_ > 1) {
96 			for (HeadIter it = prg_->atom_begin(), end = atoms.second; it != end; ) {
97 				while (it != atoms.first){ (*it)->setInUpper(false); ++it; }
98 				while (it != end)        { (*it)->clearLiteral(false); (*it)->setInUpper(false); ++it; }
99 			}
100 			for (HeadIter it = prg_->disj_begin(), end = prg_->disj_end(); it != end; ++it) {
101 				(*it)->clearLiteral(false);
102 				(*it)->setInUpper(false);
103 			}
104 			prg_->ctx()->popVars(prg_->ctx()->numVars() - startVar);
105 			litToNode_.clear();
106 		}
107 		VarVec& supported = prg_->getSupportedBodies(true);
108 		if (!classifyProgram(supported)) { return false; }
109 		res = simplifyClassifiedProgram(atoms, pass_ != maxPass_, supported);
110 	} while (res == value_free && pass_ != maxPass_);
111 	return res != value_false;
112 }
113 
114 // Computes necessary equivalence-classes starting from the supported bodies
115 // of a program.
classifyProgram(const VarVec & supported)116 bool Preprocessor::classifyProgram(const VarVec& supported) {
117 	Var bodyId; PrgBody* body;
118 	VarVec::size_type index = 0;
119 	follow_.clear();
120 	if (!prg_->propagate(true)) { return false; }
121 	for (VarVec::size_type i = 0;;) {
122 		while ( (bodyId = nextBodyId(index)) != varMax ) {
123 			body = addBodyVar(bodyId);
124 			if (prg_->hasConflict())    { return false; }
125 			if (!addHeadsToUpper(body)) { return false; }
126 		}
127 		follow_.clear();
128 		index = 0;
129 		// select next unclassified supported body
130 		for (; i < supported.size(); ++i) {
131 			bodyId  = supported[i];
132 			body    = prg_->getBody(bodyId);
133 			if (bodyInfo_[bodyId].bSeen == 0 && body->relevant()) {
134 				follow_.push_back(bodyId);
135 				break;
136 			}
137 			else if (!body->relevant() && body->hasVar()) {
138 				body->clearLiteral(false);
139 			}
140 		}
141 		if (follow_.empty()) break;
142 	}
143 	return !prg_->hasConflict();
144 }
145 
simplifyClassifiedProgram(const HeadRange & atoms,bool more,VarVec & supported)146 ValueRep Preprocessor::simplifyClassifiedProgram(const HeadRange& atoms, bool more, VarVec& supported) {
147 	ValueRep res = value_true, simp;
148 	if (!prg_->propagate()) { return value_false; }
149 	supported.clear();
150 	// simplify supports
151 	for (uint32 i = 0; i != prg_->numBodies(); ++i) {
152 		PrgBody* b = prg_->getBody(i);
153 		if (bodyInfo_[i].bSeen == 0 || !b->relevant()) {
154 			// !bodyInfo_[i].bSeen: body is unsupported
155 			// !b->relevant()     : body is eq to other body or was derived to false
156 			// In either case, body is no longer relevant and can be ignored.
157 			b->clearLiteral(true);
158 			b->markRemoved();
159 		}
160 		else if ( (simp = simplifyBody(b, more, supported)) != value_true ) {
161 			if (simp == value_false) { return simp; }
162 			res = value_free;
163 		}
164 	}
165 	if (!prg_->propagate()) { return value_false; }
166 	PrgEdge noSup = PrgEdge::noEdge();
167 	for (LogicProgram::VarIter it = prg_->unfreeze_begin(), end = prg_->unfreeze_end(); it != end; ++it) {
168 		PrgAtom* a = prg_->getAtom(*it);
169 		ValueRep v = a->value();
170 		if      (!a->simplifySupports(*prg_, true)){ return value_false; }
171 		else if (!a->inUpper() && v != value_false){
172 			if (!prg_->assignValue(a, value_false, noSup)){ return value_false; }
173 			if (more && a->hasDep(PrgAtom::dep_all))      { res = value_free; }
174 		}
175 	}
176 	if (!prg_->propagate()) { return value_false; }
177 	bool strong = more && res == value_true;
178 	HeadRange heads[2] = { HeadRange(prg_->disj_begin(), prg_->disj_end()), atoms };
179 	for (const HeadRange* range = heads, *endR = heads+2; range != endR; ++range) {
180 		for (HeadIter it = range->first, end = range->second; it != end; ++it) {
181 			PrgHead* head = *it;
182 			if ((simp = simplifyHead(head, strong)) != value_true) {
183 				if      (simp == value_false){ return simp; }
184 				else if (strong)             { strong = false; res = value_free; }
185 			}
186 		}
187 	}
188 	if (!prg_->propagate()) { res = value_false; }
189 	return res;
190 }
191 
192 // associates a variable with the body if necessary
addBodyVar(Var bodyId)193 PrgBody* Preprocessor::addBodyVar(Var bodyId) {
194 	// make sure we don't add an irrelevant body
195 	PrgBody* body = prg_->getBody(bodyId);
196 	assert((body->isSupported() && !body->eq()) || body->hasVar());
197 	body->clearLiteral(false);    // clear var in case we are iterating
198 	bodyInfo_[bodyId].bSeen = 1;  // mark as seen, so we don't classify the body again
199 	bool   known = bodyInfo_[bodyId].known == body->size();
200 	uint32 eqId;
201 	if (!body->simplifyBody(*prg_, known, &eqId) || !body->simplifyHeads(*prg_, false)) {
202 		prg_->setConflict();
203 		return body;
204 	}
205 	if (superfluous(body)) {
206 		body->markRemoved();
207 		return body;
208 	}
209 	if (eqId == bodyId) {
210 		// The body is unique
211 		body->assignVar(*prg_);
212 		PrgAtom* aEq = body->size() == 1 ? prg_->getAtom(body->goal(0).var()) : 0;
213 		if      (!known)                          { body->markDirty(); }
214 		else if (aEq && aEq->var() == body->var()){
215 			// Body is equivalent to an atom or its negation
216 			// Check if the atom is itself equivalent to a body.
217 			// If so, the body is equivalent to the atom's body.
218 			PrgBody* r = 0; // possible eq-body
219 			uint32 rId = varMax;
220 			if (body->goal(0).sign()) {
221 				Var dualAtom = getRootAtom(body->literal());
222 				aEq = dualAtom != varMax ? prg_->getAtom(dualAtom) : 0;
223 			}
224 			if (aEq && aEq->supports() && aEq->supps_begin()->isBody()) {
225 				rId = aEq->supps_begin()->node();
226 				r   = prg_->getBody(rId);
227 				if (r && r->var() == aEq->var()) {
228 					mergeEqBodies(body, rId, false);
229 				}
230 			}
231 		}
232 	}
233 	else {
234 		// body is eq to eq body
235 		mergeEqBodies(body, eqId, true);
236 	}
237 	return body;
238 }
239 
240 // Adds all heads of body to the upper closure if not yet present and
241 // associates variables with the heads if necessary.
242 // The body b is the supported body that provides a support for the heads.
243 // RETURN: true if no conflict
244 // POST  : the addition of atoms to the closure was propagated
addHeadsToUpper(PrgBody * body)245 bool Preprocessor::addHeadsToUpper(PrgBody* body) {
246 	PrgHead* head;
247 	PrgEdge  support;
248 	bool ok  = !prg_->hasConflict();
249 	int dirty= 0;
250 	for (PrgBody::head_iterator it = body->heads_begin(), end = body->heads_end(); it != end && ok; ++it) {
251 		head   = prg_->getHead(*it);
252 		support= PrgEdge::newEdge(*body, it->type());
253 		if (head->relevant() && head->value() != value_false) {
254 			if (body->value() == value_true && head->isAtom()) {
255 				// Since b is true, it is always a valid support for head, head can never become unfounded.
256 				// So ignore it during SCC check and unfounded set computation.
257 				head->setIgnoreScc(true);
258 				if (support.isNormal() && head->isAtom()) {
259 					ok = propagateAtomValue(static_cast<PrgAtom*>(head), value_true, support);
260 				}
261 			}
262 			if (!head->inUpper()) {
263 				// first time we see this head - assign var...
264 				ok = addHeadToUpper(head, support);
265 			}
266 			else if (head->supports() && head->supps_begin()->isNormal()) {
267 				PrgEdge source = *head->supps_begin();
268 				assert(source.isBody());
269 				if (prg_->getBody(source.node())->var() == body->var()) {
270 					// Check if we really need a new variable for head.
271 					head->markDirty();
272 				}
273 			}
274 			head->addSupport(support, PrgHead::no_simplify);
275 		}
276 		dirty += (head->eq() || head->value() == value_false);
277 	}
278 	if (dirty) {
279 		// remove eq atoms from head
280 		prg_->getBody(body->id())->markHeadsDirty();
281 	}
282 	return ok;
283 }
284 
285 // Propagates that a was added to the "upper"-closure.
286 // If atom a has a truth-value or is eq to a', we'll remove
287 // it from all bodies. If there is an atom x, s.th. a.lit == ~x.lit, we mark all
288 // bodies containing both a and x for simplification in order to detect
289 // duplicates/contradictory body-literals.
290 // In case that a == a', we also mark all bodies containing a
291 // for head simplification in order to detect rules like: a' :- a,B. and a' :- B,not a.
propagateAtomVar(PrgAtom * a,PrgEdge source)292 bool Preprocessor::propagateAtomVar(PrgAtom* a, PrgEdge source) {
293 	const Var aId   = a->id();
294 	PrgAtom* comp   = 0;
295 	ValueRep value  = a->value();
296 	bool fullEq     = eq();
297 	bool removeAtom = value == value_true || value == value_false;
298 	bool removeNeg  = removeAtom  || value == value_weak_true;
299 	Literal aLit    = a->literal();
300 	if (fullEq) {
301 		if (getRootAtom(aLit) == varMax) {
302 			setRootAtom(aLit, aId);
303 		}
304 		else if (prg_->mergeEqAtoms(a, getRootAtom(aLit))) {
305 			assert(source.isBody());
306 			removeAtom = true;
307 			removeNeg  = true;
308 			value      = a->value();
309 			PrgBody* B = prg_->getBody(source.node());
310 			a->setEqGoal(posLit(a->id()));
311 			// set positive eq goal - replace if a == {not a'}, replace a with not a' in bodies
312 			if (getRootAtom(~aLit) != varMax && B->literal() == aLit && B->size() == 1 && B->goal(0).sign()) {
313 				a->setEqGoal(negLit(getRootAtom(~aLit)));
314 			}
315 			a->clearLiteral(true); // equivalent atoms don't need vars
316 		}
317 		else { return false; }
318 	}
319 	if (getRootAtom(~aLit) != varMax) {
320 		PrgAtom* negA = prg_->getAtom(getRootAtom(~aLit));
321 		assert(aLit == ~negA->literal());
322 		// propagate any truth-value to complementary eq-class
323 		ValueRep cv   = value_free;
324 		uint32   mark = 0;
325 		if (value != value_free && (cv = (value_false | (value^value_true))) != negA->value()) {
326 			mark        = 1;
327 			if (!propagateAtomValue(negA, cv, PrgEdge::noEdge())) {
328 				return false;
329 			}
330 		}
331 		if ( !removeAtom ) {
332 			for (PrgAtom::dep_iterator it = (comp=negA)->deps_begin(); it != comp->deps_end(); ++it) {
333 				bodyInfo_[it->var()].mBody = 1;
334 				if (mark) { prg_->getBody(it->var())->markDirty(); }
335 			}
336 		}
337 	}
338 	for (PrgAtom::dep_iterator it = a->deps_begin(), end = a->deps_end(); it != end; ++it) {
339 		Var bodyId  = it->var();
340 		PrgBody* bn = prg_->getBody(bodyId);
341 		if (bn->relevant()) {
342 			bool wasSup = bn->isSupported();
343 			bool isSup  = wasSup || (value != value_false && !it->sign() && bn->propagateSupported(aId));
344 			bool seen   = false;
345 			bool dirty  = removeAtom || (removeNeg && it->sign());
346 			if (fullEq) {
347 				seen      = bodyInfo_[bodyId].bSeen != 0;
348 				dirty    |= bodyInfo_[bodyId].mBody == 1;
349 				if (++bodyInfo_[bodyId].known == bn->size() && !seen && isSup) {
350 					follow_.push_back( bodyId );
351 					seen    = true;
352 				}
353 			}
354 			if (!seen && isSup && !wasSup) {
355 				prg_->getSupportedBodies(false).push_back(bodyId);
356 			}
357 			if (dirty) {
358 				bn->markDirty();
359 				if (a->eq()) {
360 					bn->markHeadsDirty();
361 				}
362 			}
363 		}
364 	}
365 	if      (removeAtom) { a->clearDeps(PrgAtom::dep_all); }
366 	else if (removeNeg)  { a->clearDeps(PrgAtom::dep_neg); }
367 	if (comp) {
368 		for (PrgAtom::dep_iterator it = comp->deps_begin(), end = comp->deps_end(); it != end; ++it) {
369 			bodyInfo_[it->var()].mBody = 0;
370 		}
371 	}
372 	return true;
373 }
374 
375 // Propagates the assignment of val to a.
propagateAtomValue(PrgAtom * atom,ValueRep val,PrgEdge sup)376 bool Preprocessor::propagateAtomValue(PrgAtom* atom, ValueRep val, PrgEdge sup) {
377 	// No backpropagation possible because supports are not yet fully established.
378 	return prg_->assignValue(atom, val, sup) && prg_->propagate(false);
379 }
380 
mergeEqBodies(PrgBody * body,Var rootId,bool equalLits)381 bool Preprocessor::mergeEqBodies(PrgBody* body, Var rootId, bool equalLits) {
382 	PrgBody* root = prg_->mergeEqBodies(body, rootId, equalLits, false);
383 	if (root && root != body && bodyInfo_[root->id()].bSeen == 0) {
384 		// If root is not yet classified, we can ignore body.
385 		// The heads of body are added to the "upper"-closure
386 		// once root is eventually classified.
387 		body->clearHeads();
388 		body->markRemoved();
389 	}
390 	return root != 0;
391 }
392 
hasRootLiteral(PrgBody * body) const393 bool Preprocessor::hasRootLiteral(PrgBody* body) const {
394 	return body->size() >= 1
395 		&& getRootAtom(body->literal()) == varMax
396 		&& getRootAtom(~body->literal())== varMax;
397 }
398 
399 // Pre: body is simplified!
superfluous(PrgBody * body) const400 bool Preprocessor::superfluous(PrgBody* body) const {
401 	if (!body->relevant()) { return true; }
402 	if (!body->inRule()) {
403 		if (body->value() == value_free) { return true; }
404 		if (body->bound() <= 0)          { return true; }
405 		if (body->size() == 1)           {
406 			// unit constraint
407 			ValueRep exp = body->value() ^ (int)body->goal(0).sign();
408 			ValueRep got = prg_->getAtom(body->goal(0).var())->value();
409 			assert(got != value_free || !prg_->options().backprop);
410 			return got != value_free && (got&value_true) == (exp&value_true);
411 		}
412 	}
413 	return false;
414 }
415 
416 // Simplify the classified body with the given id.
417 // Return:
418 //  value_false    : conflict
419 //  value_true     : ok
420 //  value_weak_true: ok but program should be reclassified
simplifyBody(PrgBody * b,bool reclass,VarVec & supported)421 ValueRep Preprocessor::simplifyBody(PrgBody* b, bool reclass, VarVec& supported) {
422 	assert(b->relevant() && bodyInfo_[b->id()].bSeen == 1);
423 	bodyInfo_[b->id()].bSeen = 0;
424 	bodyInfo_[b->id()].known = 0;
425 	bool   hadHeads = b->hasHeads();
426 	bool   hasRoot  = hasRootLiteral(b);
427 	uint32 eqId     = b->id();
428 	if (!b->simplify(*prg_, true, &eqId)) {
429 		return value_false;
430 	}
431 	ValueRep ret = value_true;
432 	if (reclass) {
433 		if (hadHeads && b->value() == value_false) {
434 			assert(b->hasHeads() == false);
435 			// New false body. If it was derived to false, we can ignore the body.
436 			// Otherwise, we have a new integrity constraint.
437 			if (!b->relevant()) {
438 				b->clearLiteral(true);
439 			}
440 		}
441 		else if (b->var() != 0 && superfluous(b)) {
442 			// Body is no longer needed. All heads are either superfluous or equivalent
443 			// to other atoms.
444 			// Reclassify only if var is not used
445 			if (getRootAtom(b->literal()) == varMax) { ret = value_weak_true; }
446 			b->clearLiteral(true);
447 			b->markRemoved();
448 		}
449 		else if (b->value() == value_true && b->var() != 0) {
450 			// New fact body
451 			for (PrgBody::head_iterator it = b->heads_begin(), end = b->heads_end(); it != end; ++it) {
452 				if (it->isNormal() && prg_->getHead(*it)->var() != 0) {
453 					ret = value_weak_true;
454 					break;
455 				}
456 			}
457 			b->markDirty();
458 		}
459 	}
460 	if (b->relevant() && eqId != b->id() && (reclass || prg_->getBody(eqId)->var() == b->var())) {
461 		// Body is now eq to some other body - reclassify if body var is not needed
462 		Var bVar = b->var();
463 		prg_->mergeEqBodies(b, eqId, true, true);
464 		if (hasRoot && bVar != b->var()) {
465 			ret = value_weak_true;
466 		}
467 	}
468 	if (b->relevant() && b->resetSupported()) {
469 		supported.push_back(b->id());
470 	}
471 	return ret;
472 }
473 
474 // Simplify the classified head h.
475 // Update list of bodies defining this head and check
476 // if atom or disjunction has a distinct var although it is eq to some body.
477 // Return:
478 //  value_false    : conflict
479 //  value_true     : ok
480 //  value_weak_true: ok but atom should be reclassified
simplifyHead(PrgHead * h,bool more)481 ValueRep Preprocessor::simplifyHead(PrgHead* h, bool more) {
482 	if (!h->hasVar() || !h->relevant()) {
483 		// unsupported or eq
484 		h->clearLiteral(false);
485 		h->markRemoved();
486 		h->clearSupports();
487 		h->setInUpper(false);
488 		return value_true;
489 	}
490 	assert(h->inUpper());
491 	ValueRep v       = h->value();
492 	ValueRep ret     = value_true;
493 	PrgEdge support  = h->supports() ? *h->supps_begin() : PrgEdge::noEdge();
494 	uint32 numSuppLits= 0;
495 	if (!h->simplifySupports(*prg_, true, &numSuppLits)) {
496 		return value_false;
497 	}
498 	if (v != h->value() && (h->value() == value_false || (h->value() == value_true && h->var() != 0))) {
499 		ret = value_weak_true;
500 	}
501 	if (more) {
502 		if (numSuppLits == 0 && h->hasVar()) {
503 			// unsupported head does not need a variable
504 			ret = value_weak_true;
505 		}
506 		else if (h->supports() > 0 && h->supps_begin()->rep != support.rep) {
507 			// support for head has changed
508 			ret = value_weak_true;
509 		}
510 		else if ((support.isNormal() && h->supports() == 1) || (h->supports() > 1 && numSuppLits == 1 && h->isAtom())) {
511 			assert(support.isBody());
512 			PrgBody* supBody = prg_->getBody(support.node());
513 			if (supBody->literal() != h->literal()) {
514 				if (h->supports() > 1) {
515 					// atom is equivalent to one of its bodies
516 					EdgeVec temp(h->supps_begin(), h->supps_end());
517 					h->clearSupports();
518 					support = temp[0];
519 					for (EdgeIterator it = temp.begin(), end = temp.end(); it != end; ++it) {
520 						assert(!it->isDisj());
521 						PrgBody* B = prg_->getBody(it->node());
522 						if (it->isNormal() && B->size() == 1 && B->goal(0).sign()) {
523 							support = *it;
524 						}
525 						B->removeHead(h, it->type());
526 					}
527 					supBody = prg_->getBody(support.node());
528 					supBody->addHead(h, support.type());
529 					if (!supBody->simplifyHeads(*prg_, true)) {
530 						return value_false;
531 					}
532 				}
533 				ret = value_weak_true;
534 				if (h->value() == value_weak_true || h->value() == value_true) {
535 					supBody->assignValue(h->value());
536 					supBody->propagateValue(*prg_, true);
537 				}
538 			}
539 		}
540 	}
541 	return ret;
542 }
543 
544 } }
545