summaryrefslogtreecommitdiff
path: root/Test/dafny1/SchorrWaite.dfy
blob: b29a6829e9aaeecb999b84e7e29acb594f089d69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
// RUN: %diff "%s.expect" "%t"

// Rustan Leino
// 7 November 2008
// Schorr-Waite and other marking algorithms, written and verified in Dafny.
// Copyright (c) 2008, Microsoft.

class Node {
  var children: seq<Node>
  var marked: bool
  var childrenVisited: int
  ghost var pathFromRoot: Path
}

datatype Path = Empty | Extend(Path, Node)


class Main {
  method RecursiveMark(root: Node, ghost S: set<Node>)
    requires root in S
    // S is closed under 'children':
    requires forall n :: n in S ==> n != null &&
                forall ch :: ch in n.children ==> ch == null || ch in S
    requires forall n :: n in S ==> !n.marked && n.childrenVisited == 0
    modifies S
    ensures root.marked
    // nodes reachable from 'root' are marked:
    ensures forall n :: n in S && n.marked ==>
                forall ch :: ch in n.children && ch != null ==> ch.marked
    ensures forall n :: n in S ==>
                n.childrenVisited == old(n.childrenVisited) &&
                n.children == old(n.children)
  {
    RecursiveMarkWorker(root, S, {});
  }

  method RecursiveMarkWorker(root: Node, ghost S: set<Node>, ghost stackNodes: set<Node>)
    requires root != null && root in S
    requires forall n :: n in S ==> n != null &&
                forall ch :: ch in n.children ==> ch == null || ch in S
    requires forall n :: n in S && n.marked ==>
                n in stackNodes ||
                forall ch :: ch in n.children && ch != null ==> ch.marked
    requires forall n :: n in stackNodes ==> n != null && n.marked
    modifies S
    ensures root.marked
    // nodes reachable from 'root' are marked:
    ensures forall n :: n in S && n.marked ==>
                n in stackNodes ||
                forall ch :: ch in n.children && ch != null ==> ch.marked
    ensures forall n: Node :: n in S && old(n.marked) ==> n.marked
    ensures forall n :: n in S ==>
                n.childrenVisited == old(n.childrenVisited) &&
                n.children == old(n.children)
    decreases S - stackNodes
  {
    if !root.marked {
      root.marked := true;
      var i := 0;
      while i < |root.children|
        invariant root.marked && i <= |root.children|
        invariant forall n :: n in S && n.marked ==>
                n == root ||
                n in stackNodes ||
                forall ch :: ch in n.children && ch != null ==> ch.marked
        invariant forall j :: 0 <= j < i ==>
                    root.children[j] == null || root.children[j].marked
        invariant forall n: Node :: n in S && old(n.marked) ==> n.marked
        invariant forall n :: n in S ==>
                n.childrenVisited == old(n.childrenVisited) &&
                n.children == old(n.children)
      {
        var c := root.children[i];
        if c != null {
          RecursiveMarkWorker(c, S, stackNodes + {root});
        }
        i := i + 1;
      }
    }
  }

  // ---------------------------------------------------------------------------------

