Why3 Standard Library index
The length is a non-mutable field, so that we get for free that modification of an array does not modify its length.
module Array use int.Int use map.Map type array [@extraction:array] 'a = private { mutable ghost elts : int -> 'a; length : int } invariant { 0 <= length } function ([]) (a: array 'a) (i: int) : 'a = a.elts i val ([]) (a: array 'a) (i: int) : 'a requires { [@expl:index in array bounds] 0 <= i < length a } ensures { result = a[i] } val ghost function ([<-]) (a: array 'a) (i: int) (v: 'a): array 'a ensures { result.length = a.length } ensures { result.elts = Map.set a.elts i v } val ([]<-) (a: array 'a) (i: int) (v: 'a) : unit writes {a} requires { [@expl:index in array bounds] 0 <= i < length a } ensures { a.elts = Map.set (old a).elts i v } ensures { a = (old a)[i <- v] }
unsafe get/set operations with no precondition
exception OutOfBounds let defensive_get (a: array 'a) (i: int) ensures { 0 <= i < length a /\ result = a[i] } raises { OutOfBounds -> i < 0 \/ i >= length a } = if i < 0 || i >= length a then raise OutOfBounds; a[i] let defensive_set (a: array 'a) (i: int) (v: 'a) ensures { 0 <= i < length a } ensures { a = (old a)[i <- v] } raises { OutOfBounds -> (i < 0 \/ i >= length a) /\ a = old a } = if i < 0 || i >= length a then raise OutOfBounds; a[i] <- v function make (n: int) (v: 'a) : array 'a axiom make_spec : forall n:int, v:'a. n >= 0 -> (forall i:int. 0 <= i < n -> (make n v)[i] = v) /\ length (make n v) = n val make [@extraction:array_make] (n: int) (v: 'a) : array 'a requires { [@expl:array creation size] n >= 0 } ensures { forall i:int. 0 <= i < n -> result[i] = v } ensures { result.length = n } val empty () : array 'a ensures { result.length = 0 } let copy (a: array 'a) : array 'a ensures { length result = length a } ensures { forall i:int. 0 <= i < length result -> result[i] = a[i] } = let len = length a in if len = 0 then empty () else begin let b = make len a[0] in for i = 1 to len - 1 do invariant { forall k. 0 <= k < i -> b[k] = a[k] } b[i] <- a[i] done; b end let sub (a: array 'a) (ofs: int) (len: int) : array 'a requires { 0 <= ofs /\ 0 <= len /\ ofs + len <= length a } ensures { length result = len } ensures { forall i:int. 0 <= i < len -> result[i] = a[ofs + i] } = if length a = 0 then begin assert { len = 0 }; empty () end else begin let b = make len a[0] in for i = 0 to len-1 do invariant { forall k. 0 <= k < i -> b[k] = a[ofs+k] } b[i] <- a[ofs+i]; done; b end let fill (a: array 'a) (ofs: int) (len: int) (v: 'a) requires { 0 <= ofs /\ 0 <= len /\ ofs + len <= length a } ensures { forall i:int. (0 <= i < ofs \/ ofs + len <= i < length a) -> a[i] = old a[i] } ensures { forall i:int. ofs <= i < ofs + len -> a[i] = v } = for k = 0 to len - 1 do invariant { forall i:int. (0 <= i < ofs \/ ofs + len <= i < length a) -> a[i] = old a[i] } invariant { forall i:int. ofs <= i < ofs + k -> a[i] = v } a[ofs + k] <- v done let blit (a1: array 'a) (ofs1: int) (a2: array 'a) (ofs2: int) (len: int) : unit writes {a2} requires { 0 <= ofs1 /\ 0 <= len /\ ofs1 + len <= length a1 } requires { 0 <= ofs2 /\ ofs2 + len <= length a2 } ensures { forall i:int. (0 <= i < ofs2 \/ ofs2 + len <= i < length a2) -> a2[i] = old a2[i] } ensures { forall i:int. ofs2 <= i < ofs2 + len -> a2[i] = a1[ofs1 + i - ofs2] } = for i = 0 to len - 1 do invariant { forall k. not (0 <= k < i) -> a2[ofs2 + k] = old a2[ofs2 + k] } invariant { forall k. 0 <= k < i -> a2[ofs2 + k] = a1[ofs1 + k] } a2[ofs2 + i] <- a1[ofs1 + i]; done let append (a1: array 'a) (a2: array 'a) : array 'a ensures { length result = length a1 + length a2 } ensures { forall i. 0 <= i < length a1 -> result[i] = a1[i] } ensures { forall i. 0 <= i < length a2 -> result[length a1 + i] = a2[i] } = if length a1 = 0 then copy a2 else begin let a = make (length a1 + length a2) a1[0] in blit a1 0 a 0 (length a1); blit a2 0 a (length a1) (length a2); a end let self_blit (a: array 'a) (ofs1: int) (ofs2: int) (len: int) : unit writes {a} requires { 0 <= ofs1 /\ 0 <= len /\ ofs1 + len <= length a } requires { 0 <= ofs2 /\ ofs2 + len <= length a } ensures { forall i:int. (0 <= i < ofs2 \/ ofs2 + len <= i < length a) -> a[i] = old a[i] } ensures { forall i:int. ofs2 <= i < ofs2 + len -> a[i] = old a[ofs1 + i - ofs2] } = if ofs1 <= ofs2 then (* from right to left *) for k = len - 1 downto 0 do invariant { forall i:int. (0 <= i <= ofs2 + k \/ ofs2 + len <= i < length a) -> a[i] = (old a)[i] } invariant { forall i:int. ofs2 + k < i < ofs2 + len -> a[i] = (old a)[ofs1 + i - ofs2] } a[ofs2 + k] <- a[ofs1 + k] done else (* from left to right *) for k = 0 to len - 1 do invariant { forall i:int. (0 <= i < ofs2 \/ ofs2 + k <= i < length a) -> a[i] = (old a)[i] } invariant { forall i:int. ofs2 <= i < ofs2 + k -> a[i] = (old a)[ofs1 + i - ofs2] } a[ofs2 + k] <- a[ofs1 + k] done end module Init use int.Int use export Array let init (n: int) (f: int -> 'a) : array 'a requires { [@expl:array creation size] n >= 0 } ensures { forall i:int. 0 <= i < n -> result[i] = f i } ensures { result.length = n } = if n = 0 then empty () else begin let a = make n (f 0) in for i = 1 to n - 1 do invariant { forall k. 0 <= k < i -> a[k] = f k } a[i] <- f i done; a end end
module IntArraySorted use int.Int use Array clone map.MapSorted as M with type elt = int, predicate le = (<=) predicate sorted_sub (a : array int) (l u : int) = M.sorted_sub a.elts l u
sorted_sub a l u
is true whenever the array segment a(l..u-1)
is sorted w.r.t order relation le
predicate sorted (a : array int) = M.sorted_sub a.elts 0 a.length
sorted a
is true whenever the array a
is sorted w.r.t le
end module Sorted use int.Int use Array type elt predicate le elt elt predicate sorted_sub (a: array elt) (l u: int) = forall i1 i2 : int. l <= i1 < i2 < u -> le a[i1] a[i2]
sorted_sub a l u
is true whenever the array segment a(l..u-1)
is sorted w.r.t order relation le
predicate sorted (a: array elt) = forall i1 i2 : int. 0 <= i1 < i2 < length a -> le a[i1] a[i2]
sorted a
is true whenever the array a
is sorted w.r.t le
end
module ArrayEq use int.Int use Array use map.MapEq predicate array_eq_sub (a1 a2: array 'a) (l u: int) = a1.length = a2.length /\ 0 <= l <= a1.length /\ 0 <= u <= a1.length /\ map_eq_sub a1.elts a2.elts l u predicate array_eq (a1 a2: array 'a) = a1.length = a2.length /\ map_eq_sub a1.elts a2.elts 0 (length a1) end module ArrayExchange use int.Int use Array use map.MapExchange as M predicate exchange (a1 a2: array 'a) (i j: int) = a1.length = a2.length /\ M.exchange a1.elts a2.elts 0 a1.length i j
exchange a1 a2 i j
means that arrays a1
and a2
only differ
by the swapping of elements at indices i
and j
end
module ArrayPermut use int.Int use Array use map.MapPermut as M use map.MapEq use ArrayEq use export ArrayExchange predicate permut (a1 a2: array 'a) (l u: int) = a1.length = a2.length /\ 0 <= l <= a1.length /\ 0 <= u <= a1.length /\ M.permut a1.elts a2.elts l u
permut a1 a2 l u
is true when the segment
a1(l..u-1)
is a permutation of the segment a2(l..u-1)
.
Values outside of the interval (l..u-1)
are ignored.
predicate permut_sub (a1 a2: array 'a) (l u: int) = map_eq_sub a1.elts a2.elts 0 l /\ permut a1 a2 l u /\ map_eq_sub a1.elts a2.elts u (length a1)
permut_sub a1 a2 l u
is true when the segment
a1(l..u-1)
is a permutation of the segment a2(l..u-1)
and values outside of the interval (l..u-1)
are equal.
predicate permut_all (a1 a2: array 'a) = a1.length = a2.length /\ M.permut a1.elts a2.elts 0 a1.length
permut_all a1 a2 l u
is true when array a1
is a permutation
of array a2
.
lemma exchange_permut_sub: forall a1 a2: array 'a, i j l u: int. exchange a1 a2 i j -> l <= i < u -> l <= j < u -> 0 <= l -> u <= length a1 -> permut_sub a1 a2 l u lemma permut_sub_trans: forall a1 a2 a3: array 'a, l u: int. 0 <= l -> u <= length a1 -> permut_sub a1 a2 l u -> permut_sub a2 a3 l u -> permut_sub a1 a3 l u lemma permut_sub_weakening: forall a1 a2: array 'a, l1 u1 l2 u2: int. permut_sub a1 a2 l1 u1 -> 0 <= l2 <= l1 -> u1 <= u2 <= length a1 -> permut_sub a1 a2 l2 u2
we can always enlarge the interval
lemma exchange_permut_all: forall a1 a2: array 'a, i j: int. exchange a1 a2 i j -> permut_all a1 a2 end module ArraySwap use int.Int use Array use export ArrayExchange let swap (a:array 'a) (i:int) (j:int) : unit requires { 0 <= i < length a /\ 0 <= j < length a } writes { a } ensures { exchange (old a) a i j } = let v = a[i] in a[i] <- a[j]; a[j] <- v end
module ArraySum use Array use int.Sum as S function sum (a: array int) (l h: int) : int = S.sum a.elts l h
sum a l h
is the sum of a[i]
for l <= i < h
end
module NumOf use Array use int.NumOf as N function numof (pr: int -> 'a -> bool) (a: array 'a) (l u: int) : int = N.numof (fun i -> pr i a[i]) l u
the number of a[i]
such that l <= i < u
and pr i a[i]
end module NumOfEq use Array use int.NumOf as N function numof (a: array 'a) (v: 'a) (l u: int) : int = N.numof (fun i -> a[i] = v) l u
the number of a[i]
such that l <= i < u
and a[i] = v
end module ToList use int.Int use Array use list.List let rec function to_list (a: array 'a) (l u: int) : list 'a requires { l >= 0 /\ u <= a.length } variant { u - l } = if u <= l then Nil else Cons a[l] (to_list a (l+1) u) use list.Append let rec lemma to_list_append (a: array 'a) (l m u: int) requires { 0 <= l <= m <= u <= a.length } variant { m - l } ensures { to_list a l m ++ to_list a m u = to_list a l u } = if l < m then to_list_append a (l+1) m u end module ToSeq use int.Int use Array use seq.Seq as S let rec function to_seq_sub (a: array 'a) (l u: int) : S.seq 'a requires { l >= 0 /\ u <= a.length } variant { u - l } = if u <= l then S.empty else S.cons a[l] (to_seq_sub a (l+1) u) let rec lemma to_seq_length (a: array 'a) (l u: int) requires { 0 <= l <= u <= length a } variant { u - l } ensures { S.length (to_seq_sub a l u) = u - l } = if l < u then to_seq_length a (l+1) u let rec lemma to_seq_nth (a: array 'a) (l i u: int) requires { 0 <= l <= i < u <= length a } variant { i - l } ensures { S.get (to_seq_sub a l u) (i - l) = a[i] } = if l < i then to_seq_nth a (l+1) i u let function to_seq (a: array 'a) : S.seq 'a = to_seq_sub a 0 (length a) meta coercion function to_seq end
We show that swapping two elements that are ill-sorted decreases the number of inversions. Useful to prove the termination of sorting algorithms that use swaps.
module Inversions use Array use ArrayExchange use int.Int use int.Sum use int.NumOf (* to prove termination, we count the total number of inversions *) predicate inversion (a: array int) (i j: int) = a[i] > a[j] function inversions_for (a: array int) (i: int) : int = numof (inversion a i) i (length a) function inversions (a: array int) : int = sum (inversions_for a) 0 (length a) (* the key lemma to prove termination: whenever we swap two consecutive values that are ill-sorted, the total number of inversions decreases *) let lemma exchange_inversion (a1 a2: array int) (i0: int) requires { 0 <= i0 < length a1 - 1 } requires { a1[i0] > a1[i0 + 1] } requires { exchange a1 a2 i0 (i0 + 1) } ensures { inversions a2 < inversions a1 } = assert { inversion a1 i0 (i0+1) }; assert { not (inversion a2 i0 (i0+1)) }; assert { forall i. 0 <= i < i0 -> inversions_for a2 i = inversions_for a1 i by numof (inversion a2 i) i (length a2) = numof (inversion a2 i) i i0 + numof (inversion a2 i) i0 (i0+1) + numof (inversion a2 i) (i0+1) (i0+2) + numof (inversion a2 i) (i0+2) (length a2) /\ numof (inversion a1 i) i (length a1) = numof (inversion a1 i) i i0 + numof (inversion a1 i) i0 (i0+1) + numof (inversion a1 i) (i0+1) (i0+2) + numof (inversion a1 i) (i0+2) (length a1) /\ numof (inversion a2 i) i0 (i0+1) = numof (inversion a1 i) (i0+1) (i0+2) /\ numof (inversion a2 i) (i0+1) (i0+2) = numof (inversion a1 i) i0 (i0+1) /\ numof (inversion a2 i) i i0 = numof (inversion a1 i) i i0 /\ numof (inversion a2 i) (i0+2) (length a2) = numof (inversion a1 i) (i0+2) (length a1) }; assert { forall i. i0 + 1 < i < length a1 -> inversions_for a2 i = inversions_for a1 i }; assert { inversions_for a2 i0 = inversions_for a1 (i0+1) by numof (inversion a1 (i0+1)) (i0+2) (length a1) = numof (inversion a2 i0 ) (i0+2) (length a1) }; assert { 1 + inversions_for a2 (i0+1) = inversions_for a1 i0 by numof (inversion a1 i0) i0 (length a1) = numof (inversion a1 i0) (i0+1) (length a1) = 1 + numof (inversion a1 i0) (i0+2) (length a1) = 1 + numof (inversion a2 (i0+1)) (i0+2) (length a2) }; let sum_decomp (a: array int) (i j k: int) requires { 0 <= i <= j <= k <= length a = length a1 } ensures { sum (inversions_for a) i k = sum (inversions_for a) i j + sum (inversions_for a) j k } = () in let decomp (a: array int) requires { length a = length a1 } ensures { inversions a = sum (inversions_for a) 0 i0 + inversions_for a i0 + inversions_for a (i0+1) + sum (inversions_for a) (i0+2) (length a) } = sum_decomp a 0 i0 (length a); sum_decomp a i0 (i0+1) (length a); sum_decomp a (i0+1) (i0+2) (length a); in decomp a1; decomp a2; () end
Generated by why3doc 1.8.0