Skip to content

Commit b3338dd

Browse files
committed
correct merge sort, now with is_sorted proof
1 parent 3d8662d commit b3338dd

File tree

1 file changed

+119
-35
lines changed

1 file changed

+119
-35
lines changed

tests/cn/mergesort.c

Lines changed: 119 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
struct node {
22
int value;
3-
struct node* next;
3+
struct node *next;
44
};
55

66
/*@
@@ -25,7 +25,7 @@ function [rec] ({list fst, list snd}) cn_split(list xs) {
2525
{fst: Nil {}, snd: Nil {}}
2626
}
2727
Cons {head: h1, tail: Nil {}} => {
28-
{fst: Nil {}, snd: xs}
28+
{fst: xs, snd: Nil {}}
2929
}
3030
Cons {head: h1, tail: Cons {head : h2, tail : tl2 }} => {
3131
let P = cn_split(tl2);
@@ -37,22 +37,23 @@ function [rec] ({list fst, list snd}) cn_split(list xs) {
3737
3838
function [rec] (list) cn_merge(list xs, list ys) {
3939
match xs {
40-
Nil {} => {
41-
ys
42-
}
43-
Cons {head: x, tail: xs1} => {
44-
match ys {
45-
Nil {} => {
46-
xs
40+
Nil {} => {
41+
ys
42+
}
43+
Cons {head: x, tail: xs_} => {
44+
match ys {
45+
Nil {} => {
46+
xs
47+
}
48+
Cons {head: y, tail: ys_} => {
49+
if (x <= y) {
50+
Cons {head: x, tail: cn_merge(xs_, ys)}
51+
} else {
52+
Cons {head: y, tail: cn_merge(xs, ys_)}
4753
}
48-
Cons { head: y, tail: ys1} => {
49-
let tail = cn_merge(xs1, ys1);
50-
(x < y) ?
51-
(Cons { head: x, tail: Cons {head: y, tail: tail}})
52-
: (Cons { head: y, tail: Cons {head: x, tail: tail}})
53-
}
54-
}
54+
}
5555
}
56+
}
5657
}
5758
}
5859
@@ -72,11 +73,44 @@ function [rec] (list) cn_mergesort(list xs) {
7273
}
7374
}
7475
}
76+
77+
function (boolean) smaller (i32 head, list xs) {
78+
match xs {
79+
Nil {} => {
80+
true
81+
}
82+
Cons {head : x, tail : _} => {
83+
head <= x
84+
}
85+
}
86+
}
87+
88+
function [rec] (boolean) is_sorted(list xs) {
89+
match xs {
90+
Nil {} => {
91+
true
92+
}
93+
Cons {head: head, tail: tail} => {
94+
smaller (head, tail) && is_sorted(tail)
95+
}
96+
}
97+
}
98+
99+
function (list) tl (list xs) {
100+
match xs {
101+
Nil {} => {
102+
Nil {}
103+
}
104+
Cons {head : _, tail : tail} => {
105+
tail
106+
}
107+
}
108+
}
75109
@*/
76110

77111
struct node_pair {
78-
struct node* fst;
79-
struct node* snd;
112+
struct node *fst;
113+
struct node *snd;
80114
};
81115

82116
struct node_pair split(struct node *xs)
@@ -92,7 +126,7 @@ struct node_pair split(struct node *xs)
92126
} else {
93127
struct node *cdr = xs->next;
94128
if (cdr == 0) {
95-
struct node_pair r = {.fst = 0, .snd = xs};
129+
struct node_pair r = {.fst = xs, .snd = 0};
96130
return r;
97131
} else {
98132
struct node_pair p = split(cdr->next);
@@ -104,7 +138,7 @@ struct node_pair split(struct node *xs)
104138
}
105139
}
106140

