| OLD | NEW |
| (Empty) |
| 1 library; | |
| 2 import self as self; | |
| 3 import "dart:core" as core; | |
| 4 | |
| 5 class DeltaBlue extends core::Object { | |
| 6 constructor •() → void | |
| 7 : super core::Object::•() | |
| 8 ; | |
| 9 method run() → void { | |
| 10 self::chainTest(100); | |
| 11 self::projectionTest(100); | |
| 12 } | |
| 13 } | |
| 14 class Strength extends core::Object { | |
| 15 final field core::int value; | |
| 16 final field core::String name; | |
| 17 const constructor •(core::int value, core::String name) → void | |
| 18 : self::Strength::value = value, self::Strength::name = name, super core::Ob
ject::•() | |
| 19 ; | |
| 20 method nextWeaker() → self::Strength | |
| 21 return const <self::Strength>[self::STRONG_PREFERRED, self::PREFERRED, self:
:STRONG_DEFAULT, self::NORMAL, self::WEAK_DEFAULT, self::WEAKEST].[](this.value)
; | |
| 22 static method stronger(self::Strength s1, self::Strength s2) → core::bool { | |
| 23 return s1.value.<(s2.value); | |
| 24 } | |
| 25 static method weaker(self::Strength s1, self::Strength s2) → core::bool { | |
| 26 return s1.value.>(s2.value); | |
| 27 } | |
| 28 static method weakest(self::Strength s1, self::Strength s2) → self::Strength { | |
| 29 return self::Strength::weaker(s1, s2) ? s1 : s2; | |
| 30 } | |
| 31 static method strongest(self::Strength s1, self::Strength s2) → self::Strength
{ | |
| 32 return self::Strength::stronger(s1, s2) ? s1 : s2; | |
| 33 } | |
| 34 } | |
| 35 abstract class Constraint extends core::Object { | |
| 36 final field self::Strength strength; | |
| 37 const constructor •(self::Strength strength) → void | |
| 38 : self::Constraint::strength = strength, super core::Object::•() | |
| 39 ; | |
| 40 abstract method isSatisfied() → core::bool; | |
| 41 abstract method markUnsatisfied() → void; | |
| 42 abstract method addToGraph() → void; | |
| 43 abstract method removeFromGraph() → void; | |
| 44 abstract method chooseMethod(core::int mark) → void; | |
| 45 abstract method markInputs(core::int mark) → void; | |
| 46 abstract method inputsKnown(core::int mark) → core::bool; | |
| 47 abstract method output() → self::Variable; | |
| 48 abstract method execute() → void; | |
| 49 abstract method recalculate() → void; | |
| 50 method addConstraint() → void { | |
| 51 this.addToGraph(); | |
| 52 self::planner.incrementalAdd(this); | |
| 53 } | |
| 54 method satisfy(dynamic mark) → self::Constraint { | |
| 55 this.chooseMethod(mark); | |
| 56 if(!this.isSatisfied()) { | |
| 57 if(this.strength.==(self::REQUIRED)) { | |
| 58 core::print("Could not satisfy a required constraint!"); | |
| 59 } | |
| 60 return null; | |
| 61 } | |
| 62 this.markInputs(mark); | |
| 63 self::Variable out = this.output(); | |
| 64 self::Constraint overridden = out.determinedBy; | |
| 65 if(!overridden.==(null)) | |
| 66 overridden.markUnsatisfied(); | |
| 67 out.determinedBy = this; | |
| 68 if(!self::planner.addPropagate(this, mark)) | |
| 69 core::print("Cycle encountered"); | |
| 70 out.mark = mark; | |
| 71 return overridden; | |
| 72 } | |
| 73 method destroyConstraint() → void { | |
| 74 if(this.isSatisfied()) | |
| 75 self::planner.incrementalRemove(this); | |
| 76 this.removeFromGraph(); | |
| 77 } | |
| 78 method isInput() → core::bool | |
| 79 return false; | |
| 80 } | |
| 81 abstract class UnaryConstraint extends self::Constraint { | |
| 82 final field self::Variable myOutput; | |
| 83 field core::bool satisfied = false; | |
| 84 constructor •(self::Variable myOutput, self::Strength strength) → void | |
| 85 : self::UnaryConstraint::myOutput = myOutput, super self::Constraint::•(stre
ngth) { | |
| 86 this.addConstraint(); | |
| 87 } | |
| 88 method addToGraph() → void { | |
| 89 this.myOutput.addConstraint(this); | |
| 90 this.satisfied = false; | |
| 91 } | |
| 92 method chooseMethod(core::int mark) → void { | |
| 93 this.satisfied = !this.myOutput.mark.==(mark) && self::Strength::stronger(th
is.strength, this.myOutput.walkStrength); | |
| 94 } | |
| 95 method isSatisfied() → core::bool | |
| 96 return this.satisfied; | |
| 97 method markInputs(core::int mark) → void {} | |
| 98 method output() → self::Variable | |
| 99 return this.myOutput; | |
| 100 method recalculate() → void { | |
| 101 this.myOutput.walkStrength = this.strength; | |
| 102 this.myOutput.stay = !this.isInput(); | |
| 103 if(this.myOutput.stay) | |
| 104 this.execute(); | |
| 105 } | |
| 106 method markUnsatisfied() → void { | |
| 107 this.satisfied = false; | |
| 108 } | |
| 109 method inputsKnown(core::int mark) → core::bool | |
| 110 return true; | |
| 111 method removeFromGraph() → void { | |
| 112 if(!this.myOutput.==(null)) | |
| 113 this.myOutput.removeConstraint(this); | |
| 114 this.satisfied = false; | |
| 115 } | |
| 116 } | |
| 117 class StayConstraint extends self::UnaryConstraint { | |
| 118 constructor •(self::Variable v, self::Strength str) → void | |
| 119 : super self::UnaryConstraint::•(v, str) | |
| 120 ; | |
| 121 method execute() → void {} | |
| 122 } | |
| 123 class EditConstraint extends self::UnaryConstraint { | |
| 124 constructor •(self::Variable v, self::Strength str) → void | |
| 125 : super self::UnaryConstraint::•(v, str) | |
| 126 ; | |
| 127 method isInput() → core::bool | |
| 128 return true; | |
| 129 method execute() → void {} | |
| 130 } | |
| 131 abstract class BinaryConstraint extends self::Constraint { | |
| 132 field self::Variable v1; | |
| 133 field self::Variable v2; | |
| 134 field core::int direction = self::NONE; | |
| 135 constructor •(self::Variable v1, self::Variable v2, self::Strength strength) →
void | |
| 136 : self::BinaryConstraint::v1 = v1, self::BinaryConstraint::v2 = v2, super se
lf::Constraint::•(strength) { | |
| 137 this.addConstraint(); | |
| 138 } | |
| 139 method chooseMethod(core::int mark) → void { | |
| 140 if(this.v1.mark.==(mark)) { | |
| 141 this.direction = !this.v2.mark.==(mark) && self::Strength::stronger(this.s
trength, this.v2.walkStrength) ? self::FORWARD : self::NONE; | |
| 142 } | |
| 143 if(this.v2.mark.==(mark)) { | |
| 144 this.direction = !this.v1.mark.==(mark) && self::Strength::stronger(this.s
trength, this.v1.walkStrength) ? self::BACKWARD : self::NONE; | |
| 145 } | |
| 146 if(self::Strength::weaker(this.v1.walkStrength, this.v2.walkStrength)) { | |
| 147 this.direction = self::Strength::stronger(this.strength, this.v1.walkStren
gth) ? self::BACKWARD : self::NONE; | |
| 148 } | |
| 149 else { | |
| 150 this.direction = self::Strength::stronger(this.strength, this.v2.walkStren
gth) ? self::FORWARD : self::BACKWARD; | |
| 151 } | |
| 152 } | |
| 153 method addToGraph() → void { | |
| 154 this.v1.addConstraint(this); | |
| 155 this.v2.addConstraint(this); | |
| 156 this.direction = self::NONE; | |
| 157 } | |
| 158 method isSatisfied() → core::bool | |
| 159 return !this.direction.==(self::NONE); | |
| 160 method markInputs(core::int mark) → void { | |
| 161 this.input().mark = mark; | |
| 162 } | |
| 163 method input() → self::Variable | |
| 164 return this.direction.==(self::FORWARD) ? this.v1 : this.v2; | |
| 165 method output() → self::Variable | |
| 166 return this.direction.==(self::FORWARD) ? this.v2 : this.v1; | |
| 167 method recalculate() → void { | |
| 168 self::Variable ihn = this.input(); | |
| 169 self::Variable out = this.output(); | |
| 170 out.walkStrength = self::Strength::weakest(this.strength, ihn.walkStrength); | |
| 171 out.stay = ihn.stay; | |
| 172 if(out.stay) | |
| 173 this.execute(); | |
| 174 } | |
| 175 method markUnsatisfied() → void { | |
| 176 this.direction = self::NONE; | |
| 177 } | |
| 178 method inputsKnown(core::int mark) → core::bool { | |
| 179 self::Variable i = this.input(); | |
| 180 return i.mark.==(mark) || i.stay || i.determinedBy.==(null); | |
| 181 } | |
| 182 method removeFromGraph() → void { | |
| 183 if(!this.v1.==(null)) | |
| 184 this.v1.removeConstraint(this); | |
| 185 if(!this.v2.==(null)) | |
| 186 this.v2.removeConstraint(this); | |
| 187 this.direction = self::NONE; | |
| 188 } | |
| 189 } | |
| 190 class ScaleConstraint extends self::BinaryConstraint { | |
| 191 final field self::Variable scale; | |
| 192 final field self::Variable offset; | |
| 193 constructor •(self::Variable src, self::Variable scale, self::Variable offset,
self::Variable dest, self::Strength strength) → void | |
| 194 : self::ScaleConstraint::scale = scale, self::ScaleConstraint::offset = offs
et, super self::BinaryConstraint::•(src, dest, strength) | |
| 195 ; | |
| 196 method addToGraph() → void { | |
| 197 this.{=self::BinaryConstraint::addToGraph}(); | |
| 198 this.scale.addConstraint(this); | |
| 199 this.offset.addConstraint(this); | |
| 200 } | |
| 201 method removeFromGraph() → void { | |
| 202 this.{=self::BinaryConstraint::removeFromGraph}(); | |
| 203 if(!this.scale.==(null)) | |
| 204 this.scale.removeConstraint(this); | |
| 205 if(!this.offset.==(null)) | |
| 206 this.offset.removeConstraint(this); | |
| 207 } | |
| 208 method markInputs(core::int mark) → void { | |
| 209 this.{=self::BinaryConstraint::markInputs}(mark); | |
| 210 this.scale.mark = this.offset.mark = mark; | |
| 211 } | |
| 212 method execute() → void { | |
| 213 if(this.direction.==(self::FORWARD)) { | |
| 214 this.v2.value = this.v1.value.*(this.scale.value).+(this.offset.value); | |
| 215 } | |
| 216 else { | |
| 217 this.v1.value = this.v2.value.-(this.offset.value).~/(this.scale.value); | |
| 218 } | |
| 219 } | |
| 220 method recalculate() → void { | |
| 221 self::Variable ihn = this.input(); | |
| 222 self::Variable out = this.output(); | |
| 223 out.walkStrength = self::Strength::weakest(this.strength, ihn.walkStrength); | |
| 224 out.stay = ihn.stay && this.scale.stay && this.offset.stay; | |
| 225 if(out.stay) | |
| 226 this.execute(); | |
| 227 } | |
| 228 } | |
| 229 class EqualityConstraint extends self::BinaryConstraint { | |
| 230 constructor •(self::Variable v1, self::Variable v2, self::Strength strength) →
void | |
| 231 : super self::BinaryConstraint::•(v1, v2, strength) | |
| 232 ; | |
| 233 method execute() → void { | |
| 234 this.output().value = this.input().value; | |
| 235 } | |
| 236 } | |
| 237 class Variable extends core::Object { | |
| 238 field core::List<self::Constraint> constraints = <self::Constraint>[]; | |
| 239 field self::Constraint determinedBy = null; | |
| 240 field core::int mark = 0; | |
| 241 field self::Strength walkStrength = self::WEAKEST; | |
| 242 field core::bool stay = true; | |
| 243 field core::int value; | |
| 244 final field core::String name; | |
| 245 constructor •(core::String name, core::int value) → void | |
| 246 : self::Variable::name = name, self::Variable::value = value, super core::Ob
ject::•() | |
| 247 ; | |
| 248 method addConstraint(self::Constraint c) → void { | |
| 249 this.constraints.add(c); | |
| 250 } | |
| 251 method removeConstraint(self::Constraint c) → void { | |
| 252 this.constraints.remove(c); | |
| 253 if(this.determinedBy.==(c)) | |
| 254 this.determinedBy = null; | |
| 255 } | |
| 256 } | |
| 257 class Planner extends core::Object { | |
| 258 field core::int currentMark = 0; | |
| 259 constructor •() → void | |
| 260 : super core::Object::•() | |
| 261 ; | |
| 262 method incrementalAdd(self::Constraint c) → void { | |
| 263 core::int mark = this.newMark(); | |
| 264 for (self::Constraint overridden = c.satisfy(mark); !overridden.==(null); ov
erridden = overridden.satisfy(mark)) | |
| 265 ; | |
| 266 } | |
| 267 method incrementalRemove(self::Constraint c) → void { | |
| 268 self::Variable out = c.output(); | |
| 269 c.markUnsatisfied(); | |
| 270 c.removeFromGraph(); | |
| 271 core::List<self::Constraint> unsatisfied = this.removePropagateFrom(out); | |
| 272 self::Strength strength = self::REQUIRED; | |
| 273 do { | |
| 274 for (core::int i = 0; i.<(unsatisfied.length); i = i.+(1)) { | |
| 275 self::Constraint u = unsatisfied.[](i); | |
| 276 if(u.strength.==(strength)) | |
| 277 this.incrementalAdd(u); | |
| 278 } | |
| 279 strength = strength.nextWeaker(); | |
| 280 } | |
| 281 while (!strength.==(self::WEAKEST)) | |
| 282 } | |
| 283 method newMark() → core::int | |
| 284 return this.currentMark = this.currentMark.+(1); | |
| 285 method makePlan(core::List<self::Constraint> sources) → self::Plan { | |
| 286 core::int mark = this.newMark(); | |
| 287 self::Plan plan = new self::Plan::•(); | |
| 288 core::List<self::Constraint> todo = sources; | |
| 289 while (todo.length.>(0)) { | |
| 290 self::Constraint c = todo.removeLast(); | |
| 291 if(!c.output().mark.==(mark) && c.inputsKnown(mark)) { | |
| 292 plan.addConstraint(c); | |
| 293 c.output().mark = mark; | |
| 294 this.addConstraintsConsumingTo(c.output(), todo); | |
| 295 } | |
| 296 } | |
| 297 return plan; | |
| 298 } | |
| 299 method extractPlanFromConstraints(core::List<self::Constraint> constraints) →
self::Plan { | |
| 300 core::List<self::Constraint> sources = <self::Constraint>[]; | |
| 301 for (core::int i = 0; i.<(constraints.length); i = i.+(1)) { | |
| 302 self::Constraint c = constraints.[](i); | |
| 303 if(c.isInput() && c.isSatisfied()) | |
| 304 sources.add(c); | |
| 305 } | |
| 306 return this.makePlan(sources); | |
| 307 } | |
| 308 method addPropagate(self::Constraint c, core::int mark) → core::bool { | |
| 309 core::List<self::Constraint> todo = <self::Constraint>[c]; | |
| 310 while (todo.length.>(0)) { | |
| 311 self::Constraint d = todo.removeLast(); | |
| 312 if(d.output().mark.==(mark)) { | |
| 313 this.incrementalRemove(c); | |
| 314 return false; | |
| 315 } | |
| 316 d.recalculate(); | |
| 317 this.addConstraintsConsumingTo(d.output(), todo); | |
| 318 } | |
| 319 return true; | |
| 320 } | |
| 321 method removePropagateFrom(self::Variable out) → core::List<self::Constraint>
{ | |
| 322 out.determinedBy = null; | |
| 323 out.walkStrength = self::WEAKEST; | |
| 324 out.stay = true; | |
| 325 core::List<self::Constraint> unsatisfied = <self::Constraint>[]; | |
| 326 core::List<self::Variable> todo = <self::Variable>[out]; | |
| 327 while (todo.length.>(0)) { | |
| 328 self::Variable v = todo.removeLast(); | |
| 329 for (core::int i = 0; i.<(v.constraints.length); i = i.+(1)) { | |
| 330 self::Constraint c = v.constraints.[](i); | |
| 331 if(!c.isSatisfied()) | |
| 332 unsatisfied.add(c); | |
| 333 } | |
| 334 self::Constraint determining = v.determinedBy; | |
| 335 for (core::int i = 0; i.<(v.constraints.length); i = i.+(1)) { | |
| 336 self::Constraint next = v.constraints.[](i); | |
| 337 if(!next.==(determining) && next.isSatisfied()) { | |
| 338 next.recalculate(); | |
| 339 todo.add(next.output()); | |
| 340 } | |
| 341 } | |
| 342 } | |
| 343 return unsatisfied; | |
| 344 } | |
| 345 method addConstraintsConsumingTo(self::Variable v, core::List<self::Constraint
> coll) → void { | |
| 346 self::Constraint determining = v.determinedBy; | |
| 347 for (core::int i = 0; i.<(v.constraints.length); i = i.+(1)) { | |
| 348 self::Constraint c = v.constraints.[](i); | |
| 349 if(!c.==(determining) && c.isSatisfied()) | |
| 350 coll.add(c); | |
| 351 } | |
| 352 } | |
| 353 } | |
| 354 class Plan extends core::Object { | |
| 355 field core::List<self::Constraint> list = <self::Constraint>[]; | |
| 356 constructor •() → void | |
| 357 : super core::Object::•() | |
| 358 ; | |
| 359 method addConstraint(self::Constraint c) → void { | |
| 360 this.list.add(c); | |
| 361 } | |
| 362 method size() → core::int | |
| 363 return this.list.length; | |
| 364 method execute() → void { | |
| 365 for (core::int i = 0; i.<(this.list.length); i = i.+(1)) { | |
| 366 this.list.[](i).execute(); | |
| 367 } | |
| 368 } | |
| 369 } | |
| 370 static const field dynamic REQUIRED = const self::Strength::•(0, "required"); | |
| 371 static const field dynamic STRONG_PREFERRED = const self::Strength::•(1, "strong
Preferred"); | |
| 372 static const field dynamic PREFERRED = const self::Strength::•(2, "preferred"); | |
| 373 static const field dynamic STRONG_DEFAULT = const self::Strength::•(3, "strongDe
fault"); | |
| 374 static const field dynamic NORMAL = const self::Strength::•(4, "normal"); | |
| 375 static const field dynamic WEAK_DEFAULT = const self::Strength::•(5, "weakDefaul
t"); | |
| 376 static const field dynamic WEAKEST = const self::Strength::•(6, "weakest"); | |
| 377 static const field core::int NONE = 1; | |
| 378 static const field core::int FORWARD = 2; | |
| 379 static const field core::int BACKWARD = 0; | |
| 380 static field self::Planner planner = null; | |
| 381 static method main() → dynamic { | |
| 382 new self::DeltaBlue::•().run(); | |
| 383 } | |
| 384 static method chainTest(core::int n) → void { | |
| 385 self::planner = new self::Planner::•(); | |
| 386 self::Variable prev = null; | |
| 387 self::Variable first = null; | |
| 388 self::Variable last = null; | |
| 389 for (core::int i = 0; i.<=(n); i = i.+(1)) { | |
| 390 self::Variable v = new self::Variable::•("v${i}", 0); | |
| 391 if(!prev.==(null)) | |
| 392 new self::EqualityConstraint::•(prev, v, self::REQUIRED); | |
| 393 if(i.==(0)) | |
| 394 first = v; | |
| 395 if(i.==(n)) | |
| 396 last = v; | |
| 397 prev = v; | |
| 398 } | |
| 399 new self::StayConstraint::•(last, self::STRONG_DEFAULT); | |
| 400 self::EditConstraint edit = new self::EditConstraint::•(first, self::PREFERRED
); | |
| 401 self::Plan plan = self::planner.extractPlanFromConstraints(<self::Constraint>[
edit]); | |
| 402 for (core::int i = 0; i.<(100); i = i.+(1)) { | |
| 403 first.value = i; | |
| 404 plan.execute(); | |
| 405 if(!last.value.==(i)) { | |
| 406 core::print("Chain test failed:"); | |
| 407 core::print("Expected last value to be ${i} but it was ${last.value}."); | |
| 408 } | |
| 409 } | |
| 410 } | |
| 411 static method projectionTest(core::int n) → void { | |
| 412 self::planner = new self::Planner::•(); | |
| 413 self::Variable scale = new self::Variable::•("scale", 10); | |
| 414 self::Variable offset = new self::Variable::•("offset", 1000); | |
| 415 self::Variable src = null; | |
| 416 self::Variable dst = null; | |
| 417 core::List<self::Variable> dests = <self::Variable>[]; | |
| 418 for (core::int i = 0; i.<(n); i = i.+(1)) { | |
| 419 src = new self::Variable::•("src", i); | |
| 420 dst = new self::Variable::•("dst", i); | |
| 421 dests.add(dst); | |
| 422 new self::StayConstraint::•(src, self::NORMAL); | |
| 423 new self::ScaleConstraint::•(src, scale, offset, dst, self::REQUIRED); | |
| 424 } | |
| 425 self::change(src, 17); | |
| 426 if(!dst.value.==(1170)) | |
| 427 core::print("Projection 1 failed"); | |
| 428 self::change(dst, 1050); | |
| 429 if(!src.value.==(5)) | |
| 430 core::print("Projection 2 failed"); | |
| 431 self::change(scale, 5); | |
| 432 for (core::int i = 0; i.<(n.-(1)); i = i.+(1)) { | |
| 433 if(!dests.[](i).value.==(i.*(5).+(1000))) | |
| 434 core::print("Projection 3 failed"); | |
| 435 } | |
| 436 self::change(offset, 2000); | |
| 437 for (core::int i = 0; i.<(n.-(1)); i = i.+(1)) { | |
| 438 if(!dests.[](i).value.==(i.*(5).+(2000))) | |
| 439 core::print("Projection 4 failed"); | |
| 440 } | |
| 441 } | |
| 442 static method change(self::Variable v, core::int newValue) → void { | |
| 443 self::EditConstraint edit = new self::EditConstraint::•(v, self::PREFERRED); | |
| 444 self::Plan plan = self::planner.extractPlanFromConstraints(<self::EditConstrai
nt>[edit]); | |
| 445 for (core::int i = 0; i.<(10); i = i.+(1)) { | |
| 446 v.value = newValue; | |
| 447 plan.execute(); | |
| 448 } | |
| 449 edit.destroyConstraint(); | |
| 450 } | |
| OLD | NEW |