  method IterativeMark(root: Node, ghost S: set<Node>)
    requires root in S
    // S is closed under 'children':
    requires forall n :: n in S ==> n != null &&
                forall ch :: ch in n.children ==> ch == null || ch in S
    requires forall n :: n in S ==> !n.marked && n.childrenVisited == 0
    modifies S
    ensures root.marked
    // nodes reachable from 'root' are marked:
    ensures forall n :: n in S && n.marked ==>
                forall ch :: ch in n.children && ch != null ==> ch.marked
    ensures forall n :: n in S ==>
                n.childrenVisited == old(n.childrenVisited) &&
                n.children == old(n.children)
  {
    var t := root;
    t.marked := true;
    var stackNodes := [];
    ghost var unmarkedNodes := S - {t};
    while true
      invariant root.marked && t in S && t !in stackNodes
      // stackNodes has no duplicates:
      invariant forall i, j :: 0 <= i < j < |stackNodes| ==>
                  stackNodes[i] != stackNodes[j]
      invariant forall n :: n in stackNodes ==> n in S
      invariant forall n :: n in stackNodes || n == t ==>
                  n.marked &&
                  0 <= n.childrenVisited <= |n.children| &&
                  forall j :: 0 <= j < n.childrenVisited ==>
                    n.children[j] == null || n.children[j].marked
      invariant forall n :: n in stackNodes ==> n.childrenVisited < |n.children|
      // nodes on the stack are linked:
      invariant forall j :: 0 <= j && j+1 < |stackNodes| ==>
                  stackNodes[j].children[stackNodes[j].childrenVisited] == stackNodes[j+1]
      invariant 0 < |stackNodes| ==>
        stackNodes[|stackNodes|-1].children[stackNodes[|stackNodes|-1].childrenVisited] == t
      invariant forall n :: n in S && n.marked && n !in stackNodes && n != t ==>
                  forall ch :: ch in n.children && ch != null ==> ch.marked
      invariant forall n :: n in S && n !in stackNodes && n != t ==>
                  n.childrenVisited == old(n.childrenVisited)
      invariant forall n: Node :: n in S ==> n.children == old(n.children)
      invariant forall n :: n in S && !n.marked ==> n in unmarkedNodes
      decreases unmarkedNodes, stackNodes, |t.children| - t.childrenVisited
    {
      if t.childrenVisited == |t.children| {
        // pop
        t.childrenVisited := 0;
        if |stackNodes| == 0 {
          return;
        }
        t := stackNodes[|stackNodes| - 1];
        stackNodes := stackNodes[..|stackNodes| - 1];
        t.childrenVisited := t.childrenVisited + 1;
      } else if t.children[t.childrenVisited] == null || t.children[t.childrenVisited].marked {
        // just advance to next child
        t.childrenVisited := t.childrenVisited + 1;
      } else {
        // push
        stackNodes := stackNodes + [t];
        t := t.children[t.childrenVisited];
        t.marked := true;
        unmarkedNodes := unmarkedNodes - {t};
      }
    }
  }

  // ---------------------------------------------------------------------------------

  predicate Reachable(from: Node, to: Node, S: set<Node>)
    reads S
    requires null !in S
  {
    exists via: Path :: ReachableVia(from, via, to, S)
  }

  predicate ReachableVia(from: Node, via: Path, to: Node, S: set<Node>)
    reads S
    requires null !in S
    decreases via
  {
    match via
    case Empty => from == to
    case Extend(prefix, n) => n in S && to in n.children && ReachableVia(from, prefix, n, S)
  }

