predicate sorted(xs: seq) { forall j, k :: 0 <= j < k < |xs| ==> xs[j] <= xs[k] } method partition(pivot:int,xs:seq) returns (xs1:seq,xs2:seq) ensures (forall x | x in xs1 :: x <= pivot) && (forall x | x in xs2 :: x >= pivot) ensures |xs1| + |xs2| == |xs| ensures multiset(xs) == multiset(xs1) + multiset(xs2) { xs1 := []; xs2 := []; var i := 0; while (i < |xs|) invariant i <= |xs| invariant (forall x | x in xs1 :: x <= pivot) && (forall x | x in xs2 :: x >= pivot) invariant |xs1| + |xs2| == i invariant multiset(xs[..i]) == multiset(xs1) + multiset(xs2) { if (xs[i] <= pivot) { xs1 := xs1 + [xs[i]]; } else { xs2 := xs2 + [xs[i]]; } assert (xs[..i] + [xs[i]]) == xs[..i+1]; // ugh i := i + 1; } assert (xs[..|xs|] == xs); // ugh } lemma multiset_in(xs1:seq,xs2:seq) requires multiset(xs1) == multiset(xs2) ensures forall x | x in xs1 :: x in xs2 { forall x | x in xs1 ensures x in multiset(xs1) { } // a longer way: // var i := 0; // while (i < |xs1|) // invariant 0 <= i <= |xs1| // invariant forall j | 0 <= j < i :: xs1[j] in multiset(xs1) // { // i := i + 1; // } } method qsort(xs:seq) returns (xs':seq) decreases |xs| ensures (sorted(xs')) ensures multiset(xs) == multiset(xs') { if |xs| == 0 { return []; } else if |xs| == 1 { return xs; } else { var xs1,xs2 := partition(xs[0],xs[1..]); var xs1' := qsort(xs1); multiset_in(xs1',xs1); var xs2' := qsort(xs2); multiset_in(xs2',xs2); assert xs == [xs[0]] + xs[1..]; // ugh return xs1' + [xs[0]] + xs2'; } }