5a5ddca7bc
BUG= R=jmesserly@google.com Review URL: https://codereview.chromium.org/1059583002
470 lines
15 KiB
JavaScript
470 lines
15 KiB
JavaScript
var DeltaBlue;
|
|
(function(exports) {
|
|
'use strict';
|
|
// Function main: () → dynamic
|
|
function main() {
|
|
new DeltaBlue().report();
|
|
}
|
|
class DeltaBlue extends BenchmarkBase.BenchmarkBase {
|
|
DeltaBlue() {
|
|
super.BenchmarkBase("DeltaBlue");
|
|
}
|
|
run() {
|
|
chainTest(100);
|
|
projectionTest(100);
|
|
}
|
|
}
|
|
class Strength extends core.Object {
|
|
Strength(value, name) {
|
|
this.value = value;
|
|
this.name = name;
|
|
}
|
|
nextWeaker() {
|
|
return /* Unimplemented const */new core.List$(Strength).from([STRONG_PREFERRED, PREFERRED, STRONG_DEFAULT, NORMAL, WEAK_DEFAULT, WEAKEST])[core.$get](this.value);
|
|
}
|
|
static stronger(s1, s2) {
|
|
return dart.notNull(s1.value) < dart.notNull(s2.value);
|
|
}
|
|
static weaker(s1, s2) {
|
|
return dart.notNull(s1.value) > dart.notNull(s2.value);
|
|
}
|
|
static weakest(s1, s2) {
|
|
return Strength.weaker(s1, s2) ? s1 : s2;
|
|
}
|
|
static strongest(s1, s2) {
|
|
return Strength.stronger(s1, s2) ? s1 : s2;
|
|
}
|
|
}
|
|
let REQUIRED = new Strength(0, "required");
|
|
let STRONG_PREFERRED = new Strength(1, "strongPreferred");
|
|
let PREFERRED = new Strength(2, "preferred");
|
|
let STRONG_DEFAULT = new Strength(3, "strongDefault");
|
|
let NORMAL = new Strength(4, "normal");
|
|
let WEAK_DEFAULT = new Strength(5, "weakDefault");
|
|
let WEAKEST = new Strength(6, "weakest");
|
|
class Constraint extends core.Object {
|
|
Constraint(strength) {
|
|
this.strength = strength;
|
|
}
|
|
addConstraint() {
|
|
this.addToGraph();
|
|
exports.planner.incrementalAdd(this);
|
|
}
|
|
satisfy(mark) {
|
|
this.chooseMethod(dart.as(mark, core.int));
|
|
if (!dart.notNull(this.isSatisfied())) {
|
|
if (dart.equals(this.strength, REQUIRED)) {
|
|
core.print("Could not satisfy a required constraint!");
|
|
}
|
|
return null;
|
|
}
|
|
this.markInputs(dart.as(mark, core.int));
|
|
let out = this.output();
|
|
let overridden = out.determinedBy;
|
|
if (overridden != null)
|
|
overridden.markUnsatisfied();
|
|
out.determinedBy = this;
|
|
if (!dart.notNull(exports.planner.addPropagate(this, dart.as(mark, core.int))))
|
|
core.print("Cycle encountered");
|
|
out.mark = dart.as(mark, core.int);
|
|
return overridden;
|
|
}
|
|
destroyConstraint() {
|
|
if (this.isSatisfied())
|
|
exports.planner.incrementalRemove(this);
|
|
this.removeFromGraph();
|
|
}
|
|
isInput() {
|
|
return false;
|
|
}
|
|
}
|
|
class UnaryConstraint extends Constraint {
|
|
UnaryConstraint(myOutput, strength) {
|
|
this.myOutput = myOutput;
|
|
this.satisfied = false;
|
|
super.Constraint(strength);
|
|
this.addConstraint();
|
|
}
|
|
addToGraph() {
|
|
this.myOutput.addConstraint(this);
|
|
this.satisfied = false;
|
|
}
|
|
chooseMethod(mark) {
|
|
this.satisfied = this.myOutput.mark != mark && dart.notNull(Strength.stronger(this.strength, this.myOutput.walkStrength));
|
|
}
|
|
isSatisfied() {
|
|
return this.satisfied;
|
|
}
|
|
markInputs(mark) {}
|
|
output() {
|
|
return this.myOutput;
|
|
}
|
|
recalculate() {
|
|
this.myOutput.walkStrength = this.strength;
|
|
this.myOutput.stay = !dart.notNull(this.isInput());
|
|
if (this.myOutput.stay)
|
|
this.execute();
|
|
}
|
|
markUnsatisfied() {
|
|
this.satisfied = false;
|
|
}
|
|
inputsKnown(mark) {
|
|
return true;
|
|
}
|
|
removeFromGraph() {
|
|
if (this.myOutput != null)
|
|
this.myOutput.removeConstraint(this);
|
|
this.satisfied = false;
|
|
}
|
|
}
|
|
class StayConstraint extends UnaryConstraint {
|
|
StayConstraint(v, str) {
|
|
super.UnaryConstraint(v, str);
|
|
}
|
|
execute() {}
|
|
}
|
|
class EditConstraint extends UnaryConstraint {
|
|
EditConstraint(v, str) {
|
|
super.UnaryConstraint(v, str);
|
|
}
|
|
isInput() {
|
|
return true;
|
|
}
|
|
execute() {}
|
|
}
|
|
let NONE = 1;
|
|
let FORWARD = 2;
|
|
let BACKWARD = 0;
|
|
class BinaryConstraint extends Constraint {
|
|
BinaryConstraint(v1, v2, strength) {
|
|
this.v1 = v1;
|
|
this.v2 = v2;
|
|
this.direction = NONE;
|
|
super.Constraint(strength);
|
|
this.addConstraint();
|
|
}
|
|
chooseMethod(mark) {
|
|
if (this.v1.mark == mark) {
|
|
this.direction = this.v2.mark != mark && dart.notNull(Strength.stronger(this.strength, this.v2.walkStrength)) ? FORWARD : NONE;
|
|
}
|
|
if (this.v2.mark == mark) {
|
|
this.direction = this.v1.mark != mark && dart.notNull(Strength.stronger(this.strength, this.v1.walkStrength)) ? BACKWARD : NONE;
|
|
}
|
|
if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) {
|
|
this.direction = Strength.stronger(this.strength, this.v1.walkStrength) ? BACKWARD : NONE;
|
|
} else {
|
|
this.direction = Strength.stronger(this.strength, this.v2.walkStrength) ? FORWARD : BACKWARD;
|
|
}
|
|
}
|
|
addToGraph() {
|
|
this.v1.addConstraint(this);
|
|
this.v2.addConstraint(this);
|
|
this.direction = NONE;
|
|
}
|
|
isSatisfied() {
|
|
return this.direction != NONE;
|
|
}
|
|
markInputs(mark) {
|
|
this.input().mark = mark;
|
|
}
|
|
input() {
|
|
return this.direction == FORWARD ? this.v1 : this.v2;
|
|
}
|
|
output() {
|
|
return this.direction == FORWARD ? this.v2 : this.v1;
|
|
}
|
|
recalculate() {
|
|
let ihn = this.input(), out = this.output();
|
|
out.walkStrength = Strength.weakest(this.strength, ihn.walkStrength);
|
|
out.stay = ihn.stay;
|
|
if (out.stay)
|
|
this.execute();
|
|
}
|
|
markUnsatisfied() {
|
|
this.direction = NONE;
|
|
}
|
|
inputsKnown(mark) {
|
|
let i = this.input();
|
|
return i.mark == mark || dart.notNull(i.stay) || dart.notNull(i.determinedBy == null);
|
|
}
|
|
removeFromGraph() {
|
|
if (this.v1 != null)
|
|
this.v1.removeConstraint(this);
|
|
if (this.v2 != null)
|
|
this.v2.removeConstraint(this);
|
|
this.direction = NONE;
|
|
}
|
|
}
|
|
class ScaleConstraint extends BinaryConstraint {
|
|
ScaleConstraint(src, scale, offset, dest, strength) {
|
|
this.scale = scale;
|
|
this.offset = offset;
|
|
super.BinaryConstraint(src, dest, strength);
|
|
}
|
|
addToGraph() {
|
|
super.addToGraph();
|
|
this.scale.addConstraint(this);
|
|
this.offset.addConstraint(this);
|
|
}
|
|
removeFromGraph() {
|
|
super.removeFromGraph();
|
|
if (this.scale != null)
|
|
this.scale.removeConstraint(this);
|
|
if (this.offset != null)
|
|
this.offset.removeConstraint(this);
|
|
}
|
|
markInputs(mark) {
|
|
super.markInputs(mark);
|
|
this.scale.mark = this.offset.mark = mark;
|
|
}
|
|
execute() {
|
|
if (this.direction == FORWARD) {
|
|
this.v2.value = dart.notNull(this.v1.value) * dart.notNull(this.scale.value) + dart.notNull(this.offset.value);
|
|
} else {
|
|
this.v1.value = ((dart.notNull(this.v2.value) - dart.notNull(this.offset.value)) / dart.notNull(this.scale.value)).truncate();
|
|
}
|
|
}
|
|
recalculate() {
|
|
let ihn = this.input(), out = this.output();
|
|
out.walkStrength = Strength.weakest(this.strength, ihn.walkStrength);
|
|
out.stay = dart.notNull(ihn.stay) && dart.notNull(this.scale.stay) && dart.notNull(this.offset.stay);
|
|
if (out.stay)
|
|
this.execute();
|
|
}
|
|
}
|
|
class EqualityConstraint extends BinaryConstraint {
|
|
EqualityConstraint(v1, v2, strength) {
|
|
super.BinaryConstraint(v1, v2, strength);
|
|
}
|
|
execute() {
|
|
this.output().value = this.input().value;
|
|
}
|
|
}
|
|
class Variable extends core.Object {
|
|
Variable(name, value) {
|
|
this.constraints = new core.List$(Constraint).from([]);
|
|
this.name = name;
|
|
this.value = value;
|
|
this.determinedBy = null;
|
|
this.mark = 0;
|
|
this.walkStrength = WEAKEST;
|
|
this.stay = true;
|
|
}
|
|
addConstraint(c) {
|
|
this.constraints[core.$add](c);
|
|
}
|
|
removeConstraint(c) {
|
|
this.constraints[core.$remove](c);
|
|
if (dart.equals(this.determinedBy, c))
|
|
this.determinedBy = null;
|
|
}
|
|
}
|
|
class Planner extends core.Object {
|
|
Planner() {
|
|
this.currentMark = 0;
|
|
}
|
|
incrementalAdd(c) {
|
|
let mark = this.newMark();
|
|
for (let overridden = c.satisfy(mark); overridden != null; overridden = overridden.satisfy(mark))
|
|
;
|
|
}
|
|
incrementalRemove(c) {
|
|
let out = c.output();
|
|
c.markUnsatisfied();
|
|
c.removeFromGraph();
|
|
let unsatisfied = this.removePropagateFrom(out);
|
|
let strength = REQUIRED;
|
|
do {
|
|
for (let i = 0; dart.notNull(i) < dart.notNull(unsatisfied[core.$length]); i = dart.notNull(i) + 1) {
|
|
let u = unsatisfied[core.$get](i);
|
|
if (dart.equals(u.strength, strength))
|
|
this.incrementalAdd(u);
|
|
}
|
|
strength = strength.nextWeaker();
|
|
} while (!dart.equals(strength, WEAKEST));
|
|
}
|
|
newMark() {
|
|
return this.currentMark = dart.notNull(this.currentMark) + 1;
|
|
}
|
|
makePlan(sources) {
|
|
let mark = this.newMark();
|
|
let plan = new Plan();
|
|
let todo = sources;
|
|
while (dart.notNull(todo[core.$length]) > 0) {
|
|
let c = todo[core.$removeLast]();
|
|
if (c.output().mark != mark && dart.notNull(c.inputsKnown(mark))) {
|
|
plan.addConstraint(c);
|
|
c.output().mark = mark;
|
|
this.addConstraintsConsumingTo(c.output(), todo);
|
|
}
|
|
}
|
|
return plan;
|
|
}
|
|
extractPlanFromConstraints(constraints) {
|
|
let sources = new core.List$(Constraint).from([]);
|
|
for (let i = 0; dart.notNull(i) < dart.notNull(constraints[core.$length]); i = dart.notNull(i) + 1) {
|
|
let c = constraints[core.$get](i);
|
|
if (dart.notNull(c.isInput()) && dart.notNull(c.isSatisfied()))
|
|
sources[core.$add](c);
|
|
}
|
|
return this.makePlan(sources);
|
|
}
|
|
addPropagate(c, mark) {
|
|
let todo = new core.List$(Constraint).from([c]);
|
|
while (dart.notNull(todo[core.$length]) > 0) {
|
|
let d = todo[core.$removeLast]();
|
|
if (d.output().mark == mark) {
|
|
this.incrementalRemove(c);
|
|
return false;
|
|
}
|
|
d.recalculate();
|
|
this.addConstraintsConsumingTo(d.output(), todo);
|
|
}
|
|
return true;
|
|
}
|
|
removePropagateFrom(out) {
|
|
out.determinedBy = null;
|
|
out.walkStrength = WEAKEST;
|
|
out.stay = true;
|
|
let unsatisfied = new core.List$(Constraint).from([]);
|
|
let todo = new core.List$(Variable).from([out]);
|
|
while (dart.notNull(todo[core.$length]) > 0) {
|
|
let v = todo[core.$removeLast]();
|
|
for (let i = 0; dart.notNull(i) < dart.notNull(v.constraints[core.$length]); i = dart.notNull(i) + 1) {
|
|
let c = v.constraints[core.$get](i);
|
|
if (!dart.notNull(c.isSatisfied()))
|
|
unsatisfied[core.$add](c);
|
|
}
|
|
let determining = v.determinedBy;
|
|
for (let i = 0; dart.notNull(i) < dart.notNull(v.constraints[core.$length]); i = dart.notNull(i) + 1) {
|
|
let next = v.constraints[core.$get](i);
|
|
if (dart.notNull(!dart.equals(next, determining)) && dart.notNull(next.isSatisfied())) {
|
|
next.recalculate();
|
|
todo[core.$add](next.output());
|
|
}
|
|
}
|
|
}
|
|
return unsatisfied;
|
|
}
|
|
addConstraintsConsumingTo(v, coll) {
|
|
let determining = v.determinedBy;
|
|
for (let i = 0; dart.notNull(i) < dart.notNull(v.constraints[core.$length]); i = dart.notNull(i) + 1) {
|
|
let c = v.constraints[core.$get](i);
|
|
if (dart.notNull(!dart.equals(c, determining)) && dart.notNull(c.isSatisfied()))
|
|
coll[core.$add](c);
|
|
}
|
|
}
|
|
}
|
|
class Plan extends core.Object {
|
|
Plan() {
|
|
this.list = new core.List$(Constraint).from([]);
|
|
}
|
|
addConstraint(c) {
|
|
this.list[core.$add](c);
|
|
}
|
|
size() {
|
|
return this.list[core.$length];
|
|
}
|
|
execute() {
|
|
for (let i = 0; dart.notNull(i) < dart.notNull(this.list[core.$length]); i = dart.notNull(i) + 1) {
|
|
this.list[core.$get](i).execute();
|
|
}
|
|
}
|
|
}
|
|
// Function chainTest: (int) → void
|
|
function chainTest(n) {
|
|
exports.planner = new Planner();
|
|
let prev = null, first = null, last = null;
|
|
for (let i = 0; dart.notNull(i) <= dart.notNull(n); i = dart.notNull(i) + 1) {
|
|
let v = new Variable("v", 0);
|
|
if (prev != null)
|
|
new EqualityConstraint(prev, v, REQUIRED);
|
|
if (i == 0)
|
|
first = v;
|
|
if (i == n)
|
|
last = v;
|
|
prev = v;
|
|
}
|
|
new StayConstraint(last, STRONG_DEFAULT);
|
|
let edit = new EditConstraint(first, PREFERRED);
|
|
let plan = exports.planner.extractPlanFromConstraints(new core.List$(Constraint).from([edit]));
|
|
for (let i = 0; dart.notNull(i) < 100; i = dart.notNull(i) + 1) {
|
|
first.value = i;
|
|
plan.execute();
|
|
if (last.value != i) {
|
|
core.print("Chain test failed:");
|
|
core.print(`Expected last value to be ${i} but it was ${last.value}.`);
|
|
}
|
|
}
|
|
}
|
|
// Function projectionTest: (int) → void
|
|
function projectionTest(n) {
|
|
exports.planner = new Planner();
|
|
let scale = new Variable("scale", 10);
|
|
let offset = new Variable("offset", 1000);
|
|
let src = null, dst = null;
|
|
let dests = new core.List$(Variable).from([]);
|
|
for (let i = 0; dart.notNull(i) < dart.notNull(n); i = dart.notNull(i) + 1) {
|
|
src = new Variable("src", i);
|
|
dst = new Variable("dst", i);
|
|
dests[core.$add](dst);
|
|
new StayConstraint(src, NORMAL);
|
|
new ScaleConstraint(src, scale, offset, dst, REQUIRED);
|
|
}
|
|
change(src, 17);
|
|
if (dst.value != 1170)
|
|
core.print("Projection 1 failed");
|
|
change(dst, 1050);
|
|
if (src.value != 5)
|
|
core.print("Projection 2 failed");
|
|
change(scale, 5);
|
|
for (let i = 0; dart.notNull(i) < dart.notNull(n) - 1; i = dart.notNull(i) + 1) {
|
|
if (dests[core.$get](i).value != dart.notNull(i) * 5 + 1000)
|
|
core.print("Projection 3 failed");
|
|
}
|
|
change(offset, 2000);
|
|
for (let i = 0; dart.notNull(i) < dart.notNull(n) - 1; i = dart.notNull(i) + 1) {
|
|
if (dests[core.$get](i).value != dart.notNull(i) * 5 + 2000)
|
|
core.print("Projection 4 failed");
|
|
}
|
|
}
|
|
// Function change: (Variable, int) → void
|
|
function change(v, newValue) {
|
|
let edit = new EditConstraint(v, PREFERRED);
|
|
let plan = exports.planner.extractPlanFromConstraints(new core.List$(EditConstraint).from([edit]));
|
|
for (let i = 0; dart.notNull(i) < 10; i = dart.notNull(i) + 1) {
|
|
v.value = newValue;
|
|
plan.execute();
|
|
}
|
|
edit.destroyConstraint();
|
|
}
|
|
exports.planner = null;
|
|
// Exports:
|
|
exports.main = main;
|
|
exports.DeltaBlue = DeltaBlue;
|
|
exports.Strength = Strength;
|
|
exports.REQUIRED = REQUIRED;
|
|
exports.STRONG_PREFERRED = STRONG_PREFERRED;
|
|
exports.PREFERRED = PREFERRED;
|
|
exports.STRONG_DEFAULT = STRONG_DEFAULT;
|
|
exports.NORMAL = NORMAL;
|
|
exports.WEAK_DEFAULT = WEAK_DEFAULT;
|
|
exports.WEAKEST = WEAKEST;
|
|
exports.Constraint = Constraint;
|
|
exports.UnaryConstraint = UnaryConstraint;
|
|
exports.StayConstraint = StayConstraint;
|
|
exports.EditConstraint = EditConstraint;
|
|
exports.NONE = NONE;
|
|
exports.FORWARD = FORWARD;
|
|
exports.BACKWARD = BACKWARD;
|
|
exports.BinaryConstraint = BinaryConstraint;
|
|
exports.ScaleConstraint = ScaleConstraint;
|
|
exports.EqualityConstraint = EqualityConstraint;
|
|
exports.Variable = Variable;
|
|
exports.Planner = Planner;
|
|
exports.Plan = Plan;
|
|
exports.chainTest = chainTest;
|
|
exports.projectionTest = projectionTest;
|
|
exports.change = change;
|
|
})(DeltaBlue || (DeltaBlue = {}));
|