Skip to content

Commit 5df3164

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

7 files changed

Lines changed: 464 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 model
11+
-- that wraparound with a small `NextIndex` function instead of `mod`, so the
12+
-- verification conditions 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: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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+
// Return whether the deque has no live elements.
50+
procedure DequeEmpty() returns (b : bool)
51+
spec
52+
{
53+
ensures (b ==> front == back);
54+
ensures (front == back ==> b);
55+
}
56+
{
57+
if (front == back) {
58+
b := true;
59+
} else {
60+
b := false;
61+
}
62+
};
63+
64+
// Add `x` to the back of the deque.
65+
procedure PushBack(x : int) returns ()
66+
spec
67+
{
68+
requires 0 <= front && front <= back;
69+
requires back < n;
70+
modifies D;
71+
modifies back;
72+
73+
ensures back == old(back) + 1;
74+
ensures D[old(back)] == x;
75+
ensures (
76+
∀ i:int .
77+
old(front) <= i && i < old(back) ==> D[i] == old(D[i])
78+
);
79+
}
80+
{
81+
D := D[back := x];
82+
back := back + 1;
83+
};
84+
85+
// Remove and return the last element of the deque.
86+
procedure PopBack() returns (x : int)
87+
spec
88+
{
89+
requires front < back;
90+
modifies back;
91+
92+
ensures back == old(back) - 1;
93+
ensures x == old(D[old(back) - 1]);
94+
ensures front <= back;
95+
}
96+
{
97+
back := back - 1;
98+
x := D[back];
99+
};
100+
101+
// Add `x` to the front of the deque.
102+
procedure PushFront(x : int) returns ()
103+
spec
104+
{
105+
requires 0 < front && front <= back;
106+
requires back <= n;
107+
modifies D;
108+
modifies front;
109+
110+
ensures front == old(front) - 1;
111+
ensures D[front] == x;
112+
ensures (
113+
∀ i:int .
114+
old(front) <= i && i < old(back) ==> D[i] == old(D[i])
115+
);
116+
}
117+
{
118+
front := front - 1;
119+
D := D[front := x];
120+
};
121+
122+
// Remove and return the first element of the deque.
123+
procedure PopFront() returns (x : int)
124+
spec
125+
{
126+
requires front < back;
127+
modifies front;
128+
129+
ensures front == old(front) + 1;
130+
ensures x == old(D[old(front)]);
131+
ensures front <= back;
132+
}
133+
{
134+
x := D[front];
135+
front := front + 1;
136+
};
137+
138+
#end
139+
140+
example : Strata.smtVCsCorrect dequeArrayPgm := by
141+
gen_smt_vcs
142+
all_goals grind
143+
144+
end Strata

0 commit comments

Comments
 (0)