1 // Copyright 2008 the V8 project authors. All rights reserved.
2 // Copyright 1996 John Maloney and Mario Wolczko.
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.
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.
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
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.
26 * A JavaScript implementation of the DeltaBlue constraint-solving
27 * algorithm, as described in:
29 * "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
30 * Bjorn N. Freeman-Benson and John Maloney
31 * January 1990 Communications of the ACM,
32 * also available as University of Washington TR 89-08-06.
34 * Beware: this benchmark is written in a grotesque style where
35 * the constraint model is built by side-effects from constructors.
36 * I've kept it this way to avoid deviating too much from the original
40 // This thing is an evil hack that we use throughout this benchmark so that we can use this benchmark to
41 // stress our varargs implementation. We also have tests for specific features of the varargs code, but
42 // having a somewhat large-ish benchmark that uses varargs a lot (even if it's in a silly way) is great
43 // for shaking out bugs.
46 for (var i
= 0; i
< arguments
.length
; ++i
)
47 array
.push(arguments
[i
]);
52 /* --- O b j e c t M o d e l --- */
54 Object
.prototype.inheritsFrom = function (shuper
) {
55 function Inheriter() { }
56 Inheriter
.prototype = shuper
.prototype;
57 this.prototype = new Inheriter(...args());
58 this.superConstructor
= shuper
;
61 function OrderedCollection() {
62 this.elms
= new Array(...args());
65 OrderedCollection
.prototype.add = function (elm
) {
66 this.elms
.push(...args(elm
));
69 OrderedCollection
.prototype.at = function (index
) {
70 return this.elms
[index
];
73 OrderedCollection
.prototype.size = function () {
74 return this.elms
.length
;
77 OrderedCollection
.prototype.removeFirst = function () {
78 return this.elms
.pop(...args());
81 OrderedCollection
.prototype.remove = function (elm
) {
82 var index
= 0, skipped
= 0;
83 for (var i
= 0; i
< this.elms
.length
; i
++) {
84 var value
= this.elms
[i
];
86 this.elms
[index
] = value
;
92 for (var i
= 0; i
< skipped
; i
++)
93 this.elms
.pop(...args());
101 * Strengths are used to measure the relative importance of constraints.
102 * New strengths may be inserted in the strength hierarchy without
103 * disrupting current constraints. Strengths cannot be created outside
104 * this class, so pointer comparison can be used for value comparison.
106 function Strength(strengthValue
, name
) {
107 this.strengthValue
= strengthValue
;
111 Strength
.stronger = function (s1
, s2
) {
112 return s1
.strengthValue
< s2
.strengthValue
;
115 Strength
.weaker = function (s1
, s2
) {
116 return s1
.strengthValue
> s2
.strengthValue
;
119 Strength
.weakestOf = function (s1
, s2
) {
120 return this.weaker(...args(s1
, s2
)) ? s1
: s2
;
123 Strength
.strongest = function (s1
, s2
) {
124 return this.stronger(...args(s1
, s2
)) ? s1
: s2
;
127 Strength
.prototype.nextWeaker = function () {
128 switch (this.strengthValue
) {
129 case 0: return Strength
.WEAKEST
;
130 case 1: return Strength
.WEAK_DEFAULT
;
131 case 2: return Strength
.NORMAL
;
132 case 3: return Strength
.STRONG_DEFAULT
;
133 case 4: return Strength
.PREFERRED
;
134 case 5: return Strength
.REQUIRED
;
138 // Strength constants.
139 Strength
.REQUIRED
= new Strength(...args(0, "required"));
140 Strength
.STONG_PREFERRED
= new Strength(...args(1, "strongPreferred"));
141 Strength
.PREFERRED
= new Strength(...args(2, "preferred"));
142 Strength
.STRONG_DEFAULT
= new Strength(...args(3, "strongDefault"));
143 Strength
.NORMAL
= new Strength(...args(4, "normal"));
144 Strength
.WEAK_DEFAULT
= new Strength(...args(5, "weakDefault"));
145 Strength
.WEAKEST
= new Strength(...args(6, "weakest"));
148 * C o n s t r a i n t
152 * An abstract class representing a system-maintainable relationship
153 * (or "constraint") between a set of variables. A constraint supplies
154 * a strength instance variable; concrete subclasses provide a means
155 * of storing the constrained variables and other information required
156 * to represent a constraint.
158 function Constraint(strength
) {
159 this.strength
= strength
;
163 * Activate this constraint and attempt to satisfy it.
165 Constraint
.prototype.addConstraint = function () {
166 this.addToGraph(...args());
167 planner
.incrementalAdd(...args(this));
171 * Attempt to find a way to enforce this constraint. If successful,
172 * record the solution, perhaps modifying the current dataflow
173 * graph. Answer the constraint that this constraint overrides, if
174 * there is one, or nil, if there isn't.
175 * Assume: I am not already satisfied.
177 Constraint
.prototype.satisfy = function (mark
) {
178 this.chooseMethod(...args(mark
));
179 if (!this.isSatisfied(...args())) {
180 if (this.strength
== Strength
.REQUIRED
)
181 alert(...args("Could not satisfy a required constraint!"));
184 this.markInputs(...args(mark
));
185 var out
= this.output(...args());
186 var overridden
= out
.determinedBy
;
187 if (overridden
!= null) overridden
.markUnsatisfied(...args());
188 out
.determinedBy
= this;
189 if (!planner
.addPropagate(...args(this, mark
)))
190 alert(...args("Cycle encountered"));
195 Constraint
.prototype.destroyConstraint = function () {
196 if (this.isSatisfied(...args())) planner
.incrementalRemove(...args(this));
197 else this.removeFromGraph(...args());
201 * Normal constraints are not input constraints. An input constraint
202 * is one that depends on external state, such as the mouse, the
203 * keybord, a clock, or some arbitraty piece of imperative code.
205 Constraint
.prototype.isInput = function () {
210 * U n a r y C o n s t r a i n t
214 * Abstract superclass for constraints having a single possible output
217 function UnaryConstraint(v
, strength
) {
218 UnaryConstraint
.superConstructor
.call(this, strength
);
220 this.satisfied
= false;
221 this.addConstraint(...args());
224 UnaryConstraint
.inheritsFrom(...args(Constraint
));
227 * Adds this constraint to the constraint graph
229 UnaryConstraint
.prototype.addToGraph = function () {
230 this.myOutput
.addConstraint(...args(this));
231 this.satisfied
= false;
235 * Decides if this constraint can be satisfied and records that
238 UnaryConstraint
.prototype.chooseMethod = function (mark
) {
239 this.satisfied
= (this.myOutput
.mark
!= mark
)
240 && Strength
.stronger(...args(this.strength
, this.myOutput
.walkStrength
));
244 * Returns true if this constraint is satisfied in the current solution.
246 UnaryConstraint
.prototype.isSatisfied = function () {
247 return this.satisfied
;
250 UnaryConstraint
.prototype.markInputs = function (mark
) {
255 * Returns the current output variable.
257 UnaryConstraint
.prototype.output = function () {
258 return this.myOutput
;
262 * Calculate the walkabout strength, the stay flag, and, if it is
263 * 'stay', the value for the current output of this constraint. Assume
264 * this constraint is satisfied.
266 UnaryConstraint
.prototype.recalculate = function () {
267 this.myOutput
.walkStrength
= this.strength
;
268 this.myOutput
.stay
= !this.isInput(...args());
269 if (this.myOutput
.stay
) this.execute(...args()); // Stay optimization
273 * Records that this constraint is unsatisfied
275 UnaryConstraint
.prototype.markUnsatisfied = function () {
276 this.satisfied
= false;
279 UnaryConstraint
.prototype.inputsKnown = function () {
283 UnaryConstraint
.prototype.removeFromGraph = function () {
284 if (this.myOutput
!= null) this.myOutput
.removeConstraint(...args(this));
285 this.satisfied
= false;
289 * S t a y C o n s t r a i n t
293 * Variables that should, with some level of preference, stay the same.
294 * Planners may exploit the fact that instances, if satisfied, will not
295 * change their output during plan execution. This is called "stay
298 function StayConstraint(v
, str
) {
299 StayConstraint
.superConstructor
.call(this, v
, str
);
302 StayConstraint
.inheritsFrom(...args(UnaryConstraint
));
304 StayConstraint
.prototype.execute = function () {
305 // Stay constraints do nothing
309 * E d i t C o n s t r a i n t
313 * A unary input constraint used to mark a variable that the client
316 function EditConstraint(v
, str
) {
317 EditConstraint
.superConstructor
.call(this, v
, str
);
320 EditConstraint
.inheritsFrom(...args(UnaryConstraint
));
323 * Edits indicate that a variable is to be changed by imperative code.
325 EditConstraint
.prototype.isInput = function () {
329 EditConstraint
.prototype.execute = function () {
330 // Edit constraints do nothing
334 * B i n a r y C o n s t r a i n t
337 var Direction
= new Object(...args());
339 Direction
.FORWARD
= 1;
340 Direction
.BACKWARD
= -1;
343 * Abstract superclass for constraints having two possible output
346 function BinaryConstraint(var1
, var2
, strength
) {
347 BinaryConstraint
.superConstructor
.call(this, strength
);
350 this.direction
= Direction
.NONE
;
351 this.addConstraint(...args());
354 BinaryConstraint
.inheritsFrom(...args(Constraint
));
357 * Decides if this constraint can be satisfied and which way it
358 * should flow based on the relative strength of the variables related,
359 * and record that decision.
361 BinaryConstraint
.prototype.chooseMethod = function (mark
) {
362 if (this.v1
.mark
== mark
) {
363 this.direction
= (this.v2
.mark
!= mark
&& Strength
.stronger(...args(this.strength
, this.v2
.walkStrength
)))
367 if (this.v2
.mark
== mark
) {
368 this.direction
= (this.v1
.mark
!= mark
&& Strength
.stronger(...args(this.strength
, this.v1
.walkStrength
)))
372 if (Strength
.weaker(...args(this.v1
.walkStrength
, this.v2
.walkStrength
))) {
373 this.direction
= Strength
.stronger(...args(this.strength
, this.v1
.walkStrength
))
377 this.direction
= Strength
.stronger(...args(this.strength
, this.v2
.walkStrength
))
384 * Add this constraint to the constraint graph
386 BinaryConstraint
.prototype.addToGraph = function () {
387 this.v1
.addConstraint(...args(this));
388 this.v2
.addConstraint(...args(this));
389 this.direction
= Direction
.NONE
;
393 * Answer true if this constraint is satisfied in the current solution.
395 BinaryConstraint
.prototype.isSatisfied = function () {
396 return this.direction
!= Direction
.NONE
;
400 * Mark the input variable with the given mark.
402 BinaryConstraint
.prototype.markInputs = function (mark
) {
403 this.input(...args()).mark
= mark
;
407 * Returns the current input variable
409 BinaryConstraint
.prototype.input = function () {
410 return (this.direction
== Direction
.FORWARD
) ? this.v1
: this.v2
;
414 * Returns the current output variable
416 BinaryConstraint
.prototype.output = function () {
417 return (this.direction
== Direction
.FORWARD
) ? this.v2
: this.v1
;
421 * Calculate the walkabout strength, the stay flag, and, if it is
422 * 'stay', the value for the current output of this
423 * constraint. Assume this constraint is satisfied.
425 BinaryConstraint
.prototype.recalculate = function () {
426 var ihn
= this.input(...args()), out
= this.output(...args());
427 out
.walkStrength
= Strength
.weakestOf(...args(this.strength
, ihn
.walkStrength
));
429 if (out
.stay
) this.execute(...args());
433 * Record the fact that this constraint is unsatisfied.
435 BinaryConstraint
.prototype.markUnsatisfied = function () {
436 this.direction
= Direction
.NONE
;
439 BinaryConstraint
.prototype.inputsKnown = function (mark
) {
440 var i
= this.input(...args());
441 return i
.mark
== mark
|| i
.stay
|| i
.determinedBy
== null;
444 BinaryConstraint
.prototype.removeFromGraph = function () {
445 if (this.v1
!= null) this.v1
.removeConstraint(...args(this));
446 if (this.v2
!= null) this.v2
.removeConstraint(...args(this));
447 this.direction
= Direction
.NONE
;
451 * S c a l e C o n s t r a i n t
455 * Relates two variables by the linear scaling relationship: "v2 =
456 * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
457 * this relationship but the scale factor and offset are considered
460 function ScaleConstraint(src
, scale
, offset
, dest
, strength
) {
461 this.direction
= Direction
.NONE
;
463 this.offset
= offset
;
464 ScaleConstraint
.superConstructor
.call(this, src
, dest
, strength
);
467 ScaleConstraint
.inheritsFrom(...args(BinaryConstraint
));
470 * Adds this constraint to the constraint graph.
472 ScaleConstraint
.prototype.addToGraph = function () {
473 ScaleConstraint
.superConstructor
.prototype.addToGraph
.call(this);
474 this.scale
.addConstraint(...args(this));
475 this.offset
.addConstraint(...args(this));
478 ScaleConstraint
.prototype.removeFromGraph = function () {
479 ScaleConstraint
.superConstructor
.prototype.removeFromGraph
.call(this);
480 if (this.scale
!= null) this.scale
.removeConstraint(...args(this));
481 if (this.offset
!= null) this.offset
.removeConstraint(...args(this));
484 ScaleConstraint
.prototype.markInputs = function (mark
) {
485 ScaleConstraint
.superConstructor
.prototype.markInputs
.call(this, mark
);
486 this.scale
.mark
= this.offset
.mark
= mark
;
490 * Enforce this constraint. Assume that it is satisfied.
492 ScaleConstraint
.prototype.execute = function () {
493 if (this.direction
== Direction
.FORWARD
) {
494 this.v2
.value
= this.v1
.value
* this.scale
.value
+ this.offset
.value
;
496 this.v1
.value
= (this.v2
.value
- this.offset
.value
) / this.scale
.value
;
501 * Calculate the walkabout strength, the stay flag, and, if it is
502 * 'stay', the value for the current output of this constraint. Assume
503 * this constraint is satisfied.
505 ScaleConstraint
.prototype.recalculate = function () {
506 var ihn
= this.input(...args()), out
= this.output(...args());
507 out
.walkStrength
= Strength
.weakestOf(...args(this.strength
, ihn
.walkStrength
));
508 out
.stay
= ihn
.stay
&& this.scale
.stay
&& this.offset
.stay
;
509 if (out
.stay
) this.execute(...args());
513 * E q u a l i t y C o n s t r a i n t
517 * Constrains two variables to have the same value.
519 function EqualityConstraint(var1
, var2
, strength
) {
520 EqualityConstraint
.superConstructor
.call(this, var1
, var2
, strength
);
523 EqualityConstraint
.inheritsFrom(...args(BinaryConstraint
));
526 * Enforce this constraint. Assume that it is satisfied.
528 EqualityConstraint
.prototype.execute = function () {
529 this.output(...args()).value
= this.input(...args()).value
;
537 * A constrained variable. In addition to its value, it maintain the
538 * structure of the constraint graph, the current dataflow graph, and
539 * various parameters of interest to the DeltaBlue incremental
542 function Variable(name
, initialValue
) {
543 this.value
= initialValue
|| 0;
544 this.constraints
= new OrderedCollection(...args());
545 this.determinedBy
= null;
547 this.walkStrength
= Strength
.WEAKEST
;
553 * Add the given constraint to the set of all constraints that refer
556 Variable
.prototype.addConstraint = function (c
) {
557 this.constraints
.add(...args(c
));
561 * Removes all traces of c from this variable.
563 Variable
.prototype.removeConstraint = function (c
) {
564 this.constraints
.remove(...args(c
));
565 if (this.determinedBy
== c
) this.determinedBy
= null;
573 * The DeltaBlue planner
576 this.currentMark
= 0;
580 * Attempt to satisfy the given constraint and, if successful,
581 * incrementally update the dataflow graph. Details: If satifying
582 * the constraint is successful, it may override a weaker constraint
583 * on its output. The algorithm attempts to resatisfy that
584 * constraint using some other method. This process is repeated
585 * until either a) it reaches a variable that was not previously
586 * determined by any constraint or b) it reaches a constraint that
587 * is too weak to be satisfied using any of its methods. The
588 * variables of constraints that have been processed are marked with
589 * a unique mark value so that we know where we've been. This allows
590 * the algorithm to avoid getting into an infinite loop even if the
591 * constraint graph has an inadvertent cycle.
593 Planner
.prototype.incrementalAdd = function (c
) {
594 var mark
= this.newMark(...args());
595 var overridden
= c
.satisfy(...args(mark
));
596 while (overridden
!= null)
597 overridden
= overridden
.satisfy(...args(mark
));
601 * Entry point for retracting a constraint. Remove the given
602 * constraint and incrementally update the dataflow graph.
603 * Details: Retracting the given constraint may allow some currently
604 * unsatisfiable downstream constraint to be satisfied. We therefore collect
605 * a list of unsatisfied downstream constraints and attempt to
606 * satisfy each one in turn. This list is traversed by constraint
607 * strength, strongest first, as a heuristic for avoiding
608 * unnecessarily adding and then overriding weak constraints.
609 * Assume: c is satisfied.
611 Planner
.prototype.incrementalRemove = function (c
) {
612 var out
= c
.output(...args());
613 c
.markUnsatisfied(...args());
614 c
.removeFromGraph(...args());
615 var unsatisfied
= this.removePropagateFrom(...args(out
));
616 var strength
= Strength
.REQUIRED
;
618 for (var i
= 0; i
< unsatisfied
.size(...args()); i
++) {
619 var u
= unsatisfied
.at(...args(i
));
620 if (u
.strength
== strength
)
621 this.incrementalAdd(...args(u
));
623 strength
= strength
.nextWeaker(...args());
624 } while (strength
!= Strength
.WEAKEST
);
628 * Select a previously unused mark value.
630 Planner
.prototype.newMark = function () {
631 return ++this.currentMark
;
635 * Extract a plan for resatisfaction starting from the given source
636 * constraints, usually a set of input constraints. This method
637 * assumes that stay optimization is desired; the plan will contain
638 * only constraints whose output variables are not stay. Constraints
639 * that do no computation, such as stay and edit constraints, are
640 * not included in the plan.
641 * Details: The outputs of a constraint are marked when it is added
642 * to the plan under construction. A constraint may be appended to
643 * the plan when all its input variables are known. A variable is
644 * known if either a) the variable is marked (indicating that has
645 * been computed by a constraint appearing earlier in the plan), b)
646 * the variable is 'stay' (i.e. it is a constant at plan execution
647 * time), or c) the variable is not determined by any
648 * constraint. The last provision is for past states of history
649 * variables, which are not stay but which are also not computed by
651 * Assume: sources are all satisfied.
653 Planner
.prototype.makePlan = function (sources
) {
654 var mark
= this.newMark(...args());
655 var plan
= new Plan(...args());
657 while (todo
.size(...args()) > 0) {
658 var c
= todo
.removeFirst(...args());
659 if (c
.output(...args()).mark
!= mark
&& c
.inputsKnown(...args(mark
))) {
660 plan
.addConstraint(...args(c
));
661 c
.output(...args()).mark
= mark
;
662 this.addConstraintsConsumingTo(...args(c
.output(...args()), todo
));
669 * Extract a plan for resatisfying starting from the output of the
670 * given constraints, usually a set of input constraints.
672 Planner
.prototype.extractPlanFromConstraints = function (constraints
) {
673 var sources
= new OrderedCollection(...args());
674 for (var i
= 0; i
< constraints
.size(...args()); i
++) {
675 var c
= constraints
.at(...args(i
));
676 if (c
.isInput(...args()) && c
.isSatisfied(...args()))
677 // not in plan already and eligible for inclusion
678 sources
.add(...args(c
));
680 return this.makePlan(...args(sources
));
684 * Recompute the walkabout strengths and stay flags of all variables
685 * downstream of the given constraint and recompute the actual
686 * values of all variables whose stay flag is true. If a cycle is
687 * detected, remove the given constraint and answer
688 * false. Otherwise, answer true.
689 * Details: Cycles are detected when a marked variable is
690 * encountered downstream of the given constraint. The sender is
691 * assumed to have marked the inputs of the given constraint with
692 * the given mark. Thus, encountering a marked node downstream of
693 * the output constraint means that there is a path from the
694 * constraint's output to one of its inputs.
696 Planner
.prototype.addPropagate = function (c
, mark
) {
697 var todo
= new OrderedCollection(...args());
698 todo
.add(...args(c
));
699 while (todo
.size(...args()) > 0) {
700 var d
= todo
.removeFirst(...args());
701 if (d
.output(...args()).mark
== mark
) {
702 this.incrementalRemove(...args(c
));
705 d
.recalculate(...args());
706 this.addConstraintsConsumingTo(...args(d
.output(...args()), todo
));
713 * Update the walkabout strengths and stay flags of all variables
714 * downstream of the given constraint. Answer a collection of
715 * unsatisfied constraints sorted in order of decreasing strength.
717 Planner
.prototype.removePropagateFrom = function (out
) {
718 out
.determinedBy
= null;
719 out
.walkStrength
= Strength
.WEAKEST
;
721 var unsatisfied
= new OrderedCollection(...args());
722 var todo
= new OrderedCollection(...args());
723 todo
.add(...args(out
));
724 while (todo
.size(...args()) > 0) {
725 var v
= todo
.removeFirst(...args());
726 for (var i
= 0; i
< v
.constraints
.size(...args()); i
++) {
727 var c
= v
.constraints
.at(...args(i
));
728 if (!c
.isSatisfied(...args()))
729 unsatisfied
.add(...args(c
));
731 var determining
= v
.determinedBy
;
732 for (var i
= 0; i
< v
.constraints
.size(...args()); i
++) {
733 var next
= v
.constraints
.at(...args(i
));
734 if (next
!= determining
&& next
.isSatisfied(...args())) {
735 next
.recalculate(...args());
736 todo
.add(...args(next
.output(...args())));
743 Planner
.prototype.addConstraintsConsumingTo = function (v
, coll
) {
744 var determining
= v
.determinedBy
;
745 var cc
= v
.constraints
;
746 for (var i
= 0; i
< cc
.size(...args()); i
++) {
747 var c
= cc
.at(...args(i
));
748 if (c
!= determining
&& c
.isSatisfied(...args()))
749 coll
.add(...args(c
));
758 * A Plan is an ordered list of constraints to be executed in sequence
759 * to resatisfy all currently satisfiable constraints in the face of
760 * one or more changing inputs.
763 this.v
= new OrderedCollection(...args());
766 Plan
.prototype.addConstraint = function (c
) {
767 this.v
.add(...args(c
));
770 Plan
.prototype.size = function () {
771 return this.v
.size(...args());
774 Plan
.prototype.constraintAt = function (index
) {
775 return this.v
.at(...args(index
));
778 Plan
.prototype.execute = function () {
779 for (var i
= 0; i
< this.size(...args()); i
++) {
780 var c
= this.constraintAt(...args(i
));
781 c
.execute(...args());
790 * This is the standard DeltaBlue benchmark. A long chain of equality
791 * constraints is constructed with a stay constraint on one end. An
792 * edit constraint is then added to the opposite end and the time is
793 * measured for adding and removing this constraint, and extracting
794 * and executing a constraint satisfaction plan. There are two cases.
795 * In case 1, the added constraint is stronger than the stay
796 * constraint and values must propagate down the entire length of the
797 * chain. In case 2, the added constraint is weaker than the stay
798 * constraint so it cannot be accomodated. The cost in this case is,
799 * of course, very low. Typical situations lie somewhere between these
802 function chainTest(n
) {
803 planner
= new Planner(...args());
804 var prev
= null, first
= null, last
= null;
806 // Build chain of n equality constraints
807 for (var i
= 0; i
<= n
; i
++) {
809 var v
= new Variable(...args(name
));
811 new EqualityConstraint(...args(prev
, v
, Strength
.REQUIRED
));
812 if (i
== 0) first
= v
;
813 if (i
== n
) last
= v
;
817 new StayConstraint(...args(last
, Strength
.STRONG_DEFAULT
));
818 var edit
= new EditConstraint(...args(first
, Strength
.PREFERRED
));
819 var edits
= new OrderedCollection(...args());
820 edits
.add(...args(edit
));
821 var plan
= planner
.extractPlanFromConstraints(...args(edits
));
822 for (var i
= 0; i
< 100; i
++) {
824 plan
.execute(...args());
826 alert(...args("Chain test failed."));
831 * This test constructs a two sets of variables related to each
832 * other by a simple linear transformation (scale and offset). The
833 * time is measured to change a variable on either side of the
834 * mapping and to change the scale and offset factors.
836 function projectionTest(n
) {
837 planner
= new Planner(...args());
838 var scale
= new Variable(...args("scale", 10));
839 var offset
= new Variable(...args("offset", 1000));
840 var src
= null, dst
= null;
842 var dests
= new OrderedCollection(...args());
843 for (var i
= 0; i
< n
; i
++) {
844 src
= new Variable(...args("src" + i
, i
));
845 dst
= new Variable(...args("dst" + i
, i
));
846 dests
.add(...args(dst
));
847 new StayConstraint(...args(src
, Strength
.NORMAL
));
848 new ScaleConstraint(...args(src
, scale
, offset
, dst
, Strength
.REQUIRED
));
851 change(...args(src
, 17));
852 if (dst
.value
!= 1170) alert(...args("Projection 1 failed"));
853 change(...args(dst
, 1050));
854 if (src
.value
!= 5) alert(...args("Projection 2 failed"));
855 change(...args(scale
, 5));
856 for (var i
= 0; i
< n
- 1; i
++) {
857 if (dests
.at(...args(i
)).value
!= i
* 5 + 1000)
858 alert(...args("Projection 3 failed"));
860 change(...args(offset
, 2000));
861 for (var i
= 0; i
< n
- 1; i
++) {
862 if (dests
.at(...args(i
)).value
!= i
* 5 + 2000)
863 alert(...args("Projection 4 failed"));
867 function change(v
, newValue
) {
868 var edit
= new EditConstraint(...args(v
, Strength
.PREFERRED
));
869 var edits
= new OrderedCollection(...args());
870 edits
.add(...args(edit
));
871 var plan
= planner
.extractPlanFromConstraints(...args(edits
));
872 for (var i
= 0; i
< 10; i
++) {
874 plan
.execute(...args());
876 edit
.destroyConstraint(...args());
879 // Global variable holding the current planner.
882 function deltaBlue() {
883 chainTest(...args(25));
884 projectionTest(...args(25));
887 for (var i
= 0; i
< 5; ++i
)
888 deltaBlue(...args());