datatype List<T> = Nil | Cons(T, List)
function length(list: List): nat
case Cons(_, tl) => 1 + length(tl)
function In(x: int, list: List<int>): nat
case Cons(y, tl) => (if x == y then 1 else 0) + In(x, tl)
predicate SortedRange(m: int, n: int, list: List<int>)
case Cons(hd, tl) => m <= hd <= n && SortedRange(hd, n, tl)
function append(n0: int, n1: int, n2: int, n3: int, i: List<int>, j: List<int>): List<int>
requires n0 <= n1 <= n2 <= n3
requires SortedRange(n0, n1, i) && SortedRange(n2, n3, j)
ensures SortedRange(n0, n3, append(n0, n1, n2, n3, i, j))
ensures forall x :: In(x, append(n0, n1, n2, n3, i, j)) == In(x, i) + In(x, j)
case Cons(hd, tl) => Cons(hd, append(hd, n1, n2, n3, tl, j))
function partition(x: int, l: List<int>): (List<int>, List<int>)
ensures var (lo, hi) := partition(x, l);
(forall y :: In(y, lo) == if y <= x then In(y, l) else 0) &&
(forall y :: In(y, hi) == if x < y then In(y, l) else 0) &&
length(l) == length(lo) + length(hi)
var (lo, hi) := partition(x, tl);
function sort(min: int, max: int, i: List<int>): List<int>
requires forall x :: In(x, i) != 0 ==> min <= x <= max
ensures SortedRange(min, max, sort(min, max, i))
ensures forall x :: In(x, i) == In(x, sort(min, max, i))
var (lo, hi) := partition(hd, tl);
assert forall y :: In(y, lo) <= In(y, i);
var i' := sort(min, hd, lo);
var j' := sort(hd, max, hi);
append(min, hd, hd, max, i', Cons(hd, j'))