Files
why3/examples/amortization.mlw
Jean-Christophe Filliatre 92489c3195 a simple example of amortization
2021-04-01 13:47:23 +02:00

73 lines
1.8 KiB
Plaintext

(** A simple example of amortization.
From Chris Okasaki's "Purely Functional Data Structures"
Chapter 5 -- Fundamentals of Amortization
This is an abstraction where
- we do not implement the operations and we do not model the contents
of the queue (irrelevant here);
- we assume that push is O(1) (contrary to Okasaki's code, which has
an internal invariant |f|>=|r|).
*)
use int.Int
use list.ListRich
(** Global clock. Function `tick` is unused below but one must imagine
that it would be called at each function call and each loop iteration. *)
val ghost ref clock: int
val ghost tick () : unit
writes { clock } ensures { clock = old clock + 1 }
(** Queue abstraction. *)
type elt = int
type queue = abstract {
ghost size: int;
ghost credits: int;
}
invariant { size >= 0 }
invariant { 0 <= credits <= size }
val empty () : (q: queue)
ensures { q.size = 0 }
ensures { q.credits = 0 }
val push (_: elt) (q: queue) : (r: queue)
writes { clock }
ensures { r.size = q.size + 1 }
ensures { clock = old clock + 1 }
ensures { r.credits = q.credits + 1 }
val pop (q: queue) : (_: elt, r: queue)
requires { q.size > 0 }
writes { clock }
ensures { r.size = q.size - 1 }
ensures { r.credits <= q.credits }
ensures { clock = old clock + 1 + r.credits - q.credits }
(** Test client: Let us show that if we insert n values in a queue,
then remove all of them, the total cost is O(n). *)
let client (n: int) : unit
requires { n >= 0 }
ensures { clock <= old clock + 2*n }
= let ref q = empty () in
for k = 0 to n-1 do
invariant { q.size = q.credits = k }
invariant { clock = old clock + k }
q <- push 42 q
done;
for k = 0 to n-1 do
invariant { q.size = n-k }
invariant { clock = old clock + q.credits + k }
let _, r = pop q in
q <- r
done