diff --git a/changelog.in b/changelog.in index 3b76bb1a95..507db51a08 100755 --- a/changelog.in +++ b/changelog.in @@ -83,6 +83,17 @@ Rank: minor Fix MSVC compilation of matrix slice conversion operators by fully qualifying their dependent return types. +[ENTRY] +Module: minimodel +What: bug +Rank: minor +Issue: 139 +Thanks: yuri@FreeBSD +[DESCRIPTION] +Fix default-constructed Boolean expressions so they can be used as +empty accumulators for conjunction and disjunction without reading +uninitialized node state. + [ENTRY] Module: float What: bug diff --git a/gecode/minimodel/bool-expr.cpp b/gecode/minimodel/bool-expr.cpp index ce8e814c61..ae5519edb5 100755 --- a/gecode/minimodel/bool-expr.cpp +++ b/gecode/minimodel/bool-expr.cpp @@ -84,7 +84,7 @@ namespace Gecode { * */ BoolExpr::Node::Node(void) - : use(1), l(nullptr), r(nullptr), m(nullptr) {} + : use(1), same(0), t(NT_AND), l(nullptr), r(nullptr), m(nullptr) {} BoolExpr::Node::~Node(void) { delete m; @@ -125,8 +125,18 @@ namespace Gecode { n->x = x; } - BoolExpr::BoolExpr(const BoolExpr& l, NodeType t, const BoolExpr& r) - : n(new Node) { + BoolExpr::BoolExpr(const BoolExpr& l, NodeType t, const BoolExpr& r) { + if (((t == NT_AND) || (t == NT_OR)) && (l.n->same == 0)) { + n = r.n; + n->use++; + return; + } + if (((t == NT_AND) || (t == NT_OR)) && (r.n->same == 0)) { + n = l.n; + n->use++; + return; + } + n = new Node; int ls = ((l.n->t == t) || (l.n->t == NT_VAR)) ? l.n->same : 1; int rs = ((r.n->t == t) || (r.n->t == NT_VAR)) ? r.n->same : 1; n->same = ls+rs; @@ -140,7 +150,10 @@ namespace Gecode { BoolExpr::BoolExpr(const BoolExpr& l, NodeType t) { (void) t; assert(t == NT_NOT); - if (l.n->t == NT_NOT) { + if (l.n->same == 0) { + n = l.n; + n->use++; + } else if (l.n->t == NT_NOT) { n = l.n->l; n->use++; } else { @@ -505,6 +518,8 @@ namespace Gecode { NNF* NNF::nnf(Region& r, Node* n, bool neg) { + if (n->same == 0) + throw MiniModel::TooFewArguments("BoolExpr"); switch (n->t) { case BoolExpr::NT_VAR: case BoolExpr::NT_RLIN: diff --git a/test/int/mm-bool.cpp b/test/int/mm-bool.cpp index 6cd7fddfc2..2c7583d6f4 100644 --- a/test/int/mm-bool.cpp +++ b/test/int/mm-bool.cpp @@ -165,6 +165,41 @@ namespace Test { namespace Int { } }; + /// %Test Boolean expressions with default-constructed accumulators + class BoolExprDefaultAccumulator : public Test { + protected: + /// Boolean operation to accumulate + BoolOpcode o; + /// Whether the default expression starts on the right hand side + bool rhs; + public: + /// Create and register test + BoolExprDefaultAccumulator(BoolOpcode o0, bool rhs0) + : Test("MiniModel::BoolExpr::Default::"+ + std::string(o0 == BO_AND ? "And" : "Or")+ + (rhs0 ? "::Rhs" : "::Lhs"),3,0,1), o(o0), rhs(rhs0) {} + /// %Test whether \a x is solution + virtual bool solution(const Assignment& x) const { + int r = (o == BO_AND) ? (x[0] & x[1]) : (x[0] | x[1]); + return r == x[2]; + } + /// Post constraint on \a x + virtual void post(Gecode::Space& home, Gecode::IntVarArray& x) { + using namespace Gecode; + Gecode::BoolExpr e; + Gecode::BoolExpr x0(channel(home,x[0])); + Gecode::BoolExpr x1(channel(home,x[1])); + if (o == BO_AND) { + e = rhs ? (x0 && e) : (e && x0); + e = e && x1; + } else { + e = rhs ? (x0 || e) : (e || x0); + e = e || x1; + } + rel(home, expr(home,e), IRT_EQ, channel(home,x[2])); + } + }; + const BoolInstr bi000[] = { {BO_AND,0,1,0},{BO_AND,2,3,1},{BO_AND,0,1,0}, {BO_HLT,0,0,0} @@ -4382,6 +4417,10 @@ namespace Test { namespace Int { (void) new BoolElement("Expr::A",0); (void) new BoolElement("Expr::B",1); (void) new BoolElement("Rel",2); + (void) new BoolExprDefaultAccumulator(BO_AND,false); + (void) new BoolExprDefaultAccumulator(BO_AND,true); + (void) new BoolExprDefaultAccumulator(BO_OR,false); + (void) new BoolExprDefaultAccumulator(BO_OR,true); } };