Skip to content

Commit 0ad4c54

Browse files
committed
feat(Boole): add verified queue examples
1 parent 567be00 commit 0ad4c54

7 files changed

Lines changed: 463 additions & 59 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import Strata.MetaVerifier
2+
3+
------------------------------------------------------------
4+
namespace Strata
5+
6+
-- Circular-buffer queue.
7+
--
8+
-- The queue is represented by an array `Q`, a capacity `n`, a head pointer, a
9+
-- tail pointer, and a live element count. Unlike `queue_array_based.lean`,
10+
-- `head` and `tail` wrap back to zero when they reach the capacity. We encode
11+
-- wraparound with conditionals instead of `mod`, so the verification conditions
12+
-- remain simple arithmetic plus map-update reasoning.
13+
private def circularQueuePgm :=
14+
#strata
15+
program Boole;
16+
17+
type Array := Map int int;
18+
19+
// `NextIndex(i, n)` is `(i + 1) mod n`, axiomatized without using `mod`.
20+
function NextIndex(i : int, n : int) : int;
21+
22+
axiom (∀ i : int, n : int ::
23+
n > 0 && 0 <= i && i < n ==> 0 <= NextIndex(i, n) && NextIndex(i, n) < n);
24+
axiom (∀ n : int :: n > 0 ==> NextIndex(n - 1, n) == 0);
25+
axiom (∀ i : int, n : int ::
26+
n > 0 && 0 <= i && i + 1 < n ==> NextIndex(i, n) == i + 1);
27+
28+
var Q : Array;
29+
var n : int;
30+
var head : int;
31+
var tail : int;
32+
var count : int;
33+
34+
// Initialize an empty circular queue.
35+
procedure CircularQueueInit(cap : int) returns ()
36+
spec
37+
{
38+
requires cap > 0;
39+
modifies n;
40+
modifies head;
41+
modifies tail;
42+
modifies count;
43+
44+
ensures n == cap;
45+
ensures head == 0;
46+
ensures tail == 0;
47+
ensures count == 0;
48+
}
49+
{
50+
n := cap;
51+
head := 0;
52+
tail := 0;
53+
count := 0;
54+
};
55+
56+
// Return whether the circular queue is empty.
57+
procedure CircularQueueEmpty() returns (b : bool)
58+
spec
59+
{
60+
ensures (b ==> count == 0);
61+
ensures (count == 0 ==> b);
62+
}
63+
{
64+
if (count == 0) {
65+
b := true;
66+
} else {
67+
b := false;
68+
}
69+
};
70+
71+
// Return whether the circular queue is full.
72+
procedure CircularQueueFull() returns (b : bool)
73+
spec
74+
{
75+
ensures (b ==> count == n);
76+
ensures (count == n ==> b);
77+
}
78+
{
79+
if (count == n) {
80+
b := true;
81+
} else {
82+
b := false;
83+
}
84+
};
85+
86+
// Add `x` at the current tail position and advance the tail pointer, wrapping
87+
// to zero when the insertion occurs at the last array slot.
88+
procedure CircularEnqueue(x : int) returns ()
89+
spec
90+
{
91+
requires n > 0;
92+
requires 0 <= head && head < n;
93+
requires 0 <= tail && tail < n;
94+
requires 0 <= count && count < n;
95+
modifies Q;
96+
modifies tail;
97+
modifies count;
98+
99+
ensures count == old(count) + 1;
100+
ensures Q[old(tail)] == x;
101+
ensures 0 <= tail && tail < n;
102+
ensures (
103+
∀ i:int .
104+
0 <= i && i < n && i != old(tail) ==> Q[i] == old(Q[i])
105+
);
106+
}
107+
{
108+
Q := Q[tail := x];
109+
tail := NextIndex(tail, n);
110+
count := count + 1;
111+
};
112+
113+
// Remove and return the current head element, then advance the head pointer
114+
// with the same wraparound convention.
115+
procedure CircularDequeue() returns (x : int)
116+
spec
117+
{
118+
requires n > 0;
119+
requires 0 <= head && head < n;
120+
requires 0 <= tail && tail < n;
121+
requires 0 < count && count <= n;
122+
modifies head;
123+
modifies count;
124+
125+
ensures x == old(Q[old(head)]);
126+
ensures count == old(count) - 1;
127+
ensures 0 <= head && head < n;
128+
}
129+
{
130+
x := Q[head];
131+
head := NextIndex(head, n);
132+
count := count - 1;
133+
};
134+
135+
#end
136+
137+
example : Strata.smtVCsCorrect circularQueuePgm := by
138+
gen_smt_vcs
139+
all_goals grind
140+
141+
end Strata
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import Strata.MetaVerifier
2+
3+
------------------------------------------------------------
4+
namespace Strata
5+
6+
-- Array-backed double-ended queue.
7+
--
8+
-- A deque supports insertion and removal at both ends. This example uses a
9+
-- bounded, non-circular array model: live elements occupy indices
10+
-- `front .. back - 1`, with `front == back` meaning empty. The front can move
11+
-- left and the back can move right, so `PushFront` requires spare space before
12+
-- `front` and `PushBack` requires spare space after `back`.
13+
--
14+
-- The circular-buffer queue example covers wraparound behavior separately.
15+
-- Keeping this deque non-circular makes the specifications direct and lets the
16+
-- example focus on four endpoint operations, `old(...)`, and frame properties.
17+
private def dequeArrayPgm :=
18+
#strata
19+
program Boole;
20+
21+
type Array := Map int int;
22+
23+
var D : Array;
24+
var n : int;
25+
var front : int;
26+
var back : int;
27+
28+
// Initialize an empty deque. Starting both endpoints at `start` leaves room
29+
// to grow in either direction if `0 < start < cap`.
30+
procedure DequeInit(cap : int, start : int) returns ()
31+
spec
32+
{
33+
requires cap > 0;
34+
requires 0 <= start && start <= cap;
35+
modifies n;
36+
modifies front;
37+
modifies back;
38+
39+
ensures n == cap;
40+
ensures front == start;
41+
ensures back == start;
42+
}
43+
{
44+
n := cap;
45+
front := start;
46+
back := start;
47+
};
48+
49+
procedure DequeEmpty() returns (b : bool)
50+
spec
51+
{
52+
ensures (b ==> front == back);
53+
ensures (front == back ==> b);
54+
}
55+
{
56+
if (front == back) {
57+
b := true;
58+
} else {
59+
b := false;
60+
}
61+
};
62+
63+
// Add `x` to the back of the deque.
64+
procedure PushBack(x : int) returns ()
65+
spec
66+
{
67+
requires 0 <= front && front <= back;
68+
requires back < n;
69+
modifies D;
70+
modifies back;
71+
72+
ensures back == old(back) + 1;
73+
ensures D[old(back)] == x;
74+
ensures (
75+
∀ i:int .
76+
old(front) <= i && i < old(back) ==> D[i] == old(D[i])
77+
);
78+
}
79+
{
80+
D := D[back := x];
81+
back := back + 1;
82+
};
83+
84+
// Remove and return the last element of the deque.
85+
procedure PopBack() returns (x : int)
86+
spec
87+
{
88+
requires front < back;
89+
modifies back;
90+
91+
ensures back == old(back) - 1;
92+
ensures x == old(D[old(back) - 1]);
93+
ensures front <= back;
94+
}
95+
{
96+
back := back - 1;
97+
x := D[back];
98+
};
99+
100+
// Add `x` to the front of the deque.
101+
procedure PushFront(x : int) returns ()
102+
spec
103+
{
104+
requires 0 < front && front <= back;
105+
requires back <= n;
106+
modifies D;
107+
modifies front;
108+
109+
ensures front == old(front) - 1;
110+
ensures D[front] == x;
111+
ensures (
112+
∀ i:int .
113+
old(front) <= i && i < old(back) ==> D[i] == old(D[i])
114+
);
115+
}
116+
{
117+
front := front - 1;
118+
D := D[front := x];
119+
};
120+
121+
// Remove and return the first element of the deque.
122+
procedure PopFront() returns (x : int)
123+
spec
124+
{
125+
requires front < back;
126+
modifies front;
127+
128+
ensures front == old(front) + 1;
129+
ensures x == old(D[old(front)]);
130+
ensures front <= back;
131+
}
132+
{
133+
x := D[front];
134+
front := front + 1;
135+
};
136+
137+
#end
138+
139+
example : Strata.smtVCsCorrect dequeArrayPgm := by
140+
gen_smt_vcs
141+
all_goals grind
142+
143+
end Strata

0 commit comments

Comments
 (0)