Verifying Merge Sort in Imandra¶
Merge sort is a widely used efficient general purpose sorting algorithm invented by John von Neumann in 1945. It is a prototypical divide and conquer algorithm and forms the basis of standard library sorting functions in languages like OCaml, Java and Python.
Let's verify it in Imandra!
The main idea¶
The main idea of merge sort is as follows:
- Divide the input list into sublists
- Sort the sublists
- Merge the sorted sublists to obtain a sorted version of the original list
Merging two lists¶
We'll start by defining a function to merge
two lists in a "sorted" way. Note that our termination proof involves both arguments l
and m
, using a lexicographic product (via the [@@adm l,m]
admission annotation).
let rec merge (l : int list) (m : int list) =
match l, m with
| [], _ -> m
| _, [] -> l
| a :: ls, b :: ms ->
if a < b then
a :: (merge ls m)
else
b :: (merge l ms)
[@@adm l, m]
val merge : int list -> int list -> int list = <fun>
Termination proof
original | merge l m | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub | merge (List.tl l) m | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal | Ordinal.of_list [Ordinal.Int (_cnt l); Ordinal.Int (_cnt m)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal | Ordinal.of_list [Ordinal.Int (_cnt (List.tl l)); Ordinal.Int (_cnt m)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path | [List.hd l < List.hd m && not Is_a([], m) && not Is_a([], l)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof | detailed proof
Expand
|
original | merge l m | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub | merge l (List.tl m) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal | Ordinal.of_list [Ordinal.Int (_cnt l); Ordinal.Int (_cnt m)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal | Ordinal.of_list [Ordinal.Int (_cnt l); Ordinal.Int (_cnt (List.tl m))] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path | [not (List.hd l < List.hd m) && not Is_a([], m) && not Is_a([], l)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof | detailed proof
Expand
|
Let's experiment a bit with merge
.
Observe that merge
will merge sorted lists in a manner that respects their sortedness. But, if the lists given to merge
aren't sorted, the output of merge
need not be sorted.
Dividing into sublists¶
In merge sort, we need to divide a list into sublists. We'll do this in a simple way using the function odds
which computes "every other" element of a list. We'll then be able to split a list x
into sublists by taking odds x
and odds (List.tl x)
.
let rec odds l =
match l with
| [] -> []
| [x] -> [x]
| x :: y :: rst -> x :: odds rst
val odds : 'a list -> 'a list = <fun>
Termination proof
original | odds l | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub | odds (List.tl (List.tl l)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal | Ordinal.Int (_cnt l) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal | Ordinal.Int (_cnt (List.tl (List.tl l))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path | [not Is_a([], List.tl l) && not Is_a([], l)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof | detailed proof
Expand
|
Let's compute a bit with odds
to better understand how it works.
Defining Merge Sort¶
We can now put the pieces together and define our main merge_sort
function, as follows:
let rec merge_sort l =
match l with
| [] -> []
| [_] -> l
| _ :: ls -> merge (merge_sort (odds l)) (merge_sort (odds ls))
val merge_sort : int list -> int list = <fun>
File "jupyter cell 8", line 1, characters 0-129Error: rejected definition for function merge_sort,
Imandra could not prove termination.
hint: problematic sub-call from `merge_sort l`
to `merge_sort (odds (List.tl l))` under path
not Is_a([], List.tl l) and not Is_a([], l) could not be proved to decrease
(measured subset: (l))
See https://docs.imandra.ai/imandra-docs/notebooks/proving-program-termination/
To admit merge_sort
into Imandra, we must prove it terminating. We can do this with a custom measure function which we'll call merge_sort_measure
. We'll prove a lemma about odds
(by functional induction) to make it clear why the measure is decreasing in every recursive call. We'll install the lemma as a forward_chaining
rule, which is a good strategy for making it effective during termination analysis.
let merge_sort_measure x =
Ordinal.of_int (List.length x)
val merge_sort_measure : 'a list -> Ordinal.t = <fun>
theorem odds_len_1 x =
x <> [] && List.tl x <> []
==>
(List.length (odds x) [@trigger]) < List.length x
[@@induct functional odds] [@@forward_chaining]
val odds_len_1 : 'a list -> bool = <fun> Goal: x <> [] && List.tl x <> [] ==> List.length (odds x) < List.length x. 1 nontautological subgoal. We shall induct according to a scheme derived from odds. Induction scheme: (not (not Is_a([], List.tl x) && not Is_a([], x)) ==> φ x) && (not Is_a([], x) && not Is_a([], List.tl x) && φ (List.tl (List.tl x)) ==> φ x). 2 nontautological subgoals. Subgoal 2: H0. List.length x <= List.length (odds x) H1. not (x = []) H2. not (List.tl x = []) |--------------------------------------------------------------------------- not Is_a([], List.tl x) && not Is_a([], x) But simplification reduces this to true, using the forward-chaining rule List.len_nonnegative. Subgoal 1: H0. List.length x <= List.length (odds x) H1. not (x = []) H2. not (List.tl x = []) H3. not Is_a([], x) H4. not Is_a([], List.tl x) H5. not (List.length (List.tl (List.tl x)) <= List.length (odds (List.tl (List.tl x)))) || not (not (List.tl (List.tl x) = []) && not (List.tl (List.tl (List.tl x)) = [])) |--------------------------------------------------------------------------- false This simplifies, using the definitions of List.length and odds to the following 3 subgoals: Subgoal 1.3: H0. List.length (List.tl (List.tl x)) <= List.length (odds (List.tl (List.tl x))) H1. List.tl (List.tl x) = [] H2. List.length (List.tl x) <= List.length (odds (List.tl (List.tl x))) |--------------------------------------------------------------------------- C0. Is_a([], List.tl x) C1. Is_a([], x) But simplification reduces this to true, using the definitions of List.length and odds. Subgoal 1.2: H0. List.length (List.tl (List.tl x)) <= List.length (odds (List.tl (List.tl x))) H1. (List.tl (List.tl x)) <> [] H2. List.tl (List.tl (List.tl x)) = [] H3. List.length (List.tl x) <= List.length (odds (List.tl (List.tl x))) |--------------------------------------------------------------------------- C0. Is_a([], List.tl x) C1. Is_a([], x) This simplifies, using the definitions of List.length and odds to: Subgoal 1.2': H0. List.tl (List.tl (List.tl x)) = [] H1. List.length (List.tl (List.tl (List.tl x))) <= 0 H2. List.length (List.tl (List.tl x)) <= 0 H3. (List.tl (List.tl x)) <> [] |--------------------------------------------------------------------------- C0. Is_a([], List.tl x) C1. Is_a([], x) But simplification reduces this to true, using the definition of List.length. Subgoal 1.1: H0. List.length (List.tl x) <= List.length (odds (List.tl (List.tl x))) |--------------------------------------------------------------------------- C0. Is_a([], List.tl x) C1. Is_a([], x) C2. List.length (List.tl (List.tl x)) <= List.length (odds (List.tl (List.tl x))) But simplification reduces this to true, using the definition of List.length. ⓘ Rules: (:def List.length) (:def odds) (:fc List.len_nonnegative) (:induct odds)
ground_instances | 0 |
definitions | 22 |
inductions | 1 |
search_time | 0.563s |
start[0.563s, "Goal"] not (( :var_0: ) = []) && not (List.tl ( :var_0: ) = []) ==> List.length (odds ( :var_0: )) < List.length ( :var_0: )
subproof
(not (List.length x <= List.length (odds x)) || x = []) || List.tl x = []start[0.563s, "1"] (not (List.length x <= List.length (odds x)) || x = []) || List.tl x = []
induction on (functional odds) :scheme (not (not Is_a([], List.tl x) && not Is_a([], x)) ==> φ x) && (not Is_a([], x) && not Is_a([], List.tl x) && φ (List.tl (List.tl x)) ==> φ x)
Split (let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : bool) = not Is_a([], _x_0) in let (_x_2 : bool) = not Is_a([], x) in let (_x_3 : bool) = not (List.length x <= List.length (odds x)) in let (_x_4 : bool) = not (not (x = []) && not (_x_0 = [])) in let (_x_5 : sko_ty_0 list) = List.tl _x_0 in ((_x_1 && _x_2 || _x_3) || _x_4) && ((_x_3 || _x_4) || not ((_x_2 && _x_1) && (not (List.length _x_5 <= List.length (odds _x_5)) || not (not (_x_5 = []) && not (List.tl _x_5 = []))))) :cases [let (_x_0 : sko_ty_0 list) = List.tl x in ((not (List.length x <= List.length (odds x)) || x = []) || _x_0 = []) || not Is_a([], _x_0) && not Is_a([], x); let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = List.tl _x_0 in ((((not (List.length x <= List.length (odds x)) || x = []) || _x_0 = []) || Is_a([], x)) || Is_a([], _x_0)) || not (not (List.length _x_1 <= List.length (odds _x_1)) || not (not (_x_1 = []) && not (List.tl _x_1 = [])))])
subproof
let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = List.tl _x_0 in ((((not (List.length x <= List.length (odds x)) || x = []) || _x_0 = []) || Is_a([], x)) || Is_a([], _x_0)) || not (not (List.length _x_1 <= List.length (odds _x_1)) || not (not (_x_1 = []) && not (List.tl _x_1 = [])))start[0.562s, "1"] let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = List.tl _x_0 in ((((not (List.length x <= List.length (odds x)) || x = []) || _x_0 = []) || Is_a([], x)) || Is_a([], _x_0)) || not (not (List.length _x_1 <= List.length (odds _x_1)) || not (not (_x_1 = []) && not (List.tl _x_1 = [])))
simplify
into let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : bool) = Is_a([], _x_0) || Is_a([], x) in let (_x_2 : sko_ty_0 list) = List.tl _x_0 in let (_x_3 : int) = List.length (odds _x_2) in let (_x_4 : bool) = List.length _x_2 <= _x_3 in let (_x_5 : bool) = _x_1 || not _x_4 in let (_x_6 : bool) = _x_2 = [] in let (_x_7 : bool) = not (List.length _x_0 <= _x_3) in (((_x_5 || not _x_6) || _x_7) && (((_x_5 || _x_6) || not (List.tl _x_2 = [])) || _x_7)) && ((_x_1 || _x_4) || _x_7)
expansions [List.length, odds, List.length, List.length, odds, List.length, List.length, odds, List.length]
rewrite_steps forward_chaining List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
List.len_nonnegative
Subproof
Subproof
Subproof
subproof
let (_x_0 : sko_ty_0 list) = List.tl x in ((not (List.length x <= List.length (odds x)) || x = []) || _x_0 = []) || not Is_a([], _x_0) && not Is_a([], x)start[0.562s, "2"] let (_x_0 : sko_ty_0 list) = List.tl x in ((not (List.length x <= List.length (odds x)) || x = []) || _x_0 = []) || not Is_a([], _x_0) && not Is_a([], x)
simplify
into true
expansions []
rewrite_steps forward_chaining List.len_nonnegative
List.len_nonnegative
Now we're ready to define merge_sort
. Imandra is able prove it terminating using our custom measure (and lemma!).
let rec merge_sort l =
match l with
| [] -> []
| [_] -> l
| _ :: ls -> merge (merge_sort (odds l)) (merge_sort (odds ls))
[@@measure merge_sort_measure l]
val merge_sort : int list -> int list = <fun>
Termination proof
original | merge_sort l | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub | merge_sort (odds l) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal | merge_sort_measure l | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal | merge_sort_measure (odds l) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path | [not Is_a([], List.tl l) && not Is_a([], l)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof | detailed proof
Expand
|
original | merge_sort l | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub | merge_sort (odds (List.tl l)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal | merge_sort_measure l | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal | merge_sort_measure (odds (List.tl l)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path | [not Is_a([], List.tl l) && not Is_a([], l)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof | detailed proof
Expand
|
Let's experiment a bit with merge_sort
to gain confidence we've defined it correctly.
merge_sort [9;100;6;2;34;19;3;4;7;6]
- : int list = [2;3;4;6;6;7;9;19;34;100]
This looks pretty good! Let's now use Imandra to prove it correct.
Proving Merge Sort correct¶
What does it mean for a sorting algorithm to be correct? There are two main components:
- The result is sorted
- The result has the same elements as the original
Let's start by defining what it means for a list to be sorted.
let rec is_sorted (x : int list) =
match x with
| [] -> true
| [_] -> true
| x :: x' :: xs -> x <= x' && is_sorted (x' :: xs)
val is_sorted : int list -> bool = <fun>
Termination proof
original | is_sorted x | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub | let (_x_0 : int list) = List.tl x in is_sorted ((List.hd _x_0) :: (List.tl _x_0)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal | Ordinal.Int (_cnt x) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal | let (_x_0 : int list) = List.tl x in Ordinal.Int (_cnt ((List.hd _x_0) :: (List.tl _x_0))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path | [List.hd x <= List.hd (List.tl x) && not Is_a([], List.tl x) && not Is_a([], x)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof | detailed proof
Expand
|
As usual, let's experiment a bit with this definition.
instance (fun x -> List.length x >= 5 && is_sorted x)
- : int list -> bool = <fun> module CX : sig val x : int list end
Instance (after 11 steps, 0.022s): let (x : int list) = [133758; 133758; 133758; 133758; 133758]
ground_instances | 11 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
definitions | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
inductions | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
search_time | 0.022s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
details | Expand
|
start[0.022s] List.length ( :var_0: ) >= 5 && is_sorted ( :var_0: )
- unroll
expr (is_sorted_64 x_75)
expansions - unroll
expr (|List.length_2426| x_75)
expansions - unroll
expr (|List.length_2426| (|get.::.1_2425| x_75))
expansions - unroll
expr (|List.length_2426| (|get.::.1_2425| (|get.::.1_2425| x_75)))
expansions - unroll
expr (is_sorted_64 (|::_3| (|get.::.0_2424| (|get.::.1_2425| x_75)) (|get.::.1_2425…
expansions - unroll
expr (|List.length_2426| (|get.::.1_2425| (|get.::.1_2425| (|get.::.1_2425| x_75))))
expansions - unroll
expr (let ((a!1 (|::_3| (|get.::.0_2424| (|get.::.1_2425| (|get.::.1_2425| x_75))) (|g…
expansions - unroll
expr (let ((a!1 (|get.::.1_2425| (|get.::.1_2425| (|get.::.1_2425| (|get.::.1_2425| x_75)))))) (|List.l…
expansions - unroll
expr (let ((a!1 (|get.::.0_2424| (|get.::.1_2425| (|get.::.1_2425| (|get.::.1_2425| x_75))))) (a!2 …
expansions - unroll
expr (let ((a!1 (|get.::.1_2425| (|get.::.1_2425| (|get.::.1_2425| (|get.::.1_2425| x_75)))))) (|List.l…
expansions - unroll
expr (let ((a!1 (|get.::.1_2425| (|get.::.1_2425| (|get.::.1_2425| (|get.::.1_2425| x_75)))))) (is_sort…
expansions - Sat (Some let (x : int list) = [133758; 133758; 133758; 133758; 133758] )
instance (fun x -> List.length x >= 5 && is_sorted x && is_sorted (List.rev x))
- : int list -> bool = <fun> module CX : sig val x : int list end
Instance (after 47 steps, 0.115s): let (x : int list) = [427723; 427723; 427723; 427723; 427723]
ground_instances | 47 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
definitions | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
inductions | 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
search_time | 0.115s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
details | Expand
|
start[0.115s] List.length ( :var_0: ) >= 5 && is_sorted ( :var_0: ) && is_sorted (List.rev ( :var_0: ))
simplify
into (List.length ( :var_0: ) >= 5 && is_sorted ( :var_0: )) && is_sorted (List.rev ( :var_0: ))
expansions []
rewrite_steps forward_chaining - unroll
expr (|List.rev_2469| x_77)
expansions - unroll
expr (is_sorted_64 (|List.rev_2469| x_77))
expansions - unroll
expr (is_sorted_64 x_77)
expansions - unroll
expr (|List.length_2465| x_77)
expansions - unroll
expr (|List.rev_2469| (|get.::.1_2464| x_77))
expansions - unroll
expr (|List.append_2473| (|List.rev_2469| (|get.::.1_2464| x_77)) (|::_3| (|get.::.0_2463| x_77) |[]_…
expansions - unroll
expr (|List.length_2465| (|get.::.1_2464| x_77))
expansions - unroll
expr (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| x_77)))
expansions - unroll
expr (|List.append_2473| (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| x_77))) (|::_3| (|get.::.…
expansions - unroll
expr (let ((a!1 (|::_3| (|get.::.0_2463| (|get.::.1_2464| (|List.rev_2469| x_77))) (|g…
expansions - unroll
expr (|List.length_2465| (|get.::.1_2464| (|get.::.1_2464| x_77)))
expansions - unroll
expr (is_sorted_64 (|::_3| (|get.::.0_2463| (|get.::.1_2464| x_77)) (|get.::.1_2464…
expansions - unroll
expr (|List.append_2473| (|get.::.1_2464| (|List.rev_2469| (|get.::.1_2464| x_77))) (|::_3| (|get.::.…
expansions - unroll
expr (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))
expansions - unroll
expr (let ((a!1 (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))) (a!2 …
expansions - unroll
expr (|List.length_2465| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))
expansions - unroll
expr (let ((a!1 (|::_3| (|get.::.0_2463| (|get.::.1_2464| (|get.::.1_2464| x_77))) (|g…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.r…
expansions - unroll
expr (let ((a!1 (|get.::.0_2463| (|get.::.1_2464| (|get.::.1_2464| (|List.rev_2469| x_77))))) (a!2 …
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))) (a!2 …
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.l…
expansions - unroll
expr (let ((a!1 (|get.::.0_2463| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))) (a!2 …
expansions - unroll
expr (let ((a!1 (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))) (a!2 …
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|List.rev_2469| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.r…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.l…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (is_sort…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))) (a!2 …
expansions - unroll
expr (let ((a!1 (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))) (a!2 …
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.r…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|List.rev_2469| x_77)))))) (is_sort…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.l…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (is_sort…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|List.rev_2469| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))) (a!2 …
expansions - unroll
expr (let ((a!1 (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77))))) (a!2 …
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.r…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|List.rev_2469| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| x_77)))))) (let ((a!2…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|List.rev_2469| (|get.::.1_2464| x_77)))))) (|List.a…
expansions - unroll
expr (let ((a!1 (|get.::.1_2464| (|get.::.1_2464| (|get.::.1_2464| (|List.rev_2469| x_77)))))) (is_sort…
expansions - Sat (Some let (x : int list) = [427723; 427723; 427723; 427723; 427723] )
Proving Merge Sort sorts¶
Now that we've defined our is_sorted
predicate, we're ready to state one of our main verification goals: that merge_sort
sorts!
We can write this this way:
theorem merge_sort_sorts x =
is_sorted (merge_sort x)
Before we try to prove this for all possible cases by induction, let's ask Imandra to verify that this property holds up to our current recursion unrolling bound (100
).
verify (fun x -> is_sorted (merge_sort x))
- : int list -> bool = <fun>
expanded |
|
blocked |
|
ground_instances | 100 |
definitions | 0 |
inductions | 0 |
search_time | 1.917s |
start[1.917s] is_sorted (merge_sort ( :var_0: ))
- unroll
expr (merge_sort_56 x_79)
expansions - unroll
expr (is_sorted_64 (merge_sort_56 x_79))
expansions - unroll
expr (odds_1824 (|get.::.1_1823| x_79))
expansions - unroll
expr (merge_sort_56 (odds_1824 (|get.::.1_1823| x_79)))
expansions - unroll
expr (odds_1824 x_79)
expansions - unroll
expr (odds_1824 (|get.::.1_1823| (|get.::.1_1823| x_79)))
expansions - unroll
expr (merge_sort_56 (odds_1824 x_79))
expansions - unroll
expr (merge_14 (merge_sort_56 (odds_1824 x_79)) (merge_sort_56 (odds_1824 (|get.::.1_1823| x_79…
expansions - unroll
expr (odds_1824 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| x_79))))
expansions - unroll
expr (let ((a!1 (|::_3| (|get.::.0_1822| (|get.::.1_1823| (merge_sort_56 x_79))) (|get…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| x_79)))))) (odds_18…
expansions - unroll
expr (odds_1824 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79))))
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (merge_sort_56 a!1))
expansions - unroll
expr (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))
expansions - unroll
expr (merge_sort_56 (odds_1824 (odds_1824 (|get.::.1_1823| x_79))))
expansions - unroll
expr (let ((a!1 (|get.::.0_1822| (merge_sort_56 (odds_1824 (|get.::.1_1823| x_79))))) (a!4 (|get.::…
expansions - unroll
expr (let ((a!1 (merge_sort_56 (odds_1824 (odds_1824 (|get.::.1_1823| x_79))))) (a!2 (odds_1824 (|g…
expansions - unroll
expr (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))
expansions - unroll
expr (merge_sort_56 (odds_1824 (|get.::.1_1823| (odds_1824 x_79))))
expansions - unroll
expr (odds_1824 (odds_1824 x_79))
expansions - unroll
expr (merge_sort_56 (odds_1824 (odds_1824 x_79)))
expansions - unroll
expr (let ((a!1 (merge_sort_56 (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (merge_14 (merge_sort_…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 a!1…
expansions - unroll
expr (odds_1824 (|get.::.1_1823| (|get.::.1_1823| (odds_1824 x_79))))
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| x_79)))))) (odds_18…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 (|g…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 a!1))
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (merge_sort_56 (odds…
expansions - unroll
expr (odds_1824 (odds_1824 (odds_1824 (|get.::.1_1823| x_79))))
expansions - unroll
expr (let ((a!1 (|get.::.0_1822| (|get.::.1_1823| (|get.::.1_1823| (merge_sort_56 x_79))))) (a!2 (|…
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79))))) (a!2 (merge_sort_…
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (merge_sort_56 a!1))
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (odds_1824 (|get.::.1_1823| x_79))))) (a!2 (|get.::.1_1823| (…
expansions - unroll
expr (let ((a!1 (|get.::.0_1822| (merge_sort_56 (odds_1824 (|get.::.1_1823| x_79))))) (a!3 (|get.::…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| x_79)))))) (odds_18…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 a!1…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 a!1))
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (merge_sort_56 (odds…
expansions - unroll
expr (odds_1824 (odds_1824 (|get.::.1_1823| (odds_1824 x_79))))
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 (|get.::.…
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (merge_sort_56 a!1))
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (|get.::.1_1823| (odds_1824 x_79))))) (a!2 (|get.::.1_1823| (…
expansions - unroll
expr (odds_1824 (|get.::.1_1823| (odds_1824 (odds_1824 x_79))))
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (odds_1824 x_79)))))) (merge_sort_56 a!1))
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (merge_sort_56 (odds…
expansions - unroll
expr (odds_1824 (odds_1824 (odds_1824 x_79)))
expansions - unroll
expr (merge_sort_56 (odds_1824 (odds_1824 (odds_1824 x_79))))
expansions - unroll
expr (let ((a!1 (merge_sort_56 (odds_1824 (odds_1824 (odds_1824 x_79))))) (a!2 (odds_1824 (|get.::.…
expansions - unroll
expr (let ((a!1 (merge_sort_56 (odds_1824 (|get.::.1_1823| (odds_1824 x_79))))) (a!2 (|get.::.0_182…
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 a!1))
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 (|g…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| x_79)))))) (odds_18…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 (|g…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 (|get.::.…
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (merge_sort_56 (odds…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 (|get.::.…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 (|get.::.…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (merge_sort_56 (odds…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 (odds_182…
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (merge_14 (merge_sor…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| (merge_sort_56 x_79)))))) (is_sorted…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (merge_sort_56 (odds…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (let ((a!2 (merge_sort…
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 (|get.::.1_1823…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 (|g…
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (merge_sort_56 (odds_1824 …
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (odds_1824 a!1))
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (merge_sort_56 (odds_1824 …
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (odds_1824 (|get.::.1_1823| x_79)))))) (merge_14 (merge_sort_56 (…
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| x_79))))) (a!2 (merge_sort_…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (|get.::.1_1823| x_79))))) (a!2 (odds_1824 (…
expansions - unroll
expr (let ((a!1 (|get.::.0_1822| (merge_sort_56 (odds_1824 (|get.::.1_1823| x_79))))) (a!3 (|get.::…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 (|g…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 (|get.::.…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| (|get.::.1_1823| x_79)))))) (let ((a!2…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (odds_1824 (odds_1824 x_79)))))) (odds_1824 a!1))
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (|get.::.1_1823| (odds_1824 (odds_1824 x_79)))))) (odds_1824 (|get.::.…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 (|get.::.…
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (odds_1824 x_79)))))) (odds_1824 (|get.::.1_1823…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 (|get.::.…
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (odds_1824 x_79)))))) (merge_sort_56 (odds_1824 …
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (odds_1824 x_79)))))) (odds_1824 a!1))
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (odds_1824 x_79)))))) (merge_sort_56 (odds_1824 …
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (odds_1824 x_79)))))) (odds_1824 a!1))
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (merge_sort_56 (odds…
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (odds_1824 x_79)))))) (merge_14 (merge_sort_56 (…
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (odds_1824 (odds_1824 x_79)))))) (merge_sort_56 (odds_1824 …
expansions - unroll
expr (odds_1824 (odds_1824 (odds_1824 (odds_1824 x_79))))
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (odds_1824 (odds_1824 x_79)))))) (merge_sort_56 a!1))
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 (odds_182…
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (odds_1824 (odds_1824 x_79))))) (a!2 (|get.::.1_1823| (odds_1…
expansions - unroll
expr (let ((a!1 (odds_1824 (|get.::.1_1823| (odds_1824 (odds_1824 x_79))))) (a!2 (merge_sort_56 (od…
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 (|get.::.1_1823…
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (merge_sort_56 (odds_1824 …
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (merge_sort_56 (odds…
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (odds_1824 a!1))
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79))))) (a!2 (odds_1824 (…
expansions - unroll
expr (let ((a!1 (merge_sort_56 (odds_1824 (|get.::.1_1823| (odds_1824 x_79))))) (a!2 (|get.::.0_182…
expansions - unroll
expr (let ((a!1 (odds_1824 (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (merge_sort_56 (odds_1824 …
expansions - unroll
expr (let ((a!1 (|get.::.1_1823| (odds_1824 (|get.::.1_1823| (odds_1824 x_79)))))) (let ((a!2 (merge_sort…
expansions
Excellent. This gives us quite some confidence that this result is true. Let's now try to prove it by induction. We'll want to use an induction principle suggested by the definition of merge_sort
. Let's use our usual strategy of #max_induct 1
, so we can analyse Imandra's output to help us find needed lemmata.
Note, we could prove this theorem directly without any lemmas by using #max_induct 3
, but it's good practice to use #max_induct 1
and have Imandra help us develop useful collections of lemmas.
theorem merge_sort_sorts x =
is_sorted (merge_sort x)
[@@induct functional merge_sort]
val merge_sort_sorts : int list -> bool = <fun> Goal: is_sorted (merge_sort x). 1 nontautological subgoal. We shall induct according to a scheme derived from merge_sort. Induction scheme: (not (not Is_a([], List.tl x) && not Is_a([], x)) ==> φ x) && (not Is_a([], x) && not Is_a([], List.tl x) && φ (odds (List.tl x)) && φ (odds x) ==> φ x). 2 nontautological subgoals. Subgoal 2: |--------------------------------------------------------------------------- C0. not Is_a([], List.tl x) && not Is_a([], x) C1. is_sorted (merge_sort x) But simplification reduces this to true, using the definitions of is_sorted and merge_sort. Subgoal 1: H0. not Is_a([], x) H1. not Is_a([], List.tl x) H2. is_sorted (merge_sort (odds (List.tl x))) H3. is_sorted (merge_sort (odds x)) |--------------------------------------------------------------------------- is_sorted (merge_sort x) This simplifies, using the definition of merge_sort to: Subgoal 1': H0. is_sorted (merge_sort (odds (List.tl x))) H1. is_sorted (merge_sort (odds x)) |--------------------------------------------------------------------------- C0. Is_a([], List.tl x) C1. Is_a([], x) C2. is_sorted (merge (merge_sort (odds x)) (merge_sort (odds (List.tl x)))) We can eliminate destructors by the following substitution: x -> x1 :: x2 This produces the modified subgoal: Subgoal 1'': H0. is_sorted (merge_sort (odds (x1 :: x2))) H1. is_sorted (merge_sort (odds x2)) |--------------------------------------------------------------------------- C0. Is_a([], x2) C1. is_sorted (merge (merge_sort (odds (x1 :: x2))) (merge_sort (odds x2))) Candidates for generalization: odds (x1 :: x2) odds x2 This produces the modified subgoal: Subgoal 1''': H0. is_sorted (merge_sort gen_1) H1. is_sorted (merge_sort gen_2) |--------------------------------------------------------------------------- C0. Is_a([], x2) C1. is_sorted (merge (merge_sort gen_1) (merge_sort gen_2)) Candidates for generalization: merge_sort gen_1 merge_sort gen_2 This produces the modified subgoal: Subgoal 1'''': H0. is_sorted gen_3 H1. is_sorted gen_4 |--------------------------------------------------------------------------- C0. Is_a([], x2) C1. is_sorted (merge gen_3 gen_4) Must try induction. ⚠ Aborting proof attempt for merge_sort_sorts. ⓘ Rules: (:def is_sorted) (:def merge_sort) (:induct merge_sort) Checkpoints: H0. is_sorted gen_3 H1. is_sorted gen_4 |--------------------------------------------------------------------------- C0. Is_a([], x2) C1. is_sorted (merge gen_3 gen_4) Error[/server]: Maximum induction depth reached (1). You can set this with #max_induct.
Analysing the output of this proof attempt, we notice the following components of our Checkpoint
:
H1. is_sorted gen_1
H2. is_sorted gen_2
|---------------------------------------------------------------------------
is_sorted (merge gen_2 gen_1)
Ah, of course! We need to prove a lemma that merge
respects the sortedness of its inputs.
Let's do this by functional induction following the definition of merge
and install it as a rewrite
rule.
We'll allow nested induction (#max_induct 2
), but it's a good exercise to do this proof without it (deriving an additional lemma!).
#max_induct 2;;
theorem merge_sorts x y =
is_sorted x && is_sorted y
==>
is_sorted (merge x y)
[@@induct functional merge]
[@@rewrite]
val merge_sorts : int list -> int list -> bool = <fun> Goal: is_sorted x && is_sorted y ==> is_sorted (merge x y). 1 nontautological subgoal. We shall induct according to a scheme derived from merge. Induction scheme: (not (List.hd x < List.hd y && not Is_a([], y) && not Is_a([], x)) && not (not (List.hd x < List.hd y) && not Is_a([], y) && not Is_a([], x)) ==> φ x y) && (not Is_a([], x) && not Is_a([], y) && not (List.hd x < List.hd y) && φ x (List.tl y) ==> φ x y) && (not Is_a([], x) && not Is_a([], y) && List.hd x < List.hd y && φ (List.tl x) y ==> φ x y). 3 nontautological subgoals. Subgoal 3: H0. is_sorted x H1. is_sorted y H2. not ((not (List.hd y <= List.hd x) && not Is_a([], y)) && not Is_a([], x)) H3. not ((List.hd y <= List.hd x && not Is_a([], y)) && not Is_a([], x)) |--------------------------------------------------------------------------- is_sorted (merge x y) But simplification reduces this to true, using the definitions of is_sorted and merge. Subgoal 2: H0. is_sorted x H1. is_sorted y H2. not Is_a([], x) H3. not Is_a([], y) H4. List.hd y <= List.hd x H5. is_sorted x && is_sorted (List.tl y) ==> is_sorted (merge x (List.tl y)) |--------------------------------------------------------------------------- is_sorted (merge x y) This simplifies, using the definition of merge to the following 2 subgoals: Subgoal 2.2: H0. List.hd y <= List.hd x H1. is_sorted x H2. is_sorted y |--------------------------------------------------------------------------- C0. Is_a([], y) C1. Is_a([], x) C2. is_sorted (merge x (List.tl y)) C3. is_sorted (List.tl y) C4. is_sorted ((List.hd y) :: (merge x (List.tl y))) But we verify Subgoal 2.2 by recursive unrolling. Subgoal 2.1: H0. List.hd y <= List.hd x H1. is_sorted x H2. is_sorted y H3. is_sorted (merge x (List.tl y)) |--------------------------------------------------------------------------- C0. Is_a([], y) C1. Is_a([], x) C2. is_sorted ((List.hd y) :: (merge x (List.tl y))) But we verify Subgoal 2.1 by recursive unrolling. Subgoal 1: H0. is_sorted x H1. is_sorted y H2. not Is_a([], x) H3. not Is_a([], y) H4. not (List.hd y <= List.hd x) H5. is_sorted (List.tl x) && is_sorted y ==> is_sorted (merge (List.tl x) y) |--------------------------------------------------------------------------- is_sorted (merge x y) This simplifies, using the definition of merge to the following 2 subgoals: Subgoal 1.2: H0. is_sorted x H1. is_sorted y |--------------------------------------------------------------------------- C0. Is_a([], y) C1. Is_a([], x) C2. is_sorted (merge (List.tl x) y) C3. is_sorted (List.tl x) C4. List.hd y <= List.hd x C5. is_sorted ((List.hd x) :: (merge (List.tl x) y)) But we verify Subgoal 1.2 by recursive unrolling. Subgoal 1.1: H0. is_sorted x H1. is_sorted y H2. is_sorted (merge (List.tl x) y) |--------------------------------------------------------------------------- C0. Is_a([], y) C1. Is_a([], x) C2. List.hd y <= List.hd x C3. is_sorted ((List.hd x) :: (merge (List.tl x) y)) But we verify Subgoal 1.1 by recursive unrolling. ⓘ Rules: (:def is_sorted) (:def merge) (:induct merge)
ground_instances | 19 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
definitions | 12 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
inductions | 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
search_time | 1.863s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
details | Expand
|
start[1.863s, "Goal"] is_sorted ( :var_0: ) && is_sorted ( :var_1: ) ==> is_sorted (merge ( :var_0: ) ( :var_1: ))
subproof
(not (is_sorted x) || not (is_sorted y)) || is_sorted (merge x y)start[1.863s, "1"] (not (is_sorted x) || not (is_sorted y)) || is_sorted (merge x y)
induction on (functional merge) :scheme (not (List.hd x < List.hd y && not Is_a([], y) && not Is_a([], x)) && not (not (List.hd x < List.hd y) && not Is_a([], y) && not Is_a([], x)) ==> φ x y) && (not Is_a([], x) && not Is_a([], y) && not (List.hd x < List.hd y) && φ x (List.tl y) ==> φ x y) && (not Is_a([], x) && not Is_a([], y) && List.hd x < List.hd y && φ (List.tl x) y ==> φ x y)
Split (let (_x_0 : bool) = is_sorted x in let (_x_1 : bool) = is_sorted y in let (_x_2 : bool) = is_sorted (merge x y) || not (_x_0 && _x_1) in let (_x_3 : bool) = List.hd y <= List.hd x in let (_x_4 : bool) = not _x_3 in let (_x_5 : bool) = not Is_a([], y) in let (_x_6 : bool) = not Is_a([], x) in let (_x_7 : bool) = _x_6 && _x_5 in let (_x_8 : int list) = List.tl y in let (_x_9 : int list) = List.tl x in ((_x_2 || not (not ((_x_4 && _x_5) && _x_6) && not ((_x_3 && _x_5) && _x_6))) && (_x_2 || not ((_x_7 && _x_3) && (is_sorted (merge x _x_8) || not (_x_0 && is_sorted _x_8))))) && (_x_2 || not ((_x_7 && _x_4) && (is_sorted (merge _x_9 y) || not (is_sorted _x_9 && _x_1)))) :cases [let (_x_0 : bool) = List.hd y <= List.hd x in let (_x_1 : bool) = not Is_a([], y) in let (_x_2 : bool) = not Is_a([], x) in (((not (is_sorted x) || not (is_sorted y)) || (not _x_0 && _x_1) && _x_2) || (_x_0 && _x_1) && _x_2) || is_sorted (merge x y); let (_x_0 : bool) = is_sorted x in let (_x_1 : int list) = List.tl y in (((((not _x_0 || not (is_sorted y)) || Is_a([], x)) || Is_a([], y)) || not (List.hd y <= List.hd x)) || not (_x_0 && is_sorted _x_1 ==> is_sorted (merge x _x_1))) || is_sorted (merge x y); let (_x_0 : bool) = is_sorted y in let (_x_1 : int list) = List.tl x in (((((not (is_sorted x) || not _x_0) || Is_a([], x)) || Is_a([], y)) || List.hd y <= List.hd x) || not (is_sorted _x_1 && _x_0 ==> is_sorted (merge _x_1 y))) || is_sorted (merge x y)])
subproof
let (_x_0 : bool) = is_sorted y in let (_x_1 : int list) = List.tl x in (((((not (is_sorted x) || not _x_0) || Is_a([], x)) || Is_a([], y)) || List.hd y <= List.hd x) || not (is_sorted _x_1 && _x_0 ==> is_sorted (merge _x_1 y))) || is_sorted (merge x y)start[1.861s, "1"] let (_x_0 : bool) = is_sorted y in let (_x_1 : int list) = List.tl x in (((((not (is_sorted x) || not _x_0) || Is_a([], x)) || Is_a([], y)) || List.hd y <= List.hd x) || not (is_sorted _x_1 && _x_0 ==> is_sorted (merge _x_1 y))) || is_sorted (merge x y)
simplify
into let (_x_0 : bool) = Is_a([], y) || Is_a([], x) in let (_x_1 : int list) = List.tl x in let (_x_2 : int list) = merge _x_1 y in let (_x_3 : bool) = is_sorted _x_2 in let (_x_4 : int) = List.hd x in let (_x_5 : bool) = List.hd y <= _x_4 in let (_x_6 : bool) = not (is_sorted x) in let (_x_7 : bool) = not (is_sorted y) in let (_x_8 : bool) = is_sorted (_x_4 :: _x_2) in ((((((_x_0 || _x_3) || is_sorted _x_1) || _x_5) || _x_6) || _x_7) || _x_8) && (((((_x_0 || _x_5) || _x_6) || _x_7) || _x_8) || not _x_3)
expansions [merge, merge]
rewrite_steps forward_chaining Subproof
Subproof
subproof
let (_x_0 : bool) = is_sorted x in let (_x_1 : int list) = List.tl y in (((((not _x_0 || not (is_sorted y)) || Is_a([], x)) || Is_a([], y)) || not (List.hd y <= List.hd x)) || not (_x_0 && is_sorted _x_1 ==> is_sorted (merge x _x_1))) || is_sorted (merge x y)start[1.861s, "2"] let (_x_0 : bool) = is_sorted x in let (_x_1 : int list) = List.tl y in (((((not _x_0 || not (is_sorted y)) || Is_a([], x)) || Is_a([], y)) || not (List.hd y <= List.hd x)) || not (_x_0 && is_sorted _x_1 ==> is_sorted (merge x _x_1))) || is_sorted (merge x y)
simplify
into let (_x_0 : bool) = Is_a([], y) || Is_a([], x) in let (_x_1 : int list) = List.tl y in let (_x_2 : int list) = merge x _x_1 in let (_x_3 : bool) = is_sorted _x_2 in let (_x_4 : int) = List.hd y in let (_x_5 : bool) = not (_x_4 <= List.hd x) in let (_x_6 : bool) = not (is_sorted x) in let (_x_7 : bool) = not (is_sorted y) in let (_x_8 : bool) = is_sorted (_x_4 :: _x_2) in ((((((_x_0 || _x_3) || is_sorted _x_1) || _x_5) || _x_6) || _x_7) || _x_8) && (((((_x_0 || _x_5) || _x_6) || _x_7) || not _x_3) || _x_8)
expansions [merge, merge]
rewrite_steps forward_chaining Subproof
Subproof
subproof
let (_x_0 : bool) = List.hd y <= List.hd x in let (_x_1 : bool) = not Is_a([], y) in let (_x_2 : bool) = not Is_a([], x) in (((not (is_sorted x) || not (is_sorted y)) || (not _x_0 && _x_1) && _x_2) || (_x_0 && _x_1) && _x_2) || is_sorted (merge x y)start[1.861s, "3"] let (_x_0 : bool) = List.hd y <= List.hd x in let (_x_1 : bool) = not Is_a([], y) in let (_x_2 : bool) = not Is_a([], x) in (((not (is_sorted x) || not (is_sorted y)) || (not _x_0 && _x_1) && _x_2) || (_x_0 && _x_1) && _x_2) || is_sorted (merge x y)
simplify
into true
expansions [merge, is_sorted, merge, is_sorted, merge, is_sorted, merge, is_sorted]
rewrite_steps forward_chaining
Excellent! Now that we have that key lemma proved and available as a rewrite rule, let's return to our original goal:
theorem merge_sort_sorts x =
is_sorted (merge_sort x)
[@@induct functional merge_sort]
val merge_sort_sorts : int list -> bool = <fun> Goal: is_sorted (merge_sort x). 1 nontautological subgoal. We shall induct according to a scheme derived from merge_sort. Induction scheme: (not (not Is_a([], List.tl x) && not Is_a([], x)) ==> φ x) && (not Is_a([], x) && not Is_a([], List.tl x) && φ (odds (List.tl x)) && φ (odds x) ==> φ x). 2 nontautological subgoals. Subgoal 2: |--------------------------------------------------------------------------- C0. not Is_a([], List.tl x) && not Is_a([], x) C1. is_sorted (merge_sort x) But simplification reduces this to true, using the definitions of is_sorted and merge_sort. Subgoal 1: H0. not Is_a([], x) H1. not Is_a([], List.tl x) H2. is_sorted (merge_sort (odds (List.tl x))) H3. is_sorted (merge_sort (odds x)) |--------------------------------------------------------------------------- is_sorted (merge_sort x) This simplifies, using the definition of merge_sort to: Subgoal 1': H0. is_sorted (merge_sort (odds (List.tl x))) H1. is_sorted (merge_sort (odds x)) |--------------------------------------------------------------------------- C0. Is_a([], List.tl x) C1. Is_a([], x) C2. is_sorted (merge (merge_sort (odds x)) (merge_sort (odds (List.tl x)))) But simplification reduces this to true, using the rewrite rule merge_sorts. ⓘ Rules: (:def is_sorted) (:def merge_sort) (:rw merge_sorts) (:induct merge_sort)
ground_instances | 0 |
definitions | 5 |
inductions | 1 |
search_time | 0.256s |
start[0.256s, "Goal"] is_sorted (merge_sort ( :var_0: ))
subproof
is_sorted (merge_sort x)start[0.256s, "1"] is_sorted (merge_sort x)
induction on (functional merge_sort) :scheme (not (not Is_a([], List.tl x) && not Is_a([], x)) ==> φ x) && (not Is_a([], x) && not Is_a([], List.tl x) && φ (odds (List.tl x)) && φ (odds x) ==> φ x)
Split (let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = not Is_a([], _x_0) in let (_x_2 : bool) = not Is_a([], x) in let (_x_3 : bool) = is_sorted (merge_sort x) in (_x_1 && _x_2 || _x_3) && (_x_3 || not (((_x_2 && _x_1) && is_sorted (merge_sort (odds _x_0))) && is_sorted (merge_sort (odds x)))) :cases [not Is_a([], List.tl x) && not Is_a([], x) || is_sorted (merge_sort x); let (_x_0 : int list) = List.tl x in (((Is_a([], x) || Is_a([], _x_0)) || not (is_sorted (merge_sort (odds _x_0)))) || not (is_sorted (merge_sort (odds x)))) || is_sorted (merge_sort x)])
subproof
let (_x_0 : int list) = List.tl x in (((Is_a([], x) || Is_a([], _x_0)) || not (is_sorted (merge_sort (odds _x_0)))) || not (is_sorted (merge_sort (odds x)))) || is_sorted (merge_sort x)start[0.256s, "1"] let (_x_0 : int list) = List.tl x in (((Is_a([], x) || Is_a([], _x_0)) || not (is_sorted (merge_sort (odds _x_0)))) || not (is_sorted (merge_sort (odds x)))) || is_sorted (merge_sort x)
simplify
into let (_x_0 : int list) = List.tl x in let (_x_1 : int list) = merge_sort (odds _x_0) in let (_x_2 : int list) = merge_sort (odds x) in (((Is_a([], _x_0) || Is_a([], x)) || not (is_sorted _x_1)) || not (is_sorted _x_2)) || is_sorted (merge _x_2 _x_1)
expansions merge_sort
rewrite_steps forward_chaining simplify
into true
expansions []
rewrite_steps merge_sorts
forward_chaining
subproof
not Is_a([], List.tl x) && not Is_a([], x) || is_sorted (merge_sort x)start[0.256s, "2"] not Is_a([], List.tl x) && not Is_a([], x) || is_sorted (merge_sort x)
simplify
into true
expansions [is_sorted, merge_sort, is_sorted, merge_sort]
rewrite_steps forward_chaining
Beautiful! So now we've proved half of our specification: merge_sort
sorts!
Merge Sort contains the right elements¶
Let's now turn to the second half of our correctness criterion: That merge_sort x
contains "the same" elements as x
.
What does this mean, and why does it matter?
An incorrect sorting function¶
Consider the sorting function bad_sort
defined as bad_sort x = []
.
Clearly, we could prove theorem bad_sort_sorts x = is_sorted (bad_sort x)
, even though we'd never want to use bad_sort
as a sorting function in practice.
That is, computing a sorted list is only one piece of the puzzle. We must also verify that merge_sort x
contains exactly the same elements as x
, including their multiplicity).
Multiset equivalence¶
How can we express this concept? We'll use the notion of a multiset and count the number of occurrences of each element of x
, and make sure these are respected by merge_sort
.
We'll begin by defining a function num_occurs x y
which counts the number of times an element x
appears in the list y
.
let rec num_occurs x y =
match y with
| [] -> 0
| hd :: tl when hd = x ->
1 + num_occurs x tl
| _ :: tl ->
num_occurs x tl
val num_occurs : 'a -> 'a list -> int = <fun>
Termination proof
original | num_occurs x y | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub | num_occurs x (List.tl y) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal | Ordinal.Int (_cnt y) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal | Ordinal.Int (_cnt (List.tl y)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path | [List.hd y = x && not Is_a([], y)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof | detailed proof
Expand
|
original | num_occurs x y | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub | num_occurs x (List.tl y) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original ordinal | Ordinal.Int (_cnt y) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sub ordinal | Ordinal.Int (_cnt (List.tl y)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
path | [not (List.hd y = x) && not Is_a([], y)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
proof | detailed proof
Expand
|
Anticipating the need to reason about the interplay of num_occurs
and merge
, let's prove the following nice lemmas characterising their relationship (applying the max_induct 1
strategy to find these lemmas as above is a great exercise).
theorem num_occur_merge a x y =
num_occurs a (merge x y) = num_occurs a x + num_occurs a y
[@@induct functional merge]
[@@rewrite]
val num_occur_merge : int -> int list -> int list -> bool = <fun> Goal: num_occurs a (merge x y) = num_occurs a x + num_occurs a y. 1 nontautological subgoal. We shall induct according to a scheme derived from merge. Induction scheme: (not (List.hd x < List.hd y && not Is_a([], y) && not Is_a([], x)) && not (not (List.hd x < List.hd y) && not Is_a([], y) && not Is_a([], x)) ==> φ a x y) && (not Is_a([], x) && not Is_a([], y) && not (List.hd x < List.hd y) && φ a x (List.tl y) ==> φ a x y) && (not Is_a([], x) && not Is_a([], y) && List.hd x < List.hd y && φ a (List.tl x) y ==> φ a x y). 3 nontautological subgoals. Subgoal 3: H0. not ((not (List.hd y <= List.hd x) && not Is_a([], y)) && not Is_a([], x)) H1. not ((List.hd y <= List.hd x && not Is_a([], y)) && not Is_a([], x)) |--------------------------------------------------------------------------- num_occurs a (merge x y) = num_occurs a x + num_occurs a y But simplification reduces this to true, using the definitions of merge and num_occurs. Subgoal 2: H0. not Is_a([], x) H1. not Is_a([], y) H2. List.hd y <= List.hd x H3. num_occurs a (merge x (List.tl y)) = num_occurs a x + num_occurs a (List.tl y) |--------------------------------------------------------------------------- num_occurs a (merge x y) = num_occurs a x + num_occurs a y But simplification reduces this to true, using the definitions of merge and num_occurs. Subgoal 1: H0. not Is_a([], x) H1. not Is_a([], y) H2. not (List.hd y <= List.hd x) H3. num_occurs a (merge (List.tl x) y) = num_occurs a (List.tl x) + num_occurs a y |--------------------------------------------------------------------------- num_occurs a (merge x y) = num_occurs a x + num_occurs a y But simplification reduces this to true, using the definitions of merge and num_occurs. ⓘ Rules: (:def merge) (:def num_occurs) (:induct merge)
ground_instances | 0 |
definitions | 14 |
inductions | 1 |
search_time | 0.194s |
start[0.194s, "Goal"] num_occurs ( :var_0: ) (merge ( :var_1: ) ( :var_2: )) = num_occurs ( :var_0: ) ( :var_1: ) + num_occurs ( :var_0: ) ( :var_2: )
subproof
num_occurs a (merge x y) = num_occurs a x + num_occurs a ystart[0.194s, "1"] num_occurs a (merge x y) = num_occurs a x + num_occurs a y
induction on (functional merge) :scheme (not (List.hd x < List.hd y && not Is_a([], y) && not Is_a([], x)) && not (not (List.hd x < List.hd y) && not Is_a([], y) && not Is_a([], x)) ==> φ a x y) && (not Is_a([], x) && not Is_a([], y) && not (List.hd x < List.hd y) && φ a x (List.tl y) ==> φ a x y) && (not Is_a([], x) && not Is_a([], y) && List.hd x < List.hd y && φ a (List.tl x) y ==> φ a x y)
Split (let (_x_0 : bool) = List.hd y <= List.hd x in let (_x_1 : bool) = not _x_0 in let (_x_2 : bool) = not Is_a([], y) in let (_x_3 : bool) = not Is_a([], x) in let (_x_4 : int) = num_occurs a x in let (_x_5 : int) = num_occurs a y in let (_x_6 : bool) = num_occurs a (merge x y) = _x_4 + _x_5 in let (_x_7 : bool) = _x_3 && _x_2 in let (_x_8 : int list) = List.tl y in let (_x_9 : int list) = List.tl x in ((not (not ((_x_1 && _x_2) && _x_3) && not ((_x_0 && _x_2) && _x_3)) || _x_6) && (not ((_x_7 && _x_0) && num_occurs a (merge x _x_8) = _x_4 + num_occurs a _x_8) || _x_6)) && (not ((_x_7 && _x_1) && num_occurs a (merge _x_9 y) = num_occurs a _x_9 + _x_5) || _x_6) :cases [let (_x_0 : bool) = List.hd y <= List.hd x in let (_x_1 : bool) = not Is_a([], y) in let (_x_2 : bool) = not Is_a([], x) in ((not _x_0 && _x_1) && _x_2 || (_x_0 && _x_1) && _x_2) || num_occurs a (merge x y) = num_occurs a x + num_occurs a y; let (_x_0 : int list) = List.tl y in let (_x_1 : int) = num_occurs a x in (((Is_a([], x) || Is_a([], y)) || not (List.hd y <= List.hd x)) || not (num_occurs a (merge x _x_0) = _x_1 + num_occurs a _x_0)) || num_occurs a (merge x y) = _x_1 + num_occurs a y; let (_x_0 : int list) = List.tl x in let (_x_1 : int) = num_occurs a y in (((Is_a([], x) || Is_a([], y)) || List.hd y <= List.hd x) || not (num_occurs a (merge _x_0 y) = num_occurs a _x_0 + _x_1)) || num_occurs a (merge x y) = num_occurs a x + _x_1])
subproof
let (_x_0 : int list) = List.tl x in let (_x_1 : int) = num_occurs a y in (((Is_a([], x) || Is_a([], y)) || List.hd y <= List.hd x) || not (num_occurs a (merge _x_0 y) = num_occurs a _x_0 + _x_1)) || num_occurs a (merge x y) = num_occurs a x + _x_1start[0.193s, "1"] let (_x_0 : int list) = List.tl x in let (_x_1 : int) = num_occurs a y in (((Is_a([], x) || Is_a([], y)) || List.hd y <= List.hd x) || not (num_occurs a (merge _x_0 y) = num_occurs a _x_0 + _x_1)) || num_occurs a (merge x y) = num_occurs a x + _x_1
simplify
into true
expansions [num_occurs, num_occurs, merge]
rewrite_steps forward_chaining
subproof
let (_x_0 : int list) = List.tl y in let (_x_1 : int) = num_occurs a x in (((Is_a([], x) || Is_a([], y)) || not (List.hd y <= List.hd x)) || not (num_occurs a (merge x _x_0) = _x_1 + num_occurs a _x_0)) || num_occurs a (merge x y) = _x_1 + num_occurs a ystart[0.193s, "2"] let (_x_0 : int list) = List.tl y in let (_x_1 : int) = num_occurs a x in (((Is_a([], x) || Is_a([], y)) || not (List.hd y <= List.hd x)) || not (num_occurs a (merge x _x_0) = _x_1 + num_occurs a _x_0)) || num_occurs a (merge x y) = _x_1 + num_occurs a y
simplify
into true
expansions [num_occurs, num_occurs, merge]
rewrite_steps forward_chaining
subproof
let (_x_0 : bool) = List.hd y <= List.hd x in let (_x_1 : bool) = not Is_a([], y) in let (_x_2 : bool) = not Is_a([], x) in ((not _x_0 && _x_1) && _x_2 || (_x_0 && _x_1) && _x_2) || num_occurs a (merge x y) = num_occurs a x + num_occurs a ystart[0.193s, "3"] let (_x_0 : bool) = List.hd y <= List.hd x in let (_x_1 : bool) = not Is_a([], y) in let (_x_2 : bool) = not Is_a([], x) in ((not _x_0 && _x_1) && _x_2 || (_x_0 && _x_1) && _x_2) || num_occurs a (merge x y) = num_occurs a x + num_occurs a y
simplify
into true
expansions [num_occurs, merge, num_occurs, merge, num_occurs, merge, num_occurs, merge]
rewrite_steps forward_chaining
theorem num_occurs_arith (x : int list) (a : int) =
x <> [] && List.tl x <> []
==>
num_occurs a (odds (List.tl x))
=
num_occurs a x - num_occurs a (odds x)
[@@induct functional num_occurs]
[@@rewrite]
val num_occurs_arith : int list -> int -> bool = <fun> Goal: x <> [] && List.tl x <> [] ==> num_occurs a (odds (List.tl x)) = num_occurs a x - num_occurs a (odds x). 1 nontautological subgoal. We shall induct according to a scheme derived from num_occurs. Induction scheme: (not (List.hd x = a && not Is_a([], x)) && not (not (List.hd x = a) && not Is_a([], x)) ==> φ a x) && (not Is_a([], x) && not (List.hd x = a) && φ a (List.tl x) ==> φ a x) && (not Is_a([], x) && List.hd x = a && φ a (List.tl x) ==> φ a x). 3 nontautological subgoals. Subgoal 3: H0. not (x = []) H1. not (List.tl x = []) H2. not (List.hd x = a && not Is_a([], x)) H3. not (not (List.hd x = a) && not Is_a([], x)) |--------------------------------------------------------------------------- num_occurs a (odds (List.tl x)) = num_occurs a x + -1 * num_occurs a (odds x) But simplification reduces this to true. Subgoal 2: H0. not (x = []) H1. not (List.tl x = []) H2. not Is_a([], x) H3. not (List.hd x = a) H4. not (List.tl x = []) && not (List.tl (List.tl x) = []) ==> num_occurs a (odds (List.tl (List.tl x))) = num_occurs a (List.tl x) + -1 * num_occurs a (odds (List.tl x)) |--------------------------------------------------------------------------- num_occurs a (odds (List.tl x)) = num_occurs a x + -1 * num_occurs a (odds x) This simplifies, using the definitions of num_occurs and odds to the following 4 subgoals: Subgoal 2.4: H0. (List.tl x) <> [] H1. (List.tl (List.tl x)) <> [] H2. num_occurs a (odds (List.tl (List.tl x))) = num_occurs a (List.tl (List.tl x)) + -1 * num_occurs a (odds (List.tl x)) |--------------------------------------------------------------------------- C0. List.hd x = a C1. Is_a([], x) C2. List.hd (List.tl x) = a C3. num_occurs a (odds (List.tl x)) = num_occurs a (List.tl x) + -1 * num_occurs a (odds (List.tl (List.tl x))) But simplification reduces this to true, using the definition of num_occurs. Subgoal 2.3: H0. (List.tl x) <> [] H1. (List.tl (List.tl x)) <> [] H2. num_occurs a (odds (List.tl (List.tl x))) = 1 + num_occurs a (List.tl (List.tl x)) + -1 * num_occurs a (odds (List.tl x)) H3. List.hd (List.tl x) = a |--------------------------------------------------------------------------- C0. List.hd x = a C1. Is_a([], x) C2. num_occurs a (odds (List.tl x)) = num_occurs a (List.tl x) + -1 * num_occurs a (odds (List.tl (List.tl x))) But simplification reduces this to true, using the definition of num_occurs. Subgoal 2.2: H0. (List.tl x) <> [] H1. List.tl (List.tl x) = [] |--------------------------------------------------------------------------- C0. List.hd x = a C1. Is_a([], x) C2. List.hd (List.tl x) = a C3. 0 = num_occurs a (List.tl x) + -1 * num_occurs a (odds (List.tl (List.tl x))) This simplifies, using the definitions of num_occurs and odds to: Subgoal 2.2': H0. List.tl (List.tl x) = [] H1. (List.tl x) <> [] |--------------------------------------------------------------------------- C0. List.hd x = a C1. Is_a([], x) C2. List.hd (List.tl x) = a C3. 0 = num_occurs a (List.tl (List.tl x)) But simplification reduces this to true, using the definition of num_occurs. Subgoal 2.1: H0. (List.tl x) <> [] H1. List.tl (List.tl x) = [] H2. List.hd (List.tl x) = a |--------------------------------------------------------------------------- C0. List.hd x = a C1. Is_a([], x) C2. 1 = num_occurs a (List.tl x) + -1 * num_occurs a (odds (List.tl (List.tl x))) This simplifies, using the definitions of num_occurs and odds to: Subgoal 2.1': H0. List.tl (List.tl x) = [] H1. (List.tl x) <> [] |--------------------------------------------------------------------------- C0. Is_a([], x) C1. List.hd x = List.hd (List.tl x) C2. 0 = num_occurs (List.hd (List.tl x)) (List.tl (List.tl x)) But simplification reduces this to true, using the definition of num_occurs. Subgoal 1: H0. not (x = []) H1. not (List.tl x = []) H2. not Is_a([], x) H3. List.hd x = a H4. not (List.tl x = []) && not (List.tl (List.tl x) = []) ==> num_occurs a (odds (List.tl (List.tl x))) = num_occurs a (List.tl x) + -1 * num_occurs a (odds (List.tl x)) |--------------------------------------------------------------------------- num_occurs a (odds (List.tl x)) = num_occurs a x + -1 * num_occurs a (odds x) This simplifies, using the definitions of num_occurs and odds to the following 4 subgoals: Subgoal 1.4: H0. (List.tl x) <> [] H1. (List.tl (List.tl x)) <> [] H2. num_occurs (List.hd x) (odds (List.tl (List.tl x))) = num_occurs (List.hd x) (List.tl (List.tl x)) + -1 * num_occurs (List.hd x) (odds (List.tl x)) |--------------------------------------------------------------------------- C0. Is_a([], x) C1. num_occurs (List.hd x) (odds (List.tl x)) = num_occurs (List.hd x) (List.tl x) + -1 * num_occurs (List.hd x) (odds (List.tl (List.tl x))) C2. List.hd (List.tl x) = List.hd x But simplification reduces this to true, using the definition of num_occurs. Subgoal 1.3: H0. (List.tl x) <> [] H1. (List.tl (List.tl x)) <> [] H2. List.hd (List.tl x) = List.hd x H3. num_occurs (List.hd x) (odds (List.tl (List.tl x))) = 1 + num_occurs (List.hd x) (List.tl (List.tl x)) + -1 * num_occurs (List.hd x) (odds (List.tl x)) |--------------------------------------------------------------------------- C0. Is_a([], x) C1. num_occurs (List.hd x) (odds (List.tl x)) = num_occurs (List.hd x) (List.tl x) + -1 * num_occurs (List.hd x) (odds (List.tl (List.tl x))) But simplification reduces this to true, using the definition of num_occurs. Subgoal 1.2: H0. (List.tl x) <> [] H1. List.tl (List.tl x) = [] |--------------------------------------------------------------------------- C0. Is_a([], x) C1. 0 = num_occurs (List.hd x) (List.tl x) + -1 * num_occurs (List.hd x) (odds (List.tl (List.tl x))) C2. List.hd (List.tl x) = List.hd x This simplifies, using the definitions of num_occurs and odds to: Subgoal 1.2': H0. List.tl (List.tl x) = [] H1. (List.tl x) <> [] |--------------------------------------------------------------------------- C0. Is_a([], x) C1. 0 = num_occurs (List.hd x) (List.tl (List.tl x)) C2. List.hd (List.tl x) = List.hd x But simplification reduces this to true, using the definition of num_occurs. Subgoal 1.1: H0. (List.tl x) <> [] H1. List.tl (List.tl x) = [] H2. List.hd (List.tl x) = List.hd x |--------------------------------------------------------------------------- C0. Is_a([], x) C1. 1 = num_occurs (List.hd x) (List.tl x) + -1 * num_occurs (List.hd x) (odds (List.tl (List.tl x))) This simplifies, using the definitions of num_occurs and odds to: Subgoal 1.1': H0. List.tl (List.tl x) = [] H1. (List.tl x) <> [] H2. List.hd (List.tl x) = List.hd x |--------------------------------------------------------------------------- C0. Is_a([], x) C1. 0 = num_occurs (List.hd x) (List.tl (List.tl x)) But simplification reduces this to true, using the definition of num_occurs. ⓘ Rules: (:def num_occurs) (:def odds) (:induct num_occurs)
ground_instances | 0 |
definitions | 48 |
inductions | 1 |
search_time | 1.262s |
start[1.262s, "Goal"] let (_x_0 : int list) = List.tl ( :var_0: ) in not (( :var_0: ) = []) && not (_x_0 = []) ==> num_occurs ( :var_1: ) (odds _x_0) = num_occurs ( :var_1: ) ( :var_0: ) - num_occurs ( :var_1: ) (odds ( :var_0: ))
subproof
let (_x_0 : int list) = List.tl x in (x = [] || _x_0 = []) || num_occurs a (odds _x_0) = num_occurs a x + -1 * num_occurs a (odds x)start[1.261s, "1"] let (_x_0 : int list) = List.tl x in (x = [] || _x_0 = []) || num_occurs a (odds _x_0) = num_occurs a x + -1 * num_occurs a (odds x)
induction on (functional num_occurs) :scheme (not (List.hd x = a && not Is_a([], x)) && not (not (List.hd x = a) && not Is_a([], x)) ==> φ a x) && (not Is_a([], x) && not (List.hd x = a) && φ a (List.tl x) ==> φ a x) && (not Is_a([], x) && List.hd x = a && φ a (List.tl x) ==> φ a x)
Split (let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = not (_x_0 = []) in let (_x_2 : int) = num_occurs a (odds _x_0) in let (_x_3 : bool) = not (not (x = []) && _x_1) || _x_2 = num_occurs a x + -1 * num_occurs a (odds x) in let (_x_4 : bool) = List.hd x = a in let (_x_5 : bool) = not Is_a([], x) in let (_x_6 : bool) = not _x_4 in let (_x_7 : int list) = List.tl _x_0 in let (_x_8 : bool) = not (_x_1 && not (_x_7 = [])) || num_occurs a (odds _x_7) = num_occurs a _x_0 + -1 * _x_2 in ((_x_3 || not (not (_x_4 && _x_5) && not (_x_6 && _x_5))) && (_x_3 || not ((_x_5 && _x_6) && _x_8))) && (_x_3 || not ((_x_5 && _x_4) && _x_8)) :cases [let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = List.hd x = a in let (_x_2 : bool) = not Is_a([], x) in (((x = [] || _x_0 = []) || _x_1 && _x_2) || not _x_1 && _x_2) || num_occurs a (odds _x_0) = num_occurs a x + -1 * num_occurs a (odds x); let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = _x_0 = [] in let (_x_2 : int list) = List.tl _x_0 in let (_x_3 : int) = num_occurs a (odds _x_0) in ((((x = [] || _x_1) || Is_a([], x)) || List.hd x = a) || not (not _x_1 && not (_x_2 = []) ==> num_occurs a (odds _x_2) = num_occurs a _x_0 + -1 * _x_3)) || _x_3 = num_occurs a x + -1 * num_occurs a (odds x); let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = _x_0 = [] in let (_x_2 : int list) = List.tl _x_0 in let (_x_3 : int) = num_occurs a (odds _x_0) in ((((x = [] || _x_1) || Is_a([], x)) || not (List.hd x = a)) || not (not _x_1 && not (_x_2 = []) ==> num_occurs a (odds _x_2) = num_occurs a _x_0 + -1 * _x_3)) || _x_3 = num_occurs a x + -1 * num_occurs a (odds x)])
subproof
let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = _x_0 = [] in let (_x_2 : int list) = List.tl _x_0 in let (_x_3 : int) = num_occurs a (odds _x_0) in ((((x = [] || _x_1) || Is_a([], x)) || not (List.hd x = a)) || not (not _x_1 && not (_x_2 = []) ==> num_occurs a (odds _x_2) = num_occurs a _x_0 + -1 * _x_3)) || _x_3 = num_occurs a x + -1 * num_occurs a (odds x)start[1.260s, "1"] let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = _x_0 = [] in let (_x_2 : int list) = List.tl _x_0 in let (_x_3 : int) = num_occurs a (odds _x_0) in ((((x = [] || _x_1) || Is_a([], x)) || not (List.hd x = a)) || not (not _x_1 && not (_x_2 = []) ==> num_occurs a (odds _x_2) = num_occurs a _x_0 + -1 * _x_3)) || _x_3 = num_occurs a x + -1 * num_occurs a (odds x)
simplify
into let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = Is_a([], x) || _x_0 = [] in let (_x_2 : int list) = List.tl _x_0 in let (_x_3 : bool) = _x_2 = [] in let (_x_4 : int) = List.hd x in let (_x_5 : int) = num_occurs _x_4 (odds _x_0) in let (_x_6 : int) = num_occurs _x_4 (odds _x_2) in let (_x_7 : int) = num_occurs _x_4 _x_0 + -1 * _x_6 in let (_x_8 : bool) = (_x_1 || _x_3) || _x_5 = _x_7 in let (_x_9 : int) = num_occurs _x_4 _x_2 in let (_x_10 : int) = -1 * _x_5 in let (_x_11 : bool) = List.hd _x_0 = _x_4 in let (_x_12 : bool) = not _x_11 in let (_x_13 : bool) = _x_1 || not _x_3 in ((((_x_8 || not (_x_6 = _x_9 + _x_10)) || _x_11) && ((_x_8 || _x_12) || not (_x_6 = 1 + _x_9 + _x_10))) && ((_x_13 || 0 = _x_7) || _x_11)) && ((_x_13 || 1 = _x_7) || _x_12)
expansions [num_occurs, odds, num_occurs, num_occurs, num_occurs, num_occurs, odds, num_occurs, odds, num_occurs, num_occurs, odds, num_occurs, num_occurs]
rewrite_steps forward_chaining Subproof
Subproof
Subproof
Subproof
subproof
let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = _x_0 = [] in let (_x_2 : int list) = List.tl _x_0 in let (_x_3 : int) = num_occurs a (odds _x_0) in ((((x = [] || _x_1) || Is_a([], x)) || List.hd x = a) || not (not _x_1 && not (_x_2 = []) ==> num_occurs a (odds _x_2) = num_occurs a _x_0 + -1 * _x_3)) || _x_3 = num_occurs a x + -1 * num_occurs a (odds x)start[1.261s, "2"] let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = _x_0 = [] in let (_x_2 : int list) = List.tl _x_0 in let (_x_3 : int) = num_occurs a (odds _x_0) in ((((x = [] || _x_1) || Is_a([], x)) || List.hd x = a) || not (not _x_1 && not (_x_2 = []) ==> num_occurs a (odds _x_2) = num_occurs a _x_0 + -1 * _x_3)) || _x_3 = num_occurs a x + -1 * num_occurs a (odds x)
simplify
into let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = (List.hd x = a || Is_a([], x)) || _x_0 = [] in let (_x_2 : int list) = List.tl _x_0 in let (_x_3 : bool) = _x_2 = [] in let (_x_4 : bool) = _x_1 || _x_3 in let (_x_5 : int) = num_occurs a (odds _x_2) in let (_x_6 : int) = num_occurs a _x_2 in let (_x_7 : int) = num_occurs a (odds _x_0) in let (_x_8 : int) = -1 * _x_7 in let (_x_9 : bool) = List.hd _x_0 = a in let (_x_10 : int) = num_occurs a _x_0 + -1 * _x_5 in let (_x_11 : bool) = _x_7 = _x_10 in let (_x_12 : bool) = not _x_9 in let (_x_13 : bool) = _x_1 || not _x_3 in (((((_x_4 || not (_x_5 = _x_6 + _x_8)) || _x_9) || _x_11) && (((_x_4 || not (_x_5 = 1 + _x_6 + _x_8)) || _x_12) || _x_11)) && ((_x_13 || _x_9) || 0 = _x_10)) && ((_x_13 || _x_12) || 1 = _x_10)
expansions [num_occurs, odds, num_occurs, num_occurs, num_occurs, num_occurs, odds, num_occurs, odds, num_occurs, num_occurs, odds, num_occurs, num_occurs]
rewrite_steps forward_chaining Subproof
Subproof
Subproof
Subproof
subproof
let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = List.hd x = a in let (_x_2 : bool) = not Is_a([], x) in (((x = [] || _x_0 = []) || _x_1 && _x_2) || not _x_1 && _x_2) || num_occurs a (odds _x_0) = num_occurs a x + -1 * num_occurs a (odds x)start[1.261s, "3"] let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = List.hd x = a in let (_x_2 : bool) = not Is_a([], x) in (((x = [] || _x_0 = []) || _x_1 && _x_2) || not _x_1 && _x_2) || num_occurs a (odds _x_0) = num_occurs a x + -1 * num_occurs a (odds x)
simplify
into true
expansions []
rewrite_steps forward_chaining
Finally, let's put the pieces together and prove our main remaining theorem.
And now, our second main theorem at last:
theorem merge_sort_elements a (x : int list) =
num_occurs a x = num_occurs a (merge_sort x)
[@@induct functional merge_sort]
val merge_sort_elements : int -> int list -> bool = <fun> Goal: num_occurs a x = num_occurs a (merge_sort x). 1 nontautological subgoal. We shall induct according to a scheme derived from merge_sort. Induction scheme: (not (not Is_a([], List.tl x) && not Is_a([], x)) ==> φ a x) && (not Is_a([], x) && not Is_a([], List.tl x) && φ a (odds (List.tl x)) && φ a (odds x) ==> φ a x). 2 nontautological subgoals. Subgoal 2: |--------------------------------------------------------------------------- C0. not Is_a([], List.tl x) && not Is_a([], x) C1. num_occurs a x = num_occurs a (merge_sort x) This simplifies, using the definitions of merge_sort and num_occurs to the following 3 subgoals: Subgoal 2.3: H0. Is_a([], List.tl x) |--------------------------------------------------------------------------- C0. Is_a([], x) C1. List.hd x = a C2. num_occurs a x = 0 This simplifies, using the definition of num_occurs to: Subgoal 2.3': H0. Is_a([], List.tl x) |--------------------------------------------------------------------------- C0. Is_a([], x) C1. List.hd x = a C2. num_occurs a (List.tl x) = 0 But simplification reduces this to true, using the definition of num_occurs. Subgoal 2.2: H0. Is_a([], List.tl x) H1. List.hd x = a |--------------------------------------------------------------------------- C0. Is_a([], x) C1. num_occurs a x = 1 This simplifies, using the definition of num_occurs to: Subgoal 2.2': H0. Is_a([], List.tl x) |--------------------------------------------------------------------------- C0. Is_a([], x) C1. num_occurs (List.hd x) (List.tl x) = 0 But simplification reduces this to true, using the definition of num_occurs. Subgoal 2.1: H0. Is_a([], List.tl x) H1. Is_a([], x) |--------------------------------------------------------------------------- num_occurs a x = 0 But simplification reduces this to true, using the definition of num_occurs. Subgoal 1: H0. not Is_a([], x) H1. not Is_a([], List.tl x) H2. num_occurs a (odds (List.tl x)) = num_occurs a (merge_sort (odds (List.tl x))) H3. num_occurs a (odds x) = num_occurs a (merge_sort (odds x)) |--------------------------------------------------------------------------- num_occurs a x = num_occurs a (merge_sort x) This simplifies, using the definitions of merge_sort and num_occurs, and the rewrite rule num_occurs_arith to the following 2 subgoals: Subgoal 1.2: H0. num_occurs a (odds x) = num_occurs a (merge_sort (odds x)) H1. num_occurs a (List.tl x) + -1 * num_occurs a (odds x) = num_occurs a (merge_sort (odds (List.tl x))) |--------------------------------------------------------------------------- C0. Is_a([], List.tl x) C1. Is_a([], x) C2. List.hd x = a C3. num_occurs a (List.tl x) = num_occurs a (merge (merge_sort (odds x)) (merge_sort (odds (List.tl x)))) But simplification reduces this to true, using the rewrite rule num_occur_merge. Subgoal 1.1: H0. num_occurs a (odds x) = num_occurs a (merge_sort (odds x)) H1. List.hd x = a H2. num_occurs a (List.tl x) + -1 * num_occurs a (odds x) = -1 + num_occurs a (merge_sort (odds (List.tl x))) |--------------------------------------------------------------------------- C0. Is_a([], List.tl x) C1. Is_a([], x) C2. num_occurs a (List.tl x) = -1 + num_occurs a (merge (merge_sort (odds x)) (merge_sort (odds (List.tl x)))) But simplification reduces this to true, using the rewrite rule num_occur_merge. ⓘ Rules: (:def merge_sort) (:def num_occurs) (:rw num_occur_merge) (:rw num_occurs_arith) (:induct merge_sort)
ground_instances | 0 |
definitions | 17 |
inductions | 1 |
search_time | 1.532s |
start[1.532s, "Goal"] num_occurs ( :var_0: ) ( :var_1: ) = num_occurs ( :var_0: ) (merge_sort ( :var_1: ))
subproof
num_occurs a x = num_occurs a (merge_sort x)start[1.532s, "1"] num_occurs a x = num_occurs a (merge_sort x)
induction on (functional merge_sort) :scheme (not (not Is_a([], List.tl x) && not Is_a([], x)) ==> φ a x) && (not Is_a([], x) && not Is_a([], List.tl x) && φ a (odds (List.tl x)) && φ a (odds x) ==> φ a x)
Split (let (_x_0 : int list) = List.tl x in let (_x_1 : bool) = not Is_a([], _x_0) in let (_x_2 : bool) = not Is_a([], x) in let (_x_3 : bool) = num_occurs a x = num_occurs a (merge_sort x) in let (_x_4 : int list) = odds _x_0 in let (_x_5 : int list) = odds x in (_x_1 && _x_2 || _x_3) && (not (((_x_2 && _x_1) && num_occurs a _x_4 = num_occurs a (merge_sort _x_4)) && num_occurs a _x_5 = num_occurs a (merge_sort _x_5)) || _x_3) :cases [not Is_a([], List.tl x) && not Is_a([], x) || num_occurs a x = num_occurs a (merge_sort x); let (_x_0 : int list) = List.tl x in let (_x_1 : int list) = odds _x_0 in let (_x_2 : int list) = odds x in (((Is_a([], x) || Is_a([], _x_0)) || not (num_occurs a _x_1 = num_occurs a (merge_sort _x_1))) || not (num_occurs a _x_2 = num_occurs a (merge_sort _x_2))) || num_occurs a x = num_occurs a (merge_sort x)])
subproof
let (_x_0 : int list) = List.tl x in let (_x_1 : int list) = odds _x_0 in let (_x_2 : int list) = odds x in (((Is_a([], x) || Is_a([], _x_0)) || not (num_occurs a _x_1 = num_occurs a (merge_sort _x_1))) || not (num_occurs a _x_2 = num_occurs a (merge_sort _x_2))) || num_occurs a x = num_occurs a (merge_sort x)start[1.531s, "1"] let (_x_0 : int list) = List.tl x in let (_x_1 : int list) = odds _x_0 in let (_x_2 : int list) = odds x in (((Is_a([], x) || Is_a([], _x_0)) || not (num_occurs a _x_1 = num_occurs a (merge_sort _x_1))) || not (num_occurs a _x_2 = num_occurs a (merge_sort _x_2))) || num_occurs a x = num_occurs a (merge_sort x)
simplify
into let (_x_0 : int list) = List.tl x in let (_x_1 : int list) = odds x in let (_x_2 : int) = num_occurs a _x_1 in let (_x_3 : int list) = merge_sort _x_1 in let (_x_4 : bool) = (Is_a([], _x_0) || Is_a([], x)) || not (_x_2 = num_occurs a _x_3) in let (_x_5 : bool) = List.hd x = a in let (_x_6 : int) = num_occurs a _x_0 in let (_x_7 : int) = _x_6 + -1 * _x_2 in let (_x_8 : int list) = merge_sort (odds _x_0) in let (_x_9 : int) = num_occurs a _x_8 in let (_x_10 : int) = num_occurs a (merge _x_3 _x_8) in (((_x_4 || _x_5) || not (_x_7 = _x_9)) || _x_6 = _x_10) && (((_x_4 || not _x_5) || not (_x_7 = -1 + _x_9)) || _x_6 = -1 + _x_10)
expansions [merge_sort, num_occurs, merge_sort, num_occurs, num_occurs]
rewrite_steps num_occurs_arith
forward_chaining Subproof
Subproof
subproof
not Is_a([], List.tl x) && not Is_a([], x) || num_occurs a x = num_occurs a (merge_sort x)start[1.531s, "2"] not Is_a([], List.tl x) && not Is_a([], x) || num_occurs a x = num_occurs a (merge_sort x)
simplify
into let (_x_0 : bool) = not Is_a([], List.tl x) in let (_x_1 : bool) = Is_a([], x) in let (_x_2 : bool) = _x_0 || _x_1 in let (_x_3 : bool) = List.hd x = a in let (_x_4 : int) = num_occurs a x in let (_x_5 : bool) = _x_4 = 0 in (((_x_2 || _x_3) || _x_5) && ((_x_2 || not _x_3) || _x_4 = 1)) && ((_x_0 || not _x_1) || _x_5)
expansions [num_occurs, merge_sort, num_occurs, num_occurs, num_occurs, num_occurs, merge_sort]
rewrite_steps forward_chaining Subproof
Subproof
Subproof
So now we're sure merge_sort
really is a proper sorting function.
Merge Sort is Correct!¶
To recap, we've proved:
theorem merge_sort_sorts x =
is_sorted (merge_sort x)
and
theorem merge_sort_elements a (x : int list) =
num_occurs a x = num_occurs a (merge_sort x)
Beautiful! Happy proving (and sorting)!