/********************************************************************
* AUTHORS: Vijay Ganesh, David L. Dill
*
* BEGIN DATE: April, 2006
*
* LICENSE: Please view LICENSE file in the home dir of this Program
********************************************************************/
// -*- c++ -*-
// Simplifying create methods for Boolean operations.
// These are only very simple local simplifications.
// This is somewhat redundant with Vijay's simplifier code. They
// need to be merged.
// FIXME: control with optimize flag.
static bool _trace_simpbool = 0;
static bool _disable_simpbool = 0;
#include "AST.h"
// SMTLIB experimental hack. Try allocating a single stack here for
// children to reduce growing of vectors.
//BEEV::ASTVec child_stack;
namespace BEEV {
ASTNode BeevMgr::CreateSimpForm(Kind kind, ASTVec &children = _empty_ASTVec) {
if (_disable_simpbool) {
return CreateNode(kind, children);
}
else {
switch (kind) {
case NOT: return CreateSimpNot(children[0]); break;
case AND: return CreateSimpAndOr(1, children); break;
case OR: return CreateSimpAndOr(0, children); break;
case NAND: return CreateSimpNot(CreateSimpAndOr(1, children)); break;
case NOR: return CreateSimpNot(CreateSimpAndOr(0, children)); break;
case IFF: {
// Not sure children can ever be empty, but what the heck.
// if (children.size() == 0) {
// return ASTTrue;
// }
// Convert IFF to XOR ASAP. IFF is not associative, so this makes
// flattening much easier.
children[0] = CreateSimpNot(children[0]);
return CreateSimpXor(children); break;
}
case XOR:
return CreateSimpXor(children); break;
// FIXME: Earlier, check that this only has two arguments
case IMPLIES: return CreateSimpAndOr(0, CreateSimpNot(children[0]), children[1]); break;
case ITE: return CreateSimpFormITE(children[0], children[1], children[2]);
default: return CreateNode(kind, children);
}
}
}
// specialized versions
ASTNode BeevMgr::CreateSimpForm(Kind kind,
const ASTNode& child0) {
ASTVec children;
//child_stack.clear(); // could just reset top pointer.
children.push_back(child0);
//child_stack.push_back(child0);
return CreateSimpForm(kind, children);
//return CreateSimpForm(kind, child_stack);
}
ASTNode BeevMgr::CreateSimpForm(Kind kind,
const ASTNode& child0,
const ASTNode& child1) {
ASTVec children;
//child_stack.clear(); // could just reset top pointer.
children.push_back(child0);
//child_stack.push_back(child0);
children.push_back(child1);
//child_stack.push_back(child1);
return CreateSimpForm(kind, children);
//return CreateSimpForm(kind, child_stack);
}
ASTNode BeevMgr::CreateSimpForm(Kind kind,
const ASTNode& child0,
const ASTNode& child1,
const ASTNode& child2) {
ASTVec children;
//child_stack.clear(); // could just reset top pointer.
children.push_back(child0);
//child_stack.push_back(child0);
children.push_back(child1);
//child_stack.push_back(child1);
children.push_back(child2);
//child_stack.push_back(child2);
return CreateSimpForm(kind, children);
//return CreateSimpForm(kind, child_stack);
}
ASTNode BeevMgr::CreateSimpNot(const ASTNode& form) {
Kind k = form.GetKind();
switch (k) {
case FALSE: { return ASTTrue; }
case TRUE: { return ASTFalse; }
case NOT: { return form[0]; } // NOT NOT cancellation
case XOR: {
// Push negation down in this case.
// FIXME: Separate pre-pass to push negation down?
// CreateSimp should be local, and this isn't.
// It isn't memoized. Arg.
ASTVec children = form.GetChildren();
children[0] = CreateSimpNot(children[0]);
return CreateSimpXor(children);
}
default: { return CreateNode(NOT, form); }
}
}
// I don't think this is even called, since it called
// CreateSimpAndOr instead of CreateSimpXor until 1/9/07 with no
// ill effects. Calls seem to go to the version that takes a vector
// of children.
ASTNode BeevMgr::CreateSimpXor(const ASTNode& form1, const ASTNode& form2) {
ASTVec children;
children.push_back(form1);
children.push_back(form2);
return CreateSimpXor(children);
}
ASTNode BeevMgr::CreateSimpAndOr(bool IsAnd, const ASTNode& form1, const ASTNode& form2) {
ASTVec children;
children.push_back(form1);
children.push_back(form2);
return CreateSimpAndOr(IsAnd, children);
}
ASTNode BeevMgr::CreateSimpAndOr(bool IsAnd, ASTVec &children) {
if (_trace_simpbool) {
cout << "========" << endl << "CreateSimpAndOr " << (IsAnd ? "AND " : "OR ") ;
lpvec(children);
cout << endl;
}
ASTVec new_children;
// sort so that identical nodes occur in sequential runs, followed by
// their negations.
SortByExprNum(children);
ASTNode annihilator = (IsAnd ? ASTFalse : ASTTrue);
ASTNode identity = (IsAnd ? ASTTrue : ASTFalse);
ASTNode retval;
ASTVec::const_iterator it_end = children.end();
ASTVec::const_iterator next_it;
for(ASTVec::const_iterator it = children.begin(); it != it_end; it = next_it) {
next_it = it + 1;
bool nextexists = (next_it < it_end);
if (*it == annihilator) {
retval = annihilator;
if (_trace_simpbool) {
cout << "returns " << retval << endl;
}
return retval;
}
else if (*it == identity) {
// just drop it
}
else if (nextexists && (*next_it == *it)) {
// drop it
// cout << "Dropping [" << it->GetNodeNum() << "]" << endl;
}
else if (nextexists && (next_it->GetKind() == NOT) && ((*next_it)[0] == *it)) {
// form and negation -- return FALSE for AND, TRUE for OR.
retval = annihilator;
// cout << "X and/or NOT X" << endl;
if (_trace_simpbool) {
cout << "returns " << retval << endl;
}
return retval;
}
else {
// add to children
new_children.push_back(*it);
}
}
// If we get here, we saw no annihilators, and children should
// be only the non-True nodes.
if (new_children.size() < 2) {
if (0 == new_children.size()) {
retval = identity;
}
else {
// there is just one child
retval = new_children[0];
}
}
else {
// 2 or more children. Create a new node.
retval = CreateNode(IsAnd ? AND : OR, new_children);
}
if (_trace_simpbool) {
cout << "returns " << retval << endl;
}
return retval;
}
// Constant children are accumulated in "accumconst".
ASTNode BeevMgr::CreateSimpXor(ASTVec &children) {
if (_trace_simpbool) {
cout << "========" << endl
<< "CreateSimpXor ";
lpvec(children);
cout << endl;
}
// Change this not to init to children if flattening code is present.
// ASTVec flat_children = children; // empty vector
ASTVec flat_children; // empty vector
ASTVec::const_iterator it_end = children.end();
if (xor_flatten) {
bool fflag = 0; // ***Temp debugging
// Experimental flattening code.
for(ASTVec::iterator it = children.begin(); it != it_end; it++) {
Kind ck = it->GetKind();
const ASTVec &gchildren = it->GetChildren();
if (XOR == ck) {
fflag = 1;
// append grandchildren to children
flat_children.insert(flat_children.end(), gchildren.begin(), gchildren.end());
}
else {
flat_children.push_back(*it);
}
}
if (_trace_simpbool && fflag) {
cout << "========" << endl;
cout << "Flattening: " << endl;
lpvec(children);
cout << "--------" << endl;
cout << "Flattening result: " << endl;
lpvec(flat_children);
}
}
else {
flat_children = children;
}
// sort so that identical nodes occur in sequential runs, followed by
// their negations.
SortByExprNum(flat_children);
ASTNode retval;
// This is the C Boolean value of all constant args seen. It is initially
// 0. TRUE children cause it to change value.
bool accumconst = 0;
ASTVec new_children;
it_end = flat_children.end();
ASTVec::iterator next_it;
for(ASTVec::iterator it = flat_children.begin(); it != it_end; it++) {
next_it = it + 1;
bool nextexists = (next_it < it_end);
if (ASTTrue == *it) {
accumconst = !accumconst;
}
else if (ASTFalse == *it) {
// Ignore it
}
else if (nextexists && (*next_it == *it)) {
// x XOR x = FALSE. Skip current, write "false" into next_it
// so that it gets tossed, too.
*next_it = ASTFalse;
}
else if (nextexists && (next_it->GetKind() == NOT) && ((*next_it)[0] == *it)) {
// x XOR NOT x = TRUE. Skip current, write "true" into next_it
// so that it gets tossed, too.
*next_it = ASTTrue;
}
else if (NOT == it->GetKind()) {
// If child is (NOT alpha), we can flip accumconst and use alpha.
// This is ok because (NOT alpha) == TRUE XOR alpha
accumconst = !accumconst;
// CreateSimpNot just takes child of not.
new_children.push_back(CreateSimpNot(*it));
}
else {
new_children.push_back(*it);
}
}
// Children should be non-constant.
if (new_children.size() < 2) {
if (0 == new_children.size()) {
// XOR(TRUE, FALSE) -- accumconst will be 1.
if (accumconst) {
retval = ASTTrue;
}
else {
retval = ASTFalse;
}
}
else {
// there is just one child
// XOR(x, TRUE) -- accumconst will be 1.
if (accumconst) {
retval = CreateSimpNot(new_children[0]);
}
else {
retval = new_children[0];
}
}
}
else {
// negate first child if accumconst == 1
if (accumconst) {
new_children[0] = CreateSimpNot(new_children[0]);
}
retval = CreateNode(XOR, new_children);
}
if (_trace_simpbool) {
cout << "returns " << retval << endl;
}
return retval;
}
// FIXME: How do I know whether ITE is a formula or not?
ASTNode BeevMgr::CreateSimpFormITE(const ASTNode& child0,
const ASTNode& child1,
const ASTNode& child2) {
ASTNode retval;
if (_trace_simpbool) {
cout << "========" << endl << "CreateSimpFormITE "
<< child0
<< child1
<< child2 << endl;
}
if (ASTTrue == child0) {
retval = child1;
}
else if (ASTFalse == child0) {
retval = child2;
}
else if (child1 == child2) {
retval = child1;
}
// ITE(x, TRUE, y ) == x OR y
else if (ASTTrue == child1) {
retval = CreateSimpAndOr(0, child0, child2);
}
// ITE(x, FALSE, y ) == (!x AND y)
else if (ASTFalse == child1) {
retval = CreateSimpAndOr(1, CreateSimpNot(child0), child2);
}
// ITE(x, y, TRUE ) == (!x OR y)
else if (ASTTrue == child2) {
retval = CreateSimpAndOr(0, CreateSimpNot(child0), child1);
}
// ITE(x, y, FALSE ) == (x AND y)
else if (ASTFalse == child2) {
retval = CreateSimpAndOr(1, child0, child1);
}
// ITE (x, !y, y) == x XOR y
// else if (NOT == child1.GetKind() && (child1[0] == child2)) {
// retval = CreateSimpXor(child0, child2);
// }
// // ITE (x, y, !y) == x IFF y. I think other cases are covered
// // by XOR/IFF optimizations
// else if (NOT == child2.GetKind() && (child2[0] == child1)) {
// retval = CreateSimpXor(CreateSimpNot(child0), child2);
// }
else {
retval = CreateNode(ITE, child0, child1, child2);
}
if (_trace_simpbool) {
cout << "returns " << retval << endl;
}
return retval;
}
} // BEEV namespace