StableSorter.scala [raw]
import stainless.annotation._
import stainless.collection._
import stainless.lang._
import stainless.proof._
object StableSorter {
// Inserting into a stable list adds to the content and increases the length
def insert[T](t: T, l: List[T], key: T => BigInt): List[T] = {
l match {
case Nil() => t :: l
case Cons(hd, tl) if key(t) <= key(hd) => t :: l
case Cons(hd, tl) => hd :: insert(t, tl, key)
}
} ensuring { res => res.content == Set(t) ++ l.content && res.length == 1 + l.length }
// Sorting a list preserves the content and preserves the length
def sort[T](l: List[T], key: T => BigInt): List[T] = { l match {
case Nil() => l
case Cons(hd, tl) => {
val tlSorted = sort(tl, key)
insert(hd, tlSorted, key)
}
}} ensuring { res => res.content == l.content && res.length == l.length }
// To define stability, we first annotate the input list with the initial indices...
def annotateList[T](l: List[T], n: BigInt): List[(T, BigInt)] = {
l match {
case Nil() => Nil[(T, BigInt)]()
case Cons(hd, tl) => {
val tlAnn = annotateList(tl, n + 1)
hint((hd, n) :: tlAnn, trivProp2(annotateList(tl, n + 1), n))
}
}
} ensuring { res => l2AtLeast(res, n) }
// ... where:
def l2AtLeast[T](l: List[(T, BigInt)], n: BigInt): Boolean = l match {
case Nil() => true
case Cons((hd, hdIndex), tl) => n <= hdIndex && l2AtLeast(tl, n)
}
// ... and the following trivial property holds:
@induct
def trivProp2[T](l: List[(T, BigInt)], n: BigInt): Boolean = {
require(l2AtLeast(l, n + 1))
l2AtLeast(l, n)
}.holds
// Next, we identify an appropriate value which is increasing in a stably sorted list:
def isStableSorted[T](l: List[(T, BigInt)], key: T => BigInt): Boolean = l match {
case Nil() => true
case Cons((hd, hdIndex), tl) => isStableSortedAndAtLeast(tl, key, key(hd), hdIndex)
}
def isStableSortedAndAtLeast[T](
l: List[(T, BigInt)],
key: T => BigInt,
x: BigInt,
minIndex: BigInt): Boolean = l match {
case Nil() => true
case Cons((hd, hdIndex), tl) => {
val hdSmall = x < key(hd) || x == key(hd) && minIndex <= hdIndex
val tlSorted = isStableSortedAndAtLeast(tl, key, key(hd), hdIndex)
hdSmall && tlSorted
}
}
// The insertion sort we initially defined is stable:
def sortStableProp[T](l: List[T], key: T => BigInt): Boolean = {
require(sortStablePropInt(l, 0, key))
val lAnn = annotateList(l, 0)
val keyAnn = (tn: (T, BigInt)) => key(tn._1)
val lAnnSorted = sort(lAnn, keyAnn)
isStableSorted(lAnnSorted, key)
}.holds
// To prove that insertion sort is stable, we first show that insertion is stable:
@induct
def insertStableProp[T](t: T, n: BigInt, l: List[(T, BigInt)], key: T => BigInt): Boolean = {
require(isStableSorted(l, key) && l2AtLeast(l, n))
val keyAnn = (tn: (T, BigInt)) => key(tn._1)
val res = insert((t, n), l, keyAnn)
isStableSorted(res, key) && l2AtLeast(res, n)
}.holds
// ... and complete the proof of stability.
def sortStablePropInt[T](l: List[T], n: BigInt, key: T => BigInt): Boolean = {
val lAnn = annotateList(l, n)
val keyAnn = (tn: (T, BigInt)) => key(tn._1)
val lAnnSorted = sort(lAnn, keyAnn)
lAnn match {
case Nil() => isStableSorted(lAnnSorted, key)
case Cons((hd, hdIndex), tlAnn) => {
val Cons(_, xs) = l
val tlAnnSorted = sort(tlAnn, keyAnn)
sortStablePropInt(xs, n + 1, key) &&
n == hdIndex &&
l2AtLeast(tlAnn, n) &&
l2AtLeast(tlAnnSorted, n + 1) &&
trivProp2(tlAnnSorted, n) &&
l2AtLeast(tlAnnSorted, n) &&
insertStableProp(hd, hdIndex, tlAnnSorted, key) &&
isStableSorted(lAnnSorted, key) &&
l2AtLeast(lAnnSorted, n)
}
}
}.holds
def hint[T](t: T, lemma: Boolean): T = {
require(lemma)
t
}
}
back