Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions proofs/two_stack_queue.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
lemma {:induction false} EnqueueCorrect<T>(q: TwoStackQueue<T>, x: T)

Copilot AI Dec 4, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lemma references TwoStackQueue and QueueContents/Enqueue without importing or including the definitions from testcases/two_stack_queue.dfy. This file needs an include statement at the top to reference the main definitions.

Copilot uses AI. Check for mistakes.
ensures QueueContents(Enqueue(q, x)) == QueueContents(q) + [x]
{

}

lemma {:induction false} DequeueCorrect<T>(q: TwoStackQueue<T>)
requires |QueueContents(q)| > 0
ensures var (q', x) := Dequeue(q);
x == QueueContents(q)[0] && QueueContents(q') == QueueContents(q)[1..]
{

}

lemma {:induction false} TransferCorrect<T>(q: TwoStackQueue<T>)
requires |q.outbox| == 0
ensures QueueContents(Transfer(q)) == QueueContents(q)
{

}
76 changes: 76 additions & 0 deletions testcases/two_stack_queue.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Two-Stack Queue: Simulating a Queue with Two Stacks
// Proves that FIFO queue behavior can be implemented using two LIFO stacks

// Two-stack queue state
datatype TwoStackQueue<T> = Queue(inbox: seq<T>, outbox: seq<T>)

// Get the logical queue contents (FIFO order)
function {:induction false} QueueContents<T>(q: TwoStackQueue<T>): seq<T>
{
q.outbox + Reverse(q.inbox)
}

// Reverse a sequence
function {:induction false} Reverse<T>(s: seq<T>): seq<T>
decreases |s|
{
if |s| == 0 then []
else Reverse(s[1..]) + [s[0]]
}

// Enqueue: push to inbox stack
function {:induction false} Enqueue<T>(q: TwoStackQueue<T>, x: T): TwoStackQueue<T>
{
Queue(q.inbox + [x], q.outbox)
}

// Transfer: move all elements from inbox to outbox (reverses order)
function {:induction false} Transfer<T>(q: TwoStackQueue<T>): TwoStackQueue<T>
requires |q.outbox| == 0
{
Queue([], Reverse(q.inbox))
}

// Dequeue: pop from outbox, transfer if needed
function {:induction false} Dequeue<T>(q: TwoStackQueue<T>): (TwoStackQueue<T>, T)
requires |QueueContents(q)| > 0
{
if |q.outbox| > 0 then
(Queue(q.inbox, q.outbox[..|q.outbox|-1]), q.outbox[|q.outbox|-1])
else
var q' := Transfer(q);
(Queue(q'.inbox, q'.outbox[..|q'.outbox|-1]), q'.outbox[|q'.outbox|-1])
}

// Test: Enqueue preserves FIFO semantics
method {:induction false} EnqueueTest<T>(q: TwoStackQueue<T>, x: T)
{
var q' := Enqueue(q, x);
assert QueueContents(q') == QueueContents(q) + [x] by {
EnqueueCorrect(q, x);
}
}

// Test: Dequeue removes from front
method {:induction false} DequeueTest<T>(q: TwoStackQueue<T>)
requires |QueueContents(q)| > 0
{
var (q', x) := Dequeue(q);
var contents := QueueContents(q);
assert x == contents[0] by {
DequeueCorrect(q);
}
assert QueueContents(q') == contents[1..] by {
DequeueCorrect(q);
}
}

// Test: Transfer maintains queue contents
method {:induction false} TransferTest<T>(q: TwoStackQueue<T>)
requires |q.outbox| == 0
{
var q' := Transfer(q);
assert QueueContents(q') == QueueContents(q) by {
TransferCorrect(q);
}
}