Capucine Example: Memoized FibonacciA data structure which computes the Fibonacci function using a table to memoize the results. |
To compile, go into the capucine directory and type:
make examples/memo.gwhy
class Long
{
value: int;
}
type option ('a)
logic none: option ('a)
logic some (x: 'a): option ('a)
logic get_some (x: option ('a)): 'a
axiom get_some_some:
forall x: 'a.
get_some (some (x)) = x
axiom none_or_some:
forall x: option ('a).
x = none() || exists y: 'a. x = some (y)
axiom none_xor_some:
forall x: 'a. none() <> some (x)
axiom some_injective:
forall x:'a. forall y:'a. some(x) = some(y) ==> x = y
type map ('a, 'b)
logic empty_map: map ('a, 'b)
logic store (x: map ('a, 'b), y: 'a, z: 'b): map ('a, 'b)
logic select (x: map ('a, 'b), y: 'a): option ('b)
axiom select_empty:
forall y: 'a. select(empty_map(), y) = none()
class Table [Rval: Long]
{
group Rkeys: Long;
contents: map ([Rkeys], [Rval]);
}
logic model [Rv: Long, Rh: Table [Rv] ] (h: [Rh], i: int): option ([Rv])
axiom model_footprint :
forall region Rv1: Long.
forall region Rv2: Long.
forall region Rh1: Table [Rv1].
forall region Rh2: Table [Rv2].
forall h: [?]. forall i:int.
(get(Rh1, h, contents) = get(Rh2, h, contents)) ==>
model [Rv1,Rh1](h,i) = model [Rv2,Rh2](h,i)
axiom model_typing:
forall region Rv: Long.
forall region Rh: Table [Rv].
forall h: [?].
forall x: int.
forall y: [?].
h in Rh ==>
model [Rv, Rh] (h, x) = some (y) ==>
y in Rv
(* signature typique des tables de hash à la Java, implémentée ici à
l'aide d'une map logique pour simplifier *)
fun h_create [Rv: Long, R: Table [Rv]] (): [R]
consumes R^e
produces R^c
post forall i: int. model[Rv, R](result, i) = none()
{
let h = new [R];
h.contents <- #empty_map ();
return h
}
fun h_add [Rv: Long, R: Table [Rv], Rk: Long] (h: [R], k: [Rk], v: [Rv]): unit
consumes R^c Rk^c
produces R^c
post
model [Rv, R] (h, get(Rk, k, value)) = some (v) &&
forall i: int.
i <> get(Rk, k, value) ==> model [Rv, R] (h, i) = model [Rv, R] (h, i)@pre
{
adopt Rk as h.Rkeys;
h.contents <- #store (h.contents, k, v);
}
fun h_get [Rv: Long, R: Table [Rv], Rk: Long] (h: [R], k: [Rk]): option ([Rv])
consumes R^c Rk^c
produces R^c Rk^c
post result = model [Rv, R] (h, get(Rk, k, value))
{
return #none() (* just so it types, but we actually want to hide the implementation *)
}
(****************************************************************)
(* l'idée c'est que si on ne voit que :
class Table [Rval: Long]
logic model [Rv: Long, Rh: Table [Rv]] (h: [Rh], k: int): option ([Rv])
alors on peut toujours écrire Memo. *)
logic fibo (x: int): int
axiom fibo1: forall i:int. i <= 1 ==> fibo(i) = i
axiom fibo2: forall i:int. i >= 2 ==> fibo(i) = fibo(i-1) + fibo(i-2)
class Fibo
{
group Rv: Long;
single Rh: Table [Rv];
hash: [Rh];
invariant
forall x: int.
forall y: [?]. (* [Rv] *) (* y in Rv ==> *)
model [Rv, Rh] (hash, x) = some (y) ==> get(Rv, y, value) = fibo(x)
}
(* l'invariant parle de la région Rv, possédée par Memo,
mais ses locations sont stockées dans l'objet Table et pas dans Memo *)
fun new_fibo [R: Fibo] (): [R]
consumes R^e
produces R^c
{
let f = new [R];
let h = h_create [f.Rv, f.Rh] ();
f.hash <- h;
return f
}
(* pas besoin de mettre new_fibo dans la these *)
fun fibonacci [R: Fibo] (f: [R], i: int): int
consumes R^c
produces R^c
pre i >= 0
post result = fibo(i)
{
use invariant f;
let region Rr: Long;
let r = new [Rr];
if i <= 1 then
{
r.value <- i;
} else {
let region Rk: Long;
let k = new [Rk];
k.value <- i;
let ro = h_get [f.Rv, f.Rh, Rk] (f.hash, k);
if ro = #none() then
{
let f2 = fibonacci [R] (f, i - 2);
let f1 = fibonacci [R] (f, i - 1);
r.value <- f1 + f2;
(* copy r to be able to adopt it in this branch of the if *)
let region Rv: Long;
let v = new [Rv];
(* this should be a "use" statement instead of an "assert",
but is not implemented yet in Capucine *)
assert not (v in get(R, f, Rv));
v.value <- r.value;
use invariant f;
use get(R, f, hash) in get(R, f, Rh);
(*
assert forall x: int.
forall y: [?].
model [get(R,f,Rv), get(R,f,Rh)] (get(R,f,hash), x) = some (y) ==>
y in get(R,f,Rv);
*)
adopt Rv as f.Rv;
(* use invariant f; *)
let ignore = h_add [f.Rv, f.Rh, Rk] (f.hash, k, v);
} else {
let j = #get_some (ro);
r.value <- j.value;
(* hack to drop permission Rk^c *)
let region Rtmp: Long;
adopt Rk as Rtmp;
};
};
return r.value
}