1// Copyright 2008 the V8 project authors. All rights reserved. 2// Copyright 1996 John Maloney and Mario Wolczko. 3 4// This program is free software; you can redistribute it and/or modify 5// it under the terms of the GNU General Public License as published by 6// the Free Software Foundation; either version 2 of the License, or 7// (at your option) any later version. 8// 9// This program is distributed in the hope that it will be useful, 10// but WITHOUT ANY WARRANTY; without even the implied warranty of 11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12// GNU General Public License for more details. 13// 14// You should have received a copy of the GNU General Public License 15// along with this program; if not, write to the Free Software 16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 18 19// This implementation of the DeltaBlue benchmark is derived 20// from the Smalltalk implementation by John Maloney and Mario 21// Wolczko. Some parts have been translated directly, whereas 22// others have been modified more aggresively to make it feel 23// more like a JavaScript program. 24 25 26var DeltaBlue = new BenchmarkSuite('DeltaBlue', 66118, [ 27 new Benchmark('DeltaBlue', deltaBlue) 28]); 29 30 31/** 32 * A JavaScript implementation of the DeltaBlue constraint-solving 33 * algorithm, as described in: 34 * 35 * "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver" 36 * Bjorn N. Freeman-Benson and John Maloney 37 * January 1990 Communications of the ACM, 38 * also available as University of Washington TR 89-08-06. 39 * 40 * Beware: this benchmark is written in a grotesque style where 41 * the constraint model is built by side-effects from constructors. 42 * I've kept it this way to avoid deviating too much from the original 43 * implementation. 44 */ 45 46 47/* --- O b j e c t M o d e l --- */ 48 49Object.defineProperty(Object.prototype, "inheritsFrom", { 50 51 value: function (shuper) { 52 function Inheriter() { } 53 Inheriter.prototype = shuper.prototype; 54 this.prototype = new Inheriter(); 55 this.superConstructor = shuper; 56 } 57}); 58 59function OrderedCollection() { 60 this.elms = new Array(); 61} 62 63OrderedCollection.prototype.add = function (elm) { 64 this.elms.push(elm); 65} 66 67OrderedCollection.prototype.at = function (index) { 68 return this.elms[index]; 69} 70 71OrderedCollection.prototype.size = function () { 72 return this.elms.length; 73} 74 75OrderedCollection.prototype.removeFirst = function () { 76 return this.elms.pop(); 77} 78 79OrderedCollection.prototype.remove = function (elm) { 80 var index = 0, skipped = 0; 81 for (var i = 0; i < this.elms.length; i++) { 82 var value = this.elms[i]; 83 if (value != elm) { 84 this.elms[index] = value; 85 index++; 86 } else { 87 skipped++; 88 } 89 } 90 for (var i = 0; i < skipped; i++) 91 this.elms.pop(); 92} 93 94/* --- * 95 * S t r e n g t h 96 * --- */ 97 98/** 99 * Strengths are used to measure the relative importance of constraints. 100 * New strengths may be inserted in the strength hierarchy without 101 * disrupting current constraints. Strengths cannot be created outside 102 * this class, so pointer comparison can be used for value comparison. 103 */ 104function Strength(strengthValue, name) { 105 this.strengthValue = strengthValue; 106 this.name = name; 107} 108 109Strength.stronger = function (s1, s2) { 110 return s1.strengthValue < s2.strengthValue; 111} 112 113Strength.weaker = function (s1, s2) { 114 return s1.strengthValue > s2.strengthValue; 115} 116 117Strength.weakestOf = function (s1, s2) { 118 return this.weaker(s1, s2) ? s1 : s2; 119} 120 121Strength.strongest = function (s1, s2) { 122 return this.stronger(s1, s2) ? s1 : s2; 123} 124 125Strength.prototype.nextWeaker = function () { 126 switch (this.strengthValue) { 127 case 0: return Strength.WEAKEST; 128 case 1: return Strength.WEAK_DEFAULT; 129 case 2: return Strength.NORMAL; 130 case 3: return Strength.STRONG_DEFAULT; 131 case 4: return Strength.PREFERRED; 132 case 5: return Strength.REQUIRED; 133 } 134} 135 136// Strength constants. 137Strength.REQUIRED = new Strength(0, "required"); 138Strength.STONG_PREFERRED = new Strength(1, "strongPreferred"); 139Strength.PREFERRED = new Strength(2, "preferred"); 140Strength.STRONG_DEFAULT = new Strength(3, "strongDefault"); 141Strength.NORMAL = new Strength(4, "normal"); 142Strength.WEAK_DEFAULT = new Strength(5, "weakDefault"); 143Strength.WEAKEST = new Strength(6, "weakest"); 144 145/* --- * 146 * C o n s t r a i n t 147 * --- */ 148 149/** 150 * An abstract class representing a system-maintainable relationship 151 * (or "constraint") between a set of variables. A constraint supplies 152 * a strength instance variable; concrete subclasses provide a means 153 * of storing the constrained variables and other information required 154 * to represent a constraint. 155 */ 156function Constraint(strength) { 157 this.strength = strength; 158} 159 160/** 161 * Activate this constraint and attempt to satisfy it. 162 */ 163Constraint.prototype.addConstraint = function () { 164 this.addToGraph(); 165 planner.incrementalAdd(this); 166} 167 168/** 169 * Attempt to find a way to enforce this constraint. If successful, 170 * record the solution, perhaps modifying the current dataflow 171 * graph. Answer the constraint that this constraint overrides, if 172 * there is one, or nil, if there isn't. 173 * Assume: I am not already satisfied. 174 */ 175Constraint.prototype.satisfy = function (mark) { 176 this.chooseMethod(mark); 177 if (!this.isSatisfied()) { 178 if (this.strength == Strength.REQUIRED) 179 alert("Could not satisfy a required constraint!"); 180 return null; 181 } 182 this.markInputs(mark); 183 var out = this.output(); 184 var overridden = out.determinedBy; 185 if (overridden != null) overridden.markUnsatisfied(); 186 out.determinedBy = this; 187 if (!planner.addPropagate(this, mark)) 188 alert("Cycle encountered"); 189 out.mark = mark; 190 return overridden; 191} 192 193Constraint.prototype.destroyConstraint = function () { 194 if (this.isSatisfied()) planner.incrementalRemove(this); 195 else this.removeFromGraph(); 196} 197 198/** 199 * Normal constraints are not input constraints. An input constraint 200 * is one that depends on external state, such as the mouse, the 201 * keybord, a clock, or some arbitraty piece of imperative code. 202 */ 203Constraint.prototype.isInput = function () { 204 return false; 205} 206 207/* --- * 208 * U n a r y C o n s t r a i n t 209 * --- */ 210 211/** 212 * Abstract superclass for constraints having a single possible output 213 * variable. 214 */ 215function UnaryConstraint(v, strength) { 216 UnaryConstraint.superConstructor.call(this, strength); 217 this.myOutput = v; 218 this.satisfied = false; 219 this.addConstraint(); 220} 221 222UnaryConstraint.inheritsFrom(Constraint); 223 224/** 225 * Adds this constraint to the constraint graph 226 */ 227UnaryConstraint.prototype.addToGraph = function () { 228 this.myOutput.addConstraint(this); 229 this.satisfied = false; 230} 231 232/** 233 * Decides if this constraint can be satisfied and records that 234 * decision. 235 */ 236UnaryConstraint.prototype.chooseMethod = function (mark) { 237 this.satisfied = (this.myOutput.mark != mark) 238 && Strength.stronger(this.strength, this.myOutput.walkStrength); 239} 240 241/** 242 * Returns true if this constraint is satisfied in the current solution. 243 */ 244UnaryConstraint.prototype.isSatisfied = function () { 245 return this.satisfied; 246} 247 248UnaryConstraint.prototype.markInputs = function (mark) { 249 // has no inputs 250} 251 252/** 253 * Returns the current output variable. 254 */ 255UnaryConstraint.prototype.output = function () { 256 return this.myOutput; 257} 258 259/** 260 * Calculate the walkabout strength, the stay flag, and, if it is 261 * 'stay', the value for the current output of this constraint. Assume 262 * this constraint is satisfied. 263 */ 264UnaryConstraint.prototype.recalculate = function () { 265 this.myOutput.walkStrength = this.strength; 266 this.myOutput.stay = !this.isInput(); 267 if (this.myOutput.stay) this.execute(); // Stay optimization 268} 269 270/** 271 * Records that this constraint is unsatisfied 272 */ 273UnaryConstraint.prototype.markUnsatisfied = function () { 274 this.satisfied = false; 275} 276 277UnaryConstraint.prototype.inputsKnown = function () { 278 return true; 279} 280 281UnaryConstraint.prototype.removeFromGraph = function () { 282 if (this.myOutput != null) this.myOutput.removeConstraint(this); 283 this.satisfied = false; 284} 285 286/* --- * 287 * S t a y C o n s t r a i n t 288 * --- */ 289 290/** 291 * Variables that should, with some level of preference, stay the same. 292 * Planners may exploit the fact that instances, if satisfied, will not 293 * change their output during plan execution. This is called "stay 294 * optimization". 295 */ 296function StayConstraint(v, str) { 297 StayConstraint.superConstructor.call(this, v, str); 298} 299 300StayConstraint.inheritsFrom(UnaryConstraint); 301 302StayConstraint.prototype.execute = function () { 303 // Stay constraints do nothing 304} 305 306/* --- * 307 * E d i t C o n s t r a i n t 308 * --- */ 309 310/** 311 * A unary input constraint used to mark a variable that the client 312 * wishes to change. 313 */ 314function EditConstraint(v, str) { 315 EditConstraint.superConstructor.call(this, v, str); 316} 317 318EditConstraint.inheritsFrom(UnaryConstraint); 319 320/** 321 * Edits indicate that a variable is to be changed by imperative code. 322 */ 323EditConstraint.prototype.isInput = function () { 324 return true; 325} 326 327EditConstraint.prototype.execute = function () { 328 // Edit constraints do nothing 329} 330 331/* --- * 332 * B i n a r y C o n s t r a i n t 333 * --- */ 334 335var Direction = new Object(); 336Direction.NONE = 0; 337Direction.FORWARD = 1; 338Direction.BACKWARD = -1; 339 340/** 341 * Abstract superclass for constraints having two possible output 342 * variables. 343 */ 344function BinaryConstraint(var1, var2, strength) { 345 BinaryConstraint.superConstructor.call(this, strength); 346 this.v1 = var1; 347 this.v2 = var2; 348 this.direction = Direction.NONE; 349 this.addConstraint(); 350} 351 352BinaryConstraint.inheritsFrom(Constraint); 353 354/** 355 * Decides if this constraint can be satisfied and which way it 356 * should flow based on the relative strength of the variables related, 357 * and record that decision. 358 */ 359BinaryConstraint.prototype.chooseMethod = function (mark) { 360 if (this.v1.mark == mark) { 361 this.direction = (this.v2.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength)) 362 ? Direction.FORWARD 363 : Direction.NONE; 364 } 365 if (this.v2.mark == mark) { 366 this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength)) 367 ? Direction.BACKWARD 368 : Direction.NONE; 369 } 370 if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) { 371 this.direction = Strength.stronger(this.strength, this.v1.walkStrength) 372 ? Direction.BACKWARD 373 : Direction.NONE; 374 } else { 375 this.direction = Strength.stronger(this.strength, this.v2.walkStrength) 376 ? Direction.FORWARD 377 : Direction.BACKWARD 378 } 379} 380 381/** 382 * Add this constraint to the constraint graph 383 */ 384BinaryConstraint.prototype.addToGraph = function () { 385 this.v1.addConstraint(this); 386 this.v2.addConstraint(this); 387 this.direction = Direction.NONE; 388} 389 390/** 391 * Answer true if this constraint is satisfied in the current solution. 392 */ 393BinaryConstraint.prototype.isSatisfied = function () { 394 return this.direction != Direction.NONE; 395} 396 397/** 398 * Mark the input variable with the given mark. 399 */ 400BinaryConstraint.prototype.markInputs = function (mark) { 401 this.input().mark = mark; 402} 403 404/** 405 * Returns the current input variable 406 */ 407BinaryConstraint.prototype.input = function () { 408 return (this.direction == Direction.FORWARD) ? this.v1 : this.v2; 409} 410 411/** 412 * Returns the current output variable 413 */ 414BinaryConstraint.prototype.output = function () { 415 return (this.direction == Direction.FORWARD) ? this.v2 : this.v1; 416} 417 418/** 419 * Calculate the walkabout strength, the stay flag, and, if it is 420 * 'stay', the value for the current output of this 421 * constraint. Assume this constraint is satisfied. 422 */ 423BinaryConstraint.prototype.recalculate = function () { 424 var ihn = this.input(), out = this.output(); 425 out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength); 426 out.stay = ihn.stay; 427 if (out.stay) this.execute(); 428} 429 430/** 431 * Record the fact that this constraint is unsatisfied. 432 */ 433BinaryConstraint.prototype.markUnsatisfied = function () { 434 this.direction = Direction.NONE; 435} 436 437BinaryConstraint.prototype.inputsKnown = function (mark) { 438 var i = this.input(); 439 return i.mark == mark || i.stay || i.determinedBy == null; 440} 441 442BinaryConstraint.prototype.removeFromGraph = function () { 443 if (this.v1 != null) this.v1.removeConstraint(this); 444 if (this.v2 != null) this.v2.removeConstraint(this); 445 this.direction = Direction.NONE; 446} 447 448/* --- * 449 * S c a l e C o n s t r a i n t 450 * --- */ 451 452/** 453 * Relates two variables by the linear scaling relationship: "v2 = 454 * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain 455 * this relationship but the scale factor and offset are considered 456 * read-only. 457 */ 458function ScaleConstraint(src, scale, offset, dest, strength) { 459 this.direction = Direction.NONE; 460 this.scale = scale; 461 this.offset = offset; 462 ScaleConstraint.superConstructor.call(this, src, dest, strength); 463} 464 465ScaleConstraint.inheritsFrom(BinaryConstraint); 466 467/** 468 * Adds this constraint to the constraint graph. 469 */ 470ScaleConstraint.prototype.addToGraph = function () { 471 ScaleConstraint.superConstructor.prototype.addToGraph.call(this); 472 this.scale.addConstraint(this); 473 this.offset.addConstraint(this); 474} 475 476ScaleConstraint.prototype.removeFromGraph = function () { 477 ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this); 478 if (this.scale != null) this.scale.removeConstraint(this); 479 if (this.offset != null) this.offset.removeConstraint(this); 480} 481 482ScaleConstraint.prototype.markInputs = function (mark) { 483 ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark); 484 this.scale.mark = this.offset.mark = mark; 485} 486 487/** 488 * Enforce this constraint. Assume that it is satisfied. 489 */ 490ScaleConstraint.prototype.execute = function () { 491 if (this.direction == Direction.FORWARD) { 492 this.v2.value = this.v1.value * this.scale.value + this.offset.value; 493 } else { 494 this.v1.value = (this.v2.value - this.offset.value) / this.scale.value; 495 } 496} 497 498/** 499 * Calculate the walkabout strength, the stay flag, and, if it is 500 * 'stay', the value for the current output of this constraint. Assume 501 * this constraint is satisfied. 502 */ 503ScaleConstraint.prototype.recalculate = function () { 504 var ihn = this.input(), out = this.output(); 505 out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength); 506 out.stay = ihn.stay && this.scale.stay && this.offset.stay; 507 if (out.stay) this.execute(); 508} 509 510/* --- * 511 * E q u a l i t y C o n s t r a i n t 512 * --- */ 513 514/** 515 * Constrains two variables to have the same value. 516 */ 517function EqualityConstraint(var1, var2, strength) { 518 EqualityConstraint.superConstructor.call(this, var1, var2, strength); 519} 520 521EqualityConstraint.inheritsFrom(BinaryConstraint); 522 523/** 524 * Enforce this constraint. Assume that it is satisfied. 525 */ 526EqualityConstraint.prototype.execute = function () { 527 this.output().value = this.input().value; 528} 529 530/* --- * 531 * V a r i a b l e 532 * --- */ 533 534/** 535 * A constrained variable. In addition to its value, it maintain the 536 * structure of the constraint graph, the current dataflow graph, and 537 * various parameters of interest to the DeltaBlue incremental 538 * constraint solver. 539 **/ 540function Variable(name, initialValue) { 541 this.value = initialValue || 0; 542 this.constraints = new OrderedCollection(); 543 this.determinedBy = null; 544 this.mark = 0; 545 this.walkStrength = Strength.WEAKEST; 546 this.stay = true; 547 this.name = name; 548} 549 550/** 551 * Add the given constraint to the set of all constraints that refer 552 * this variable. 553 */ 554Variable.prototype.addConstraint = function (c) { 555 this.constraints.add(c); 556} 557 558/** 559 * Removes all traces of c from this variable. 560 */ 561Variable.prototype.removeConstraint = function (c) { 562 this.constraints.remove(c); 563 if (this.determinedBy == c) this.determinedBy = null; 564} 565 566/* --- * 567 * P l a n n e r 568 * --- */ 569 570/** 571 * The DeltaBlue planner 572 */ 573function Planner() { 574 this.currentMark = 0; 575} 576 577/** 578 * Attempt to satisfy the given constraint and, if successful, 579 * incrementally update the dataflow graph. Details: If satifying 580 * the constraint is successful, it may override a weaker constraint 581 * on its output. The algorithm attempts to resatisfy that 582 * constraint using some other method. This process is repeated 583 * until either a) it reaches a variable that was not previously 584 * determined by any constraint or b) it reaches a constraint that 585 * is too weak to be satisfied using any of its methods. The 586 * variables of constraints that have been processed are marked with 587 * a unique mark value so that we know where we've been. This allows 588 * the algorithm to avoid getting into an infinite loop even if the 589 * constraint graph has an inadvertent cycle. 590 */ 591Planner.prototype.incrementalAdd = function (c) { 592 var mark = this.newMark(); 593 var overridden = c.satisfy(mark); 594 while (overridden != null) 595 overridden = overridden.satisfy(mark); 596} 597 598/** 599 * Entry point for retracting a constraint. Remove the given 600 * constraint and incrementally update the dataflow graph. 601 * Details: Retracting the given constraint may allow some currently 602 * unsatisfiable downstream constraint to be satisfied. We therefore collect 603 * a list of unsatisfied downstream constraints and attempt to 604 * satisfy each one in turn. This list is traversed by constraint 605 * strength, strongest first, as a heuristic for avoiding 606 * unnecessarily adding and then overriding weak constraints. 607 * Assume: c is satisfied. 608 */ 609Planner.prototype.incrementalRemove = function (c) { 610 var out = c.output(); 611 c.markUnsatisfied(); 612 c.removeFromGraph(); 613 var unsatisfied = this.removePropagateFrom(out); 614 var strength = Strength.REQUIRED; 615 do { 616 for (var i = 0; i < unsatisfied.size(); i++) { 617 var u = unsatisfied.at(i); 618 if (u.strength == strength) 619 this.incrementalAdd(u); 620 } 621 strength = strength.nextWeaker(); 622 } while (strength != Strength.WEAKEST); 623} 624 625/** 626 * Select a previously unused mark value. 627 */ 628Planner.prototype.newMark = function () { 629 return ++this.currentMark; 630} 631 632/** 633 * Extract a plan for resatisfaction starting from the given source 634 * constraints, usually a set of input constraints. This method 635 * assumes that stay optimization is desired; the plan will contain 636 * only constraints whose output variables are not stay. Constraints 637 * that do no computation, such as stay and edit constraints, are 638 * not included in the plan. 639 * Details: The outputs of a constraint are marked when it is added 640 * to the plan under construction. A constraint may be appended to 641 * the plan when all its input variables are known. A variable is 642 * known if either a) the variable is marked (indicating that has 643 * been computed by a constraint appearing earlier in the plan), b) 644 * the variable is 'stay' (i.e. it is a constant at plan execution 645 * time), or c) the variable is not determined by any 646 * constraint. The last provision is for past states of history 647 * variables, which are not stay but which are also not computed by 648 * any constraint. 649 * Assume: sources are all satisfied. 650 */ 651Planner.prototype.makePlan = function (sources) { 652 var mark = this.newMark(); 653 var plan = new Plan(); 654 var todo = sources; 655 while (todo.size() > 0) { 656 var c = todo.removeFirst(); 657 if (c.output().mark != mark && c.inputsKnown(mark)) { 658 plan.addConstraint(c); 659 c.output().mark = mark; 660 this.addConstraintsConsumingTo(c.output(), todo); 661 } 662 } 663 return plan; 664} 665 666/** 667 * Extract a plan for resatisfying starting from the output of the 668 * given constraints, usually a set of input constraints. 669 */ 670Planner.prototype.extractPlanFromConstraints = function (constraints) { 671 var sources = new OrderedCollection(); 672 for (var i = 0; i < constraints.size(); i++) { 673 var c = constraints.at(i); 674 if (c.isInput() && c.isSatisfied()) 675 // not in plan already and eligible for inclusion 676 sources.add(c); 677 } 678 return this.makePlan(sources); 679} 680 681/** 682 * Recompute the walkabout strengths and stay flags of all variables 683 * downstream of the given constraint and recompute the actual 684 * values of all variables whose stay flag is true. If a cycle is 685 * detected, remove the given constraint and answer 686 * false. Otherwise, answer true. 687 * Details: Cycles are detected when a marked variable is 688 * encountered downstream of the given constraint. The sender is 689 * assumed to have marked the inputs of the given constraint with 690 * the given mark. Thus, encountering a marked node downstream of 691 * the output constraint means that there is a path from the 692 * constraint's output to one of its inputs. 693 */ 694Planner.prototype.addPropagate = function (c, mark) { 695 var todo = new OrderedCollection(); 696 todo.add(c); 697 while (todo.size() > 0) { 698 var d = todo.removeFirst(); 699 if (d.output().mark == mark) { 700 this.incrementalRemove(c); 701 return false; 702 } 703 d.recalculate(); 704 this.addConstraintsConsumingTo(d.output(), todo); 705 } 706 return true; 707} 708 709 710/** 711 * Update the walkabout strengths and stay flags of all variables 712 * downstream of the given constraint. Answer a collection of 713 * unsatisfied constraints sorted in order of decreasing strength. 714 */ 715Planner.prototype.removePropagateFrom = function (out) { 716 out.determinedBy = null; 717 out.walkStrength = Strength.WEAKEST; 718 out.stay = true; 719 var unsatisfied = new OrderedCollection(); 720 var todo = new OrderedCollection(); 721 todo.add(out); 722 while (todo.size() > 0) { 723 var v = todo.removeFirst(); 724 for (var i = 0; i < v.constraints.size(); i++) { 725 var c = v.constraints.at(i); 726 if (!c.isSatisfied()) 727 unsatisfied.add(c); 728 } 729 var determining = v.determinedBy; 730 for (var i = 0; i < v.constraints.size(); i++) { 731 var next = v.constraints.at(i); 732 if (next != determining && next.isSatisfied()) { 733 next.recalculate(); 734 todo.add(next.output()); 735 } 736 } 737 } 738 return unsatisfied; 739} 740 741Planner.prototype.addConstraintsConsumingTo = function (v, coll) { 742 var determining = v.determinedBy; 743 var cc = v.constraints; 744 for (var i = 0; i < cc.size(); i++) { 745 var c = cc.at(i); 746 if (c != determining && c.isSatisfied()) 747 coll.add(c); 748 } 749} 750 751/* --- * 752 * P l a n 753 * --- */ 754 755/** 756 * A Plan is an ordered list of constraints to be executed in sequence 757 * to resatisfy all currently satisfiable constraints in the face of 758 * one or more changing inputs. 759 */ 760function Plan() { 761 this.v = new OrderedCollection(); 762} 763 764Plan.prototype.addConstraint = function (c) { 765 this.v.add(c); 766} 767 768Plan.prototype.size = function () { 769 return this.v.size(); 770} 771 772Plan.prototype.constraintAt = function (index) { 773 return this.v.at(index); 774} 775 776Plan.prototype.execute = function () { 777 for (var i = 0; i < this.size(); i++) { 778 var c = this.constraintAt(i); 779 c.execute(); 780 } 781} 782 783/* --- * 784 * M a i n 785 * --- */ 786 787/** 788 * This is the standard DeltaBlue benchmark. A long chain of equality 789 * constraints is constructed with a stay constraint on one end. An 790 * edit constraint is then added to the opposite end and the time is 791 * measured for adding and removing this constraint, and extracting 792 * and executing a constraint satisfaction plan. There are two cases. 793 * In case 1, the added constraint is stronger than the stay 794 * constraint and values must propagate down the entire length of the 795 * chain. In case 2, the added constraint is weaker than the stay 796 * constraint so it cannot be accomodated. The cost in this case is, 797 * of course, very low. Typical situations lie somewhere between these 798 * two extremes. 799 */ 800function chainTest(n) { 801 planner = new Planner(); 802 var prev = null, first = null, last = null; 803 804 // Build chain of n equality constraints 805 for (var i = 0; i <= n; i++) { 806 var name = "v" + i; 807 var v = new Variable(name); 808 if (prev != null) 809 new EqualityConstraint(prev, v, Strength.REQUIRED); 810 if (i == 0) first = v; 811 if (i == n) last = v; 812 prev = v; 813 } 814 815 new StayConstraint(last, Strength.STRONG_DEFAULT); 816 var edit = new EditConstraint(first, Strength.PREFERRED); 817 var edits = new OrderedCollection(); 818 edits.add(edit); 819 var plan = planner.extractPlanFromConstraints(edits); 820 for (var i = 0; i < 100; i++) { 821 first.value = i; 822 plan.execute(); 823 if (last.value != i) 824 alert("Chain test failed."); 825 } 826} 827 828/** 829 * This test constructs a two sets of variables related to each 830 * other by a simple linear transformation (scale and offset). The 831 * time is measured to change a variable on either side of the 832 * mapping and to change the scale and offset factors. 833 */ 834function projectionTest(n) { 835 planner = new Planner(); 836 var scale = new Variable("scale", 10); 837 var offset = new Variable("offset", 1000); 838 var src = null, dst = null; 839 840 var dests = new OrderedCollection(); 841 for (var i = 0; i < n; i++) { 842 src = new Variable("src" + i, i); 843 dst = new Variable("dst" + i, i); 844 dests.add(dst); 845 new StayConstraint(src, Strength.NORMAL); 846 new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED); 847 } 848 849 change(src, 17); 850 if (dst.value != 1170) alert("Projection 1 failed"); 851 change(dst, 1050); 852 if (src.value != 5) alert("Projection 2 failed"); 853 change(scale, 5); 854 for (var i = 0; i < n - 1; i++) { 855 if (dests.at(i).value != i * 5 + 1000) 856 alert("Projection 3 failed"); 857 } 858 change(offset, 2000); 859 for (var i = 0; i < n - 1; i++) { 860 if (dests.at(i).value != i * 5 + 2000) 861 alert("Projection 4 failed"); 862 } 863} 864 865function change(v, newValue) { 866 var edit = new EditConstraint(v, Strength.PREFERRED); 867 var edits = new OrderedCollection(); 868 edits.add(edit); 869 var plan = planner.extractPlanFromConstraints(edits); 870 for (var i = 0; i < 10; i++) { 871 v.value = newValue; 872 plan.execute(); 873 } 874 edit.destroyConstraint(); 875} 876 877// Global variable holding the current planner. 878var planner = null; 879 880function deltaBlue() { 881 chainTest(100); 882 projectionTest(100); 883} 884