001package org.clafer.ir; 002 003import gnu.trove.TIntCollection; 004import gnu.trove.iterator.TIntIterator; 005import gnu.trove.set.TIntSet; 006import gnu.trove.set.hash.TIntHashSet; 007import java.util.ArrayList; 008import java.util.Arrays; 009import java.util.Collection; 010import java.util.List; 011import org.clafer.common.Util; 012 013/** 014 * Import this class to access all IR building functions. 015 * <pre> 016 * import static org.clafer.ast.Asts.*; 017 * </pre> 018 * 019 * @author jimmy 020 */ 021public class Irs { 022 023 private Irs() { 024 } 025 /** 026 ******************* 027 * 028 * Domain 029 * 030 ******************* 031 */ 032 public static final IrBoolDomain TrueDomain = IrBoolDomain.TrueDomain; 033 public static final IrBoolDomain FalseDomain = IrBoolDomain.FalseDomain; 034 public static final IrBoolDomain BoolDomain = IrBoolDomain.BoolDomain; 035 public static final IrDomain EmptyDomain = new IrEmptyDomain(); 036 public static final IrDomain ZeroDomain = FalseDomain; 037 public static final IrDomain OneDomain = TrueDomain; 038 public static final IrDomain ZeroOneDomain = BoolDomain; 039 public static final IrSetVar EmptySet = new IrSetConstant(EmptyDomain); 040 041 public static IrBoolDomain domain(boolean value) { 042 return value ? TrueDomain : FalseDomain; 043 } 044 045 public static IrDomain constantDomain(int value) { 046 return boundDomain(value, value); 047 } 048 049 public static IrDomain fromToDomain(int from, int to) { 050 if (from == to) { 051 return EmptyDomain; 052 } 053 return boundDomain(from, to - 1); 054 } 055 056 public static IrDomain boundDomain(int low, int high) { 057 if (low == 0 && high == 0) { 058 return ZeroDomain; 059 } 060 if (low == 1 && high == 1) { 061 return OneDomain; 062 } 063 if (low == 0 && high == 1) { 064 return ZeroOneDomain; 065 } 066 return new IrBoundDomain(low, high); 067 } 068 069 public static IrDomain enumDomain(int... values) { 070 return enumDomain(new TIntHashSet(values)); 071 } 072 073 public static IrDomain enumDomain(TIntCollection values) { 074 return enumDomain(new TIntHashSet(values)); 075 } 076 077 public static IrDomain enumDomain(TIntSet values) { 078 switch (values.size()) { 079 case 0: 080 return EmptyDomain; 081 case 1: 082 int value = values.iterator().next(); 083 return boundDomain(value, value); 084 default: 085 int[] array = values.toArray(); 086 Arrays.sort(array); 087 // If the values are over a contiguous interval, then return a bound domain. 088 int low = array[0]; 089 int high = array[array.length - 1]; 090 if (high - low + 1 == array.length) { 091 // A contigious interval. 092 return boundDomain(low, high); 093 } 094 return new IrEnumDomain(array); 095 } 096 } 097 /** 098 ******************* 099 * 100 * Boolean 101 * 102 ******************* 103 */ 104 public static final IrBoolVar True = new IrBoolConstant(true); 105 public static final IrBoolVar False = new IrBoolConstant(false); 106 107 public static IrBoolVar constant(boolean value) { 108 return value ? True : False; 109 } 110 111 public static IrBoolVar bool(String name) { 112 return new IrBoolVar(name, BoolDomain); 113 } 114 115 public static IrBoolExpr not(IrBoolExpr proposition) { 116 Boolean constant = IrUtil.getConstant(proposition); 117 if (constant != null) { 118 // Reverse the boolean 119 return constant.booleanValue() ? False : True; 120 } 121 return proposition.negate(); 122 } 123 124 public static IrBoolExpr and(Collection<? extends IrBoolExpr> operands) { 125 return and(operands.toArray(new IrBoolExpr[operands.size()])); 126 } 127 128 public static IrBoolExpr and(IrBoolExpr... operands) { 129 List<IrBoolExpr> flatten = new ArrayList<>(operands.length); 130 for (IrBoolExpr operand : operands) { 131 if (operand instanceof IrAnd) { 132 // Invariant: No nested IrAnd 133 flatten.addAll(Arrays.asList(((IrAnd) operand).getOperands())); 134 } else { 135 flatten.add(operand); 136 } 137 } 138 List<IrBoolExpr> filter = new ArrayList<>(flatten.size()); 139 for (IrBoolExpr operand : flatten) { 140 if (IrUtil.isFalse(operand)) { 141 return False; 142 } 143 if (!IrUtil.isTrue(operand)) { 144 filter.add(operand); 145 } 146 } 147 switch (filter.size()) { 148 case 0: 149 return True; 150 case 1: 151 return filter.get(0); 152 default: 153 return new IrAnd(filter.toArray(new IrBoolExpr[filter.size()]), BoolDomain); 154 } 155 } 156 157 public static IrBoolExpr lone(Collection<? extends IrBoolExpr> operands) { 158 return lone(operands.toArray(new IrBoolExpr[operands.size()])); 159 } 160 161 public static IrBoolExpr lone(IrBoolExpr... operands) { 162 List<IrBoolExpr> filter = new ArrayList<>(operands.length); 163 int count = 0; 164 for (IrBoolExpr operand : operands) { 165 if (IrUtil.isTrue(operand)) { 166 count++; 167 if (count > 1) { 168 return False; 169 } 170 } else if (!IrUtil.isFalse(operand)) { 171 filter.add(operand); 172 } 173 } 174 assert count == 0 || count == 1; 175 switch (filter.size()) { 176 case 0: 177 return True; 178 case 1: 179 return count == 0 ? True : not(filter.get(0)); 180 default: 181 IrBoolExpr[] f = filter.toArray(new IrBoolExpr[filter.size()]); 182 return count == 0 183 ? new IrLone(f, BoolDomain) 184 : not(or(f)); 185 } 186 } 187 188 public static IrBoolExpr one(Collection<? extends IrBoolExpr> operands) { 189 return one(operands.toArray(new IrBoolExpr[operands.size()])); 190 } 191 192 public static IrBoolExpr one(IrBoolExpr... operands) { 193 List<IrBoolExpr> filter = new ArrayList<>(operands.length); 194 int count = 0; 195 for (IrBoolExpr operand : operands) { 196 if (IrUtil.isTrue(operand)) { 197 count++; 198 if (count > 1) { 199 return False; 200 } 201 } else if (!IrUtil.isFalse(operand)) { 202 filter.add(operand); 203 } 204 } 205 assert count == 0 || count == 1; 206 switch (filter.size()) { 207 case 0: 208 return count == 0 ? False : True; 209 case 1: 210 return count == 0 ? filter.get(0) : not(filter.get(0)); 211 case 2: 212 return count == 0 213 ? xor(filter.get(0), filter.get(1)) 214 : and(not(filter.get(0)), not(filter.get(1))); 215 default: 216 IrBoolExpr[] f = filter.toArray(new IrBoolExpr[filter.size()]); 217 return count == 0 218 ? new IrOne(f, BoolDomain) 219 : not(or(f)); 220 } 221 } 222 223 public static IrBoolExpr or(Collection<? extends IrBoolExpr> operands) { 224 return or(operands.toArray(new IrBoolExpr[operands.size()])); 225 } 226 227 public static IrBoolExpr or(IrBoolExpr... operands) { 228 List<IrBoolExpr> flatten = new ArrayList<>(operands.length); 229 for (IrBoolExpr operand : operands) { 230 if (operand instanceof IrOr) { 231 // Invariant: No nested IrOr 232 flatten.addAll(Arrays.asList(((IrOr) operand).getOperands())); 233 } else { 234 flatten.add(operand); 235 } 236 } 237 List<IrBoolExpr> filter = new ArrayList<>(flatten.size()); 238 for (IrBoolExpr operand : flatten) { 239 if (IrUtil.isTrue(operand)) { 240 return True; 241 } 242 if (!IrUtil.isFalse(operand)) { 243 filter.add(operand); 244 } 245 } 246 switch (filter.size()) { 247 case 0: 248 return False; 249 case 1: 250 return filter.get(0); 251 default: 252 return new IrOr(filter.toArray(new IrBoolExpr[filter.size()]), BoolDomain); 253 } 254 } 255 256 public static IrBoolExpr implies(IrBoolExpr antecedent, IrBoolExpr consequent) { 257 if (antecedent.equals(consequent)) { 258 return True; 259 } 260 if (IrUtil.isTrue(antecedent)) { 261 return consequent; 262 } 263 if (IrUtil.isFalse(antecedent)) { 264 return True; 265 } 266 if (IrUtil.isTrue(consequent)) { 267 return True; 268 } 269 if (IrUtil.isFalse(consequent)) { 270 return not(antecedent); 271 } 272 if (consequent instanceof IrImplies) { 273 // a => (b => c) <=> !a or !b or c 274 IrImplies consequentImplies = (IrImplies) consequent; 275 return or(not(antecedent), 276 not(consequentImplies.getAntecedent()), 277 consequentImplies.getConsequent()); 278 } 279 return new IrImplies(antecedent, consequent, BoolDomain); 280 } 281 282 public static IrBoolExpr notImplies(IrBoolExpr antecedent, IrBoolExpr consequent) { 283 if (antecedent.equals(consequent)) { 284 return False; 285 } 286 if (IrUtil.isTrue(antecedent)) { 287 return not(consequent); 288 } 289 if (IrUtil.isFalse(antecedent)) { 290 return False; 291 } 292 if (IrUtil.isTrue(consequent)) { 293 return False; 294 } 295 if (IrUtil.isFalse(consequent)) { 296 return antecedent; 297 } 298 return new IrNotImplies(antecedent, consequent, BoolDomain); 299 } 300 301 public static IrBoolExpr ifThenElse(IrBoolExpr antecedent, IrBoolExpr consequent, IrBoolExpr alternative) { 302 if (IrUtil.isTrue(antecedent)) { 303 return consequent; 304 } 305 if (IrUtil.isFalse(antecedent)) { 306 return alternative; 307 } 308 if (IrUtil.isTrue(consequent)) { 309 return or(antecedent, alternative); 310 } 311 if (IrUtil.isFalse(consequent)) { 312 return and(antecedent.negate(), alternative); 313 } 314 if (IrUtil.isTrue(alternative)) { 315 return or(antecedent.negate(), consequent); 316 } 317 if (IrUtil.isFalse(alternative)) { 318 return and(antecedent, consequent); 319 } 320 return new IrIfThenElse(antecedent, consequent, alternative, BoolDomain); 321 } 322 323 public static IrBoolExpr ifOnlyIf(IrBoolExpr left, IrBoolExpr right) { 324 if (left.equals(right)) { 325 return True; 326 } 327 if (IrUtil.isTrue(left)) { 328 return right; 329 } 330 if (IrUtil.isFalse(left)) { 331 return not(right); 332 } 333 if (IrUtil.isTrue(right)) { 334 return left; 335 } 336 if (IrUtil.isFalse(right)) { 337 return not(left); 338 } 339 if (left instanceof IrNot) { 340 return xor(((IrNot) left).getExpr(), right); 341 } 342 if (right instanceof IrNot) { 343 return xor(left, ((IrNot) right).getExpr()); 344 } 345 return new IrIfOnlyIf(left, right, BoolDomain); 346 } 347 348 public static IrBoolExpr xor(IrBoolExpr left, IrBoolExpr right) { 349 if (IrUtil.isTrue(left)) { 350 return not(right); 351 } 352 if (IrUtil.isFalse(left)) { 353 return right; 354 } 355 if (IrUtil.isTrue(right)) { 356 return not(left); 357 } 358 if (IrUtil.isFalse(right)) { 359 return left; 360 } 361 if (left instanceof IrNot) { 362 return ifOnlyIf(((IrNot) left).getExpr(), right); 363 } 364 if (right instanceof IrNot) { 365 return ifOnlyIf(left, ((IrNot) right).getExpr()); 366 } 367 return new IrXor(left, right, BoolDomain); 368 } 369 370 public static IrBoolExpr within(IrIntExpr value, IrDomain range) { 371 IrDomain domain = value.getDomain(); 372 if (range.isBounded() 373 && domain.getLowBound() >= range.getLowBound() 374 && domain.getHighBound() <= range.getHighBound()) { 375 return True; 376 } else if (IrUtil.isSubsetOf(domain, range)) { 377 return True; 378 } 379 if (domain.getLowBound() > range.getHighBound() 380 || domain.getHighBound() < range.getLowBound()) { 381 return False; 382 } 383 if (range.size() == 1) { 384 return equal(value, range.getLowBound()); 385 } 386 if (value.getDomain().getLowBound() == range.getLowBound() 387 && value.getDomain().getHighBound() - 1 == range.getHighBound()) { 388 return notEqual(value, value.getDomain().getHighBound()); 389 } 390 if (value.getDomain().getLowBound() + 1 == range.getLowBound() 391 && value.getDomain().getHighBound() == range.getHighBound()) { 392 return notEqual(value, value.getDomain().getLowBound()); 393 } 394 return new IrWithin(value, range, BoolDomain); 395 } 396 397 public static IrBoolExpr notWithin(IrIntExpr value, IrDomain range) { 398 IrDomain domain = value.getDomain(); 399 if (range.isBounded() 400 && domain.getLowBound() >= range.getLowBound() 401 && domain.getHighBound() <= range.getHighBound()) { 402 return False; 403 } 404 if (domain.getLowBound() > range.getHighBound() 405 || domain.getHighBound() < range.getLowBound()) { 406 return True; 407 } 408 if (range.size() == 1) { 409 return notEqual(value, range.getLowBound()); 410 } 411 return new IrNotWithin(value, range, BoolDomain); 412 } 413 414 public static IrBoolExpr compare(int left, IrCompare.Op op, IrIntExpr right) { 415 return compare(constant(left), op, right); 416 } 417 418 public static IrBoolExpr compare(IrIntExpr left, IrCompare.Op op, int right) { 419 return compare(left, op, constant(right)); 420 } 421 422 public static IrBoolExpr compare(IrIntExpr left, IrCompare.Op op, IrIntExpr right) { 423 IrDomain leftDomain = left.getDomain(); 424 IrDomain rightDomain = right.getDomain(); 425 switch (op) { 426 case Equal: 427 if (left.equals(right)) { 428 return True; 429 } 430 if (!IrUtil.intersects(leftDomain, rightDomain)) { 431 return False; 432 } 433 if (left instanceof IrBoolExpr && right instanceof IrBoolExpr) { 434 return ifOnlyIf((IrBoolExpr) left, (IrBoolExpr) right); 435 } 436 break; 437 case NotEqual: 438 if (left.equals(right)) { 439 return False; 440 } 441 if (!IrUtil.intersects(leftDomain, rightDomain)) { 442 return True; 443 } 444 if (left instanceof IrBoolExpr && right instanceof IrBoolExpr) { 445 return xor((IrBoolExpr) left, (IrBoolExpr) right); 446 } 447 break; 448 case LessThan: 449 if (left.equals(right)) { 450 return False; 451 } 452 if (leftDomain.getHighBound() < rightDomain.getLowBound()) { 453 return True; 454 } 455 if (leftDomain.getLowBound() >= rightDomain.getHighBound()) { 456 return False; 457 } 458 if (left instanceof IrBoolExpr && right instanceof IrBoolExpr) { 459 return not(implies((IrBoolExpr) right, (IrBoolExpr) left)); 460 } 461 if (left instanceof IrMinus && right instanceof IrMinus) { 462 return greaterThan(((IrMinus) left).getExpr(), ((IrMinus) right).getExpr()); 463 } 464 break; 465 case LessThanEqual: 466 if (left.equals(right)) { 467 return True; 468 } 469 if (leftDomain.getHighBound() <= rightDomain.getLowBound()) { 470 return True; 471 } 472 if (leftDomain.getLowBound() > rightDomain.getHighBound()) { 473 return False; 474 } 475 if (leftDomain.getLowBound() == rightDomain.getHighBound()) { 476 return equal(left, right); 477 } 478 if (left instanceof IrBoolExpr && right instanceof IrBoolExpr) { 479 return implies((IrBoolExpr) left, (IrBoolExpr) right); 480 } 481 if (left instanceof IrMinus && right instanceof IrMinus) { 482 return greaterThanEqual(((IrMinus) left).getExpr(), ((IrMinus) right).getExpr()); 483 } 484 break; 485 default: 486 throw new IllegalArgumentException("Unknown op: " + op); 487 } 488 return new IrCompare(left, op, right, BoolDomain); 489 } 490 491 public static IrBoolExpr equal(int left, IrIntExpr right) { 492 return equal(constant(left), right); 493 } 494 495 public static IrBoolExpr equal(IrIntExpr left, int right) { 496 return equal(left, constant(right)); 497 } 498 499 public static IrBoolExpr equal(IrIntExpr left, IrIntExpr right) { 500 return compare(left, IrCompare.Op.Equal, right); 501 } 502 503 public static IrBoolExpr equal(IrIntExpr[] left, IrIntExpr[] right) { 504 if (left.length != right.length) { 505 throw new IllegalArgumentException(); 506 } 507 IrBoolExpr[] ands = new IrBoolExpr[left.length]; 508 for (int i = 0; i < ands.length; i++) { 509 ands[i] = equal(left[i], right[i]); 510 } 511 return and(ands); 512 } 513 514 public static IrBoolExpr equality(IrSetExpr left, IrSetTest.Op op, IrSetExpr right) { 515 switch (op) { 516 case Equal: 517 if (left.equals(right)) { 518 return True; 519 } 520 if (!IrUtil.isSubsetOf(left.getKer(), right.getEnv()) 521 || !IrUtil.isSubsetOf(right.getKer(), left.getEnv())) { 522 return False; 523 } 524 if (!IrUtil.intersects(left.getCard(), right.getCard())) { 525 return False; 526 } 527 int[] constant = IrUtil.getConstant(left); 528 if (constant != null) { 529 /* 530 * The idea is that integer constraints are easier to 531 * optimize than set constraints. If the expression is a top 532 * level expression than the cardinality propagator will 533 * optimize this expression away anyways. 534 */ 535 if (constant.length == 0) { 536 return equal(card(right), 0); 537 } 538 if (constant.length == right.getEnv().size()) { 539 return IrUtil.containsAll(constant, right.getEnv()) 540 ? equal(card(right), constant.length) : False; 541 } 542 } 543 constant = IrUtil.getConstant(right); 544 if (constant != null) { 545 if (constant.length == 0) { 546 return equal(card(left), 0); 547 } 548 if (constant.length == left.getEnv().size()) { 549 return IrUtil.containsAll(constant, left.getEnv()) 550 ? equal(card(left), constant.length) : False; 551 } 552 } 553 IrIntExpr leftInt = IrUtil.asInt(left); 554 if (leftInt != null) { 555 IrIntExpr rightInt = IrUtil.asInt(right); 556 if (rightInt != null) { 557 return equal(leftInt, rightInt); 558 } 559 } 560 break; 561 case NotEqual: 562 if (left.equals(right)) { 563 return False; 564 } 565 if (!IrUtil.isSubsetOf(left.getKer(), right.getEnv()) 566 || !IrUtil.isSubsetOf(right.getKer(), left.getEnv())) { 567 return True; 568 } 569 if (!IrUtil.intersects(left.getCard(), right.getCard())) { 570 return True; 571 } 572 constant = IrUtil.getConstant(left); 573 if (constant != null) { 574 if (constant.length == 0) { 575 return notEqual(card(right), 0); 576 } 577 if (constant.length == right.getEnv().size()) { 578 return IrUtil.containsAll(constant, right.getEnv()) 579 ? notEqual(card(right), constant.length) : True; 580 } 581 } 582 constant = IrUtil.getConstant(right); 583 if (constant != null) { 584 if (constant.length == 0) { 585 return notEqual(card(left), 0); 586 } 587 if (constant.length == left.getEnv().size()) { 588 return IrUtil.containsAll(constant, left.getEnv()) 589 ? notEqual(card(left), constant.length) : True; 590 } 591 } 592 leftInt = IrUtil.asInt(left); 593 if (leftInt != null) { 594 IrIntExpr rightInt = IrUtil.asInt(right); 595 if (rightInt != null) { 596 return notEqual(leftInt, rightInt); 597 } 598 } 599 break; 600 default: 601 throw new IllegalArgumentException(); 602 } 603 return new IrSetTest(left, op, right, BoolDomain); 604 } 605 606 public static IrBoolExpr equal(IrSetExpr left, IrSetExpr right) { 607 return equality(left, IrSetTest.Op.Equal, right); 608 } 609 610 public static IrBoolExpr notEqual(int left, IrIntExpr right) { 611 return notEqual(constant(left), right); 612 } 613 614 public static IrBoolExpr notEqual(IrIntExpr left, int right) { 615 return notEqual(left, constant(right)); 616 } 617 618 public static IrBoolExpr notEqual(IrIntExpr left, IrIntExpr right) { 619 return compare(left, IrCompare.Op.NotEqual, right); 620 } 621 622 public static IrBoolExpr notEqual(IrSetExpr left, IrSetExpr right) { 623 return equality(left, IrSetTest.Op.NotEqual, right); 624 } 625 626 public static IrBoolExpr lessThan(int left, IrIntExpr right) { 627 return lessThan(constant(left), right); 628 } 629 630 public static IrBoolExpr lessThan(IrIntExpr left, int right) { 631 return lessThan(left, constant(right)); 632 } 633 634 public static IrBoolExpr lessThan(IrIntExpr left, IrIntExpr right) { 635 return compare(left, IrCompare.Op.LessThan, right); 636 } 637 638 public static IrBoolExpr lessThanEqual(int left, IrIntExpr right) { 639 return lessThanEqual(constant(left), right); 640 } 641 642 public static IrBoolExpr lessThanEqual(IrIntExpr left, int right) { 643 return lessThanEqual(left, constant(right)); 644 } 645 646 public static IrBoolExpr lessThanEqual(IrIntExpr left, IrIntExpr right) { 647 return compare(left, IrCompare.Op.LessThanEqual, right); 648 } 649 650 public static IrBoolExpr greaterThan(int left, IrIntExpr right) { 651 return greaterThan(constant(left), right); 652 } 653 654 public static IrBoolExpr greaterThan(IrIntExpr left, int right) { 655 return greaterThan(left, constant(right)); 656 } 657 658 public static IrBoolExpr greaterThan(IrIntExpr left, IrIntExpr right) { 659 return compare(right, IrCompare.Op.LessThan, left); 660 } 661 662 public static IrBoolExpr greaterThanEqual(int left, IrIntExpr right) { 663 return greaterThanEqual(constant(left), right); 664 } 665 666 public static IrBoolExpr greaterThanEqual(IrIntExpr left, int right) { 667 return greaterThanEqual(left, constant(right)); 668 } 669 670 public static IrBoolExpr greaterThanEqual(IrIntExpr left, IrIntExpr right) { 671 return compare(right, IrCompare.Op.LessThanEqual, left); 672 } 673 674 public static IrBoolExpr member(IrIntExpr element, IrSetExpr set) { 675 if (IrUtil.isSubsetOf(element.getDomain(), set.getKer())) { 676 return True; 677 } 678 if (!IrUtil.intersects(element.getDomain(), set.getEnv())) { 679 return False; 680 } 681 if (IrUtil.isConstant(set)) { 682 return within(element, set.getEnv()); 683 } 684 return new IrMember(element, set, BoolDomain); 685 } 686 687 public static IrBoolExpr notMember(IrIntExpr element, IrSetExpr set) { 688 if (!IrUtil.intersects(element.getDomain(), set.getEnv())) { 689 return True; 690 } 691 if (IrUtil.isSubsetOf(element.getDomain(), set.getKer())) { 692 return False; 693 } 694 if (IrUtil.isConstant(set)) { 695 return notWithin(element, set.getEnv()); 696 } 697 return new IrNotMember(element, set, BoolDomain); 698 } 699 700 public static IrBoolExpr subsetEq(IrSetExpr subset, IrSetExpr superset) { 701 if (IrUtil.isSubsetOf(subset.getEnv(), superset.getKer())) { 702 return True; 703 } 704 if (subset.getCard().getLowBound() == superset.getCard().getHighBound()) { 705 return equal(subset, superset); 706 } 707 return new IrSubsetEq(subset, superset, BoolDomain); 708 } 709 710 public static IrBoolExpr boolChannel(IrBoolExpr[] bools, IrSetExpr set) { 711 if (set.getEnv().isEmpty() 712 || (set.getEnv().getLowBound() >= 0 && set.getEnv().getHighBound() < bools.length)) { 713 { 714 int[] constant = IrUtil.getConstant(set); 715 if (constant != null) { 716 IrBoolExpr[] ands = new IrBoolExpr[bools.length]; 717 for (int i = 0; i < ands.length; i++) { 718 ands[i] = equal(bools[i], Util.in(i, constant) ? True : False); 719 } 720 return and(ands); 721 } 722 if (bools.length == 1) { 723 return equal(bools[0], card(set)); 724 } 725 } 726 TIntHashSet values = new TIntHashSet(); 727 IrDomain env = set.getEnv(); 728 IrDomain ker = set.getKer(); 729 boolean entailed = true; 730 for (int i = 0; i < bools.length; i++) { 731 Boolean constant = IrUtil.getConstant(bools[i]); 732 if (Boolean.TRUE.equals(constant)) { 733 if (values != null) { 734 values.add(i); 735 } 736 if (!env.contains(i)) { 737 return False; 738 } 739 if (!ker.contains(i)) { 740 entailed = false; 741 } 742 } else if (Boolean.FALSE.equals(constant)) { 743 if (ker.contains(i)) { 744 return False; 745 } 746 if (env.contains(i)) { 747 entailed = false; 748 } 749 } else { 750 values = null; 751 entailed = false; 752 } 753 } 754 if (entailed) { 755 return True; 756 } 757 if (values != null) { 758 return equal(set, constant(enumDomain(values))); 759 } 760 } 761 return new IrBoolChannel(bools, set, BoolDomain); 762 } 763 764 public static IrBoolExpr intChannel(IrIntExpr[] ints, IrSetExpr[] sets) { 765 boolean entailed = true; 766 for (int i = 0; i < ints.length; i++) { 767 Integer constant = IrUtil.getConstant(ints[i]); 768 if (constant != null) { 769 IrSetExpr set = sets[constant.intValue()]; 770 if (!set.getEnv().contains(i)) { 771 return False; 772 } else if (!set.getKer().contains(i)) { 773 entailed = false; 774 } 775 } else { 776 entailed = false; 777 } 778 } 779 if (entailed) { 780 return True; 781 } 782 return new IrIntChannel(ints, sets, BoolDomain); 783 } 784 785 public static IrBoolExpr sort(IrIntExpr... array) { 786 if (array.length <= 1) { 787 return True; 788 } 789 IrBoolExpr[] sort = new IrBoolExpr[array.length - 1]; 790 for (int i = 0; i < array.length - 1; i++) { 791 sort[i] = lessThanEqual(array[i], array[i + 1]); 792 } 793 return and(sort); 794 } 795 796 public static IrBoolExpr sortStrict(IrIntExpr... array) { 797 if (array.length <= 1) { 798 return True; 799 } 800 IrBoolExpr[] sort = new IrBoolExpr[array.length - 1]; 801 for (int i = 0; i < array.length - 1; i++) { 802 sort[i] = lessThan(array[i], array[i + 1]); 803 } 804 return and(sort); 805 } 806 807 public static IrBoolExpr sort(IrSetExpr... sets) { 808 List<IrSetExpr> filter = new ArrayList<>(sets.length); 809 boolean fixedCard = true; 810 for (IrSetExpr set : sets) { 811 if (!set.getEnv().isEmpty()) { 812 filter.add(set); 813 fixedCard = fixedCard && set.getCard().size() == 1; 814 } 815 } 816 if (filter.isEmpty()) { 817 return True; 818 } 819 if (filter.size() == 1) { 820 IrDomain env = filter.get(0).getEnv(); 821 IrDomain ker = filter.get(0).getKer(); 822 if (env.getLowBound() == 0) { 823 int i; 824 for (i = 0; i < env.getHighBound(); i++) { 825 if (!ker.contains(i)) { 826 break; 827 } 828 } 829 if (i == env.getHighBound()) { 830 // env = [0,1,...,n] 831 // ker = [0,1,...,n] or [0,1,...,n-1] 832 return True; 833 } 834 } 835 } 836 if (fixedCard) { 837 List<IrBoolExpr> ands = new ArrayList<>(); 838 int i = 0; 839 for (IrSetExpr set : filter) { 840 assert set.getCard().size() == 1; 841 int card = set.getCard().getLowBound(); 842 ands.add(equal(set, constant(Util.fromTo(i, i + card)))); 843 i += card; 844 } 845 return and(ands); 846 } 847 return new IrSortSets(filter.toArray(new IrSetExpr[filter.size()]), BoolDomain); 848 } 849 850 private static IrBoolExpr sortStrings(IrIntExpr[][] strings, boolean strict) { 851 List<IrIntExpr[]> filterStrings = new ArrayList<>(strings.length); 852 for (int i = 0; i < strings.length - 1; i++) { 853 switch (IrUtil.compareString(strings[i], strings[i + 1])) { 854 case EQ: 855 case LE: 856 if (!strict) { 857 break; 858 } 859 // fallthrough 860 case GT: 861 case GE: 862 case UNKNOWN: 863 filterStrings.add(strings[i]); 864 } 865 } 866 filterStrings.add(strings[strings.length - 1]); 867 IrIntExpr[][] fstrings = filterStrings.toArray(new IrIntExpr[filterStrings.size()][]); 868 869 if (fstrings.length < 2) { 870 return True; 871 } 872 873 IrIntExpr[] array = new IrIntExpr[fstrings.length]; 874 for (int i = 0; i < fstrings.length; i++) { 875 IrIntExpr[] string = fstrings[i]; 876 if (string.length != 1) { 877 return new IrSortStrings(fstrings, strict, BoolDomain); 878 } 879 array[i] = fstrings[i][0]; 880 } 881 return strict ? sortStrict(array) : sort(array); 882 } 883 884 public static IrBoolExpr sort(IrIntExpr[]... strings) { 885 return sortStrings(strings, false); 886 } 887 888 public static IrBoolExpr sortStrict(IrIntExpr[]... strings) { 889 return sortStrings(strings, true); 890 } 891 892 public static IrBoolExpr sortChannel(IrIntExpr[][] strings, IrIntExpr[] ints) { 893 if (strings.length != ints.length) { 894 throw new IllegalArgumentException(); 895 } 896 for (int i = 1; i < strings.length; i++) { 897 if (strings[0].length != strings[i].length) { 898 throw new IllegalArgumentException(); 899 } 900 } 901 List<IrIntExpr[]> filterStrings = new ArrayList<>(strings.length); 902 List<IrIntExpr> filterInts = new ArrayList<>(ints.length); 903 for (int i = 0; i < ints.length; i++) { 904 boolean equivalence = false; 905 for (int j = i + 1; j < ints.length; j++) { 906 if (IrUtil.Ordering.EQ.equals(IrUtil.compare(ints[i], ints[j])) 907 && IrUtil.Ordering.EQ.equals(IrUtil.compareString(strings[i], strings[j]))) { 908 equivalence = true; 909 break; 910 } 911 } 912 if (!equivalence) { 913 filterStrings.add(strings[i]); 914 filterInts.add(ints[i]); 915 } 916 } 917 assert !filterInts.isEmpty(); 918 if (filterInts.size() == 1) { 919 return equal(filterInts.get(0), 0); 920 } 921 IrIntExpr[][] fstrings = filterStrings.toArray(new IrIntExpr[filterStrings.size()][]); 922 IrIntExpr[] fints = filterInts.toArray(new IrIntExpr[filterInts.size()]); 923 int[] constant = IrUtil.getConstant(fints); 924 if (constant != null) { 925 IrIntExpr[][] partialOrdering = new IrIntExpr[constant.length][]; 926 for (int i = 0; i < constant.length; i++) { 927 int val = constant[i]; 928 if (val < 0 || val >= constant.length) { 929 return False; 930 } 931 if (partialOrdering[constant[i]] != null) { 932 throw new IllegalStateException(); 933 } 934 partialOrdering[constant[i]] = fstrings[i]; 935 } 936 return sortStrict(partialOrdering); 937 } 938 return new IrSortStringsChannel(fstrings, fints, BoolDomain); 939 } 940 941 public static IrBoolExpr allDifferent(IrIntExpr[] ints) { 942 if (ints.length < 2) { 943 return True; 944 } 945 if (ints.length == 2) { 946 return notEqual(ints[0], ints[1]); 947 } 948 IrDomain domain = ints[0].getDomain(); 949 int size = ints[0].getDomain().size(); 950 for (int i = 1; i < ints.length; i++) { 951 domain = IrUtil.union(domain, ints[i].getDomain()); 952 size += ints[i].getDomain().size(); 953 if (size != domain.size()) { 954 return new IrAllDifferent(ints, BoolDomain); 955 } 956 } 957 return True; 958 } 959 960 public static IrBoolExpr selectN(IrBoolExpr[] bools, IrIntExpr n) { 961 if (bools.length == 1) { 962 if (bools[0].equals(n)) { 963 return True; 964 } 965 } 966 boolean entailed = true; 967 IrDomain nDomain = n.getDomain(); 968 for (int i = 0; i < bools.length; i++) { 969 Boolean constant = IrUtil.getConstant(bools[i]); 970 if (Boolean.TRUE.equals(constant)) { 971 if (i >= nDomain.getHighBound()) { 972 return False; 973 } 974 if (i >= nDomain.getLowBound()) { 975 entailed = false; 976 } 977 } else if (Boolean.FALSE.equals(constant)) { 978 if (i < nDomain.getLowBound()) { 979 return False; 980 } 981 if (i < nDomain.getHighBound()) { 982 entailed = false; 983 } 984 } else { 985 entailed = false; 986 } 987 } 988 if (entailed) { 989 return True; 990 } 991 Integer constant = IrUtil.getConstant(n); 992 if (constant != null) { 993 IrBoolExpr[] ands = new IrBoolExpr[bools.length]; 994 System.arraycopy(bools, 0, ands, 0, constant.intValue()); 995 for (int i = constant.intValue(); i < bools.length; i++) { 996 ands[i] = not(bools[i]); 997 } 998 return and(ands); 999 } 1000 return new IrSelectN(bools, n, BoolDomain); 1001 } 1002 1003 public static IrBoolExpr acyclic(IrIntExpr[] edges) { 1004 return new IrAcyclic(edges, BoolDomain); 1005 } 1006 1007 public static IrBoolExpr unreachable(IrIntExpr[] edges, int from, int to) { 1008 return new IrUnreachable(edges, from, to, BoolDomain); 1009 } 1010 1011 public static IrBoolExpr filterString(IrSetExpr set, IrIntExpr[] string, IrIntExpr[] result) { 1012 if (set.getEnv().isEmpty()) { 1013 return filterString(set, 0, new IrIntExpr[0], result); 1014 } 1015 int offset = set.getEnv().getLowBound(); 1016 int end = set.getEnv().getHighBound(); 1017 return filterString(set, offset, 1018 Arrays.copyOfRange(string, offset, end + 1), 1019 result); 1020 } 1021 1022 public static IrBoolExpr filterString(IrSetExpr set, int offset, IrIntExpr[] string, IrIntExpr[] result) { 1023 int[] constant = IrUtil.getConstant(set); 1024 if (constant != null) { 1025 IrBoolExpr[] ands = new IrBoolExpr[result.length]; 1026 for (int i = 0; i < constant.length; i++) { 1027 ands[i] = equal(string[constant[i] - offset], result[i]); 1028 } 1029 for (int i = constant.length; i < result.length; i++) { 1030 ands[i] = equal(result[i], -1); 1031 } 1032 return and(ands); 1033 } 1034 IrIntExpr[] filterString = Arrays.copyOf(string, string.length); 1035 IrIntExpr[] filterResult = Arrays.copyOf(result, result.length); 1036 TIntIterator iter = set.getEnv().iterator(); 1037 int i = 0; 1038 while (iter.hasNext()) { 1039 int env = iter.next(); 1040 int x = env - offset; 1041 if (!set.getKer().contains(env) || !filterString[x].equals(filterResult[i])) { 1042 break; 1043 } 1044 filterString[x] = Zero; 1045 filterResult[i] = Zero; 1046 i++; 1047 } 1048 int cut = filterResult.length; 1049 while (cut > 0 && Integer.valueOf(-1).equals(IrUtil.getConstant(filterResult[cut - 1]))) { 1050 cut--; 1051 } 1052 if (cut != filterResult.length) { 1053 filterResult = Arrays.copyOf(filterResult, cut); 1054 } 1055 return new IrFilterString(set, offset, filterString, filterResult, BoolDomain); 1056 } 1057 /** 1058 ******************* 1059 * 1060 * Integer 1061 * 1062 ******************* 1063 */ 1064 public static IrIntVar Zero = False; 1065 public static IrIntVar One = True; 1066 1067 public static IrIntVar domainInt(String name, IrDomain domain) { 1068 if (domain.size() == 1) { 1069 return constant(domain.getLowBound()); 1070 } 1071 if (domain instanceof IrBoolDomain) { 1072 return new IrBoolVar(name, (IrBoolDomain) domain); 1073 } 1074 return new IrIntVar(name, domain); 1075 } 1076 1077 public static IrIntVar boundInt(String name, int low, int high) { 1078 return domainInt(name, boundDomain(low, high)); 1079 } 1080 1081 public static IrIntVar enumInt(String name, int[] values) { 1082 return domainInt(name, enumDomain(values)); 1083 } 1084 1085 public static IrIntVar constant(int value) { 1086 switch (value) { 1087 case 0: 1088 return Zero; 1089 case 1: 1090 return One; 1091 default: 1092 return new IrIntConstant(value); 1093 } 1094 } 1095 1096 public static IrIntExpr minus(IrIntExpr expr) { 1097 Integer constant = IrUtil.getConstant(expr); 1098 if (constant != null) { 1099 return constant(-constant.intValue()); 1100 } 1101 if (expr instanceof IrMinus) { 1102 IrMinus minus = (IrMinus) expr; 1103 return minus.getExpr(); 1104 } 1105 return new IrMinus(expr, IrUtil.minus(expr.getDomain())); 1106 1107 } 1108 1109 public static IrIntExpr card(IrSetExpr set) { 1110 IrDomain domain = set.getCard(); 1111 if (domain.size() == 1) { 1112 return constant(domain.getLowBound()); 1113 } 1114 return new IrCard(set, domain); 1115 } 1116 1117 public static IrIntExpr add(int addend1, IrIntExpr addend2) { 1118 return add(constant(addend1), addend2); 1119 } 1120 1121 public static IrIntExpr add(IrIntExpr addend1, int addend2) { 1122 return add(addend1, constant(addend2)); 1123 } 1124 1125 public static IrIntExpr add(Collection<? extends IrIntExpr> addends) { 1126 return add(addends.toArray(new IrIntExpr[addends.size()])); 1127 } 1128 1129 public static IrIntExpr add(IrIntExpr... addends) { 1130 int constants = 0; 1131 List<IrIntExpr> filter = new ArrayList<>(addends.length); 1132 for (IrIntExpr addend : addends) { 1133 if (addend instanceof IrAdd) { 1134 IrAdd add = (IrAdd) addend; 1135 // Invariant: No nested IrAdd or constants 1136 filter.addAll(Arrays.asList(add.getAddends())); 1137 constants += add.getOffset(); 1138 } else { 1139 Integer constant = IrUtil.getConstant(addend); 1140 if (constant != null) { 1141 constants += constant.intValue(); 1142 } else { 1143 filter.add(addend); 1144 } 1145 } 1146 } 1147 if (filter.isEmpty()) { 1148 return constant(constants); 1149 } 1150 if (filter.size() == 1) { 1151 IrIntExpr first = filter.get(0); 1152 if (constants == 0) { 1153 return first; 1154 } 1155 return new IrAdd(new IrIntExpr[]{first}, constants, 1156 IrUtil.offset(first.getDomain(), constants)); 1157 } 1158 int low = constants; 1159 int high = constants; 1160 for (IrIntExpr addend : filter) { 1161 low += addend.getDomain().getLowBound(); 1162 high += addend.getDomain().getHighBound(); 1163 } 1164 IrDomain domain = boundDomain(low, high); 1165 return new IrAdd(filter.toArray(new IrIntExpr[filter.size()]), constants, domain); 1166 } 1167 1168 public static IrIntExpr sub(int minuend, IrIntExpr subtrahend) { 1169 return sub(constant(minuend), subtrahend); 1170 } 1171 1172 public static IrIntExpr sub(IrIntExpr minuend, int subtrahend) { 1173 return sub(minuend, constant(subtrahend)); 1174 } 1175 1176 public static IrIntExpr sub(Collection<? extends IrIntExpr> subtrahends) { 1177 return sub(subtrahends.toArray(new IrIntExpr[subtrahends.size()])); 1178 } 1179 1180 public static IrIntExpr sub(IrIntExpr... subtrahends) { 1181 if (subtrahends.length == 0) { 1182 return Zero; 1183 } 1184 IrIntExpr[] flip = new IrIntExpr[subtrahends.length]; 1185 flip[0] = subtrahends[0]; 1186 for (int i = 1; i < flip.length; i++) { 1187 flip[i] = minus(subtrahends[i]); 1188 } 1189 return add(flip); 1190 } 1191 1192 public static IrIntExpr mul(int multiplicand, IrIntExpr multiplier) { 1193 return mul(constant(multiplicand), multiplier); 1194 } 1195 1196 public static IrIntExpr mul(IrIntExpr multiplicand, int multiplier) { 1197 return mul(multiplicand, constant(multiplier)); 1198 } 1199 1200 public static IrIntExpr mul(IrIntExpr multiplicand, IrIntExpr multiplier) { 1201 Integer multiplicandConstant = IrUtil.getConstant(multiplicand); 1202 Integer multiplierConstant = IrUtil.getConstant(multiplier); 1203 if (multiplicandConstant != null) { 1204 switch (multiplicandConstant.intValue()) { 1205 case 0: 1206 return multiplicand; 1207 case 1: 1208 return multiplier; 1209 } 1210 } 1211 if (multiplierConstant != null) { 1212 switch (multiplierConstant.intValue()) { 1213 case 0: 1214 return multiplier; 1215 case 1: 1216 return multiplicand; 1217 } 1218 } 1219 if (multiplicandConstant != null && multiplierConstant != null) { 1220 return constant(multiplicandConstant.intValue() * multiplierConstant.intValue()); 1221 } 1222 int low1 = multiplicand.getDomain().getLowBound(); 1223 int high1 = multiplicand.getDomain().getHighBound(); 1224 int low2 = multiplier.getDomain().getLowBound(); 1225 int high2 = multiplier.getDomain().getHighBound(); 1226 int min = Util.min(low1 * low2, low1 * high2, high1 * low2, high1 * high2); 1227 int max = Util.max(low1 * low2, low1 * high2, high1 * low2, high1 * high2); 1228 return new IrMul(multiplicand, multiplier, boundDomain(min, max)); 1229 } 1230 1231 public static IrIntExpr div(int dividend, IrIntExpr divisor) { 1232 return div(constant(dividend), divisor); 1233 } 1234 1235 public static IrIntExpr div(IrIntExpr dividend, int divisor) { 1236 return div(dividend, constant(divisor)); 1237 } 1238 1239 public static IrIntExpr div(IrIntExpr dividend, IrIntExpr divisor) { 1240 Integer dividendConstant = IrUtil.getConstant(dividend); 1241 Integer divisorConstant = IrUtil.getConstant(divisor); 1242 if (dividendConstant != null && dividendConstant.intValue() == 0) { 1243 return dividend; 1244 } 1245 if (divisorConstant != null && divisorConstant.intValue() == 1) { 1246 return dividend; 1247 } 1248 if (dividendConstant != null && divisorConstant != null) { 1249 return constant(dividendConstant.intValue() / divisorConstant.intValue()); 1250 } 1251 int low1 = dividend.getDomain().getLowBound(); 1252 int high1 = dividend.getDomain().getHighBound(); 1253 int low2 = divisor.getDomain().getLowBound(); 1254 int high2 = divisor.getDomain().getHighBound(); 1255 int min = Util.min(low1, -low1, high1, -high1); 1256 int max = Util.max(low1, -low1, high1, -high1); 1257 return new IrDiv(dividend, divisor, boundDomain(min, max)); 1258 } 1259 1260 public static IrIntExpr element(IrIntExpr[] array, IrIntExpr index) { 1261 IrIntExpr[] $array = index.getDomain().getHighBound() + 1 < array.length 1262 ? Arrays.copyOf(array, index.getDomain().getHighBound() + 1) 1263 : array.clone(); 1264 for (int i = 0; i < $array.length; i++) { 1265 if (!index.getDomain().contains(i)) { 1266 $array[i] = Zero; 1267 } 1268 } 1269 1270 Integer constant = IrUtil.getConstant(index); 1271 if (constant != null) { 1272 return $array[constant.intValue()]; 1273 } 1274 TIntIterator iter = index.getDomain().iterator(); 1275 assert iter.hasNext(); 1276 1277 IrDomain domain = $array[iter.next()].getDomain(); 1278 int low = domain.getLowBound(); 1279 int high = domain.getHighBound(); 1280 while (iter.hasNext()) { 1281 int val = iter.next(); 1282 if (val < $array.length) { 1283 domain = $array[val].getDomain(); 1284 low = Math.min(low, domain.getLowBound()); 1285 high = Math.max(high, domain.getHighBound()); 1286 } 1287 } 1288 domain = boundDomain(low, high); 1289 return new IrElement($array, index, domain); 1290 } 1291 1292 public static IrIntExpr count(int value, IrIntExpr[] array) { 1293 List<IrIntExpr> filter = new ArrayList<>(); 1294 int count = 0; 1295 for (IrIntExpr i : array) { 1296 Integer constant = IrUtil.getConstant(i); 1297 if (constant != null) { 1298 if (constant.intValue() == value) { 1299 count++; 1300 } 1301 } else if (i.getDomain().contains(value)) { 1302 filter.add(i); 1303 } 1304 } 1305 switch (filter.size()) { 1306 case 0: 1307 return constant(count); 1308 case 1: 1309 return add(equal(value, filter.get(0)), count); 1310 default: 1311 return add( 1312 new IrCount(value, filter.toArray(new IrIntExpr[filter.size()]), boundDomain(0, filter.size())), 1313 count); 1314 } 1315 } 1316 1317 public static IrIntExpr sum(IrSetExpr set) { 1318 int sum = Util.sum(set.getKer().iterator()); 1319 int count = set.getKer().size(); 1320 1321 // Calculate low 1322 int low = sum; 1323 int lowCount = count; 1324 TIntIterator envIter = set.getEnv().iterator(); 1325 while (lowCount < set.getCard().getHighBound() && envIter.hasNext()) { 1326 int env = envIter.next(); 1327 if (env >= 0 && lowCount >= set.getCard().getLowBound()) { 1328 break; 1329 } 1330 if (!set.getKer().contains(env)) { 1331 low += env; 1332 lowCount++; 1333 } 1334 } 1335 1336 // Calculate high 1337 int high = sum; 1338 int highCount = count; 1339 envIter = set.getEnv().iterator(false); 1340 while (highCount < set.getCard().getHighBound() && envIter.hasNext()) { 1341 int env = envIter.next(); 1342 if (env <= 0 && highCount >= set.getCard().getLowBound()) { 1343 break; 1344 } 1345 if (!set.getKer().contains(env)) { 1346 high += env; 1347 highCount++; 1348 } 1349 } 1350 1351 return new IrSetSum(set, boundDomain(low, high)); 1352 } 1353 1354 public static IrIntExpr ternary(IrBoolExpr antecedent, IrIntExpr consequent, IrIntExpr alternative) { 1355 if (IrUtil.isTrue(antecedent)) { 1356 return consequent; 1357 } 1358 if (IrUtil.isFalse(antecedent)) { 1359 return alternative; 1360 } 1361 if (consequent.equals(alternative)) { 1362 return consequent; 1363 } 1364 Integer consequentConstant = IrUtil.getConstant(consequent); 1365 Integer alternativeConstant = IrUtil.getConstant(alternative); 1366 if (consequentConstant != null && consequentConstant.equals(alternativeConstant)) { 1367 return constant(consequentConstant); 1368 } 1369 IrDomain domain = IrUtil.union(consequent.getDomain(), alternative.getDomain()); 1370 return new IrTernary(antecedent, consequent, alternative, domain); 1371 } 1372 1373 /** 1374 ******************* 1375 * 1376 * Set 1377 * 1378 ******************* 1379 */ 1380 public static IrSetVar set(String name, int lowEnv, int highEnv) { 1381 return set(name, boundDomain(lowEnv, highEnv)); 1382 } 1383 1384 public static IrSetVar set(String name, int lowEnv, int highEnv, int lowKer, int highKer) { 1385 return set(name, boundDomain(lowEnv, highEnv), boundDomain(lowKer, highKer)); 1386 } 1387 1388 public static IrSetVar set(String name, int lowEnv, int highEnv, int[] ker) { 1389 return set(name, boundDomain(lowEnv, highEnv), enumDomain(ker)); 1390 } 1391 1392 public static IrSetVar set(String name, int[] env) { 1393 return set(name, enumDomain(env)); 1394 } 1395 1396 public static IrSetVar set(String name, int[] env, int lowKer, int highKer) { 1397 return set(name, enumDomain(env), boundDomain(lowKer, highKer)); 1398 } 1399 1400 public static IrSetVar set(String name, int[] env, int[] ker) { 1401 return set(name, enumDomain(env), enumDomain(ker)); 1402 } 1403 1404 public static IrSetVar set(String name, IrDomain env) { 1405 return set(name, env, EmptyDomain); 1406 } 1407 1408 public static IrSetVar set(String name, IrDomain env, IrDomain ker) { 1409 return set(name, env, ker, boundDomain(ker.size(), env.size())); 1410 } 1411 1412 public static IrSetVar set(String name, IrDomain env, IrDomain ker, IrDomain card) { 1413 return IrUtil.asConstant(new IrSetVar(name, env, ker, card)); 1414 } 1415 1416 public static IrSetVar constant(int[] value) { 1417 return constant(enumDomain(value)); 1418 } 1419 1420 public static IrSetVar constant(TIntCollection value) { 1421 return constant(enumDomain(value)); 1422 } 1423 1424 public static IrSetVar constant(TIntSet value) { 1425 return constant(enumDomain(value)); 1426 } 1427 1428 public static IrSetVar constant(IrDomain value) { 1429 if (value.isEmpty()) { 1430 return EmptySet; 1431 } 1432 return new IrSetConstant(value); 1433 } 1434 1435 public static IrSetExpr singleton(IrIntExpr value) { 1436 Integer constant = IrUtil.getConstant(value); 1437 if (constant != null) { 1438 return constant(new int[]{constant.intValue()}); 1439 } 1440 return new IrSingleton(value, value.getDomain(), EmptyDomain); 1441 } 1442 1443 public static IrSetExpr arrayToSet(IrIntExpr[] array, Integer globalCardinality) { 1444 switch (array.length) { 1445 case 0: 1446 return EmptySet; 1447 case 1: 1448 return singleton(array[0]); 1449 default: 1450 IrDomain env = array[0].getDomain(); 1451 for (int i = 1; i < array.length; i++) { 1452 env = IrUtil.union(env, array[i].getDomain()); 1453 } 1454 TIntSet values = new TIntHashSet(); 1455 for (IrIntExpr i : array) { 1456 Integer constant = IrUtil.getConstant(i); 1457 if (constant != null) { 1458 values.add(constant.intValue()); 1459 } 1460 } 1461 IrDomain ker = enumDomain(values); 1462 int lowCard = Math.max( 1463 globalCardinality == null ? 1 : divRoundUp(array.length, globalCardinality), 1464 ker.size()); 1465 int highCard = Math.min(array.length, env.size()); 1466 IrDomain card = boundDomain(lowCard, highCard); 1467 return IrUtil.asConstant(new IrArrayToSet(array, env, ker, card, globalCardinality)); 1468 } 1469 } 1470 1471 /** 1472 * Relational join. 1473 * 1474 * Union{for all i in take} children[i] 1475 * 1476 * @param take 1477 * @param children 1478 * @param injective 1479 * @return the join expression take.children 1480 */ 1481 public static IrSetExpr joinRelation(IrSetExpr take, IrSetExpr[] children, boolean injective) { 1482 if (take.getEnv().isEmpty()) { 1483 return EmptySet; 1484 } 1485 IrSetExpr[] $children = take.getEnv().getHighBound() + 1 < children.length 1486 ? Arrays.copyOf(children, take.getEnv().getHighBound() + 1) 1487 : children.clone(); 1488 for (int i = 0; i < $children.length; i++) { 1489 if (!take.getEnv().contains(i)) { 1490 $children[i] = EmptySet; 1491 } 1492 } 1493 1494 IrIntExpr[] ints = IrUtil.asInts(children); 1495 if (ints != null) { 1496 return joinFunction(take, ints, injective ? 1 : 0); 1497 } 1498 1499 int[] constant = IrUtil.getConstant(take); 1500 if (constant != null) { 1501 IrSetExpr[] to = new IrSetExpr[constant.length]; 1502 for (int i = 0; i < to.length; i++) { 1503 to[i] = $children[constant[i]]; 1504 } 1505 return union(to, injective); 1506 } 1507 1508 // Compute env 1509 TIntIterator iter = take.getEnv().iterator(); 1510 IrDomain env; 1511 if (iter.hasNext()) { 1512 IrDomain domain = $children[iter.next()].getEnv(); 1513 while (iter.hasNext()) { 1514 domain = IrUtil.union(domain, $children[iter.next()].getEnv()); 1515 } 1516 env = domain; 1517 } else { 1518 env = EmptyDomain; 1519 } 1520 1521 // Compute ker 1522 iter = take.getKer().iterator(); 1523 IrDomain ker; 1524 if (iter.hasNext()) { 1525 IrDomain domain = $children[iter.next()].getKer(); 1526 while (iter.hasNext()) { 1527 domain = IrUtil.union(domain, $children[iter.next()].getKer()); 1528 } 1529 ker = domain; 1530 } else { 1531 ker = EmptyDomain; 1532 } 1533 1534 // Compute card 1535 IrDomain takeEnv = take.getEnv(); 1536 IrDomain takeKer = take.getKer(); 1537 IrDomain takeCard = take.getCard(); 1538 int index = 0; 1539 int[] childrenLowCards = new int[takeEnv.size() - takeKer.size()]; 1540 int[] childrenHighCards = new int[takeEnv.size() - takeKer.size()]; 1541 int cardLow = 0, cardHigh = 0; 1542 1543 iter = takeEnv.iterator(); 1544 while (iter.hasNext()) { 1545 int val = iter.next(); 1546 IrDomain childDomain = $children[val].getCard(); 1547 if (takeKer.contains(val)) { 1548 cardLow = injective 1549 ? cardLow + childDomain.getLowBound() 1550 : Math.max(cardLow, childDomain.getLowBound()); 1551 cardHigh = injective 1552 ? cardHigh + childDomain.getHighBound() 1553 : Math.max(cardHigh, childDomain.getHighBound()); 1554 } else { 1555 childrenLowCards[index] = childDomain.getLowBound(); 1556 childrenHighCards[index] = childDomain.getHighBound(); 1557 index++; 1558 } 1559 } 1560 assert index == childrenLowCards.length; 1561 assert index == childrenHighCards.length; 1562 1563 Arrays.sort(childrenLowCards); 1564 Arrays.sort(childrenHighCards); 1565 1566 for (int i = 0; i < takeCard.getLowBound() - takeKer.size(); i++) { 1567 cardLow = injective 1568 ? cardLow + childrenLowCards[i] 1569 : Math.max(cardLow, childrenLowCards[i]); 1570 } 1571 for (int i = 0; i < takeCard.getHighBound() - takeKer.size(); i++) { 1572 cardHigh = injective 1573 ? cardHigh + childrenHighCards[childrenHighCards.length - 1 - i] 1574 : Math.max(cardHigh, childrenHighCards[childrenHighCards.length - 1 - i]); 1575 } 1576 cardLow = Math.max(cardLow, ker.size()); 1577 cardHigh = Math.min(cardHigh, env.size()); 1578 IrDomain card = boundDomain(cardLow, cardHigh); 1579 1580 return new IrJoinRelation(take, $children, env, ker, card, injective); 1581 } 1582 1583 public static IrSetExpr joinFunction(IrSetExpr take, IrIntExpr[] refs, Integer globalCardinality) { 1584 if (take.getEnv().isEmpty()) { 1585 return EmptySet; 1586 } 1587 IrIntExpr[] $refs = take.getEnv().getHighBound() + 1 < refs.length 1588 ? Arrays.copyOf(refs, take.getEnv().getHighBound() + 1) 1589 : refs.clone(); 1590 for (int i = 0; i < $refs.length; i++) { 1591 if (!take.getEnv().contains(i)) { 1592 $refs[i] = Zero; 1593 } 1594 } 1595 1596 int[] constant = IrUtil.getConstant(take); 1597 if (constant != null) { 1598 IrIntExpr[] to = new IrIntExpr[constant.length]; 1599 for (int i = 0; i < to.length; i++) { 1600 to[i] = $refs[constant[i]]; 1601 } 1602 return arrayToSet(to, globalCardinality); 1603 } 1604 1605 // Compute env 1606 TIntIterator iter = take.getEnv().iterator(); 1607 IrDomain env; 1608 if (iter.hasNext()) { 1609 IrDomain domain = $refs[iter.next()].getDomain(); 1610 while (iter.hasNext()) { 1611 domain = IrUtil.union(domain, $refs[iter.next()].getDomain()); 1612 } 1613 env = domain; 1614 } else { 1615 env = EmptyDomain; 1616 } 1617 1618 // Compute ker 1619 iter = take.getKer().iterator(); 1620 TIntHashSet values = new TIntHashSet(0); 1621 while (iter.hasNext()) { 1622 Integer constantRef = IrUtil.getConstant($refs[iter.next()]); 1623 if (constantRef != null) { 1624 values.add(constantRef.intValue()); 1625 } 1626 } 1627 IrDomain ker = values.isEmpty() ? EmptyDomain : new IrEnumDomain(values.toArray()); 1628 1629 // Compute card 1630 IrDomain takeCard = take.getCard(); 1631 int lowTakeCard = takeCard.getLowBound(); 1632 int highTakeCard = takeCard.getHighBound(); 1633 IrDomain card; 1634 if (globalCardinality == null) { 1635 card = lowTakeCard == 0 1636 ? boundDomain(Math.max(0, ker.size()), Math.min(highTakeCard, env.size())) 1637 : boundDomain(Math.max(1, ker.size()), Math.min(highTakeCard, env.size())); 1638 } else { 1639 card = boundDomain( 1640 divRoundUp(Math.max(lowTakeCard, ker.size()), globalCardinality), 1641 Math.min(highTakeCard, env.size())); 1642 } 1643 1644 return new IrJoinFunction(take, $refs, env, ker, card, globalCardinality); 1645 } 1646 1647 private static int divRoundUp(int a, int b) { 1648 assert a >= 0; 1649 assert b > 0; 1650 1651 return (a + b - 1) / b; 1652 } 1653 1654 public static IrSetExpr difference(IrSetExpr minuend, IrSetExpr subtrahend) { 1655 IrDomain env = IrUtil.difference(minuend.getEnv(), subtrahend.getKer()); 1656 IrDomain ker = IrUtil.difference(minuend.getKer(), subtrahend.getEnv()); 1657 int low = Math.max(0, minuend.getCard().getLowBound() - subtrahend.getCard().getHighBound()); 1658 int high = minuend.getCard().getHighBound(); 1659 IrDomain card = boundDomain(Math.max(low, ker.size()), Math.min(high, env.size())); 1660 return new IrSetDifference(minuend, subtrahend, env, ker, card); 1661 } 1662 1663 public static IrSetExpr intersection(IrSetExpr... operands) { 1664 List<IrSetExpr> flatten = new ArrayList<>(operands.length); 1665 for (IrSetExpr operand : operands) { 1666 if (operand instanceof IrSetIntersection) { 1667 // Invariant: No nested IrSetIntersection 1668 flatten.addAll(Arrays.asList(((IrSetIntersection) operand).getOperands())); 1669 } else { 1670 flatten.add(operand); 1671 } 1672 } 1673 TIntSet constants = null; 1674 List<IrSetExpr> filter = new ArrayList<>(); 1675 for (IrSetExpr operand : flatten) { 1676 int[] constant = IrUtil.getConstant(operand); 1677 if (constant == null) { 1678 filter.add(operand); 1679 } else { 1680 if (constants == null) { 1681 constants = new TIntHashSet(constant); 1682 } else { 1683 constants.retainAll(constant); 1684 } 1685 } 1686 } 1687 if (constants != null) { 1688 filter.add(constant(constants)); 1689 } 1690 IrSetExpr[] ops = filter.toArray(new IrSetExpr[filter.size()]); 1691 switch (ops.length) { 1692 case 0: 1693 return EmptySet; 1694 case 1: 1695 return ops[0]; 1696 default: 1697 IrDomain env = IrUtil.intersectionEnvs(ops); 1698 IrDomain ker = IrUtil.intersectionKers(ops); 1699 int low = 0; 1700 int high = ops[0].getCard().getHighBound(); 1701 for (int i = 1; i < ops.length; i++) { 1702 high = Math.max(high, ops[0].getCard().getHighBound()); 1703 } 1704 IrDomain card = boundDomain( 1705 Math.max(low, ker.size()), 1706 Math.min(high, env.size())); 1707 return new IrSetIntersection(ops, env, ker, card); 1708 } 1709 } 1710 1711 public static IrSetExpr union(IrSetExpr... operands) { 1712 return union(operands, false); 1713 } 1714 1715 public static IrSetExpr union(IrSetExpr[] operands, boolean disjoint) { 1716 List<IrSetExpr> flatten = new ArrayList<>(operands.length); 1717 for (IrSetExpr operand : operands) { 1718 if (operand instanceof IrSetUnion) { 1719 // Invariant: No nested IrSetUnion 1720 flatten.addAll(Arrays.asList(((IrSetUnion) operand).getOperands())); 1721 } else { 1722 flatten.add(operand); 1723 } 1724 } 1725 TIntSet constants = new TIntHashSet(); 1726 List<IrSetExpr> filter = new ArrayList<>(); 1727 for (IrSetExpr operand : flatten) { 1728 int[] constant = IrUtil.getConstant(operand); 1729 if (constant == null) { 1730 filter.add(operand); 1731 } else { 1732 constants.addAll(constant); 1733 } 1734 } 1735 if (!constants.isEmpty()) { 1736 filter.add(constant(constants)); 1737 } 1738 IrSetExpr[] ops = filter.toArray(new IrSetExpr[filter.size()]); 1739 switch (ops.length) { 1740 case 0: 1741 return EmptySet; 1742 case 1: 1743 return ops[0]; 1744 default: 1745 IrDomain env = IrUtil.unionEnvs(ops); 1746 IrDomain ker = IrUtil.unionKers(ops); 1747 IrDomain operandCard = ops[0].getCard(); 1748 int low = operandCard.getLowBound(); 1749 int high = operandCard.getHighBound(); 1750 for (int i = 1; i < ops.length; i++) { 1751 operandCard = ops[i].getCard(); 1752 low = disjoint 1753 ? low + operandCard.getLowBound() 1754 : Math.max(low, operandCard.getLowBound()); 1755 high += operandCard.getHighBound(); 1756 } 1757 IrDomain card = boundDomain( 1758 Math.max(low, ker.size()), 1759 Math.min(high, env.size())); 1760 return IrUtil.asConstant(new IrSetUnion(ops, env, ker, card, disjoint)); 1761 } 1762 } 1763 1764 public static IrSetExpr offset(IrSetExpr set, int offset) { 1765 if (offset == 0) { 1766 return set; 1767 } 1768 if (set instanceof IrOffset) { 1769 IrOffset nested = (IrOffset) set; 1770 return offset(nested.getSet(), offset + nested.getOffset()); 1771 } 1772 IrDomain env = IrUtil.offset(set.getEnv(), offset); 1773 IrDomain ker = IrUtil.offset(set.getKer(), offset); 1774 IrDomain card = set.getCard(); 1775 return IrUtil.asConstant(new IrOffset(set, offset, env, ker, card)); 1776 } 1777 1778 public static IrSetExpr mask(IrSetExpr set, int from, int to) { 1779 if (from > to) { 1780 throw new IllegalArgumentException(); 1781 } 1782 1783 if (from <= set.getEnv().getLowBound() && to > set.getEnv().getHighBound()) { 1784 return offset(set, -from); 1785 } 1786 IrDomain env = IrUtil.mask(set.getEnv(), from, to); 1787 IrDomain ker = IrUtil.mask(set.getKer(), from, to); 1788 IrDomain card = boundDomain(ker.size(), Math.min(env.size(), set.getCard().getHighBound())); 1789 return IrUtil.asConstant(new IrMask(set, from, to, env, ker, card)); 1790 } 1791 1792 public static IrSetExpr ternary(IrBoolExpr antecedent, IrSetExpr consequent, IrSetExpr alternative) { 1793 if (IrUtil.isTrue(antecedent)) { 1794 return consequent; 1795 } 1796 if (IrUtil.isFalse(antecedent)) { 1797 return alternative; 1798 } 1799 if (consequent.equals(alternative)) { 1800 return consequent; 1801 } 1802 IrDomain env = IrUtil.union(consequent.getEnv(), alternative.getEnv()); 1803 IrDomain ker = IrUtil.intersection(consequent.getKer(), alternative.getKer()); 1804 IrDomain card = IrUtil.union(consequent.getCard(), alternative.getCard()); 1805 return new IrSetTernary(antecedent, consequent, alternative, env, ker, card); 1806 } 1807}