  method SchorrWaite(root: Node, ghost S: set<Node>)
    requires root in S
    // S is closed under 'children':
    requires forall n :: n in S ==> n != null &&
                forall ch :: ch in n.children ==> ch == null || ch in S
    // the graph starts off with nothing marked and nothing being indicated as currently being visited:
    requires forall n :: n in S ==> !n.marked && n.childrenVisited == 0
    modifies S
    // nodes reachable from 'root' are marked:
    ensures root.marked
    ensures forall n :: n in S && n.marked ==>
                forall ch :: ch in n.children && ch != null ==> ch.marked
    // every marked node was reachable from 'root' in the pre-state:
    ensures forall n :: n in S && n.marked ==> old(Reachable(root, n, S))
    // the structure of the graph has not changed:
    ensures forall n :: n in S ==>
                n.childrenVisited == old(n.childrenVisited) &&
                n.children == old(n.children)
  {
    var t := root;
    var p: Node := null;  // parent of t in original graph
    ghost var path := Path.Empty;
    t.marked := true;
    t.pathFromRoot := path;
    ghost var stackNodes := [];
    ghost var unmarkedNodes := S - {t};
    while true
      invariant root.marked && t != null && t in S && t !in stackNodes
      invariant |stackNodes| == 0 <==> p == null
      invariant 0 < |stackNodes| ==> p == stackNodes[|stackNodes|-1]
      // stackNodes has no duplicates:
      invariant forall i, j :: 0 <= i < j < |stackNodes| ==>
                  stackNodes[i] != stackNodes[j]
      invariant forall n :: n in stackNodes ==> n in S
      invariant forall n :: n in stackNodes || n == t ==>
                  n.marked &&
                  0 <= n.childrenVisited <= |n.children| &&
                  forall j :: 0 <= j < n.childrenVisited ==>
                    n.children[j] == null || n.children[j].marked
      invariant forall n :: n in stackNodes ==> n.childrenVisited < |n.children|
      invariant forall n :: n in S && n.marked && n !in stackNodes && n != t ==>
                  forall ch :: ch in n.children && ch != null ==> ch.marked
      invariant forall n :: n in S && n !in stackNodes && n != t ==>
                  n.childrenVisited == old(n.childrenVisited)
      invariant forall n :: n in S ==> n in stackNodes || n.children == old(n.children)
      invariant forall n :: n in stackNodes ==>
                  |n.children| == old(|n.children|) &&
                  forall j :: 0 <= j < |n.children| ==>
                    j == n.childrenVisited || n.children[j] == old(n.children[j])
      // every marked node is reachable:
      invariant !fresh(path);  // needed to show 'path' worthy as argument to old(Reachable(...))
      invariant old(ReachableVia(root, path, t, S));
      invariant forall n, pth :: n in S && n.marked && pth == n.pathFromRoot ==> !fresh(pth)
      invariant forall n, pth :: n in S && n.marked && pth == n.pathFromRoot ==>
                  old(ReachableVia(root, pth, n, S))
      invariant forall n :: n in S && n.marked ==> old(Reachable(root, n, S))
      // the current values of m.children[m.childrenVisited] for m's on the stack:
      invariant 0 < |stackNodes| ==> stackNodes[0].children[stackNodes[0].childrenVisited] == null
      invariant forall k :: 0 < k < |stackNodes| ==>
                  stackNodes[k].children[stackNodes[k].childrenVisited] == stackNodes[k-1]
      // the original values of m.children[m.childrenVisited] for m's on the stack:
      invariant forall k :: 0 <= k && k+1 < |stackNodes| ==>
                  old(stackNodes[k].children)[stackNodes[k].childrenVisited] == stackNodes[k+1]
      invariant 0 < |stackNodes| ==>
        old(stackNodes[|stackNodes|-1].children)[stackNodes[|stackNodes|-1].childrenVisited] == t
      invariant forall n :: n in S && !n.marked ==> n in unmarkedNodes
      decreases unmarkedNodes, stackNodes, |t.children| - t.childrenVisited
    {
      if t.childrenVisited == |t.children| {
        // pop
        t.childrenVisited := 0;
        if p == null {
          return;
        }
        var oldP := p.children[p.childrenVisited];
        // p.children[p.childrenVisited] := t;
        p.children := p.children[..p.childrenVisited] + [t] + p.children[p.childrenVisited + 1..];
        t := p;
        p := oldP;
        stackNodes := stackNodes[..|stackNodes| - 1];
        t.childrenVisited := t.childrenVisited + 1;
        path := t.pathFromRoot;

      } else if t.children[t.childrenVisited] == null || t.children[t.childrenVisited].marked {
        // just advance to next child
        t.childrenVisited := t.childrenVisited + 1;

      } else {
        // push

        var newT := t.children[t.childrenVisited];
        // t.children[t.childrenVisited] := p;
        t.children := t.children[..t.childrenVisited] + [p] + t.children[t.childrenVisited + 1..];
        p := t;
        stackNodes := stackNodes + [t];
        path := Path.Extend(path, t);
        t := newT;
        t.marked := true;
        t.pathFromRoot := path;
        unmarkedNodes := unmarkedNodes - {t};
      }
    }
  }
}