| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ssa |
| |
| import ( |
| "fmt" |
| "testing" |
| ) |
| |
| const ( |
| SetOrder = "SetOrder" |
| SetOrder_Fail = "SetOrder_Fail" |
| SetOrderOrEqual = "SetOrderOrEqual" |
| SetOrderOrEqual_Fail = "SetOrderOrEqual_Fail" |
| Ordered = "Ordered" |
| Ordered_Fail = "Ordered_Fail" |
| OrderedOrEqual = "OrderedOrEqual" |
| OrderedOrEqual_Fail = "OrderedOrEqual_Fail" |
| SetEqual = "SetEqual" |
| SetEqual_Fail = "SetEqual_Fail" |
| Equal = "Equal" |
| Equal_Fail = "Equal_Fail" |
| SetNonEqual = "SetNonEqual" |
| SetNonEqual_Fail = "SetNonEqual_Fail" |
| NonEqual = "NonEqual" |
| NonEqual_Fail = "NonEqual_Fail" |
| Checkpoint = "Checkpoint" |
| Undo = "Undo" |
| ) |
| |
| type posetTestOp struct { |
| typ string |
| a, b int |
| } |
| |
| func vconst(i int) int { |
| if i < -128 || i >= 128 { |
| panic("invalid const") |
| } |
| return 1000 + 128 + i |
| } |
| |
| func testPosetOps(t *testing.T, unsigned bool, ops []posetTestOp) { |
| var v [1512]*Value |
| for i := range v { |
| v[i] = new(Value) |
| v[i].ID = ID(i) |
| if i >= 1000 && i < 1256 { |
| v[i].Op = OpConst64 |
| v[i].AuxInt = int64(i - 1000 - 128) |
| } |
| } |
| |
| po := newPoset() |
| po.SetUnsigned(unsigned) |
| for idx, op := range ops { |
| t.Logf("op%d%v", idx, op) |
| switch op.typ { |
| case SetOrder: |
| if !po.SetOrder(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v failed", idx, op) |
| } |
| case SetOrder_Fail: |
| if po.SetOrder(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v passed", idx, op) |
| } |
| case SetOrderOrEqual: |
| if !po.SetOrderOrEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v failed", idx, op) |
| } |
| case SetOrderOrEqual_Fail: |
| if po.SetOrderOrEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v passed", idx, op) |
| } |
| case Ordered: |
| if !po.Ordered(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v failed", idx, op) |
| } |
| case Ordered_Fail: |
| if po.Ordered(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v passed", idx, op) |
| } |
| case OrderedOrEqual: |
| if !po.OrderedOrEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v failed", idx, op) |
| } |
| case OrderedOrEqual_Fail: |
| if po.OrderedOrEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v passed", idx, op) |
| } |
| case SetEqual: |
| if !po.SetEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v failed", idx, op) |
| } |
| case SetEqual_Fail: |
| if po.SetEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v passed", idx, op) |
| } |
| case Equal: |
| if !po.Equal(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v failed", idx, op) |
| } |
| case Equal_Fail: |
| if po.Equal(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v passed", idx, op) |
| } |
| case SetNonEqual: |
| if !po.SetNonEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v failed", idx, op) |
| } |
| case SetNonEqual_Fail: |
| if po.SetNonEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v passed", idx, op) |
| } |
| case NonEqual: |
| if !po.NonEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v failed", idx, op) |
| } |
| case NonEqual_Fail: |
| if po.NonEqual(v[op.a], v[op.b]) { |
| t.Errorf("FAILED: op%d%v passed", idx, op) |
| } |
| case Checkpoint: |
| po.Checkpoint() |
| case Undo: |
| t.Log("Undo stack", po.undo) |
| po.Undo() |
| default: |
| panic("unimplemented") |
| } |
| |
| if false { |
| po.DotDump(fmt.Sprintf("op%d.dot", idx), fmt.Sprintf("Last op: %v", op)) |
| } |
| |
| po.CheckIntegrity() |
| } |
| |
| // Check that the poset is completely empty |
| if err := po.CheckEmpty(); err != nil { |
| t.Error(err) |
| } |
| } |
| |
| func TestPoset(t *testing.T) { |
| testPosetOps(t, false, []posetTestOp{ |
| {Ordered_Fail, 123, 124}, |
| |
| // Dag #0: 100<101 |
| {Checkpoint, 0, 0}, |
| {SetOrder, 100, 101}, |
| {Ordered, 100, 101}, |
| {Ordered_Fail, 101, 100}, |
| {SetOrder_Fail, 101, 100}, |
| {SetOrder, 100, 101}, // repeat |
| {NonEqual, 100, 101}, |
| {NonEqual, 101, 100}, |
| {SetEqual_Fail, 100, 101}, |
| |
| // Dag #1: 4<=7<12 |
| {Checkpoint, 0, 0}, |
| {SetOrderOrEqual, 4, 7}, |
| {OrderedOrEqual, 4, 7}, |
| {SetOrder, 7, 12}, |
| {Ordered, 7, 12}, |
| {Ordered, 4, 12}, |
| {Ordered_Fail, 12, 4}, |
| {NonEqual, 4, 12}, |
| {NonEqual, 12, 4}, |
| {NonEqual_Fail, 4, 100}, |
| {OrderedOrEqual, 4, 12}, |
| {OrderedOrEqual_Fail, 12, 4}, |
| {OrderedOrEqual, 4, 7}, |
| {OrderedOrEqual_Fail, 7, 4}, |
| |
| // Dag #1: 1<4<=7<12 |
| {Checkpoint, 0, 0}, |
| {SetOrder, 1, 4}, |
| {Ordered, 1, 4}, |
| {Ordered, 1, 12}, |
| {Ordered_Fail, 12, 1}, |
| |
| // Dag #1: 1<4<=7<12, 6<7 |
| {Checkpoint, 0, 0}, |
| {SetOrder, 6, 7}, |
| {Ordered, 6, 7}, |
| {Ordered, 6, 12}, |
| {SetOrder_Fail, 7, 4}, |
| {SetOrder_Fail, 7, 6}, |
| {SetOrder_Fail, 7, 1}, |
| |
| // Dag #1: 1<4<=7<12, 1<6<7 |
| {Checkpoint, 0, 0}, |
| {Ordered_Fail, 1, 6}, |
| {SetOrder, 1, 6}, |
| {Ordered, 1, 6}, |
| {SetOrder_Fail, 6, 1}, |
| |
| // Dag #1: 1<4<=7<12, 1<4<6<7 |
| {Checkpoint, 0, 0}, |
| {Ordered_Fail, 4, 6}, |
| {Ordered_Fail, 4, 7}, |
| {SetOrder, 4, 6}, |
| {Ordered, 4, 6}, |
| {OrderedOrEqual, 4, 6}, |
| {Ordered, 4, 7}, |
| {OrderedOrEqual, 4, 7}, |
| {SetOrder_Fail, 6, 4}, |
| {Ordered_Fail, 7, 6}, |
| {Ordered_Fail, 7, 4}, |
| {OrderedOrEqual_Fail, 7, 6}, |
| {OrderedOrEqual_Fail, 7, 4}, |
| |
| // Merge: 1<4<6, 4<=7<12, 6<101 |
| {Checkpoint, 0, 0}, |
| {Ordered_Fail, 6, 101}, |
| {SetOrder, 6, 101}, |
| {Ordered, 6, 101}, |
| {Ordered, 1, 101}, |
| |
| // Merge: 1<4<6, 4<=7<12, 6<100<101 |
| {Checkpoint, 0, 0}, |
| {Ordered_Fail, 6, 100}, |
| {SetOrder, 6, 100}, |
| {Ordered, 1, 100}, |
| |
| // Undo: 1<4<6<7<12, 6<101 |
| {Ordered, 100, 101}, |
| {Undo, 0, 0}, |
| {Ordered, 100, 101}, |
| {Ordered_Fail, 6, 100}, |
| {Ordered, 6, 101}, |
| {Ordered, 1, 101}, |
| |
| // Undo: 1<4<6<7<12, 100<101 |
| {Undo, 0, 0}, |
| {Ordered_Fail, 1, 100}, |
| {Ordered_Fail, 1, 101}, |
| {Ordered_Fail, 6, 100}, |
| {Ordered_Fail, 6, 101}, |
| |
| // Merge: 1<4<6<7<12, 6<100<101 |
| {Checkpoint, 0, 0}, |
| {Ordered, 100, 101}, |
| {SetOrder, 6, 100}, |
| {Ordered, 6, 100}, |
| {Ordered, 6, 101}, |
| {Ordered, 1, 101}, |
| |
| // Undo 2 times: 1<4<7<12, 1<6<7 |
| {Undo, 0, 0}, |
| {Undo, 0, 0}, |
| {Ordered, 1, 6}, |
| {Ordered, 4, 12}, |
| {Ordered_Fail, 4, 6}, |
| {SetOrder_Fail, 6, 1}, |
| |
| // Undo 2 times: 1<4<7<12 |
| {Undo, 0, 0}, |
| {Undo, 0, 0}, |
| {Ordered, 1, 12}, |
| {Ordered, 7, 12}, |
| {Ordered_Fail, 1, 6}, |
| {Ordered_Fail, 6, 7}, |
| {Ordered, 100, 101}, |
| {Ordered_Fail, 1, 101}, |
| |
| // Undo: 4<7<12 |
| {Undo, 0, 0}, |
| {Ordered_Fail, 1, 12}, |
| {Ordered_Fail, 1, 4}, |
| {Ordered, 4, 12}, |
| {Ordered, 100, 101}, |
| |
| // Undo: 100<101 |
| {Undo, 0, 0}, |
| {Ordered_Fail, 4, 7}, |
| {Ordered_Fail, 7, 12}, |
| {Ordered, 100, 101}, |
| |
| // Recreated DAG #1 from scratch, reusing same nodes. |
| // This also stresses that Undo has done its job correctly. |
| // DAG: 1<2<(5|6), 101<102<(105|106<107) |
| {Checkpoint, 0, 0}, |
| {SetOrder, 101, 102}, |
| {SetOrder, 102, 105}, |
| {SetOrder, 102, 106}, |
| {SetOrder, 106, 107}, |
| {SetOrder, 1, 2}, |
| {SetOrder, 2, 5}, |
| {SetOrder, 2, 6}, |
| {SetEqual_Fail, 1, 6}, |
| {SetEqual_Fail, 107, 102}, |
| |
| // Now Set 2 == 102 |
| // New DAG: (1|101)<2==102<(5|6|105|106<107) |
| {Checkpoint, 0, 0}, |
| {SetEqual, 2, 102}, |
| {Equal, 2, 102}, |
| {SetEqual, 2, 102}, // trivially pass |
| {SetNonEqual_Fail, 2, 102}, // trivially fail |
| {Ordered, 1, 107}, |
| {Ordered, 101, 6}, |
| {Ordered, 101, 105}, |
| {Ordered, 2, 106}, |
| {Ordered, 102, 6}, |
| |
| // Undo SetEqual |
| {Undo, 0, 0}, |
| {Equal_Fail, 2, 102}, |
| {Ordered_Fail, 2, 102}, |
| {Ordered_Fail, 1, 107}, |
| {Ordered_Fail, 101, 6}, |
| {Checkpoint, 0, 0}, |
| {SetEqual, 2, 100}, |
| {Ordered, 1, 107}, |
| {Ordered, 100, 6}, |
| |
| // SetEqual with new node |
| {Undo, 0, 0}, |
| {Checkpoint, 0, 0}, |
| {SetEqual, 2, 400}, |
| {SetEqual, 401, 2}, |
| {Equal, 400, 401}, |
| {Ordered, 1, 400}, |
| {Ordered, 400, 6}, |
| {Ordered, 1, 401}, |
| {Ordered, 401, 6}, |
| {Ordered_Fail, 2, 401}, |
| |
| // SetEqual unseen nodes and then connect |
| {Checkpoint, 0, 0}, |
| {SetEqual, 500, 501}, |
| {SetEqual, 102, 501}, |
| {Equal, 500, 102}, |
| {Ordered, 501, 106}, |
| {Ordered, 100, 500}, |
| {SetEqual, 500, 501}, |
| {Ordered_Fail, 500, 501}, |
| {Ordered_Fail, 102, 501}, |
| |
| // SetNonEqual relations |
| {Undo, 0, 0}, |
| {Checkpoint, 0, 0}, |
| {SetNonEqual, 600, 601}, |
| {NonEqual, 600, 601}, |
| {SetNonEqual, 601, 602}, |
| {NonEqual, 601, 602}, |
| {NonEqual_Fail, 600, 602}, // non-transitive |
| {SetEqual_Fail, 601, 602}, |
| |
| // Undo back to beginning, leave the poset empty |
| {Undo, 0, 0}, |
| {Undo, 0, 0}, |
| {Undo, 0, 0}, |
| {Undo, 0, 0}, |
| }) |
| } |
| |
| func TestPosetStrict(t *testing.T) { |
| |
| testPosetOps(t, false, []posetTestOp{ |
| {Checkpoint, 0, 0}, |
| // Build: 20!=30, 10<20<=30<40. The 20<=30 will become 20<30. |
| {SetNonEqual, 20, 30}, |
| {SetOrder, 10, 20}, |
| {SetOrderOrEqual, 20, 30}, // this is affected by 20!=30 |
| {SetOrder, 30, 40}, |
| |
| {Ordered, 10, 30}, |
| {Ordered, 20, 30}, |
| {Ordered, 10, 40}, |
| {OrderedOrEqual, 10, 30}, |
| {OrderedOrEqual, 20, 30}, |
| {OrderedOrEqual, 10, 40}, |
| |
| {Undo, 0, 0}, |
| |
| // Now do the opposite: first build the DAG and then learn non-equality |
| {Checkpoint, 0, 0}, |
| {SetOrder, 10, 20}, |
| {SetOrderOrEqual, 20, 30}, // this is affected by 20!=30 |
| {SetOrder, 30, 40}, |
| |
| {Ordered, 10, 30}, |
| {Ordered_Fail, 20, 30}, |
| {Ordered, 10, 40}, |
| {OrderedOrEqual, 10, 30}, |
| {OrderedOrEqual, 20, 30}, |
| {OrderedOrEqual, 10, 40}, |
| |
| {Checkpoint, 0, 0}, |
| {SetNonEqual, 20, 30}, |
| {Ordered, 10, 30}, |
| {Ordered, 20, 30}, |
| {Ordered, 10, 40}, |
| {OrderedOrEqual, 10, 30}, |
| {OrderedOrEqual, 20, 30}, |
| {OrderedOrEqual, 10, 40}, |
| {Undo, 0, 0}, |
| |
| {Checkpoint, 0, 0}, |
| {SetOrderOrEqual, 30, 35}, |
| {OrderedOrEqual, 20, 35}, |
| {Ordered_Fail, 20, 35}, |
| {SetNonEqual, 20, 35}, |
| {Ordered, 20, 35}, |
| {Undo, 0, 0}, |
| |
| // Learn <= and >= |
| {Checkpoint, 0, 0}, |
| {SetOrderOrEqual, 50, 60}, |
| {SetOrderOrEqual, 60, 50}, |
| {OrderedOrEqual, 50, 60}, |
| {OrderedOrEqual, 60, 50}, |
| {Ordered_Fail, 50, 60}, |
| {Ordered_Fail, 60, 50}, |
| {Equal, 50, 60}, |
| {Equal, 60, 50}, |
| {NonEqual_Fail, 50, 60}, |
| {NonEqual_Fail, 60, 50}, |
| {Undo, 0, 0}, |
| |
| {Undo, 0, 0}, |
| }) |
| } |
| |
| func TestPosetCollapse(t *testing.T) { |
| testPosetOps(t, false, []posetTestOp{ |
| {Checkpoint, 0, 0}, |
| // Create a complex graph of <= relations among nodes between 10 and 25. |
| {SetOrderOrEqual, 10, 15}, |
| {SetOrderOrEqual, 15, 20}, |
| {SetOrderOrEqual, 20, vconst(20)}, |
| {SetOrderOrEqual, vconst(20), 25}, |
| {SetOrderOrEqual, 10, 12}, |
| {SetOrderOrEqual, 12, 16}, |
| {SetOrderOrEqual, 16, vconst(20)}, |
| {SetOrderOrEqual, 10, 17}, |
| {SetOrderOrEqual, 17, 25}, |
| {SetOrderOrEqual, 15, 18}, |
| {SetOrderOrEqual, 18, vconst(20)}, |
| {SetOrderOrEqual, 15, 19}, |
| {SetOrderOrEqual, 19, 25}, |
| |
| // These are other paths not part of the main collapsing path |
| {SetOrderOrEqual, 10, 11}, |
| {SetOrderOrEqual, 11, 26}, |
| {SetOrderOrEqual, 13, 25}, |
| {SetOrderOrEqual, 100, 25}, |
| {SetOrderOrEqual, 101, 15}, |
| {SetOrderOrEqual, 102, 10}, |
| {SetOrderOrEqual, 25, 103}, |
| {SetOrderOrEqual, 20, 104}, |
| |
| {Checkpoint, 0, 0}, |
| // Collapse everything by setting 10 >= 25: this should make everything equal |
| {SetOrderOrEqual, 25, 10}, |
| |
| // Check that all nodes are pairwise equal now |
| {Equal, 10, 12}, |
| {Equal, 10, 15}, |
| {Equal, 10, 16}, |
| {Equal, 10, 17}, |
| {Equal, 10, 18}, |
| {Equal, 10, 19}, |
| {Equal, 10, vconst(20)}, |
| {Equal, 10, 25}, |
| |
| {Equal, 12, 15}, |
| {Equal, 12, 16}, |
| {Equal, 12, 17}, |
| {Equal, 12, 18}, |
| {Equal, 12, 19}, |
| {Equal, 12, vconst(20)}, |
| {Equal, 12, 25}, |
| |
| {Equal, 15, 16}, |
| {Equal, 15, 17}, |
| {Equal, 15, 18}, |
| {Equal, 15, 19}, |
| {Equal, 15, vconst(20)}, |
| {Equal, 15, 25}, |
| |
| {Equal, 16, 17}, |
| {Equal, 16, 18}, |
| {Equal, 16, 19}, |
| {Equal, 16, vconst(20)}, |
| {Equal, 16, 25}, |
| |
| {Equal, 17, 18}, |
| {Equal, 17, 19}, |
| {Equal, 17, vconst(20)}, |
| {Equal, 17, 25}, |
| |
| {Equal, 18, 19}, |
| {Equal, 18, vconst(20)}, |
| {Equal, 18, 25}, |
| |
| {Equal, 19, vconst(20)}, |
| {Equal, 19, 25}, |
| |
| {Equal, vconst(20), 25}, |
| |
| // ... but not 11/26/100/101/102, which were on a different path |
| {Equal_Fail, 10, 11}, |
| {Equal_Fail, 10, 26}, |
| {Equal_Fail, 10, 100}, |
| {Equal_Fail, 10, 101}, |
| {Equal_Fail, 10, 102}, |
| {OrderedOrEqual, 10, 26}, |
| {OrderedOrEqual, 25, 26}, |
| {OrderedOrEqual, 13, 25}, |
| {OrderedOrEqual, 13, 10}, |
| |
| {Undo, 0, 0}, |
| {OrderedOrEqual, 10, 25}, |
| {Equal_Fail, 10, 12}, |
| {Equal_Fail, 10, 15}, |
| {Equal_Fail, 10, 25}, |
| |
| {Undo, 0, 0}, |
| }) |
| |
| testPosetOps(t, false, []posetTestOp{ |
| {Checkpoint, 0, 0}, |
| {SetOrderOrEqual, 10, 15}, |
| {SetOrderOrEqual, 15, 20}, |
| {SetOrderOrEqual, 20, 25}, |
| {SetOrder, 10, 16}, |
| {SetOrderOrEqual, 16, 20}, |
| // Check that we cannot collapse here because of the strict relation 10<16 |
| {SetOrderOrEqual_Fail, 20, 10}, |
| {Undo, 0, 0}, |
| }) |
| } |
| |
| func TestPosetSetEqual(t *testing.T) { |
| testPosetOps(t, false, []posetTestOp{ |
| // 10<=20<=30<40, 20<=100<110 |
| {Checkpoint, 0, 0}, |
| {SetOrderOrEqual, 10, 20}, |
| {SetOrderOrEqual, 20, 30}, |
| {SetOrder, 30, 40}, |
| {SetOrderOrEqual, 20, 100}, |
| {SetOrder, 100, 110}, |
| {OrderedOrEqual, 10, 30}, |
| {OrderedOrEqual_Fail, 30, 10}, |
| {Ordered_Fail, 10, 30}, |
| {Ordered_Fail, 30, 10}, |
| {Ordered, 10, 40}, |
| {Ordered_Fail, 40, 10}, |
| |
| // Try learning 10==20. |
| {Checkpoint, 0, 0}, |
| {SetEqual, 10, 20}, |
| {OrderedOrEqual, 10, 20}, |
| {Ordered_Fail, 10, 20}, |
| {Equal, 10, 20}, |
| {SetOrderOrEqual, 10, 20}, |
| {SetOrderOrEqual, 20, 10}, |
| {SetOrder_Fail, 10, 20}, |
| {SetOrder_Fail, 20, 10}, |
| {Undo, 0, 0}, |
| |
| // Try learning 20==10. |
| {Checkpoint, 0, 0}, |
| {SetEqual, 20, 10}, |
| {OrderedOrEqual, 10, 20}, |
| {Ordered_Fail, 10, 20}, |
| {Equal, 10, 20}, |
| {Undo, 0, 0}, |
| |
| // Try learning 10==40 or 30==40 or 10==110. |
| {Checkpoint, 0, 0}, |
| {SetEqual_Fail, 10, 40}, |
| {SetEqual_Fail, 40, 10}, |
| {SetEqual_Fail, 30, 40}, |
| {SetEqual_Fail, 40, 30}, |
| {SetEqual_Fail, 10, 110}, |
| {SetEqual_Fail, 110, 10}, |
| {Undo, 0, 0}, |
| |
| // Try learning 40==110, and then 10==40 or 10=110 |
| {Checkpoint, 0, 0}, |
| {SetEqual, 40, 110}, |
| {SetEqual_Fail, 10, 40}, |
| {SetEqual_Fail, 40, 10}, |
| {SetEqual_Fail, 10, 110}, |
| {SetEqual_Fail, 110, 10}, |
| {Undo, 0, 0}, |
| |
| // Try learning 40<20 or 30<20 or 110<10 |
| {Checkpoint, 0, 0}, |
| {SetOrder_Fail, 40, 20}, |
| {SetOrder_Fail, 30, 20}, |
| {SetOrder_Fail, 110, 10}, |
| {Undo, 0, 0}, |
| |
| // Try learning 30<=20 |
| {Checkpoint, 0, 0}, |
| {SetOrderOrEqual, 30, 20}, |
| {Equal, 30, 20}, |
| {OrderedOrEqual, 30, 100}, |
| {Ordered, 30, 110}, |
| {Undo, 0, 0}, |
| |
| {Undo, 0, 0}, |
| }) |
| } |
| |
| func TestPosetNonEqual(t *testing.T) { |
| testPosetOps(t, false, []posetTestOp{ |
| {Equal_Fail, 10, 20}, |
| {NonEqual_Fail, 10, 20}, |
| |
| // Learn 10!=20 |
| {Checkpoint, 0, 0}, |
| {SetNonEqual, 10, 20}, |
| {Equal_Fail, 10, 20}, |
| {NonEqual, 10, 20}, |
| {SetEqual_Fail, 10, 20}, |
| |
| // Learn again 10!=20 |
| {Checkpoint, 0, 0}, |
| {SetNonEqual, 10, 20}, |
| {Equal_Fail, 10, 20}, |
| {NonEqual, 10, 20}, |
| |
| // Undo. We still know 10!=20 |
| {Undo, 0, 0}, |
| {Equal_Fail, 10, 20}, |
| {NonEqual, 10, 20}, |
| {SetEqual_Fail, 10, 20}, |
| |
| // Undo again. Now we know nothing |
| {Undo, 0, 0}, |
| {Equal_Fail, 10, 20}, |
| {NonEqual_Fail, 10, 20}, |
| |
| // Learn 10==20 |
| {Checkpoint, 0, 0}, |
| {SetEqual, 10, 20}, |
| {Equal, 10, 20}, |
| {NonEqual_Fail, 10, 20}, |
| {SetNonEqual_Fail, 10, 20}, |
| |
| // Learn again 10==20 |
| {Checkpoint, 0, 0}, |
| {SetEqual, 10, 20}, |
| {Equal, 10, 20}, |
| {NonEqual_Fail, 10, 20}, |
| {SetNonEqual_Fail, 10, 20}, |
| |
| // Undo. We still know 10==20 |
| {Undo, 0, 0}, |
| {Equal, 10, 20}, |
| {NonEqual_Fail, 10, 20}, |
| {SetNonEqual_Fail, 10, 20}, |
| |
| // Undo. We know nothing |
| {Undo, 0, 0}, |
| {Equal_Fail, 10, 20}, |
| {NonEqual_Fail, 10, 20}, |
| }) |
| } |