107-
struct node* merge(struct node *xs, struct node *ys)
141+
struct node *merge(struct node *xs, struct node *ys)
108142
/*@ requires take Xs = List(xs); @*/
109143
/*@ requires take Ys = List(ys); @*/
110144
/*@ ensures take Zs = List(return); @*/
@@ -113,25 +147,18 @@ struct node* merge(struct node *xs, struct node *ys)
113147
/*@ unfold cn_merge(Xs, Ys); @*/
114148
if (xs == 0) {
115149
return ys;
150+
} else if (ys == 0) {
151+
return xs;
152+
} else if (xs->value <= ys->value) {
153+
xs->next = merge(xs->next, ys);
154+
return xs;
116155
} else {
117-
if (ys == 0) {
118-
return xs;
119-
} else {
120-
struct node *zs = merge(xs->next, ys->next);
121-
if (xs->value < ys->value) {
122-
xs->next = ys;
123-
ys->next = zs;
124-
return xs;
125-
} else {
126-
ys->next = xs;
127-
xs->next = zs;
128-
return ys;
129-
}
130-
}
156+
ys->next = merge(xs, ys->next);
157+
return ys;
131158
}
132159
}
133160

134-
struct node* naive_mergesort(struct node *xs)
161+
struct node *naive_mergesort(struct node *xs)
135162
/*@ requires take Xs = List(xs); @*/
136163
/*@ ensures take Ys = List(return); @*/
137164
/*@ ensures Ys == cn_mergesort(Xs); @*/
@@ -149,6 +176,9 @@ struct node* naive_mergesort(struct node *xs)
149176
}
150177
}
151178

179+
180+
181+
152182
int main(void)
153183
/*@ trusted; @*/
154184
{
@@ -158,3 +188,57 @@ int main(void)
158188

159189
struct node *sorted_i1 = naive_mergesort(&i1);
160190
}
191+
192+
193+
194+
195+
196+
void prove_merge_sorted(struct node *p, struct node *q)
197+
/*@ requires take xs_in = List(p);
198+
take ys_in = List(q);
199+
is_sorted(xs_in);
200+
is_sorted(ys_in);
201+
let merged = cn_merge(xs_in, ys_in);
202+
ensures take xs_out = List(p);
203+
take ys_out = List(q);
204+
xs_out == xs_in && ys_out == ys_in;
205+
is_sorted(merged);
206+
@*/
207+
{
208+
/* Unfold the definition of `merged`. */
209+
/*@ unfold cn_merge(xs_in, ys_in); @*/
210+
211+
/* If either list is empty, cn_merge just picks the other, which is
212+
sorted by assumption, so nothing left to do. */
213+
if (p == 0 || q == 0) {
214+
return;
215+
}
216+
/* For non-empty lists, cn_merge picks the smaller head and merges
217+
the rest. */
218+
else {
219+
/* If `xs_in` has the smaller head, it merges `tl(xs_in)` with
220+
`ys_in`. */
221+
if (p->value <= q->value) {
222+
/* By induction hypothesis (IH) `cn_merge(tl(xs_in), ys_in))` is
223+
sorted. To "apply" IH, expand the definition of
224+
`is_sorted(xs_in)` to prove `is_sorted(tl(xs_in))`. */
225+
/*@ unfold is_sorted(xs_in); @*/
226+
prove_merge_sorted(p->next, q);
227+
/* By definition of `cn_merge(tl(xs_in), ys_in)`, that merged
228+
list starts with the minimum of either head, ... */
229+
/*@ unfold cn_merge(tl(xs_in), ys_in); @*/
230+
/* ... so that list with `hd(xs_in)` cons'ed on is also
231+
sorted. @*/
232+
/*@ unfold is_sorted(merged); @*/
233+
return;
234+
}
235+
else {
236+
/* This is symmetric to the proof above. */
237+
/*@ unfold is_sorted(ys_in); @*/
238+
prove_merge_sorted(p, q->next);
239+
/*@ unfold cn_merge(xs_in, tl(ys_in)); @*/
240+
/*@ unfold is_sorted(merged); @*/
241+
return;
242+
}
243+
}
244+
}

0 commit comments

Comments
 (0)