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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
|
method M0(n: int)
requires var f := 100; n < f; requires var t, f := true, false; (t && f) || n < 100;
{
assert n < 200;
assert 0 <= n; // error
}
method M1()
{
assert var f := 54; var g := f + 1; g == 55;
}
method M2()
{
assert var f := 54; var f := f + 1; f == 55;
}
function method Fib(n: nat): nat
{
if n < 2 then n else Fib(n-1) + Fib(n-2)
}
method M3(a: array<int>) returns (r: int)
requires a != null && forall i :: 0 <= i < a.Length ==> a[i] == 6;
ensures (r + var t := r; t*2) == 3*r;
{
assert Fib(2) + Fib(4) == Fib(0) + Fib(1) + Fib(2) + Fib(3);
{
var x,y := Fib(8), Fib(11);
assume x == 21;
assert Fib(7) == 3 ==> Fib(9) == 24;
assume Fib(1000) == 1000;
assume Fib(9) - Fib(8) == 13;
assert Fib(9) <= Fib(10);
assert y == 89;
}
assert Fib(1000) == 1000; // does it still know this?
forall i | 0 <= i < a.Length ensures true; {
var j := i+1;
assert j < a.Length ==> a[i] == a[j];
}
}
// M4 is pretty much the same as M3, except with things rolled into expressions.
method M4(a: array<int>) returns (r: int)
requires a != null && forall i :: 0 <= i < a.Length ==> a[i] == 6;
ensures (r + var t := r; t*2) == 3*r;
{
assert Fib(2) + Fib(4) == Fib(0) + Fib(1) + Fib(2) + Fib(3);
assert
var x,y := Fib(8), Fib(11);
assume x == 21;
assert Fib(7) == 3 ==> Fib(9) == 24;
assume Fib(1000) == 1000;
assume Fib(9) - Fib(8) == 13;
assert Fib(9) <= Fib(10);
y == 89;
assert Fib(1000) == 1000; // still known, because the assume was on the path here
assert forall i :: 0 <= i < a.Length ==> var j := i+1; j < a.Length ==> a[i] == a[j];
}
var index: int;
method P(a: array<int>) returns (b: bool, ii: int)
requires a != null && exists k :: 0 <= k < a.Length && a[k] == 19;
modifies this, a;
ensures ii == index;
// The following uses a variable with a non-old definition inside an old expression:
ensures 0 <= index < a.Length && old(a[ii]) == 19;
ensures 0 <= index < a.Length && var newIndex := index; old(a[newIndex]) == 19;
// The following places both the variable and the body inside an old:
ensures b ==> old(var oldIndex := index; 0 <= oldIndex < a.Length && a[oldIndex] == 17);
// Here, the definition of the variable is old, and it's used both inside and
// inside an old expression:
ensures var oi := old(index); oi == index ==> a[oi] == 21 && old(a[oi]) == 19;
{
b := 0 <= index < a.Length && a[index] == 17;
var i, j := 0, -1;
while (i < a.Length)
invariant 0 <= i <= a.Length;
invariant forall k :: 0 <= k < i ==> a[k] == 21;
invariant forall k :: i <= k < a.Length ==> a[k] == old(a[k]);
invariant (0 <= j < i && old(a[j]) == 19) ||
(j == -1 && exists k :: i <= k < a.Length && a[k] == 19);
{
if (a[i] == 19) { j := i; }
i, a[i] := i + 1, 21;
}
index := j;
ii := index;
}
method PMain(a: array<int>)
requires a != null && exists k :: 0 <= k < a.Length && a[k] == 19;
modifies this, a;
{
var s := a[..];
var b17, ii := P(a);
assert s == old(a[..]);
assert s[index] == 19;
if (*) {
assert a[index] == 19; // error (a can have changed in P)
} else {
assert b17 ==> 0 <= old(index) < a.Length && old(a[index]) == 17;
assert index == old(index) ==> a[index] == 21 && old(a[index]) == 19;
}
}
// ---------- lemmas ----------
method Theorem0(n: int)
requires 1 <= n;
ensures 1 <= Fib(n);
{
if (n < 3) {
} else {
Theorem0(n-2);
Theorem0(n-1);
}
}
ghost method Theorem1(n: int)
requires 1 <= n;
ensures 1 <= Fib(n);
{
// in a ghost method, the induction tactic takes care of it
}
function Theorem2(n: int): int
requires 1 <= n;
ensures 1 <= Fib(n);
{
if n < 3 then 5 else
var x := Theorem2(n-2);
var y := Theorem2(n-1);
x + y
}
function Theorem3(n: int): int
requires 1 <= n;
ensures 1 <= Fib(n);
{
if n < 3 then 5 else
var x := Theorem3(n-2);
var y := Theorem3(n-1);
5
}
// ----- tricky substitution issues in the implementation -----
class TrickySubstitution {
function F0(x: int): int
ensures F0(x) == x;
{
var g :| x == g;
g
}
function F1(x: int): int
ensures F1(x) == x;
{
var f := x;
var g :| f == g;
g
}
function F2(x: int): int
ensures F2(x) == x;
{
var f, g :| f == x && f == g;
g
}
function F3(x: int): int
ensures F3(x) == x;
{
var f :| f == x;
var g :| f == g;
g
}
var v: int;
var w: int;
function F4(x: int): int
requires this.v + x == 3 && this.w == 2;
reads this;
ensures F4(x) == 5;
{
var f := this.v + x; // 3
var g :| f + this.w == g; // 5
g
}
method M(x: int)
requires this.v + x == 3 && this.w == 2;
modifies this;
{
this.v := this.v + 1;
this.w := this.w + 10;
assert 6 ==
var f := this.v + x; // 4
var g :| old(f + this.w) == g; // 6
g;
}
method N() returns (ghost r: int, ghost s: int)
requires w == 12;
modifies this;
ensures r == 12 == s && w == 13;
{
w := w + 1;
r := var y :| y == old(w); y;
s := old(var y :| y == w; y);
}
}
datatype List<T> = Nil | Cons(head: T, tail: List)
method Q(list: List<int>, anotherList: List<int>)
requires list != Nil;
{
var x :=
var Cons(h, t) := list;
Cons(h, t);
var y := match anotherList
case Nil => (match anotherList case Nil => 5)
case Cons(h, t) => h;
assert list == x;
assert anotherList.Cons? ==> y == anotherList.head;
assert anotherList.Nil? ==> y == 5;
}
// ------------- pattern LHSs ---------------
datatype Tuple<T,U> = Pair(0: T, 1: U)
function method Together(x: int, y: bool): Tuple<int, bool>
{
Pair(x, y)
}
method LikeTogether() returns (z: int)
{
if * {
z := var Pair(xx: nat, yy) := Together(-10, true); xx + 3; // error: int-to-nat failure
assert 0 <= z; // follows from the bogus type of xx
} else if * {
var t: nat := -30; // error: int-to-nat failure in assignment statement
} else {
z := var t: nat := -30; t; // error: int-to-nat failure in let expression
}
}
method Mountain() returns (z: int, t: nat)
{
z := var Pair(xx: nat, yy) := Together(10, true); xx + 3;
assert 0 <= z;
}
function method Rainbow<X>(tup: Tuple<X, int>): int
ensures 0 <= Rainbow(tup);
{
var Pair(left, right) := tup; right*right
}
datatype Friend = Agnes(int) | Agatha(int) | Jermaine(int) | Jack(int)
function Fr(x: int): Friend
{
if x < 10 then Jermaine(x) else Agnes(x)
}
method Friendly(n: nat) returns (ghost c: int)
ensures c == n;
{
if n < 3 {
c := var Jermaine(y) := Fr(n); y;
} else {
c := var Agnes(y) := Fr(n); y; // error: Fr might return something other than an Agnes
}
}
function method F_good(d: Tuple<
Tuple<bool, int>,
Tuple< Tuple<int,int>, Tuple<bool,bool> >>): int
requires 0 <= d.1.0.1 < 100;
{
var p, Pair(Pair(b0, x), Pair(Pair(y0, y1: nat), Pair(b1, b2))), q: int := d.0, d, d.1.0.1;
assert q < 200;
p.1 + if b0 then x + y0 else x + y1
}
function method F_bad(d: Tuple<
Tuple<bool, int>,
Tuple< Tuple<int,int>, Tuple<bool,bool> >>): int
{
var p, Pair(Pair(b0, x), Pair(Pair(y0, y1: nat), Pair(b1, b2))), q: int // error: int-to-nat failure
:= d.0, d, d.1.0.1;
assert q < 200; // error: assertion failure
p.1 + if b0 then x + y0 else x + y1
}
|