VerifyThis 2024: Challenge 0
solution to VerifyThis 2024 challenge 0
Authors: Jean-Christophe Filliâtre
Topics: Data Structures
Tools: Why3
References: VerifyThis @ ETAPS 2024
see also the index (by topic, by tool, by reference, by year)
VerifyThis 2024, challenge 0, The Rope Data Structure
See https://www.pm.inf.ethz.ch/research/verifythis.html
Team YYY: Jean-Christophe Filliâtre (CNRS)
use int.Int use seq.Seq use seq.FreeMonoid type char
abstract type of characters
type str = seq char
strings are sequences of characters
type rope = | Leaf str | Node int rope rope
Ropes are implemented here as an immutable data strcuture.
let rec function to_str (r: rope) : str = match r with | Leaf s -> s | Node _ rl rr -> to_str rl ++ to_str rr end
Function to_str
is a logic function (keyword function
)
that can be used in programs as well (keyword let
).
predicate valid (r: rope) = match r with | Leaf _s -> true | Node wl rl rr -> wl = length (to_str rl) && valid rl && valid rr end
We introduce a valid
predicate to express that the weight
stored in Node
is indeed the length of the left subrope.
let rec str_len (r: rope) : int requires { valid r } variant { r } ensures { result = length (to_str r) } = match r with | Leaf s -> length s | Node wl _ rr -> wl + str_len rr end
Length of a rope, descending along the right spine.
let concat (left right: rope) : rope requires { valid left } requires { valid right } ensures { valid result } ensures { to_str result = to_str left ++ to_str right } = Node (str_len left) left right
Rope concatenation (see below for the optimized version).
let rec split (r: rope) (i: int) : (left : rope, right: rope) requires { valid r } requires { 0 <= i <= length (to_str r) } variant { r } ensures { valid left } ensures { valid right } ensures { to_str r = to_str left ++ to_str right } ensures { length (to_str left) = i } = match r with | Leaf s -> (Leaf s[..i], Leaf s[i..]) | Node wl rl rr -> if i < wl then let rll, rlr = split rl i in (rll, concat rlr rr) else if i > wl then let rrl, rrr = split rr (i - wl) in (concat rl rrl, rrr) else (rl, rr) end
Splitting a rope at a given position.
let delete (r: rope) (i len: int) : rope requires { valid r } requires { 0 <= i <= length (to_str r) } requires { 0 <= len <= length (to_str r) - i } ensures { valid result } ensures { to_str result = (to_str r)[..i] ++ (to_str r)[i+len..] } = let left, remaining = split r i in let _, right = split remaining len in concat left right
Deleting characters i..i+len
.
val predicate is_short (r: str)
Predicate is_short
is left uninterpreted, as required.
let concat2 (left right: rope) : rope requires { valid left } requires { valid right } ensures { valid result } ensures { to_str result = to_str left ++ to_str right } = match right with | Leaf sr -> if is_short sr then match left with | Leaf sl -> if is_short sl then Leaf (sl ++ sr) else concat left right | Node wl rl (Leaf sl) -> if is_short sl then Node wl rl (Leaf (sl ++ sr)) else concat left right | _ -> concat left right end else concat left right | _ -> concat left right end
Optimized concatenation.
Note that the contract of concat2
is that of concat
.
let insert (r: rope) (i: int) (to_insert: str) : rope requires { valid r } requires { 0 <= i <= length (to_str r) } ensures { valid result } ensures { to_str result = (to_str r)[..i] ++ to_insert ++ (to_str r)[i..] } = let left, right = split r i in concat left (concat (Leaf to_insert) right)
Inserting a rope at position i
.
The lizard examples
val constant char_l: char val constant char_i: char val constant char_z: char val constant char_a: char val constant char_r: char val constant char_d: char let constant str_lizard: str = cons char_l (cons char_i (cons char_z ( cons char_a (cons char_r (cons char_d empty))))) let constant str_lard : str = cons char_l (cons char_a (cons char_r (cons char_d empty))) let test_delete (r: rope) : rope requires { valid r } requires { to_str r = str_lizard } ensures { to_str result = str_lard } = delete r 1 2 let constant str_iz : str = cons char_i (cons char_z empty) let test_insert (r: rope) : rope requires { valid r } requires { to_str r = str_lard } ensures { to_str result == str_lizard } = insert r 1 str_iz
download ZIP archive
Why3 Proof Results for Project "verifythis_2024_challenge0"
Theory "verifythis_2024_challenge0.Top": fully verified
Obligations | Alt-Ergo 2.4.0 | CVC4 1.8 | CVC5 1.0.2 | Z3 4.8.10 |
VC for str_len | --- | --- | --- | 0.09 |
VC for concat | --- | --- | --- | 0.04 |
VC for split | --- | 0.11 | --- | --- |
VC for delete | 0.07 | --- | --- | --- |
VC for concat2 | --- | --- | 0.17 | --- |
VC for insert | --- | --- | 0.12 | --- |
VC for str_lizard | --- | --- | --- | 0.02 |
VC for str_lard | --- | --- | --- | 0.02 |
VC for test_delete | --- | --- | --- | 0.09 |
VC for str_iz | --- | --- | --- | 0.01 |
VC for test_insert | --- | --- | --- | 0.12 |