-
Notifications
You must be signed in to change notification settings - Fork 54
/
Copy pathMergeSort.scala
137 lines (114 loc) · 3.97 KB
/
MergeSort.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* Copyright 2009-2021 EPFL, Lausanne */
import stainless.annotation._
import stainless.lang._
object MergeSort {
// Data types
sealed abstract class List
case class Cons(head : Int, tail : List) extends List
case class Nil() extends List
sealed abstract class LList
case class LCons(head : List, tail : LList) extends LList
case class LNil() extends LList
def content(list : List) : Set[Int] = list match {
case Nil() => Set.empty[Int]
case Cons(x, xs) => Set(x) ++ content(xs)
}
def lContent(llist : LList) : Set[Int] = llist match {
case LNil() => Set.empty[Int]
case LCons(x, xs) => content(x) ++ lContent(xs)
}
def size(list : List) : BigInt = (list match {
case Nil() => BigInt(0)
case Cons(_, xs) => 1 + size(xs)
}).ensuring(_ >= 0)
def lSize(llist: LList): BigInt = (llist match {
case LNil() => BigInt(0)
case LCons(_, xs) => 1 + lSize(xs)
}).ensuring (_ >= 0)
def isSorted(list : List) : Boolean = list match {
case Nil() => true
case Cons(_, Nil()) => true
case Cons(x1, Cons(x2, _)) if(x1 > x2) => false
case Cons(_, xs) => isSorted(xs)
}
def lIsSorted(llist : LList) : Boolean = llist match {
case LNil() => true
case LCons(x, xs) => isSorted(x) && lIsSorted(xs)
}
def abs(i : BigInt) : BigInt = {
if(i < 0) -i else i
}.ensuring(_ >= 0)
def mergeSpec(list1 : List, list2 : List, res : List) : Boolean = {
isSorted(res) && content(res) == content(list1) ++ content(list2)
}
def mergeFast(list1 : List, list2 : List) : List = {
require(isSorted(list1) && isSorted(list2))
(list1, list2) match {
case (_, Nil()) => list1
case (Nil(), _) => list2
case (Cons(x, xs), Cons(y, ys)) =>
if(x <= y) {
Cons(x, mergeFast(xs, list2))
} else {
Cons(y, mergeFast(list1, ys))
}
}
}.ensuring(res => mergeSpec(list1, list2, res))
def splitSpec(list : List, res : (List,List)) : Boolean = {
val s1 = size(res._1)
val s2 = size(res._2)
abs(s1 - s2) <= 1 && s1 + s2 == size(list) &&
content(res._1) ++ content(res._2) == content(list)
}
def split(list : List) : (List,List) = (list match {
case Nil() => (Nil(), Nil())
case Cons(x, Nil()) => (Cons(x, Nil()), Nil())
case Cons(x1, Cons(x2, xs)) =>
val (s1,s2) = split(xs)
(Cons(x1, s1), Cons(x2, s2))
}).ensuring(res => splitSpec(list, res))
def sortSpec(in : List, out : List) : Boolean = {
content(out) == content(in) && isSorted(out)
}
// Not really quicksort, neither mergesort.
// Note: the `s` argument is just a witness for termination (always decreases),
// and not needed for functionality. Any decent optimizer will remove it ;-)
def weirdSort(s: BigInt, in : List) : List = {
require(s == size(in))
in match {
case Nil() => Nil()
case Cons(x, Nil()) => Cons(x, Nil())
case _ =>
val (s1,s2) = split(in)
mergeFast(weirdSort(size(s1), s1), weirdSort(size(s2), s2))
}
}.ensuring(res => sortSpec(in, res))
def toLList(list : List) : LList = (list match {
case Nil() => LNil()
case Cons(x, xs) => LCons(Cons(x, Nil()), toLList(xs))
}).ensuring(res => lContent(res) == content(list) && lIsSorted(res))
def mergeMap(llist : LList) : LList = {
require(lIsSorted(llist))
llist match {
case LNil() => LNil()
case o @ LCons(x, LNil()) => o
case LCons(x, LCons(y, ys)) => LCons(mergeFast(x, y), mergeMap(ys))
}
}.ensuring { res =>
lContent(res) == lContent(llist) &&
lIsSorted(res) &&
(lSize(llist) > 1 ==> lSize(res) < lSize(llist))
}
def mergeReduce(llist : LList) : List = {
require(lIsSorted(llist))
decreases(lSize(llist))
llist match {
case LNil() => Nil()
case LCons(x, LNil()) => x
case _ => mergeReduce(mergeMap(llist))
}
}.ensuring(res => content(res) == lContent(llist) && isSorted(res))
def mergeSort(in : List) : List = {
mergeReduce(toLList(in))
}.ensuring(res => sortSpec(in, res))
}