this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2/* 3 * Main authors: 4 * Guido Tack <tack@gecode.org> 5 * Christian Schulte <schulte@gecode.org> 6 * Vincent Barichard <Vincent.Barichard@univ-angers.fr> 7 * 8 * Copyright: 9 * Guido Tack, 2004 10 * Christian Schulte, 2004 11 * Vincent Barichard, 2012 12 * 13 * This file is part of Gecode, the generic constraint 14 * development environment: 15 * http://www.gecode.org 16 * 17 * Permission is hereby granted, free of charge, to any person obtaining 18 * a copy of this software and associated documentation files (the 19 * "Software"), to deal in the Software without restriction, including 20 * without limitation the rights to use, copy, modify, merge, publish, 21 * distribute, sublicense, and/or sell copies of the Software, and to 22 * permit persons to whom the Software is furnished to do so, subject to 23 * the following conditions: 24 * 25 * The above copyright notice and this permission notice shall be 26 * included in all copies or substantial portions of the Software. 27 * 28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 29 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 31 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 32 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 33 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 34 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 35 * 36 */ 37 38#include <gecode/minimodel.hh> 39 40namespace Gecode { 41 42 /// %Node for Boolean expression 43 class BoolExpr::Node { 44 public: 45 /// Nodes are reference counted 46 unsigned int use; 47 /// Number of variables in subtree with same type (for AND and OR) 48 int same; 49 /// Type of expression 50 NodeType t; 51 /// Subexpressions 52 Node *l, *r; 53 /// Possibly a variable 54 BoolVar x; 55 /// Possibly a reified linear relation 56 LinIntRel rl; 57#ifdef GECODE_HAS_FLOAT_VARS 58 /// Possibly a reified float linear relation 59 LinFloatRel rfl; 60#endif 61#ifdef GECODE_HAS_SET_VARS 62 /// Possibly a reified set relation 63 SetRel rs; 64#endif 65 /// Possibly a misc Boolean expression 66 Misc* m; 67 68 /// Default constructor 69 Node(void); 70 /// Destructor 71 ~Node(void); 72 /// Decrement reference count and possibly free memory 73 GECODE_MINIMODEL_EXPORT 74 bool decrement(void); 75 /// Memory management 76 static void* operator new(size_t size); 77 /// Memory management 78 static void operator delete(void* p, size_t size); 79 }; 80 81 82 /* 83 * Operations for nodes 84 * 85 */ 86 BoolExpr::Node::Node(void) 87 : use(1), l(nullptr), r(nullptr), m(nullptr) {} 88 89 BoolExpr::Node::~Node(void) { 90 delete m; 91 } 92 93 void* 94 BoolExpr::Node::operator new(size_t size) { 95 return heap.ralloc(size); 96 } 97 void 98 BoolExpr::Node::operator delete(void* p, size_t) { 99 heap.rfree(p); 100 } 101 102 bool 103 BoolExpr::Node::decrement(void) { 104 if (--use == 0) { 105 if ((l != nullptr) && l->decrement()) 106 delete l; 107 if ((r != nullptr) && r->decrement()) 108 delete r; 109 return true; 110 } 111 return false; 112 } 113 114 BoolExpr::BoolExpr(void) : n(new Node) {} 115 116 BoolExpr::BoolExpr(const BoolExpr& e) : n(e.n) { 117 n->use++; 118 } 119 120 BoolExpr::BoolExpr(const BoolVar& x) : n(new Node) { 121 n->same = 1; 122 n->t = NT_VAR; 123 n->l = nullptr; 124 n->r = nullptr; 125 n->x = x; 126 } 127 128 BoolExpr::BoolExpr(const BoolExpr& l, NodeType t, const BoolExpr& r) 129 : n(new Node) { 130 int ls = ((l.n->t == t) || (l.n->t == NT_VAR)) ? l.n->same : 1; 131 int rs = ((r.n->t == t) || (r.n->t == NT_VAR)) ? r.n->same : 1; 132 n->same = ls+rs; 133 n->t = t; 134 n->l = l.n; 135 n->l->use++; 136 n->r = r.n; 137 n->r->use++; 138 } 139 140 BoolExpr::BoolExpr(const BoolExpr& l, NodeType t) { 141 (void) t; 142 assert(t == NT_NOT); 143 if (l.n->t == NT_NOT) { 144 n = l.n->l; 145 n->use++; 146 } else { 147 n = new Node; 148 n->same = 1; 149 n->t = NT_NOT; 150 n->l = l.n; 151 n->l->use++; 152 n->r = nullptr; 153 } 154 } 155 156 BoolExpr::BoolExpr(const LinIntRel& rl) 157 : n(new Node) { 158 n->same = 1; 159 n->t = NT_RLIN; 160 n->l = nullptr; 161 n->r = nullptr; 162 n->rl = rl; 163 } 164 165#ifdef GECODE_HAS_FLOAT_VARS 166 BoolExpr::BoolExpr(const LinFloatRel& rfl) 167 : n(new Node) { 168 n->same = 1; 169 n->t = NT_RLINFLOAT; 170 n->l = nullptr; 171 n->r = nullptr; 172 n->rfl = rfl; 173 } 174#endif 175 176#ifdef GECODE_HAS_SET_VARS 177 BoolExpr::BoolExpr(const SetRel& rs) 178 : n(new Node) { 179 n->same = 1; 180 n->t = NT_RSET; 181 n->l = nullptr; 182 n->r = nullptr; 183 n->rs = rs; 184 } 185 186 BoolExpr::BoolExpr(const SetCmpRel& rs) 187 : n(new Node) { 188 n->same = 1; 189 n->t = NT_RSET; 190 n->l = nullptr; 191 n->r = nullptr; 192 n->rs = rs; 193 } 194#endif 195 196 BoolExpr::BoolExpr(BoolExpr::Misc* m) 197 : n(new Node) { 198 n->same = 1; 199 n->t = NT_MISC; 200 n->l = nullptr; 201 n->r = nullptr; 202 n->m = m; 203 } 204 205 const BoolExpr& 206 BoolExpr::operator =(const BoolExpr& e) { 207 if (this != &e) { 208 if (n->decrement()) 209 delete n; 210 n = e.n; 211 n->use++; 212 } 213 return *this; 214 } 215 216 BoolExpr::Misc::~Misc(void) {} 217 218 BoolExpr::~BoolExpr(void) { 219 if (n->decrement()) 220 delete n; 221 } 222 223 namespace { 224 /// %Node for negation normalform (%NNF) 225 class NNF { 226 public: 227 typedef BoolExpr::NodeType NodeType; 228 typedef BoolExpr::Node Node; 229 /// Type of node 230 NodeType t; 231 /// Number of positive literals for node type 232 int p; 233 /// Number of negative literals for node type 234 int n; 235 /// Union depending on nodetype \a t 236 union { 237 /// For binary nodes (and, or, eqv) 238 struct { 239 /// Left subtree 240 NNF* l; 241 /// Right subtree 242 NNF* r; 243 } b; 244 /// For atomic nodes 245 struct { 246 /// Is atomic formula negative 247 bool neg; 248 /// Pointer to corresponding Boolean expression node 249 Node* x; 250 } a; 251 } u; 252 /// Create negation normalform 253 static NNF* nnf(Region& r, Node* n, bool neg); 254 /// Post propagators for nested conjunctive and disjunctive expression 255 GECODE_MINIMODEL_EXPORT 256 void post(Home home, NodeType t, 257 BoolVarArgs& bp, BoolVarArgs& bn, 258 int& ip, int& in, 259 const IntPropLevels& ipls) const; 260 /// Post propagators for expression 261 GECODE_MINIMODEL_EXPORT 262 BoolVar expr(Home home, const IntPropLevels& ipls) const; 263 /// Post propagators for relation 264 GECODE_MINIMODEL_EXPORT 265 void rel(Home home, const IntPropLevels& ipls) const; 266 /// Allocate memory from region 267 static void* operator new(size_t s, Region& r); 268 /// No-op (for exceptions) 269 static void operator delete(void*); 270 /// No-op 271 static void operator delete(void*, Region&); 272 }; 273 274 /* 275 * Operations for negation normalform 276 * 277 */ 278 forceinline void 279 NNF::operator delete(void*) {} 280 281 forceinline void 282 NNF::operator delete(void*, Region&) {} 283 284 forceinline void* 285 NNF::operator new(size_t s, Region& r) { 286 return r.ralloc(s); 287 } 288 289 BoolVar 290 NNF::expr(Home home, const IntPropLevels& ipls) const { 291 if ((t == BoolExpr::NT_VAR) && !u.a.neg) 292 return u.a.x->x; 293 BoolVar b(home,0,1); 294 switch (t) { 295 case BoolExpr::NT_VAR: 296 assert(u.a.neg); 297 Gecode::rel(home, u.a.x->x, IRT_NQ, b); 298 break; 299 case BoolExpr::NT_RLIN: 300 u.a.x->rl.post(home, b, !u.a.neg, ipls); 301 break; 302#ifdef GECODE_HAS_FLOAT_VARS 303 case BoolExpr::NT_RLINFLOAT: 304 u.a.x->rfl.post(home, b, !u.a.neg); 305 break; 306#endif 307#ifdef GECODE_HAS_SET_VARS 308 case BoolExpr::NT_RSET: 309 u.a.x->rs.post(home, b, !u.a.neg); 310 break; 311#endif 312 case BoolExpr::NT_MISC: 313 u.a.x->m->post(home, b, u.a.neg, ipls); 314 break; 315 case BoolExpr::NT_AND: 316 { 317 BoolVarArgs bp(p), bn(n); 318 int ip=0, in=0; 319 post(home, BoolExpr::NT_AND, bp, bn, ip, in, ipls); 320 clause(home, BOT_AND, bp, bn, b); 321 } 322 break; 323 case BoolExpr::NT_OR: 324 { 325 BoolVarArgs bp(p), bn(n); 326 int ip=0, in=0; 327 post(home, BoolExpr::NT_OR, bp, bn, ip, in, ipls); 328 clause(home, BOT_OR, bp, bn, b); 329 } 330 break; 331 case BoolExpr::NT_EQV: 332 { 333 bool n = false; 334 BoolVar l; 335 if (u.b.l->t == BoolExpr::NT_VAR) { 336 l = u.b.l->u.a.x->x; 337 if (u.b.l->u.a.neg) n = !n; 338 } else { 339 l = u.b.l->expr(home,ipls); 340 } 341 BoolVar r; 342 if (u.b.r->t == BoolExpr::NT_VAR) { 343 r = u.b.r->u.a.x->x; 344 if (u.b.r->u.a.neg) n = !n; 345 } else { 346 r = u.b.r->expr(home,ipls); 347 } 348 Gecode::rel(home, l, n ? BOT_XOR : BOT_EQV, r, b); 349 } 350 break; 351 default: 352 GECODE_NEVER; 353 } 354 return b; 355 } 356 357 void 358 NNF::post(Home home, NodeType t, 359 BoolVarArgs& bp, BoolVarArgs& bn, 360 int& ip, int& in, 361 const IntPropLevels& ipls) const { 362 if (this->t != t) { 363 switch (this->t) { 364 case BoolExpr::NT_VAR: 365 if (u.a.neg) { 366 bn[in++]=u.a.x->x; 367 } else { 368 bp[ip++]=u.a.x->x; 369 } 370 break; 371 case BoolExpr::NT_RLIN: 372 { 373 BoolVar b(home,0,1); 374 u.a.x->rl.post(home, b, !u.a.neg, ipls); 375 bp[ip++]=b; 376 } 377 break; 378#ifdef GECODE_HAS_FLOAT_VARS 379 case BoolExpr::NT_RLINFLOAT: 380 { 381 BoolVar b(home,0,1); 382 u.a.x->rfl.post(home, b, !u.a.neg); 383 bp[ip++]=b; 384 } 385 break; 386#endif 387#ifdef GECODE_HAS_SET_VARS 388 case BoolExpr::NT_RSET: 389 { 390 BoolVar b(home,0,1); 391 u.a.x->rs.post(home, b, !u.a.neg); 392 bp[ip++]=b; 393 } 394 break; 395#endif 396 case BoolExpr::NT_MISC: 397 { 398 BoolVar b(home,0,1); 399 u.a.x->m->post(home, b, u.a.neg, ipls); 400 bp[ip++]=b; 401 } 402 break; 403 default: 404 bp[ip++] = expr(home, ipls); 405 break; 406 } 407 } else { 408 u.b.l->post(home, t, bp, bn, ip, in, ipls); 409 u.b.r->post(home, t, bp, bn, ip, in, ipls); 410 } 411 } 412 413 void 414 NNF::rel(Home home, const IntPropLevels& ipls) const { 415 switch (t) { 416 case BoolExpr::NT_VAR: 417 Gecode::rel(home, u.a.x->x, IRT_EQ, u.a.neg ? 0 : 1); 418 break; 419 case BoolExpr::NT_RLIN: 420 u.a.x->rl.post(home, !u.a.neg, ipls); 421 break; 422#ifdef GECODE_HAS_FLOAT_VARS 423 case BoolExpr::NT_RLINFLOAT: 424 u.a.x->rfl.post(home, !u.a.neg); 425 break; 426#endif 427#ifdef GECODE_HAS_SET_VARS 428 case BoolExpr::NT_RSET: 429 u.a.x->rs.post(home, !u.a.neg); 430 break; 431#endif 432 case BoolExpr::NT_MISC: 433 { 434 BoolVar b(home,!u.a.neg,!u.a.neg); 435 u.a.x->m->post(home, b, false, ipls); 436 } 437 break; 438 case BoolExpr::NT_AND: 439 u.b.l->rel(home, ipls); 440 u.b.r->rel(home, ipls); 441 break; 442 case BoolExpr::NT_OR: 443 { 444 BoolVarArgs bp(p), bn(n); 445 int ip=0, in=0; 446 post(home, BoolExpr::NT_OR, bp, bn, ip, in, ipls); 447 clause(home, BOT_OR, bp, bn, 1); 448 } 449 break; 450 case BoolExpr::NT_EQV: 451 if (u.b.l->t==BoolExpr::NT_VAR && 452 u.b.r->t==BoolExpr::NT_RLIN) { 453 u.b.r->u.a.x->rl.post(home, u.b.l->u.a.x->x, 454 u.b.l->u.a.neg==u.b.r->u.a.neg, ipls); 455 } else if (u.b.r->t==BoolExpr::NT_VAR && 456 u.b.l->t==BoolExpr::NT_RLIN) { 457 u.b.l->u.a.x->rl.post(home, u.b.r->u.a.x->x, 458 u.b.l->u.a.neg==u.b.r->u.a.neg, ipls); 459 } else if (u.b.l->t==BoolExpr::NT_RLIN) { 460 u.b.l->u.a.x->rl.post(home, u.b.r->expr(home,ipls), 461 !u.b.l->u.a.neg,ipls); 462 } else if (u.b.r->t==BoolExpr::NT_RLIN) { 463 u.b.r->u.a.x->rl.post(home, u.b.l->expr(home,ipls), 464 !u.b.r->u.a.neg,ipls); 465#ifdef GECODE_HAS_FLOAT_VARS 466 } else if (u.b.l->t==BoolExpr::NT_VAR && 467 u.b.r->t==BoolExpr::NT_RLINFLOAT) { 468 u.b.r->u.a.x->rfl.post(home, u.b.l->u.a.x->x, 469 u.b.l->u.a.neg==u.b.r->u.a.neg); 470 } else if (u.b.r->t==BoolExpr::NT_VAR && 471 u.b.l->t==BoolExpr::NT_RLINFLOAT) { 472 u.b.l->u.a.x->rfl.post(home, u.b.r->u.a.x->x, 473 u.b.l->u.a.neg==u.b.r->u.a.neg); 474 } else if (u.b.l->t==BoolExpr::NT_RLINFLOAT) { 475 u.b.l->u.a.x->rfl.post(home, u.b.r->expr(home,ipls), 476 !u.b.l->u.a.neg); 477 } else if (u.b.r->t==BoolExpr::NT_RLINFLOAT) { 478 u.b.r->u.a.x->rfl.post(home, u.b.l->expr(home,ipls), 479 !u.b.r->u.a.neg); 480#endif 481#ifdef GECODE_HAS_SET_VARS 482 } else if (u.b.l->t==BoolExpr::NT_VAR && 483 u.b.r->t==BoolExpr::NT_RSET) { 484 u.b.r->u.a.x->rs.post(home, u.b.l->u.a.x->x, 485 u.b.l->u.a.neg==u.b.r->u.a.neg); 486 } else if (u.b.r->t==BoolExpr::NT_VAR && 487 u.b.l->t==BoolExpr::NT_RSET) { 488 u.b.l->u.a.x->rs.post(home, u.b.r->u.a.x->x, 489 u.b.l->u.a.neg==u.b.r->u.a.neg); 490 } else if (u.b.l->t==BoolExpr::NT_RSET) { 491 u.b.l->u.a.x->rs.post(home, u.b.r->expr(home,ipls), 492 !u.b.l->u.a.neg); 493 } else if (u.b.r->t==BoolExpr::NT_RSET) { 494 u.b.r->u.a.x->rs.post(home, u.b.l->expr(home,ipls), 495 !u.b.r->u.a.neg); 496#endif 497 } else { 498 Gecode::rel(home, expr(home, ipls), IRT_EQ, 1); 499 } 500 break; 501 default: 502 GECODE_NEVER; 503 } 504 } 505 506 NNF* 507 NNF::nnf(Region& r, Node* n, bool neg) { 508 switch (n->t) { 509 case BoolExpr::NT_VAR: 510 case BoolExpr::NT_RLIN: 511 case BoolExpr::NT_MISC: 512 #ifdef GECODE_HAS_FLOAT_VARS 513 case BoolExpr::NT_RLINFLOAT: 514 #endif 515 #ifdef GECODE_HAS_SET_VARS 516 case BoolExpr::NT_RSET: 517 #endif 518 { 519 NNF* x = new (r) NNF; 520 x->t = n->t; x->u.a.neg = neg; x->u.a.x = n; 521 if (neg) { 522 x->p = 0; x->n = 1; 523 } else { 524 x->p = 1; x->n = 0; 525 } 526 return x; 527 } 528 case BoolExpr::NT_NOT: 529 return nnf(r,n->l,!neg); 530 case BoolExpr::NT_AND: case BoolExpr::NT_OR: 531 { 532 NodeType t = ((n->t == BoolExpr::NT_AND) == neg) ? 533 BoolExpr::NT_OR : BoolExpr::NT_AND; 534 NNF* x = new (r) NNF; 535 x->t = t; 536 x->u.b.l = nnf(r,n->l,neg); 537 x->u.b.r = nnf(r,n->r,neg); 538 int p_l, n_l; 539 if ((x->u.b.l->t == t) || 540 (x->u.b.l->t == BoolExpr::NT_VAR)) { 541 p_l=x->u.b.l->p; n_l=x->u.b.l->n; 542 } else { 543 p_l=1; n_l=0; 544 } 545 int p_r, n_r; 546 if ((x->u.b.r->t == t) || 547 (x->u.b.r->t == BoolExpr::NT_VAR)) { 548 p_r=x->u.b.r->p; n_r=x->u.b.r->n; 549 } else { 550 p_r=1; n_r=0; 551 } 552 x->p = p_l+p_r; 553 x->n = n_l+n_r; 554 return x; 555 } 556 case BoolExpr::NT_EQV: 557 { 558 NNF* x = new (r) NNF; 559 x->t = BoolExpr::NT_EQV; 560 x->u.b.l = nnf(r,n->l,neg); 561 x->u.b.r = nnf(r,n->r,false); 562 x->p = 2; x->n = 0; 563 return x; 564 } 565 default: 566 GECODE_NEVER; 567 } 568 GECODE_NEVER; 569 return nullptr; 570 } 571 } 572 573 BoolVar 574 BoolExpr::expr(Home home, const IntPropLevels& ipls) const { 575 Region r; 576 return NNF::nnf(r,n,false)->expr(home,ipls); 577 } 578 579 void 580 BoolExpr::rel(Home home, const IntPropLevels& ipls) const { 581 Region r; 582 return NNF::nnf(r,n,false)->rel(home,ipls); 583 } 584 585 586 BoolExpr 587 operator &&(const BoolExpr& l, const BoolExpr& r) { 588 return BoolExpr(l,BoolExpr::NT_AND,r); 589 } 590 BoolExpr 591 operator ||(const BoolExpr& l, const BoolExpr& r) { 592 return BoolExpr(l,BoolExpr::NT_OR,r); 593 } 594 BoolExpr 595 operator ^(const BoolExpr& l, const BoolExpr& r) { 596 return BoolExpr(BoolExpr(l,BoolExpr::NT_EQV,r),BoolExpr::NT_NOT); 597 } 598 599 BoolExpr 600 operator !(const BoolExpr& e) { 601 return BoolExpr(e,BoolExpr::NT_NOT); 602 } 603 604 BoolExpr 605 operator !=(const BoolExpr& l, const BoolExpr& r) { 606 return !BoolExpr(l, BoolExpr::NT_EQV, r); 607 } 608 BoolExpr 609 operator ==(const BoolExpr& l, const BoolExpr& r) { 610 return BoolExpr(l, BoolExpr::NT_EQV, r); 611 } 612 BoolExpr 613 operator >>(const BoolExpr& l, const BoolExpr& r) { 614 return BoolExpr(BoolExpr(l,BoolExpr::NT_NOT), 615 BoolExpr::NT_OR,r); 616 } 617 BoolExpr 618 operator <<(const BoolExpr& l, const BoolExpr& r) { 619 return BoolExpr(BoolExpr(r,BoolExpr::NT_NOT), 620 BoolExpr::NT_OR,l); 621 } 622 623 624 /* 625 * Posting Boolean expressions and relations 626 * 627 */ 628 BoolVar 629 expr(Home home, const BoolExpr& e, const IntPropLevels& ipls) { 630 PostInfo pi(home); 631 if (!home.failed()) 632 return e.expr(home,ipls); 633 BoolVar x(home,0,1); 634 return x; 635 } 636 637 void 638 rel(Home home, const BoolExpr& e, const IntPropLevels& ipls) { 639 GECODE_POST; 640 e.rel(home,ipls); 641 } 642 643 /* 644 * Boolean element constraints 645 * 646 */ 647 648 /// \brief Boolean element expressions 649 class BElementExpr : public BoolExpr::Misc { 650 protected: 651 /// The Boolean expressions 652 BoolExpr* a; 653 /// The number of Boolean expressions 654 int n; 655 /// The linear expression for the index 656 LinIntExpr idx; 657 public: 658 /// Constructor 659 BElementExpr(const BoolVarArgs& b, const LinIntExpr& idx); 660 /// Destructor 661 virtual ~BElementExpr(void); 662 /// Constrain \a b to be equivalent to the expression (negated if \a neg) 663 virtual void post(Home home, BoolVar b, bool neg, 664 const IntPropLevels& ipls); 665 }; 666 667 BElementExpr::BElementExpr(const BoolVarArgs& b, const LinIntExpr& idx) 668 : a(static_cast<BoolExpr*>(heap.ralloc(sizeof(BoolExpr)*b.size()))), n(b.size()), idx(idx) { 669 for (int i=b.size(); i--;) 670 new (&a[i]) BoolExpr(b[i]); 671 } 672 673 BElementExpr::~BElementExpr(void) { 674 heap.free<BoolExpr>(a,n); 675 } 676 677 void 678 BElementExpr::post(Home home, BoolVar b, bool neg, 679 const IntPropLevels& ipls) { 680 IntVar z = idx.post(home, ipls); 681 if (z.assigned() && (z.val() >= 0) && (z.val() < n)) { 682 BoolExpr be = neg ? (a[z.val()] == !b) : (a[z.val()] == b); 683 be.rel(home,ipls); 684 } else { 685 BoolVarArgs x(n); 686 for (int i=n; i--;) 687 x[i] = a[i].expr(home,ipls); 688 BoolVar res = neg ? (!b).expr(home,ipls) : b; 689 element(home, x, z, res, ipls.element()); 690 } 691 } 692 693 BoolExpr 694 element(const BoolVarArgs& b, const LinIntExpr& idx) { 695 return BoolExpr(new BElementExpr(b,idx)); 696 } 697 698} 699 700// STATISTICS: minimodel-any