| library; |
| import self as self; |
| import "dart:core" as core; |
| |
| class DeltaBlue extends core::Object { |
| constructor •() → void |
| : super core::Object::•() |
| ; |
| method run() → void/Null { |
| self::chainTest(100); |
| self::projectionTest(100); |
| } |
| } |
| class Strength extends core::Object { |
| final field core::int/core::int* {int} value; |
| final field core::String/core::String* {string} name; |
| const constructor •(core::int/core::int* {int} value, core::String/core::String* {string} name) → void |
| : self::Strength::value = value, self::Strength::name = name, super core::Object::•() |
| ; |
| method nextWeaker() → self::Strength/core::Object+ {*} |
| return const <self::Strength>[self::STRONG_PREFERRED, self::PREFERRED, self::STRONG_DEFAULT, self::NORMAL, self::WEAK_DEFAULT, self::WEAKEST].[](this.value); |
| static method stronger(self::Strength/self::Strength! {other} s1, self::Strength/self::Strength! {other} s2) → core::bool/core::bool* {other} { |
| return s1.value.<(s2.value); |
| } |
| static method weaker(self::Strength/self::Strength! {other} s1, self::Strength/self::Strength! {other} s2) → core::bool/core::bool* {other} { |
| return s1.value.>(s2.value); |
| } |
| static method weakest(self::Strength/self::Strength! {other} s1, self::Strength/self::Strength! {other} s2) → self::Strength/self::Strength! {other} { |
| return self::Strength::weaker(s1, s2) ? s1 : s2; |
| } |
| static method strongest(self::Strength/Nothing s1, self::Strength/Nothing s2) → self::Strength/Nothing { |
| return self::Strength::stronger(s1, s2) ? s1 : s2; |
| } |
| } |
| abstract class Constraint extends core::Object { |
| final field self::Strength/self::Strength! {other} strength; |
| const constructor •(self::Strength/self::Strength! {other} strength) → void |
| : self::Constraint::strength = strength, super core::Object::•() |
| ; |
| abstract method isSatisfied() → core::bool/<missing type>; |
| abstract method markUnsatisfied() → void/<missing type>; |
| abstract method addToGraph() → void/<missing type>; |
| abstract method removeFromGraph() → void/<missing type>; |
| abstract method chooseMethod(core::int/<missing type> mark) → void/<missing type>; |
| abstract method markInputs(core::int/<missing type> mark) → void/<missing type>; |
| abstract method inputsKnown(core::int/<missing type> mark) → core::bool/<missing type>; |
| abstract method output() → self::Variable/<missing type>; |
| abstract method execute() → void/<missing type>; |
| abstract method recalculate() → void/<missing type>; |
| method addConstraint() → void/Null { |
| this.addToGraph(); |
| self::planner.incrementalAdd(this); |
| } |
| method satisfy(dynamic/core::num* {int,double} mark) → self::Constraint/self::Constraint+ {null,other} { |
| this.chooseMethod(mark); |
| if(!this.isSatisfied()) { |
| if(this.strength.==(self::REQUIRED)) { |
| core::print("Could not satisfy a required constraint!"); |
| } |
| return null; |
| } |
| this.markInputs(mark); |
| self::Variable/self::Variable! {null,other} out = this.output(); |
| self::Constraint/self::Constraint+ {null,other} overridden = out.determinedBy; |
| if(!overridden.==(null)) |
| overridden.markUnsatisfied(); |
| out.determinedBy = this; |
| if(!self::planner.addPropagate(this, mark)) |
| core::print("Cycle encountered"); |
| out.mark = mark; |
| return overridden; |
| } |
| method destroyConstraint() → void/Null { |
| if(this.isSatisfied()) |
| self::planner.incrementalRemove(this); |
| this.removeFromGraph(); |
| } |
| method isInput() → core::bool/core::bool* {other} |
| return false; |
| } |
| abstract class UnaryConstraint extends self::Constraint { |
| final field self::Variable/self::Variable! {null,other} myOutput; |
| field core::bool/core::bool* {other} satisfied = false; |
| constructor •(self::Variable/self::Variable! {null,other} myOutput, self::Strength/self::Strength! {other} strength) → void |
| : self::UnaryConstraint::myOutput = myOutput, super self::Constraint::•(strength) { |
| this.addConstraint(); |
| } |
| method addToGraph() → void/Null { |
| this.myOutput.addConstraint(this); |
| this.satisfied = false; |
| } |
| method chooseMethod(core::int/core::num* {int,double} mark) → void/Null { |
| this.satisfied = !this.myOutput.mark.==(mark) && self::Strength::stronger(this.strength, this.myOutput.walkStrength); |
| } |
| method isSatisfied() → core::bool/core::bool* {other} |
| return this.satisfied; |
| method markInputs(core::int/core::num* {int,double} mark) → void/Null {} |
| method output() → self::Variable/self::Variable! {null,other} |
| return this.myOutput; |
| method recalculate() → void/Null { |
| this.myOutput.walkStrength = this.strength; |
| this.myOutput.stay = !this.isInput(); |
| if(this.myOutput.stay) |
| this.execute(); |
| } |
| method markUnsatisfied() → void/Null { |
| this.satisfied = false; |
| } |
| method inputsKnown(core::int/core::num* {int,double} mark) → core::bool/core::bool* {other} |
| return true; |
| method removeFromGraph() → void/Null { |
| if(!this.myOutput.==(null)) |
| this.myOutput.removeConstraint(this); |
| this.satisfied = false; |
| } |
| } |
| class StayConstraint extends self::UnaryConstraint { |
| constructor •(self::Variable/self::Variable! {null,other} v, self::Strength/self::Strength! {other} str) → void |
| : super self::UnaryConstraint::•(v, str) |
| ; |
| method execute() → void/Null {} |
| } |
| class EditConstraint extends self::UnaryConstraint { |
| constructor •(self::Variable/self::Variable! {null,other} v, self::Strength/self::Strength! {other} str) → void |
| : super self::UnaryConstraint::•(v, str) |
| ; |
| method isInput() → core::bool/core::bool* {other} |
| return true; |
| method execute() → void/Null {} |
| } |
| abstract class BinaryConstraint extends self::Constraint { |
| field self::Variable/self::Variable! {null,other} v1; |
| field self::Variable/self::Variable! {null,other} v2; |
| field core::int/core::int* {int} direction = self::NONE; |
| constructor •(self::Variable/self::Variable! {null,other} v1, self::Variable/self::Variable! {null,other} v2, self::Strength/self::Strength! {other} strength) → void |
| : self::BinaryConstraint::v1 = v1, self::BinaryConstraint::v2 = v2, super self::Constraint::•(strength) { |
| this.addConstraint(); |
| } |
| method chooseMethod(core::int/core::num* {int,double} mark) → void/Null { |
| if(this.v1.mark.==(mark)) { |
| this.direction = !this.v2.mark.==(mark) && self::Strength::stronger(this.strength, this.v2.walkStrength) ? self::FORWARD : self::NONE; |
| } |
| if(this.v2.mark.==(mark)) { |
| this.direction = !this.v1.mark.==(mark) && self::Strength::stronger(this.strength, this.v1.walkStrength) ? self::BACKWARD : self::NONE; |
| } |
| if(self::Strength::weaker(this.v1.walkStrength, this.v2.walkStrength)) { |
| this.direction = self::Strength::stronger(this.strength, this.v1.walkStrength) ? self::BACKWARD : self::NONE; |
| } |
| else { |
| this.direction = self::Strength::stronger(this.strength, this.v2.walkStrength) ? self::FORWARD : self::BACKWARD; |
| } |
| } |
| method addToGraph() → void/Null { |
| this.v1.addConstraint(this); |
| this.v2.addConstraint(this); |
| this.direction = self::NONE; |
| } |
| method isSatisfied() → core::bool/core::bool* {other} |
| return !this.direction.==(self::NONE); |
| method markInputs(core::int/core::num* {int,double} mark) → void/Null { |
| this.input().mark = mark; |
| } |
| method input() → self::Variable/self::Variable! {null,other} |
| return this.direction.==(self::FORWARD) ? this.v1 : this.v2; |
| method output() → self::Variable/self::Variable! {null,other} |
| return this.direction.==(self::FORWARD) ? this.v2 : this.v1; |
| method recalculate() → void/Null { |
| self::Variable/self::Variable! {null,other} ihn = this.input(); |
| self::Variable/self::Variable! {null,other} out = this.output(); |
| out.walkStrength = self::Strength::weakest(this.strength, ihn.walkStrength); |
| out.stay = ihn.stay; |
| if(out.stay) |
| this.execute(); |
| } |
| method markUnsatisfied() → void/Null { |
| this.direction = self::NONE; |
| } |
| method inputsKnown(core::int/core::num* {int,double} mark) → core::bool/core::bool* {other} { |
| self::Variable/self::Variable! {null,other} i = this.input(); |
| return i.mark.==(mark) || i.stay || i.determinedBy.==(null); |
| } |
| method removeFromGraph() → void/Null { |
| if(!this.v1.==(null)) |
| this.v1.removeConstraint(this); |
| if(!this.v2.==(null)) |
| this.v2.removeConstraint(this); |
| this.direction = self::NONE; |
| } |
| } |
| class ScaleConstraint extends self::BinaryConstraint { |
| final field self::Variable/self::Variable! {other} scale; |
| final field self::Variable/self::Variable! {other} offset; |
| constructor •(self::Variable/self::Variable! {null,other} src, self::Variable/self::Variable! {other} scale, self::Variable/self::Variable! {other} offset, self::Variable/self::Variable! {null,other} dest, self::Strength/self::Strength! {other} strength) → void |
| : self::ScaleConstraint::scale = scale, self::ScaleConstraint::offset = offset, super self::BinaryConstraint::•(src, dest, strength) |
| ; |
| method addToGraph() → void/Null { |
| this.{=self::BinaryConstraint::addToGraph}(); |
| this.scale.addConstraint(this); |
| this.offset.addConstraint(this); |
| } |
| method removeFromGraph() → void/Null { |
| this.{=self::BinaryConstraint::removeFromGraph}(); |
| if(!this.scale.==(null)) |
| this.scale.removeConstraint(this); |
| if(!this.offset.==(null)) |
| this.offset.removeConstraint(this); |
| } |
| method markInputs(core::int/core::num* {int,double} mark) → void/Null { |
| this.{=self::BinaryConstraint::markInputs}(mark); |
| this.scale.mark = this.offset.mark = mark; |
| } |
| method execute() → void/Null { |
| if(this.direction.==(self::FORWARD)) { |
| this.v2.value = this.v1.value.*(this.scale.value).+(this.offset.value); |
| } |
| else { |
| this.v1.value = this.v2.value.-(this.offset.value).~/(this.scale.value); |
| } |
| } |
| method recalculate() → void/Null { |
| self::Variable/self::Variable! {null,other} ihn = this.input(); |
| self::Variable/self::Variable! {null,other} out = this.output(); |
| out.walkStrength = self::Strength::weakest(this.strength, ihn.walkStrength); |
| out.stay = ihn.stay && this.scale.stay && this.offset.stay; |
| if(out.stay) |
| this.execute(); |
| } |
| } |
| class EqualityConstraint extends self::BinaryConstraint { |
| constructor •(self::Variable/self::Variable! {null,other} v1, self::Variable/self::Variable! {other} v2, self::Strength/self::Strength! {other} strength) → void |
| : super self::BinaryConstraint::•(v1, v2, strength) |
| ; |
| method execute() → void/Null { |
| this.output().value = this.input().value; |
| } |
| } |
| class Variable extends core::Object { |
| field core::List<self::Constraint>/core::List* {other} constraints = <self::Constraint>[]; |
| field self::Constraint/self::Constraint+ {null,other} determinedBy = null; |
| field core::int/core::num* {int,double} mark = 0; |
| field self::Strength/self::Strength! {other} walkStrength = self::WEAKEST; |
| field core::bool/core::bool* {other} stay = true; |
| field core::int/core::num* {int,double} value; |
| final field core::String/core::String* {string} name; |
| constructor •(core::String/core::String* {string} name, core::int/core::num* {int,double} value) → void |
| : self::Variable::name = name, self::Variable::value = value, super core::Object::•() |
| ; |
| method addConstraint(self::Constraint/self::Constraint+ {other} c) → void/Null { |
| this.constraints.add(c); |
| } |
| method removeConstraint(self::Constraint/self::Constraint+ {other} c) → void/Null { |
| this.constraints.remove(c); |
| if(this.determinedBy.==(c)) |
| this.determinedBy = null; |
| } |
| } |
| class Planner extends core::Object { |
| field core::int/core::num* {int,double} currentMark = 0; |
| constructor •() → void |
| : super core::Object::•() |
| ; |
| method incrementalAdd(self::Constraint/core::Object+ {*} c) → void/Null { |
| core::int/core::num* {int,double} mark = this.newMark(); |
| for (self::Constraint/self::Constraint+ {null,other} overridden = c.satisfy(mark); !overridden.==(null); overridden = overridden.satisfy(mark)) |
| ; |
| } |
| method incrementalRemove(self::Constraint/self::Constraint+ {other} c) → void/Null { |
| self::Variable/self::Variable! {null,other} out = c.output(); |
| c.markUnsatisfied(); |
| c.removeFromGraph(); |
| core::List<self::Constraint>/core::List* {other} unsatisfied = this.removePropagateFrom(out); |
| self::Strength/core::Object+ {*} strength = self::REQUIRED; |
| do { |
| for (core::int/core::num* {int,double} i = 0; i.<(unsatisfied.length); i = i.+(1)) { |
| self::Constraint/core::Object+ {*} u = unsatisfied.[](i); |
| if(u.strength.==(strength)) |
| this.incrementalAdd(u); |
| } |
| strength = strength.nextWeaker(); |
| } |
| while (!strength.==(self::WEAKEST)) |
| } |
| method newMark() → core::int/core::num* {int,double} |
| return this.currentMark = this.currentMark.+(1); |
| method makePlan(core::List<self::Constraint>/core::List* {other} sources) → self::Plan/self::Plan! {other} { |
| core::int/core::num* {int,double} mark = this.newMark(); |
| self::Plan/self::Plan! {other} plan = new self::Plan::•(); |
| core::List<self::Constraint>/core::List* {other} todo = sources; |
| while (todo.length.>(0)) { |
| self::Constraint/core::Object+ {*} c = todo.removeLast(); |
| if(!c.output().mark.==(mark) && c.inputsKnown(mark)) { |
| plan.addConstraint(c); |
| c.output().mark = mark; |
| this.addConstraintsConsumingTo(c.output(), todo); |
| } |
| } |
| return plan; |
| } |
| method extractPlanFromConstraints(core::List<self::Constraint>/core::List* {other} constraints) → self::Plan/self::Plan! {other} { |
| core::List<self::Constraint>/core::List* {other} sources = <self::Constraint>[]; |
| for (core::int/core::num* {int,double} i = 0; i.<(constraints.length); i = i.+(1)) { |
| self::Constraint/core::Object+ {*} c = constraints.[](i); |
| if(c.isInput() && c.isSatisfied()) |
| sources.add(c); |
| } |
| return this.makePlan(sources); |
| } |
| method addPropagate(self::Constraint/self::Constraint+ {other} c, core::int/core::num* {int,double} mark) → core::bool/core::bool* {other} { |
| core::List<self::Constraint>/core::List* {other} todo = <self::Constraint>[c]; |
| while (todo.length.>(0)) { |
| self::Constraint/core::Object+ {*} d = todo.removeLast(); |
| if(d.output().mark.==(mark)) { |
| this.incrementalRemove(c); |
| return false; |
| } |
| d.recalculate(); |
| this.addConstraintsConsumingTo(d.output(), todo); |
| } |
| return true; |
| } |
| method removePropagateFrom(self::Variable/self::Variable! {null,other} out) → core::List<self::Constraint>/core::List* {other} { |
| out.determinedBy = null; |
| out.walkStrength = self::WEAKEST; |
| out.stay = true; |
| core::List<self::Constraint>/core::List* {other} unsatisfied = <self::Constraint>[]; |
| core::List<self::Variable>/core::List* {other} todo = <self::Variable>[out]; |
| while (todo.length.>(0)) { |
| self::Variable/core::Object+ {*} v = todo.removeLast(); |
| for (core::int/core::num* {int,double} i = 0; i.<(v.constraints.length); i = i.+(1)) { |
| self::Constraint/core::Object+ {*} c = v.constraints.[](i); |
| if(!c.isSatisfied()) |
| unsatisfied.add(c); |
| } |
| self::Constraint/self::Constraint+ {null,other} determining = v.determinedBy; |
| for (core::int/core::num* {int,double} i = 0; i.<(v.constraints.length); i = i.+(1)) { |
| self::Constraint/core::Object+ {*} next = v.constraints.[](i); |
| if(!next.==(determining) && next.isSatisfied()) { |
| next.recalculate(); |
| todo.add(next.output()); |
| } |
| } |
| } |
| return unsatisfied; |
| } |
| method addConstraintsConsumingTo(self::Variable/self::Variable! {null,other} v, core::List<self::Constraint>/core::List* {other} coll) → void/Null { |
| self::Constraint/self::Constraint+ {null,other} determining = v.determinedBy; |
| for (core::int/core::num* {int,double} i = 0; i.<(v.constraints.length); i = i.+(1)) { |
| self::Constraint/core::Object+ {*} c = v.constraints.[](i); |
| if(!c.==(determining) && c.isSatisfied()) |
| coll.add(c); |
| } |
| } |
| } |
| class Plan extends core::Object { |
| field core::List<self::Constraint>/core::List* {other} list = <self::Constraint>[]; |
| constructor •() → void |
| : super core::Object::•() |
| ; |
| method addConstraint(self::Constraint/core::Object+ {*} c) → void/Null { |
| this.list.add(c); |
| } |
| method size() → core::int/core::int* {int} |
| return this.list.length; |
| method execute() → void/Null { |
| for (core::int/core::num* {int,double} i = 0; i.<(this.list.length); i = i.+(1)) { |
| this.list.[](i).execute(); |
| } |
| } |
| } |
| static const field dynamic/self::Strength! {other} REQUIRED = const self::Strength::•(0, "required"); |
| static const field dynamic/self::Strength! {other} STRONG_PREFERRED = const self::Strength::•(1, "strongPreferred"); |
| static const field dynamic/self::Strength! {other} PREFERRED = const self::Strength::•(2, "preferred"); |
| static const field dynamic/self::Strength! {other} STRONG_DEFAULT = const self::Strength::•(3, "strongDefault"); |
| static const field dynamic/self::Strength! {other} NORMAL = const self::Strength::•(4, "normal"); |
| static const field dynamic/self::Strength! {other} WEAK_DEFAULT = const self::Strength::•(5, "weakDefault"); |
| static const field dynamic/self::Strength! {other} WEAKEST = const self::Strength::•(6, "weakest"); |
| static const field core::int/core::int* {int} NONE = 1; |
| static const field core::int/core::int* {int} FORWARD = 2; |
| static const field core::int/core::int* {int} BACKWARD = 0; |
| static field self::Planner/self::Planner! {null,other} planner = null; |
| static method main() → dynamic/Null { |
| new self::DeltaBlue::•().run(); |
| } |
| static method chainTest(core::int/core::int* {int} n) → void/Null { |
| self::planner = new self::Planner::•(); |
| self::Variable/self::Variable! {null,other} prev = null; |
| self::Variable/self::Variable! {null,other} first = null; |
| self::Variable/self::Variable! {null,other} last = null; |
| for (core::int/core::num* {int,double} i = 0; i.<=(n); i = i.+(1)) { |
| self::Variable/self::Variable! {other} v = new self::Variable::•("v${i}", 0); |
| if(!prev.==(null)) |
| new self::EqualityConstraint::•(prev, v, self::REQUIRED); |
| if(i.==(0)) |
| first = v; |
| if(i.==(n)) |
| last = v; |
| prev = v; |
| } |
| new self::StayConstraint::•(last, self::STRONG_DEFAULT); |
| self::EditConstraint/self::EditConstraint! {other} edit = new self::EditConstraint::•(first, self::PREFERRED); |
| self::Plan/self::Plan! {other} plan = self::planner.extractPlanFromConstraints(<self::Constraint>[edit]); |
| for (core::int/core::num* {int,double} i = 0; i.<(100); i = 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}."); |
| } |
| } |
| } |
| static method projectionTest(core::int/core::int* {int} n) → void/Null { |
| self::planner = new self::Planner::•(); |
| self::Variable/self::Variable! {other} scale = new self::Variable::•("scale", 10); |
| self::Variable/self::Variable! {other} offset = new self::Variable::•("offset", 1000); |
| self::Variable/self::Variable! {null,other} src = null; |
| self::Variable/self::Variable! {null,other} dst = null; |
| core::List<self::Variable>/core::List* {other} dests = <self::Variable>[]; |
| for (core::int/core::num* {int,double} i = 0; i.<(n); i = i.+(1)) { |
| src = new self::Variable::•("src", i); |
| dst = new self::Variable::•("dst", i); |
| dests.add(dst); |
| new self::StayConstraint::•(src, self::NORMAL); |
| new self::ScaleConstraint::•(src, scale, offset, dst, self::REQUIRED); |
| } |
| self::change(src, 17); |
| if(!dst.value.==(1170)) |
| core::print("Projection 1 failed"); |
| self::change(dst, 1050); |
| if(!src.value.==(5)) |
| core::print("Projection 2 failed"); |
| self::change(scale, 5); |
| for (core::int/core::num* {int,double} i = 0; i.<(n.-(1)); i = i.+(1)) { |
| if(!dests.[](i).value.==(i.*(5).+(1000))) |
| core::print("Projection 3 failed"); |
| } |
| self::change(offset, 2000); |
| for (core::int/core::num* {int,double} i = 0; i.<(n.-(1)); i = i.+(1)) { |
| if(!dests.[](i).value.==(i.*(5).+(2000))) |
| core::print("Projection 4 failed"); |
| } |
| } |
| static method change(self::Variable/self::Variable! {null,other} v, core::int/core::int* {int} newValue) → void/Null { |
| self::EditConstraint/self::EditConstraint! {other} edit = new self::EditConstraint::•(v, self::PREFERRED); |
| self::Plan/self::Plan! {other} plan = self::planner.extractPlanFromConstraints(<self::EditConstraint>[edit]); |
| for (core::int/core::num* {int,double} i = 0; i.<(10); i = i.+(1)) { |
| v.value = newValue; |
| plan.execute(); |
| } |
| edit.destroyConstraint(); |
| } |