# Recursion, Induction and Rewriting¶

In this notebook, we're going to use Imandra to prove some interesting properties of functional programs. We'll learn a bit about induction, lemmas and rewrite rules along the way.

Let's start by defining our own append function on lists. It should take two lists as input, and compute the result of appending (concatenating) the elements of the first list with those of the second list.

Note: Imandra contains a List module with predefined functions like List.append (i.e., (@)), List.rev, etc. However, we define our own versions of these functions by hand in this notebook so that all definitions and proofs are illustrated from scratch.

In [1]:
let rec append x y =
match x with
| [] -> y
| x :: xs -> x :: append xs y

Out[1]:
val append : 'a list -> 'a list -> 'a list = <fun>

termination proof

### Termination proof

call append (List.tl x) y from append x y
original:append x y
sub:append (List.tl x) y
original ordinal:Ordinal.Int (_cnt x)
sub ordinal:Ordinal.Int (_cnt (List.tl x))
path:[x <> []]
proof:
detailed proof
ground_instances:3
definitions:0
inductions:0
search_time:
0.016s
details:
Expand
smt_stats:
 num checks: 8 arith assert lower: 5 arith tableau max rows: 4 arith tableau max columns: 11 arith pivots: 4 rlimit count: 1806 mk clause: 12 datatype occurs check: 12 mk bool var: 52 arith assert upper: 5 datatype splits: 1 decisions: 10 arith row summations: 7 propagations: 12 conflicts: 7 arith fixed eqs: 4 datatype accessor ax: 10 arith conflicts: 1 arith num rows: 4 datatype constructor ax: 8 num allocs: 5.3769e+06 final checks: 4 added eqs: 37 del clause: 4 arith eq adapter: 5 memory: 15.19 max memory: 15.19
Expand
• start[0.016s]
let (_x_0 : int) = count.list (const 0) … in
let (_x_1 : ty_0 list) = List.tl … in
let (_x_2 : int) = count.list (const 0) _x_1 in
x <> [] && ((_x_0 >= 0) && (_x_2 >= 0))
==> not (_x_1 <> [])
|| Ordinal.( << ) (Ordinal.Int _x_2) (Ordinal.Int _x_0)
• ###### simplify
 into: let (_x_0 : ty_0 list) = List.tl … in let (_x_1 : int) = count.list (const 0) _x_0 in let (_x_2 : int) = count.list (const 0) … in not (_x_0 <> []) || Ordinal.( << ) (Ordinal.Int _x_1) (Ordinal.Int _x_2) || not (… <> [] && (_x_2 >= 0) && (_x_1 >= 0)) expansions: [] rewrite_steps: forward_chaining:
• unroll
 expr: (|Ordinal.<<| (|Ordinal.Int_79/boot| (|count.list_395/server| (|get.::.1_376/server|… expansions:
• unroll
 expr: (|count.list_395/server| (|get.::.1_376/server| x_382/server)) expansions:
• unroll
 expr: (|count.list_395/server| x_382/server) expansions:
• Unsat

Let's compute a bit with this function to understand how it works.

In [2]:
append [1;2;3] [4;5;6]

Out[2]:
- : Z.t list = [1;2;3;4;5;6]

In [3]:
append [] [4;5;6]

Out[3]:
- : Z.t list = [4;5;6]

In [4]:
append [1;2;3] []

Out[4]:
- : Z.t list = [1;2;3]


We can ask Imandra some questions about append. For example, do there exist lists x and y such that append x y = [1;2;3;4;5;6;7;8]?

In [5]:
verify (fun x y -> append x y <> [1;2;3;4;5;6;7;8])

Out[5]:
- : Z.t list -> Z.t list -> bool = <fun>
module CX : sig val x : Z.t list val y : Z.t list end

Counterexample (after 1 steps, 0.010s):
let x : int list = []
let y : int list = [1; 2; 3; 4; 5; 6; 7; 8]

Refuted
proof attempt
ground_instances:1
definitions:0
inductions:0
search_time:
0.010s
details:
Expand
smt_stats:
 num checks: 3 rlimit count: 619 mk clause: 3 datatype occurs check: 28 mk bool var: 29 datatype splits: 1 decisions: 2 propagations: 2 conflicts: 1 datatype accessor ax: 10 datatype constructor ax: 2 num allocs: 9.77048e+06 final checks: 3 added eqs: 26 memory: 15.43 max memory: 15.43
Expand
• start[0.010s] not (append ( :var_0: ) ( :var_1: ) = [1; 2; 3; 4; 5; 6; 7; 8])
• unroll
 expr: (append_428/server x_1262/client y_1263/client) expansions:
• Sat (Some let x : int list = [] let y : int list = [(Z.of_nativeint (1n)); (Z.of_nativeint (2n)); (Z.of_nativeint (3n)); (Z.of_nativeint (4n)); (Z.of_nativeint (5n)); (Z.of_nativeint (6n)); (Z.of_nativeint (7n)); (Z.of_nativeint (8n))] )

Ah, of course! What if we make the problem a little harder -- perhaps we want x and y to be the same length:

In [6]:
verify (fun x y -> List.length x = List.length y ==> append x y <> [1;2;3;4;5;6;7;8])

Out[6]:
- : Z.t list -> Z.t list -> bool = <fun>
module CX : sig val x : Z.t list val y : Z.t list end

Counterexample (after 15 steps, 0.021s):
let x : int list = [1; 2; 3; 4]
let y : int list = [5; 6; 7; 8]

Refuted
proof attempt
ground_instances:15
definitions:0
inductions:0
search_time:
0.021s
details:
Expand
smt_stats:
 arith offset eqs: 31 num checks: 31 arith assert lower: 106 arith tableau max rows: 18 arith tableau max columns: 38 arith pivots: 107 rlimit count: 15579 mk clause: 267 datatype occurs check: 229 mk bool var: 999 arith assert upper: 106 datatype splits: 13 decisions: 280 arith row summations: 251 arith bound prop: 2 propagations: 783 interface eqs: 2 conflicts: 63 arith fixed eqs: 139 arith assume eqs: 2 datatype accessor ax: 102 minimized lits: 25 arith num rows: 18 arith assert diseq: 1 datatype constructor ax: 191 num allocs: 1.56517e+07 final checks: 28 added eqs: 2112 del clause: 162 arith eq adapter: 167 memory: 16.04 max memory: 16.04
Expand
• start[0.021s]
List.length ( :var_0: ) = List.length ( :var_1: )
==> not (append ( :var_0: ) ( :var_1: ) = [1; 2; 3; 4; 5; 6; 7; 8])
• #### simplify

 into: not (List.length ( :var_0: ) = List.length ( :var_1: )) || not (append ( :var_0: ) ( :var_1: ) = [1; 2; 3; 4; 5; 6; 7; 8]) expansions: [] rewrite_steps: forward_chaining:
• unroll
 expr: (append_445/server x_1265/client y_1266/client) expansions:
• unroll
 expr: (|List.length_441/server| y_1266/client) expansions:
• unroll
 expr: (|List.length_441/server| x_1265/client) expansions:
• unroll
 expr: (append_445/server (|get.::.1_440/server| x_1265/client) y_1266/client) expansions:
• unroll
 expr: (|List.length_441/server| (|get.::.1_440/server| y_1266/client)) expansions:
• unroll
 expr: (|List.length_441/server| (|get.::.1_440/server| x_1265/client)) expansions:
• unroll
 expr: (append_445/server (|get.::.1_440/server| (|get.::.1_440/server| x_1265/client)) y_1266/client) expansions:
• unroll
 expr: (|List.length_441/server| (|get.::.1_440/server| (|get.::.1_440/server| y_1266/client))) expansions:
• unroll
 expr: (|List.length_441/server| (|get.::.1_440/server| (|get.::.1_440/server| x_1265/client))) expansions:
• unroll
 expr: (append_445/server (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/server| x_1265… expansions:
• unroll
 expr: (|List.length_441/server| (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/server|… expansions:
• unroll
 expr: (|List.length_441/server| (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/server|… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_440/server| (|get.::.1_440/server| (|get.::.1_440/… expansions:
• Sat (Some let x : int list = [(Z.of_nativeint (1n)); (Z.of_nativeint (2n)); (Z.of_nativeint (3n)); (Z.of_nativeint (4n))] let y : int list = [(Z.of_nativeint (5n)); (Z.of_nativeint (6n)); (Z.of_nativeint (7n)); (Z.of_nativeint (8n))] )

Nice! Remember that counterexamples are always reflected into the runtime in a module called CX. So, we can compute with this counterexample and see that it indeed refutes our conjecture:

In [7]:
append CX.x CX.y

Out[7]:
- : Z.t list = [1;2;3;4;5;6;7;8]


Let's now investigate a more interesting conjecture: That append is associative.

That is,

(forall (x,y,z) : 'a list. append x (append y z) = append (append x y) z).

Is this property true? Let's ask Imandra.

In [8]:
verify (fun x y z -> append x (append y z) = append (append x y) z)

Out[8]:
- : 'a list -> 'a list -> 'a list -> bool = <fun>

Unknown (Verified up to bound 100)
Expand
 expanded: append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))))))))))) (append y z)append (List.tl (append x y)) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y)))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))) yappend (List.tl (List.tl (List.tl y))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y)))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y)))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y)))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))) yappend (List.tl (List.tl x)) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (append x y))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl y)))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))))) (append y z)append (List.tl (List.tl x)) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))) yappend (List.tl x) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))))))))) yappend (List.tl (List.tl (List.tl (append x y)))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y)))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl y))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))))))))))))))))))) zappend (List.tl x) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))) yappend y zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl x))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))))))))))) zappend (List.tl (List.tl (List.tl x))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))) zappend (List.tl (List.tl (List.tl (List.tl x)))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl x))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))))))) yappend x (append y z)append (List.tl (List.tl (List.tl x))) yappend (List.tl (List.tl (append x y))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))) (append y z)append (List.tl (List.tl (List.tl (List.tl x)))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y)))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))) yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))))) zappend x yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y)))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))) yappend (List.tl (List.tl y)) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))) yappend (append x y) zappend (List.tl y) z blocked: append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl y)))))))))))))))))))) zappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))))))))))) (append y z)append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))))))))))))))))))) y
proof attempt
 ground_instances: 100 definitions: 0 inductions: 0 search_time: 0.137s
Expand
• start[0.137s]
append ( :var_0: ) (append ( :var_1: ) ( :var_2: ))
= append (append ( :var_0: ) ( :var_1: )) ( :var_2: )
• unroll
 expr: (append_384/server (append_384/server x_377/server y_378/server) z_379/server) expansions:
• unroll
 expr: (append_384/server x_377/server y_378/server) expansions:
• unroll
 expr: (append_384/server x_377/server (append_384/server y_378/server z_379/server)) expansions:
• unroll
 expr: (append_384/server y_378/server z_379/server) expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| (append_384/server x_377/server y_378/server)) z_379/s… expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| x_377/server) y_378/server) expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| x_377/server) (append_384/server y_378/server z_379/se… expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| y_378/server) z_379/server) expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| (append_384/server x_377/serv… expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| x_377/server)) y_378/server) expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| x_377/server)) (append_384/serv… expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/server| x_377/… expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/server| x_377/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| y_378/server)) z_379/server) expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (append_384/server (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/server| y_378/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_376/server| (|get.::.1_376/server| (|get.::.1_376/… expansions:

Imandra tells us that there are no counterexamples to this conjecture up to our current recursion unrolling bound (100).

This gives us some confidence that this conjecture is true. Let's now ask Imandra to prove it for all possible cases by induction.

In [9]:
verify (fun x y z -> append x (append y z) = append (append x y) z) [@@induct]

Out[9]:
- : 'a list -> 'a list -> 'a list -> bool = <fun>
Goal:

append x (append y z) = append (append x y) z.

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
append x (append y z) = append (append x y) z

Must try induction.

The recursive terms in the conjecture suggest 3 inductions.
Subsumption and merging reduces this to 2.

Only 1 of those schemes are unflawed.
We shall induct according to a scheme derived from append.

Induction scheme:

(not (x <> []) ==> φ x y z) && (x <> [] && φ (List.tl x) y z ==> φ x y z).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. append x (append y z) = append (append x y) z

But simplification reduces this to true, using the definition of append.

Subgoal 1.1:

H0. x <> []
H1. append (List.tl x) (append y z) = append (append (List.tl x) y) z
|---------------------------------------------------------------------------
append x (append y z) = append (append x y) z

But simplification reduces this to true, using the definition of append.

ⓘ  Rules:
(:def append)
(:induct append)


Proved
proof
 ground_instances: 0 definitions: 5 inductions: 1 search_time: 0.072s
Expand
• start[0.072s, "Goal"]
append ( :var_0: ) (append ( :var_1: ) ( :var_2: ))
= append (append ( :var_0: ) ( :var_1: )) ( :var_2: )
• #### subproof

append x (append y z) = append (append x y) z
• start[0.072s, "1"] append x (append y z) = append (append x y) z
• induction on (functional ?)
:scheme (not (x <> []) ==> φ x y z)
&& (x <> [] && φ (List.tl x) y z ==> φ x y z)
• Split (let (_x_0 : bool) = x <> [] in
let (_x_1 : sko_ty_0 list) = append y z in
let (_x_2 : bool) = append x _x_1 = append (append x y) z in
let (_x_3 : sko_ty_0 list) = List.tl x in
(_x_0 || _x_2)
&& (not (_x_0 && (append _x_3 _x_1 = append (append _x_3 y) z))
|| _x_2)
:cases [x <> [] || (append x (append y z) = append (append x y) z);
let (_x_0 : sko_ty_0 list) = List.tl x in
let (_x_1 : sko_ty_0 list) = append y z in
not (x <> [])
|| not (append _x_0 _x_1 = append (append _x_0 y) z)
|| (append x _x_1 = append (append x y) z)])
• ##### subproof
let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = append y z in not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z) || (append x _x_1 = append (append x y) z)
• start[0.039s, "1.1"]
let (_x_0 : sko_ty_0 list) = List.tl x in
let (_x_1 : sko_ty_0 list) = append y z in
not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z)
|| (append x _x_1 = append (append x y) z)
• ###### simplify
 into: true expansions: [append, append, append] rewrite_steps: forward_chaining:
• ##### subproof
x <> [] || (append x (append y z) = append (append x y) z)
• start[0.039s, "1.2"]
x <> [] || (append x (append y z) = append (append x y) z)
• ###### simplify
 into: true expansions: [append, append] rewrite_steps: forward_chaining:

Beautiful! So, we see Imandra proved our goal by induction on x, using a structural induction principle derived from the 'a list datatype. We now know the property holds for all possible inputs (of which there are infinitely many!).

By the way, we can always view Imandra's current session configuration with the #config directive:

In [10]:
#config

Out[10]:
----------------------------------------------------------------------------
Imandra session configuration
----------------------------------------------------------------------------
0. Mode: logic (see: program,logic)
1. Load path: [] (see: dir)
2. Undo (implicit push): false (see: undo)
3. Console tags: waterfall, suggestions (see: console_tags)
4. Console color: true (see: console_color)
5. Interactive hints: true (see: interactive_hints)
6. Timeout: 60000 (see: timeout)
7. Allow redefinition: true (see: redef)
8. Recursion unroll depth: 100 (see: unroll)
9. SAT seed: 0 (see: sat_seed)
10. SMT seed: 0 (see: smt_seed)
11. Solver seed: 0 (see: solver_seed)
12. Top results: true (see: top_results)
13. Validate definitions: true (see: validate)
14. Skip proofs: false (see: skip_proofs)
15. Max induction depth: 3 (see: max_induct)
16. Induction unroll depth: 8 (see: induct_unroll)
17. Backchain limit: 100 (see: backchain_limit)
18. Enable all definitions when unrolling: true (see: unroll_enable_all)
19. Algebraic number approx. precision: 10 (see: pp_approx)
20. Reflect approximate models: false (see: reflect_approx_models)
21. Max size for callgraphs: 80 (see: callgraph_size)
22. Include expansion steps in callgraphs: false (see: callgraph_include_expansions)
23. Store proofs: false (see: store_proofs)
----------------------------------------------------------------------------


# Lemmas and Rules¶

When we use the verify command, we give it a closed formula (e.g., a lambda term such as (fun x y z -> foo x y z) and ask Imandra to prove that the goal always evaluates to true. Notice that the verify command does not give the verification goal a name.

Once we've proved a goal, we often want to record it as a theorem. This is both to document our verification progress and to make it possible for our proved goal to be used as a lemma in subsequent verification efforts.

This can be done by the theorem command.

For example, we could name our result assoc_append and install it as a theorem as follows:

theorem assoc_append x y z = append x (append y z) = append (append x y) z) [@@induct]


By default, a theorem is not installed as a rule that will be used automatically in subsequent verification efforts. To install the theorem as a rule, we can use the [@@rewrite] or [@@forward_chaining] attributes.

Let's focus in this notebook on the use of theorems as rewrite rules.

## Rewrite Rules¶

A rewrite rule is a theorem of the form (H1 && ... && Hk ==> LHS = RHS). It instructs Imandra's simplifier to replace terms it matches with the LHS with the suitably instantiated RHS, provided that the corresponding instantiations of the hypotheses H1, ..., Hk can be proved.

It is also allowed for the conclusion of the rule to be a Boolean term, e.g., (H1 && ... && Hk ==> foo) is interpreted with an RHS of true, i.e., (H1 && ... && Hk ==> foo = true).

Good rewrite rules can have a powerful normalising effect on formulas. We usually want to orient them so that the RHS is a better (i.e., simpler or more canonical) term than the LHS.

Let us see the use of rewrite rules through a prove about the function reverse.

## Reverse¶

Let's define a function to reverse a list. Note how it uses our append function we defined above:

In [11]:
let rec reverse x =
match x with
| [] -> []
| x :: xs -> append (reverse xs) [x]

Out[11]:
val reverse : 'a list -> 'a list = <fun>

termination proof

### Termination proof

call reverse (List.tl x) from reverse x
original:reverse x
sub:reverse (List.tl x)
original ordinal:Ordinal.Int (_cnt x)
sub ordinal:Ordinal.Int (_cnt (List.tl x))
path:[x <> []]
proof:
detailed proof
ground_instances:3
definitions:0
inductions:0
search_time:
0.010s
details:
Expand
smt_stats:
 num checks: 8 arith assert lower: 5 arith tableau max rows: 4 arith tableau max columns: 11 arith pivots: 4 rlimit count: 1806 mk clause: 12 datatype occurs check: 12 mk bool var: 52 arith assert upper: 5 datatype splits: 1 decisions: 10 arith row summations: 7 propagations: 12 conflicts: 7 arith fixed eqs: 4 datatype accessor ax: 10 arith conflicts: 1 arith num rows: 4 datatype constructor ax: 8 num allocs: 1.95992e+08 final checks: 4 added eqs: 37 del clause: 4 arith eq adapter: 5 memory: 17.76 max memory: 18.22
Expand
• start[0.010s]
let (_x_0 : int) = count.list (const 0) x in
let (_x_1 : ty_0 list) = List.tl x in
let (_x_2 : int) = count.list (const 0) _x_1 in
x <> [] && ((_x_0 >= 0) && (_x_2 >= 0))
==> not (_x_1 <> [])
|| Ordinal.( << ) (Ordinal.Int _x_2) (Ordinal.Int _x_0)
• ###### simplify
 into: let (_x_0 : ty_0 list) = List.tl x in let (_x_1 : int) = count.list (const 0) _x_0 in let (_x_2 : int) = count.list (const 0) x in not (_x_0 <> []) || Ordinal.( << ) (Ordinal.Int _x_1) (Ordinal.Int _x_2) || not (x <> [] && (_x_2 >= 0) && (_x_1 >= 0)) expansions: [] rewrite_steps: forward_chaining:
• unroll
 expr: (|Ordinal.<<| (|Ordinal.Int_79/boot| (|count.list_742/server| (|get.::.1_721/server|… expansions:
• unroll
 expr: (|count.list_742/server| (|get.::.1_721/server| x_731/server)) expansions:
• unroll
 expr: (|count.list_742/server| x_731/server) expansions:
• Unsat

Let's compute with reverse to help gain confidence we defined it correctly.

In [12]:
reverse [1;2;3;4;5]

Out[12]:
- : Z.t list = [5;4;3;2;1]


Let's ask Imandra, are there any lists that are their own reverse?

In [13]:
instance (fun x -> reverse x = x)

Out[13]:
- : 'a list -> bool = <fun>
module CX : sig type t = C_t_0 val x : t list end

Instance (after 1 steps, 0.009s):
type t = C_t_0
let x : t list = []

Instance
proof attempt
ground_instances:1
definitions:0
inductions:0
search_time:
0.009s
details:
Expand
smt_stats:
 num checks: 3 rlimit count: 423 mk clause: 4 datatype occurs check: 2 mk bool var: 12 decisions: 1 propagations: 2 conflicts: 1 datatype accessor ax: 2 datatype constructor ax: 1 num allocs: 2.23472e+08 final checks: 2 added eqs: 9 memory: 17.98 max memory: 18.22
Expand
• start[0.009s] reverse ( :var_0: ) = ( :var_0: )
• unroll
 expr: (reverse_795/server x_792/server) expansions:
• Sat (Some type t = C_t_0 let x : t list = [] )

Ah! Of course, the empty list is its own reverse. What about longer lists, such as those of length 5?

Note that here we use the keyword instance, which tries to find values that satisfy the given property, instead of trying to prove the property.

In [14]:
instance (fun x -> List.length x >= 5 && reverse x = x)

Out[14]:
- : 'a list -> bool = <fun>
module CX : sig type t = C_t_0 | C_t_1 | C_t_2 | C_t_3 val x : t list end

Instance (after 33 steps, 0.036s):
type t = C_t_0 | C_t_1 | C_t_2 | C_t_3
let x : t list = [C_t_1; C_t_2; C_t_3; C_t_2; C_t_1]

Instance
proof attempt
ground_instances:33
definitions:0
inductions:0
search_time:
0.036s
details:
Expand
smt_stats:
 num checks: 67 arith assert lower: 55 arith tableau max rows: 14 arith tableau max columns: 25 arith pivots: 39 rlimit count: 34463 mk clause: 193 datatype occurs check: 811 mk bool var: 1515 arith assert upper: 44 datatype splits: 156 decisions: 1023 arith row summations: 242 propagations: 1161 conflicts: 89 arith fixed eqs: 45 datatype accessor ax: 218 minimized lits: 43 arith conflicts: 9 arith num rows: 14 arith assert diseq: 10 datatype constructor ax: 466 num allocs: 2.56462e+08 final checks: 85 added eqs: 3642 del clause: 126 arith eq adapter: 60 memory: 19.02 max memory: 19.02
Expand
• start[0.036s]
(List.length ( :var_0: ) >= 5) && (reverse ( :var_0: ) = ( :var_0: ))
• unroll
 expr: (reverse_841/server x_834/server) expansions:
• unroll
 expr: (|List.length_837/server| x_834/server) expansions:
• unroll
 expr: (append_845/server (reverse_841/server (|get.::.1_833/server| x_834/server)) (|::| (|get.::.0_83… expansions:
• unroll
 expr: (reverse_841/server (|get.::.1_833/server| x_834/server)) expansions:
• unroll
 expr: (|List.length_837/server| (|get.::.1_833/server| x_834/server)) expansions:
• unroll
 expr: (append_845/server (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/server| x_834/ser… expansions:
• unroll
 expr: (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/server| x_834/server))) expansions:
• unroll
 expr: (|List.length_837/server| (|get.::.1_833/server| (|get.::.1_833/server| x_834/server))) expansions:
• unroll
 expr: (append_845/server (|get.::.1_833/server| (reverse_841/server (|get.::.1_833/server| x_834/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (reverse_841/server (|get.::.1_833/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/ser… expansions:
• unroll
 expr: (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/server| x_834… expansions:
• unroll
 expr: (|List.length_837/server| (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/server|… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (reverse_841/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (reverse_841/server (|get.::.1_833/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (reverse_841/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (reverse_841/server (|get.::.1_833/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (|get.::.1_833/… expansions:
• unroll
 expr: (let ((a!1 (reverse_841/server (|get.::.1_833/server| (|get.::.1_833/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_833/server| (|get.::.1_833/server| (reverse_841/se… expansions:
• Sat (Some type t = C_t_0 | C_t_1 | C_t_2 | C_t_3 let x : t list = [C_t_1; C_t_2; C_t_3; C_t_2; C_t_1] )

Ah! Of course, palindromes! This counterexample is also interesting because, in addition to giving us a concrete value CX.x, this counterexample also involves the synthesis of an algebraic datatype. This is because our goal was polymorphic. If we want, for example, an int list counterexample, we can simply annotate our goal with types:

In [15]:
instance (fun (x : int list) -> List.length x >= 5 && reverse x = x)

Out[15]:
- : Z.t list -> bool = <fun>
module CX : sig val x : Z.t list end

Instance (after 32 steps, 0.035s):
let x : int list = [6; 10; 8; 10; 6]

Instance
proof attempt
ground_instances:32
definitions:0
inductions:0
search_time:
0.035s
details:
Expand
smt_stats:
 num checks: 65 arith assert lower: 52 arith tableau max rows: 14 arith tableau max columns: 25 arith pivots: 38 rlimit count: 32923 mk clause: 185 datatype occurs check: 783 mk bool var: 1459 arith assert upper: 41 datatype splits: 148 decisions: 1023 arith row summations: 212 propagations: 1029 conflicts: 85 arith fixed eqs: 40 datatype accessor ax: 204 minimized lits: 35 arith conflicts: 8 arith num rows: 14 arith assert diseq: 10 datatype constructor ax: 462 num allocs: 2.95251e+08 final checks: 76 added eqs: 3274 del clause: 111 arith eq adapter: 56 memory: 19.7 max memory: 19.7
Expand
• start[0.035s]
(List.length ( :var_0: ) >= 5) && (reverse ( :var_0: ) = ( :var_0: ))
• unroll
 expr: (reverse_955/server x_1289/client) expansions:
• unroll
 expr: (|List.length_951/server| x_1289/client) expansions:
• unroll
 expr: (append_959/server (reverse_955/server (|get.::.1_950/server| x_1289/client)) (|::| (|get.::.0_9… expansions:
• unroll
 expr: (reverse_955/server (|get.::.1_950/server| x_1289/client)) expansions:
• unroll
 expr: (|List.length_951/server| (|get.::.1_950/server| x_1289/client)) expansions:
• unroll
 expr: (append_959/server (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/server| x_1289/cl… expansions:
• unroll
 expr: (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/server| x_1289/client))) expansions:
• unroll
 expr: (|List.length_951/server| (|get.::.1_950/server| (|get.::.1_950/server| x_1289/client))) expansions:
• unroll
 expr: (append_959/server (|get.::.1_950/server| (reverse_955/server (|get.::.1_950/server| x_1289/cl… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (reverse_955/server (|get.::.1_950/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/ser… expansions:
• unroll
 expr: (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/server| x_128… expansions:
• unroll
 expr: (|List.length_951/server| (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/server|… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (reverse_955/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (reverse_955/server (|get.::.1_950/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (reverse_955/se… expansions:
• unroll
 expr: (let ((a!1 (reverse_955/server (|get.::.1_950/server| (|get.::.1_950/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (reverse_955/server (|get.::.1_950/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (|get.::.1_950/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_950/server| (|get.::.1_950/server| (reverse_955/se… expansions:
• Sat (Some let x : int list = [(Z.of_nativeint (6n)); (Z.of_nativeint (10n)); (Z.of_nativeint (8n)); (Z.of_nativeint (10n)); (Z.of_nativeint (6n))] )

Excellent! And we can of course compute with our counterexample:

In [16]:
reverse CX.x

Out[16]:
- : Z.t list = [6;10;8;10;6]

In [17]:
reverse CX.x = CX.x

Out[17]:
- : bool = true


# Reverse of Reverse is the Identity¶

Now that we've defined our reverse function and experimented a bit with it, let's try to prove an interesting theorem. Let's prove that

(forall x, (reverse (reverse x)) = x).

That is, if we take an arbitrary list x and we reverse it twice, we always get our original x back unscathed.

As usual, let's start by asking Imandra to verify this via bounded checking (a.k.a. recursive unrolling).

In [18]:
verify (fun x -> reverse (reverse x) = x)

Out[18]:
- : 'a list -> bool = <fun>

Unknown (Verified up to bound 100)
Expand
 expanded: let (_x_0 : sko_ty_0 list) = List.tl (List.tl x) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl x in append (reverse (List.tl _x_0)) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))let (_x_0 : sko_ty_0 list) = List.tl (reverse x) in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (reverse x))) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (reverse x))))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x))))))let (_x_0 : sko_ty_0 list) = List.tl (List.tl x) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x)))))))let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl x)) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl x) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]reverse (List.tl x)reverse (List.tl (List.tl (List.tl (reverse x))))let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl x)) in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl x))) in append (reverse (List.tl _x_0)) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl x)))))) [List.hd x]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl x)) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (reverse x)) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = reverse x in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]reverse (List.tl (reverse x))let (_x_0 : sko_ty_0 list) = List.tl x in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl x)))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl x) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (reverse x) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = reverse x in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]reverse (List.tl (List.tl (reverse x)))let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl x))) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl x))))))))) [List.hd x]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl x)) in append (reverse (List.tl _x_0)) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl x)))))let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (reverse x))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (reverse x) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl x) in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl x)) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]append (reverse (List.tl x)) [List.hd x]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (reverse x))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (reverse x)) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (reverse x) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = reverse x in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x)))))))))let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl x))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl x))))))) [List.hd x]let (_x_0 : sko_ty_0 list) = reverse x in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (reverse x)) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (reverse x)))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x)))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl x))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl x)))))))) [List.hd x]let (_x_0 : sko_ty_0 list) = List.tl x in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (reverse x) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (reverse x)) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = reverse x in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (reverse x)))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (reverse x))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (reverse x))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl x in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl x)))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]append (List.tl (List.tl (List.tl (reverse (List.tl x))))) [List.hd x]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))))let (_x_0 : sko_ty_0 list) = reverse x in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl x in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl x)))) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl x) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]reverse xlet (_x_0 : sko_ty_0 list) = reverse x in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (reverse x) in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (reverse x)) in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (reverse x)))) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (reverse x))) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x))))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x)))))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (reverse x))))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (reverse x)))))let (_x_0 : sko_ty_0 list) = List.tl x in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl x)))) in append (reverse (List.tl _x_0)) [List.hd _x_0]append (List.tl (reverse (List.tl x))) [List.hd x]reverse (List.tl (List.tl (List.tl x)))let (_x_0 : sko_ty_0 list) = List.tl (reverse x) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl x)) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]append (List.tl (List.tl (reverse (List.tl x)))) [List.hd x]reverse (List.tl (List.tl x))let (_x_0 : sko_ty_0 list) = List.tl (List.tl x) in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl x in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (reverse x)))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (reverse x)) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = reverse x in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x))))))))let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl x))) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl x)))))))))) [List.hd x]let (_x_0 : sko_ty_0 list) = List.tl x in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl x))))reverse (reverse x) blocked: let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl x)) in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = reverse x in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (reverse x)) in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (reverse x)))) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x)))))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x)))))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl x))) in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl x))))))))))) [List.hd x]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))))let (_x_0 : sko_ty_0 list) = List.tl x in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl x)))) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (reverse x) in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (reverse x))) in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x))))))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (reverse x))))) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl x) in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))))) [List.hd _x_0]let (_x_0 : sko_ty_0 list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse x))))))))))
proof attempt
 ground_instances: 100 definitions: 0 inductions: 0 search_time: 0.197s
Expand
• start[0.197s] reverse (reverse ( :var_0: )) = ( :var_0: )
• unroll
 expr: (reverse_530/server (reverse_530/server x_527/server)) expansions:
• unroll
 expr: (reverse_530/server x_527/server) expansions:
• unroll
 expr: (append_534/server (reverse_530/server (|get.::.1_526/server| (reverse_530/server x_527/server))) … expansions:
• unroll
 expr: (reverse_530/server (|get.::.1_526/server| (reverse_530/server x_527/server))) expansions:
• unroll
 expr: (append_534/server (reverse_530/server (|get.::.1_526/server| x_527/server)) (|::| (|get.::.0_52… expansions:
• unroll
 expr: (reverse_530/server (|get.::.1_526/server| x_527/server)) expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (append_534/server (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/server| x_527/ser… expansions:
• unroll
 expr: (append_534/server (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/server| x_527/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/server| x_527/server))) expansions:
• unroll
 expr: (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/server x_527/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/server| x_527… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (reverse_530/se… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (|get.::.1_526/server| (|get.::.1_526/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_526/server| (reverse_530/server (|get.::.1_526/ser… expansions:
• unroll
 expr: (let ((a!1 (reverse_530/server (|get.::.1_526/server| (|get.::.1_526/ser… expansions:

Imandra tells us that there are no counterexamples up to our current unrolling bound (100).

So, our conjecture seems like a good candidate for proof by induction.

In [19]:
verify (fun x -> reverse (reverse x) = x) [@@induct]

Out[19]:
- : 'a list -> bool = <fun>
Goal:

reverse (reverse x) = x.

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
reverse (reverse x) = x

Must try induction.

We shall induct according to a scheme derived from reverse.

Induction scheme:

(not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse (reverse x) = x

But simplification reduces this to true, using the definition of reverse.

Subgoal 1.1:

H0. x <> []
H1. reverse (reverse (List.tl x)) = List.tl x
|---------------------------------------------------------------------------
reverse (reverse x) = x

This simplifies, using the definition of reverse to:

Subgoal 1.1':

H0. x <> []
H1. reverse (reverse (List.tl x)) = List.tl x
|---------------------------------------------------------------------------
reverse (append (reverse (List.tl x)) [List.hd x]) = x

We can eliminate destructors by the following substitution:
x -> x1 :: x2

This produces the modified subgoal:

Subgoal 1.1'':

H0. reverse (reverse x2) = x2
|---------------------------------------------------------------------------
reverse (append (reverse x2) [x1]) = x1 :: x2

Cross-fertilizing with:

reverse (reverse x2) = x2

This produces the modified subgoal:

Subgoal 1.1''':

|---------------------------------------------------------------------------
reverse (append (reverse x2) [x1]) = x1 :: (reverse (reverse x2))

Candidates for generalization:

reverse x2

This produces the modified subgoal:

Subgoal 1.1'''':

|---------------------------------------------------------------------------
reverse (append gen_1 [x1]) = x1 :: (reverse gen_1)

Must try induction.

The recursive terms in the conjecture suggest 2 inductions.
Subsumption and merging reduces this to 1.

We shall induct according to a scheme derived from append.

Induction scheme:

(not (gen_1 <> []) ==> φ gen_1 x1)
&& (gen_1 <> [] && φ (List.tl gen_1) x1 ==> φ gen_1 x1).

2 nontautological subgoals.

Subgoal 1.1''''.2:

|---------------------------------------------------------------------------
C0. gen_1 <> []
C1. reverse (append gen_1 [x1]) = x1 :: (reverse gen_1)

But simplification reduces this to true, using the definitions of append and
reverse.

Subgoal 1.1''''.1:

H0. gen_1 <> []
H1. reverse (append (List.tl gen_1) [x1]) = x1 :: (reverse (List.tl gen_1))
|---------------------------------------------------------------------------
reverse (append gen_1 [x1]) = x1 :: (reverse gen_1)

This simplifies, using the definitions of append and reverse to:

Subgoal 1.1''''.1':

H0. gen_1 <> []
H1. reverse (append (List.tl gen_1) [x1]) = x1 :: (reverse (List.tl gen_1))
|---------------------------------------------------------------------------
append (reverse (append (List.tl gen_1) [x1])) [List.hd gen_1]
= x1 :: (append (reverse (List.tl gen_1)) [List.hd gen_1])

But we verify Subgoal 1.1''''.1' by recursive unrolling.

ⓘ  Rules:
(:def append)
(:def reverse)
(:induct append)
(:induct reverse)


Proved
proof
ground_instances:2
definitions:11
inductions:2
search_time:
0.445s
details:
Expand
smt_stats:
 num checks: 5 arith-make-feasible: 3 arith-max-columns: 4 rlimit count: 90289 mk clause: 28 datatype occurs check: 19 mk bool var: 60 datatype splits: 4 decisions: 13 propagations: 15 conflicts: 3 datatype accessor ax: 7 datatype constructor ax: 6 num allocs: 2.37357e+09 final checks: 4 added eqs: 61 del clause: 20 memory: 22.41 max memory: 22.83
Expand
• start[0.445s, "Goal"] reverse (reverse ( :var_0: )) = ( :var_0: )
• #### subproof

reverse (reverse x) = x
• start[0.444s, "1"] reverse (reverse x) = x
• induction on (functional ?)
:scheme (not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x)
• Split (let (_x_0 : bool) = x <> [] in
let (_x_1 : bool) = reverse (reverse x) = x in
let (_x_2 : sko_ty_0 list) = List.tl x in
(_x_0 || _x_1)
&& (not (_x_0 && (reverse (reverse _x_2) = _x_2)) || _x_1)
:cases [x <> [] || (reverse (reverse x) = x);
let (_x_0 : sko_ty_0 list) = List.tl x in
not (x <> []) || not (reverse (reverse _x_0) = _x_0)
|| (reverse (reverse x) = x)])
• ##### subproof
let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (reverse (reverse _x_0) = _x_0) || (reverse (reverse x) = x)
• start[0.402s, "1.1"]
let (_x_0 : sko_ty_0 list) = List.tl x in
not (x <> []) || not (reverse (reverse _x_0) = _x_0)
|| (reverse (reverse x) = x)
• ###### simplify
 into: let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse _x_0 in not (x <> []) || not (reverse _x_1 = _x_0) || (reverse (append _x_1 [List.hd x]) = x) expansions: reverse rewrite_steps: forward_chaining:
• Elim_destructor (:cstor ( :: ) :replace x1 :: x2 :context [])
• Generalize (let (_x_0 : sko_ty_0 list) = reverse x2 in
reverse (append _x_0 [x1]) = x1 :: (reverse _x_0)
:as (gen_1 : sko_ty_0 list))
• induction on (functional ?)
:scheme (not (gen_1 <> []) ==> φ gen_1 x1)
&& (gen_1 <> [] && φ (List.tl gen_1) x1 ==> φ gen_1 x1)
• Split (let (_x_0 : bool) = gen_1 <> [] in
let (_x_1 : sko_ty_0 list) = [x1] in
let (_x_2 : bool)
= reverse (append gen_1 _x_1) = x1 :: (reverse gen_1)
in
let (_x_3 : sko_ty_0 list) = List.tl gen_1 in
(_x_0 || _x_2)
&& (not (_x_0 && (reverse (append _x_3 _x_1) = x1 :: (reverse _x_3)))
|| _x_2)
:cases [gen_1 <> []
|| (reverse (append gen_1 [x1]) = x1 :: (reverse gen_1));
let (_x_0 : sko_ty_0 list) = List.tl gen_1 in
let (_x_1 : sko_ty_0 list) = [x1] in
not (gen_1 <> [])
|| not (reverse (append _x_0 _x_1) = x1 :: (reverse _x_0))
|| (reverse (append gen_1 _x_1) = x1 :: (reverse gen_1))])
• Subproof
• Subproof
• ##### subproof
x <> [] || (reverse (reverse x) = x)
• start[0.402s, "1.2"] x <> [] || (reverse (reverse x) = x)
• ###### simplify
 into: true expansions: [reverse, reverse] rewrite_steps: forward_chaining:

Success!

Imandra proves this fact automatically, and its inductive proof actually involves a nested subinduction.

Imandra has powerful techniques for automating complex inductions, involving simplification, destructor elimination, generalization and more. But it's often the case that nested inductions actually suggest lemmas that could be of general use.

To illustrate this, let's take a look at one of the interesting subgoals in our proof:

reverse (append gen_1 (x1 :: [])) = x1 :: (reverse gen_1)


This looks like a very nice fact. Instead of Imandra having to derive this fact as a subinduction, let's prove it as a theorem itself and install it as a rewrite rule. Then we will be able to apply it later and shorten our subsequent proofs.

In [20]:
theorem rev_app_single x y = reverse (append x [y]) = y :: (reverse x) [@@induct] [@@rewrite]

Out[20]:
val rev_app_single : 'a list -> 'a -> bool = <fun>
Goal:

reverse (append x [y]) = y :: (reverse x).

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
reverse (append x [y]) = y :: (reverse x)

Must try induction.

The recursive terms in the conjecture suggest 2 inductions.
Subsumption and merging reduces this to 1.

We shall induct according to a scheme derived from append.

Induction scheme:

(not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse (append x [y]) = y :: (reverse x)

But simplification reduces this to true, using the definitions of append and
reverse.

Subgoal 1.1:

H0. x <> []
H1. reverse (append (List.tl x) [y]) = y :: (reverse (List.tl x))
|---------------------------------------------------------------------------
reverse (append x [y]) = y :: (reverse x)

This simplifies, using the definitions of append and reverse to:

Subgoal 1.1':

H0. x <> []
H1. reverse (append (List.tl x) [y]) = y :: (reverse (List.tl x))
|---------------------------------------------------------------------------
append (reverse (append (List.tl x) [y])) [List.hd x]
= y :: (append (reverse (List.tl x)) [List.hd x])

But we verify Subgoal 1.1' by recursive unrolling.

ⓘ  Rules:
(:def append)
(:def reverse)
(:induct append)


Proved
proof
ground_instances:2
definitions:8
inductions:1
search_time:
0.162s
details:
Expand
smt_stats:
 num checks: 5 arith-make-feasible: 3 arith-max-columns: 4 rlimit count: 29960 mk clause: 28 datatype occurs check: 21 mk bool var: 60 datatype splits: 4 decisions: 13 propagations: 15 conflicts: 3 datatype accessor ax: 7 datatype constructor ax: 6 num allocs: 3.35742e+09 final checks: 4 added eqs: 61 del clause: 20 memory: 22.76 max memory: 22.86
Expand
• start[0.162s, "Goal"]
reverse (append ( :var_0: ) [( :var_1: )])
= ( :var_1: ) :: (reverse ( :var_0: ))
• #### subproof

reverse (append x [y]) = y :: (reverse x)
• start[0.161s, "1"] reverse (append x [y]) = y :: (reverse x)
• induction on (functional ?)
:scheme (not (x <> []) ==> φ x y)
&& (x <> [] && φ (List.tl x) y ==> φ x y)
• Split (let (_x_0 : bool) = x <> [] in
let (_x_1 : sko_ty_0 list) = [y] in
let (_x_2 : bool) = reverse (append x _x_1) = y :: (reverse x) in
let (_x_3 : sko_ty_0 list) = List.tl x in
(_x_0 || _x_2)
&& (not (_x_0 && (reverse (append _x_3 _x_1) = y :: (reverse _x_3)))
|| _x_2)
:cases [x <> [] || (reverse (append x [y]) = y :: (reverse x));
let (_x_0 : sko_ty_0 list) = List.tl x in
let (_x_1 : sko_ty_0 list) = [y] in
not (x <> [])
|| not (reverse (append _x_0 _x_1) = y :: (reverse _x_0))
|| (reverse (append x _x_1) = y :: (reverse x))])
• ##### subproof
let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = [y] in not (x <> []) || not (reverse (append _x_0 _x_1) = y :: (reverse _x_0)) || (reverse (append x _x_1) = y :: (reverse x))
• start[0.123s, "1.1"]
let (_x_0 : sko_ty_0 list) = List.tl x in
let (_x_1 : sko_ty_0 list) = [y] in
not (x <> []) || not (reverse (append _x_0 _x_1) = y :: (reverse _x_0))
|| (reverse (append x _x_1) = y :: (reverse x))
• ###### simplify
 into: let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse (append _x_0 [y]) in let (_x_2 : sko_ty_0 list) = reverse _x_0 in let (_x_3 : sko_ty_0 list) = [List.hd x] in not (x <> []) || not (_x_1 = y :: _x_2) || (append _x_1 _x_3 = y :: (append _x_2 _x_3)) expansions: [reverse, reverse, append] rewrite_steps: forward_chaining:
• ###### subproof
let (_x_0 : sko_ty_0 list) = List.tl ( :var_0: ) in let (_x_1 : sko_ty_0 list) = reverse (append _x_0 [( :var_1: )]) in let (_x_2 : sko_ty_0 list) = reverse _x_0 in let (_x_3 : sko_ty_0 list) = [List.hd ( :var_0: )] in not (( :var_0: ) <> []) || not (_x_1 = ( :var_1: ) :: _x_2) || (append _x_1 _x_3 = ( :var_1: ) :: (append _x_2 _x_3))
Start (let (_x_0 : sko_ty_0 list) = List.tl ( :var_0: ) in
let (_x_1 : sko_ty_0 list) = reverse (append _x_0 [( :var_1: )]) in
let (_x_2 : sko_ty_0 list) = reverse _x_0 in
let (_x_3 : sko_ty_0 list) = [List.hd ( :var_0: )] in
not (( :var_0: ) <> []) || not (_x_1 = ( :var_1: ) :: _x_2)
|| (append _x_1 _x_3 = ( :var_1: ) :: (append _x_2 _x_3)) :time 0.010s)
• ##### subproof
x <> [] || (reverse (append x [y]) = y :: (reverse x))
• start[0.123s, "1.2"] x <> [] || (reverse (append x [y]) = y :: (reverse x))
• ###### simplify
 into: true expansions: [reverse, reverse, append, reverse, append] rewrite_steps: forward_chaining:

Now, if we were to try to prove our original reverse (reverse x) goal again, we'd get a shorter proof that makes use of our new lemma:

In [21]:
verify (fun x -> reverse (reverse x) = x) [@@induct]

Out[21]:
- : 'a list -> bool = <fun>
Goal:

reverse (reverse x) = x.

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
reverse (reverse x) = x

Must try induction.

We shall induct according to a scheme derived from reverse.

Induction scheme:

(not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse (reverse x) = x

But simplification reduces this to true, using the definition of reverse.

Subgoal 1.1:

H0. x <> []
H1. reverse (reverse (List.tl x)) = List.tl x
|---------------------------------------------------------------------------
reverse (reverse x) = x

This simplifies, using the definition of reverse to:

Subgoal 1.1':

H0. x <> []
H1. reverse (reverse (List.tl x)) = List.tl x
|---------------------------------------------------------------------------
reverse (append (reverse (List.tl x)) [List.hd x]) = x

But simplification reduces this to true, using the rewrite rule
rev_app_single.

ⓘ  Rules:
(:def reverse)
(:rw rev_app_single)
(:induct reverse)


Proved
proof
 ground_instances: 0 definitions: 3 inductions: 1 search_time: 0.124s
Expand
• start[0.124s, "Goal"] reverse (reverse ( :var_0: )) = ( :var_0: )
• #### subproof

reverse (reverse x) = x
• start[0.124s, "1"] reverse (reverse x) = x
• induction on (functional ?)
:scheme (not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x)
• Split (let (_x_0 : bool) = x <> [] in
let (_x_1 : bool) = reverse (reverse x) = x in
let (_x_2 : sko_ty_0 list) = List.tl x in
(_x_0 || _x_1)
&& (not (_x_0 && (reverse (reverse _x_2) = _x_2)) || _x_1)
:cases [x <> [] || (reverse (reverse x) = x);
let (_x_0 : sko_ty_0 list) = List.tl x in
not (x <> []) || not (reverse (reverse _x_0) = _x_0)
|| (reverse (reverse x) = x)])
• ##### subproof
let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (reverse (reverse _x_0) = _x_0) || (reverse (reverse x) = x)
• start[0.089s, "1.1"]
let (_x_0 : sko_ty_0 list) = List.tl x in
not (x <> []) || not (reverse (reverse _x_0) = _x_0)
|| (reverse (reverse x) = x)
• ###### simplify
 into: let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse _x_0 in not (x <> []) || not (reverse _x_1 = _x_0) || (reverse (append _x_1 [List.hd x]) = x) expansions: reverse rewrite_steps: forward_chaining:
• ###### simplify
 into: true expansions: [] rewrite_steps: rev_app_single forward_chaining:
• ##### subproof
x <> [] || (reverse (reverse x) = x)
• start[0.089s, "1.2"] x <> [] || (reverse (reverse x) = x)
• ###### simplify
 into: true expansions: [reverse, reverse] rewrite_steps: forward_chaining:

Excellent!

## Reverse Append¶

Now, let's try to prove another interesting conjecture about our functions append and reverse:

reverse (append x y) = append (reverse y) (reverse x)

Actually, first let's pretend we made a mistake in formulating this conjecture, and accidentally swapped the x and y on the RHS (Right Hand Side) of the equality. Let's constrain the types of lists involved to make the counterexample especially easy to read:

In [22]:
verify (fun (x : int list) y -> reverse (append x y) = append (reverse x) (reverse y))

Out[22]:
- : Z.t list -> Z.t list -> bool = <fun>
module CX : sig val x : Z.t list val y : Z.t list end

Counterexample (after 21 steps, 0.026s):
let x : int list = [0]
let y : int list = [2]

Refuted
proof attempt
ground_instances:21
definitions:0
inductions:0
search_time:
0.026s
details:
Expand
smt_stats:
 num checks: 43 rlimit count: 22741 mk clause: 110 datatype occurs check: 1009 mk bool var: 1357 datatype splits: 242 decisions: 902 propagations: 809 conflicts: 62 datatype accessor ax: 213 minimized lits: 2 datatype constructor ax: 467 final checks: 68 added eqs: 2938 del clause: 18 memory: 23.39 max memory: 23.39 num allocs: 4.36114e+09
Expand
• start[0.026s]
reverse (append ( :var_0: ) ( :var_1: ))
= append (reverse ( :var_0: )) (reverse ( :var_1: ))
• unroll
 expr: (append_2149/server (reverse_2154/server x_1304/client) (reverse_2154/server y_1305/client)) expansions:
• unroll
 expr: (reverse_2154/server y_1305/client) expansions:
• unroll
 expr: (reverse_2154/server x_1304/client) expansions:
• unroll
 expr: (reverse_2154/server (append_2149/server x_1304/client y_1305/client)) expansions:
• unroll
 expr: (append_2149/server x_1304/client y_1305/client) expansions:
• unroll
 expr: (append_2149/server (|get.::.1_2148/server| (reverse_2154/server x_1304/client)) (reverse_2154/s… expansions:
• unroll
 expr: (append_2149/server (reverse_2154/server (|get.::.1_2148/server| y_1305/client)) (|::| (|get.::.… expansions:
• unroll
 expr: (reverse_2154/server (|get.::.1_2148/server| y_1305/client)) expansions:
• unroll
 expr: (append_2149/server (reverse_2154/server (|get.::.1_2148/server| x_1304/client)) (|::| (|get.::.… expansions:
• unroll
 expr: (reverse_2154/server (|get.::.1_2148/server| x_1304/client)) expansions:
• unroll
 expr: (append_2149/server (reverse_2154/server (|get.::.1_2148/server| (append_2149/server x_1304/cl… expansions:
• unroll
 expr: (reverse_2154/server (|get.::.1_2148/server| (append_2149/server x_1304/client y_1305/client))) expansions:
• unroll
 expr: (append_2149/server (|get.::.1_2148/server| x_1304/client) y_1305/client) expansions:
• unroll
 expr: (append_2149/server (|get.::.1_2148/server| (reverse_2154/server (|get.::.1_2148/server| x_130… expansions:
• unroll
 expr: (append_2149/server (|get.::.1_2148/server| (|get.::.1_2148/server| (reverse_2154/server x_130… expansions:
• unroll
 expr: (append_2149/server (reverse_2154/server (|get.::.1_2148/server| (|get.::.1_2148/server| x_130… expansions:
• unroll
 expr: (let ((a!1 (reverse_2154/server (|get.::.1_2148/server| (|get.::.1_2148/… expansions:
• unroll
 expr: (reverse_2154/server (|get.::.1_2148/server| (|get.::.1_2148/server| (append_2149/server x_130… expansions:
• unroll
 expr: (reverse_2154/server (|get.::.1_2148/server| (|get.::.1_2148/server| x_1304/client))) expansions:
• unroll
 expr: (append_2149/server (|get.::.1_2148/server| (reverse_2154/server (|get.::.1_2148/server| y_130… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_2148/server| (reverse_2154/server (|get.::.1_2148/… expansions:
• Sat (Some let x : int list = [(Z.of_nativeint (0n))] let y : int list = [(Z.of_nativeint (2n))] )

Ah! Let's do it right this time:

In [23]:
verify (fun (x : int list) y -> reverse (append x y) = append (reverse y) (reverse x))

Out[23]:
- : Z.t list -> Z.t list -> bool = <fun>

Unknown (Verified up to bound 100)
Expand
 expanded: append (List.tl (List.tl (List.tl (List.tl (List.tl x))))) ylet (_x_0 : int list) = List.tl x in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]append (List.tl (List.tl (reverse (List.tl x)))) [List.hd x]append x yreverse (List.tl (List.tl (List.tl (List.tl (List.tl x)))))append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))) ylet (_x_0 : int list) = List.tl x in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]append (List.tl (reverse (List.tl x))) [List.hd x]let (_x_0 : int list) = append x y in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (append x y)))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (append x y))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : int list) = List.tl (append x y) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (append x y)) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : int list) = append x y in append (reverse (List.tl _x_0)) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl x))) in append (reverse (List.tl _x_0)) [List.hd _x_0]reverse (List.tl (append x y))let (_x_0 : int list) = List.tl (List.tl (List.tl y)) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : int list) = List.tl y in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl y)) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl y in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : int list) = List.tl y in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : int list) = List.tl y in append (reverse (List.tl _x_0)) [List.hd _x_0]append (List.tl (List.tl (List.tl (reverse (List.tl x))))) [List.hd x]append (List.tl (List.tl (List.tl (List.tl x)))) ylet (_x_0 : int list) = List.tl (List.tl x) in append (reverse (List.tl _x_0)) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl y))))let (_x_0 : int list) = List.tl (List.tl (List.tl x)) in append (reverse (List.tl _x_0)) [List.hd _x_0]reverse (List.tl (List.tl y))reverse yappend (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse y))))))) (reverse x)append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse y)))))) (reverse x)let (_x_0 : int list) = List.tl (List.tl (List.tl (append x y))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (append x y)) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : int list) = List.tl (append x y) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : int list) = append x y in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (reverse y))))) (reverse x)append (List.tl (List.tl (List.tl (reverse y)))) (reverse x)append (List.tl (List.tl (reverse y))) (reverse x)append (List.tl (reverse y)) (reverse x)reverse xappend (reverse y) (reverse x)reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))reverse (List.tl (List.tl (append x y)))append (reverse (List.tl x)) [List.hd x]reverse (List.tl (List.tl (List.tl x)))let (_x_0 : int list) = List.tl (List.tl (List.tl x)) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl x) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]reverse (List.tl (List.tl x))let (_x_0 : int list) = List.tl x in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl x) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : int list) = append x y in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (append x y)))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (append x y))) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (append x y) in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (append x y)) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl x))))let (_x_0 : int list) = List.tl (List.tl (append x y)) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl (append x y) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : int list) = append x y in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))))let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl x)))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl x)))))) [List.hd x]reverse (List.tl (List.tl (List.tl (append x y))))append (List.tl (List.tl x)) ylet (_x_0 : int list) = List.tl (List.tl y) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl y))))))) [List.hd y]let (_x_0 : int list) = List.tl (List.tl y) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl y)))))) [List.hd y]append (List.tl (List.tl (List.tl (reverse (List.tl y))))) [List.hd y]let (_x_0 : int list) = List.tl (List.tl y) in append (reverse (List.tl _x_0)) [List.hd _x_0]append (List.tl (List.tl (reverse (List.tl y)))) [List.hd y]append (List.tl (reverse (List.tl y))) [List.hd y]append (reverse (List.tl y)) [List.hd y]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl x)))) in append (reverse (List.tl _x_0)) [List.hd _x_0]append (List.tl x) yreverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))let (_x_0 : int list) = List.tl (List.tl (List.tl x)) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]reverse (List.tl x)let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl x))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl y)))append (List.tl (List.tl (List.tl x))) yreverse (List.tl y)let (_x_0 : int list) = append x y in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (append x y)))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (append x y))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : int list) = List.tl (append x y) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (append x y)) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (append x y) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = append x y in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))) ylet (_x_0 : int list) = List.tl (List.tl x) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (append x y)))))let (_x_0 : int list) = List.tl x in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]reverse (append x y) blocked: let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl y)) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : int list) = List.tl y in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]let (_x_0 : int list) = List.tl x in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl x))))))) [List.hd x]let (_x_0 : int list) = append x y in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))) in append (List.tl (reverse (List.tl _x_0))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse y)))))))) (reverse x)let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl (append x y))))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (append x y)))) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (append x y))) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (append x y) in append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (append x y)) in append (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl x)) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x)))))))) ylet (_x_0 : int list) = List.tl (List.tl x) in append (List.tl (List.tl (List.tl (List.tl (reverse (List.tl _x_0)))))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl y) in append (List.tl (List.tl (List.tl (reverse (List.tl _x_0))))) [List.hd _x_0]append (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (reverse (List.tl y)))))))) [List.hd y]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl y))) in append (reverse (List.tl _x_0)) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl x))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]let (_x_0 : int list) = List.tl (List.tl (List.tl (List.tl (List.tl x)))) in append (List.tl (List.tl (reverse (List.tl _x_0)))) [List.hd _x_0]reverse (List.tl (List.tl (List.tl (List.tl (List.tl y)))))reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl x))))))))reverse (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (List.tl (append x y)))))))))
proof attempt
 ground_instances: 100 definitions: 0 inductions: 0 search_time: 0.245s
Expand
• start[0.245s]
reverse (append ( :var_0: ) ( :var_1: ))
= append (reverse ( :var_1: )) (reverse ( :var_0: ))
• unroll
 expr: (append_1082/server (reverse_1087/server y_1081/server) (reverse_1087/server x_1080/server)) expansions:
• unroll
 expr: (reverse_1087/server x_1080/server) expansions:
• unroll
 expr: (reverse_1087/server y_1081/server) expansions:
• unroll
 expr: (reverse_1087/server (append_1082/server x_1080/server y_1081/server)) expansions:
• unroll
 expr: (append_1082/server x_1080/server y_1081/server) expansions:
• unroll
 expr: (append_1082/server (|get.::.1_1079/server| (reverse_1087/server y_1081/server)) (reverse_1087/s… expansions:
• unroll
 expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| x_1080/server)) (|::| (|get.::.… expansions:
• unroll
 expr: (reverse_1087/server (|get.::.1_1079/server| x_1080/server)) expansions:
• unroll
 expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| y_1081/server)) (|::| (|get.::.… expansions:
• unroll
 expr: (reverse_1087/server (|get.::.1_1079/server| y_1081/server)) expansions:
• unroll
 expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| (append_1082/server x_1080/se… expansions:
• unroll
 expr: (reverse_1087/server (|get.::.1_1079/server| (append_1082/server x_1080/server y_1081/server))) expansions:
• unroll
 expr: (append_1082/server (|get.::.1_1079/server| x_1080/server) y_1081/server) expansions:
• unroll
 expr: (append_1082/server (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/server| x_108… expansions:
• unroll
 expr: (append_1082/server (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087/server y_108… expansions:
• unroll
 expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| x_108… expansions:
• unroll
 expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| x_1080/server))) expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (append_1082/server (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/server| y_108… expansions:
• unroll
 expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| (append_1082/server x_108… expansions:
• unroll
 expr: (append_1082/server (|get.::.1_1079/server| (|get.::.1_1079/server| x_1080/server)) y_1081/serve… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (append_1082/server (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| y_108… expansions:
• unroll
 expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| y_1081/server))) expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_1079/server| x… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (append_1082/server (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_1079/server| x_… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_1079/server| y… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (reverse_1087… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (reverse_1087/server (|get.::.1_1079/… expansions:
• unroll
 expr: (let ((a!1 (|get.::.1_1079/server| (|get.::.1_1079/server| (|get.::.1_10… expansions:
• unroll
 expr: (let ((a!1 (reverse_1087/server (|get.::.1_1079/server| (|get.::.1_1079/… expansions:

Our conjecture has passed through to depth 100 recursive unrolling, so we feel pretty confident it is true. Let's try to prove it by induction:

In [24]:
verify (fun x y -> reverse (append x y) = append (reverse y) (reverse x)) [@@induct]

Out[24]:
- : 'a list -> 'a list -> bool = <fun>
Goal:

reverse (append x y) = append (reverse y) (reverse x).

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
reverse (append x y) = append (reverse y) (reverse x)

Must try induction.

The recursive terms in the conjecture suggest 3 inductions.
Subsumption and merging reduces this to 2.

Only 1 of those schemes are unflawed.
We shall induct according to a scheme derived from append.

Induction scheme:

(not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse (append x y) = append (reverse y) (reverse x)

This simplifies, using the definitions of append and reverse to:

Subgoal 1.2':

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse y = append (reverse y) []

Must try induction.

We shall induct according to a scheme derived from reverse.

Induction scheme:

(not (y <> []) ==> φ x y) && (y <> [] && φ x (List.tl y) ==> φ x y).

2 nontautological subgoals.

Subgoal 1.2'.2:

|---------------------------------------------------------------------------
C0. y <> []
C1. x <> []
C2. reverse y = append (reverse y) []

But simplification reduces this to true, using the definitions of append and
reverse.

Subgoal 1.2'.1:

H0. y <> []
H1. x <> [] || (reverse (List.tl y) = append (reverse (List.tl y)) [])
|---------------------------------------------------------------------------
C0. x <> []
C1. reverse y = append (reverse y) []

This simplifies, using the definition of reverse to:

Subgoal 1.2'.1':

H0. reverse (List.tl y) = append (reverse (List.tl y)) []
H1. y <> []
|---------------------------------------------------------------------------
C0. x <> []
C1. append (reverse (List.tl y)) [List.hd y]
= append (append (reverse (List.tl y)) [List.hd y]) []

We can eliminate destructors by the following substitution:
y -> y1 :: y2

This produces the modified subgoal:

Subgoal 1.2'.1'':

H0. reverse y2 = append (reverse y2) []
|---------------------------------------------------------------------------
C0. append (reverse y2) [y1] = append (append (reverse y2) [y1]) []
C1. x <> []

Cross-fertilizing with:

reverse y2 = append (reverse y2) []

This produces the modified subgoal:

Subgoal 1.2'.1''':

|---------------------------------------------------------------------------
C0. append (append (reverse y2) []) [y1]
= append (append (reverse y2) [y1]) []
C1. x <> []

Must try induction.

We shall induct according to a scheme derived from reverse.

Induction scheme:

(not (y2 <> []) ==> φ x y1 y2)
&& (y2 <> [] && φ x y1 (List.tl y2) ==> φ x y1 y2).

2 nontautological subgoals.

Subgoal 1.2'.1'''.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. y2 <> []
C2. append (append (reverse y2) []) [y1]
= append (append (reverse y2) [y1]) []

But simplification reduces this to true, using the definitions of append and
reverse.

Subgoal 1.2'.1'''.1:

H0. y2 <> []
H1. (append (append (reverse (List.tl y2)) []) [y1]
= append (append (reverse (List.tl y2)) [y1]) [])
|| x <> []
|---------------------------------------------------------------------------
C0. x <> []
C1. append (append (reverse y2) []) [y1]
= append (append (reverse y2) [y1]) []

This simplifies, using the definition of reverse to:

Subgoal 1.2'.1'''.1':

H0. append (append (reverse (List.tl y2)) []) [y1]
= append (append (reverse (List.tl y2)) [y1]) []
H1. y2 <> []
|---------------------------------------------------------------------------
C0. x <> []
C1. append (append (append (reverse (List.tl y2)) [List.hd y2]) []) [y1]
= append (append (append (reverse (List.tl y2)) [List.hd y2]) [y1]) []

We can eliminate destructors by the following substitution:
y2 -> y21 :: y22

This produces the modified subgoal:

Subgoal 1.2'.1'''.1'':

H0. append (append (reverse y22) []) [y1]
= append (append (reverse y22) [y1]) []
|---------------------------------------------------------------------------
C0. x <> []
C1. append (append (append (reverse y22) [y21]) []) [y1]
= append (append (append (reverse y22) [y21]) [y1]) []

Candidates for generalization:

reverse y22

This produces the modified subgoal:

Subgoal 1.2'.1'''.1''':

H0. append (append gen_1 []) [y1] = append (append gen_1 [y1]) []
|---------------------------------------------------------------------------
C0. x <> []
C1. append (append (append gen_1 [y21]) []) [y1]
= append (append (append gen_1 [y21]) [y1]) []

Must try induction.

⚠  Aborting proof attempt for _verify_target.

ⓘ  Rules:
(:def append)
(:def reverse)
(:induct append)
(:induct reverse)

Checkpoints:

H0. append (append gen_1 []) [y1] = append (append gen_1 [y1]) []
|---------------------------------------------------------------------------
C0. x <> []
C1. append (append (append gen_1 [y21]) []) [y1]
= append (append (append gen_1 [y21]) [y1]) []

|---------------------------------------------------------------------------
C0. append (append (reverse y2) []) [y1]
= append (append (reverse y2) [y1]) []
C1. x <> []

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse y = append (reverse y) []

Error:
Maximum induction depth reached (3). You can set this with #max_induct.
At <none>:1


Success! And in a proof with three inductions! If we inspect the proof, we'll see that those subsequent inductions actually suggest some very useful lemmas.

In fact, this phenomenon of goals proved by subinductions suggesting useful lemmas happens so often, that we commonly like to work with a very low #max_induct value. Let's attempt the same proof as above, but instruct Imandra to only perform inductions of depth 1 (i.e., no subinductions allowed):

In [25]:
#max_induct 1

Out[25]:

In [26]:
verify (fun x y -> reverse (append x y) = append (reverse y) (reverse x)) [@@induct]

Out[26]:
- : 'a list -> 'a list -> bool = <fun>
Goal:

reverse (append x y) = append (reverse y) (reverse x).

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
reverse (append x y) = append (reverse y) (reverse x)

Must try induction.

The recursive terms in the conjecture suggest 3 inductions.
Subsumption and merging reduces this to 2.

Only 1 of those schemes are unflawed.
We shall induct according to a scheme derived from append.

Induction scheme:

(not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse (append x y) = append (reverse y) (reverse x)

This simplifies, using the definitions of append and reverse to:

Subgoal 1.2':

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse y = append (reverse y) []

Must try induction.

⚠  Aborting proof attempt for _verify_target.

ⓘ  Rules:
(:def append)
(:def reverse)
(:induct append)

Checkpoints:

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse y = append (reverse y) []

Error:
Maximum induction depth reached (1). You can set this with #max_induct.
At <none>:1


We see above that Imandra is unable to prove our theorem with #max_induct set to 1. However, by inspecting the subgoal Imandra was working on before it hit its #max_induct limit (nicely presented to us as a Checkpoint at the end of the proof attempt), we can find a useful lemma we should prove!

In particular, note this checkpoint:

gen_1 = append gen_1 []

This, oriented as append gen_1 [] = gen_1 is a great rule. Note that it really does require induction, as append recurses on its first argument, not its second.

Let's ask Imandra to prove this as a theorem and to install it as a rewrite rule:

In [27]:
theorem append_nil x = append x [] = x [@@induct] [@@rewrite]

Out[27]:
val append_nil : 'a list -> bool = <fun>
Goal:

append x [] = x.

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
append x [] = x

Must try induction.

We shall induct according to a scheme derived from append.

Induction scheme:

(not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. append x [] = x

But simplification reduces this to true, using the definition of append.

Subgoal 1.1:

H0. x <> []
H1. append (List.tl x) [] = List.tl x
|---------------------------------------------------------------------------
append x [] = x

But simplification reduces this to true, using the definition of append.

ⓘ  Rules:
(:def append)
(:induct append)


Proved
proof
 ground_instances: 0 definitions: 2 inductions: 1 search_time: 0.101s
Expand
• start[0.101s, "Goal"] append ( :var_0: ) [] = ( :var_0: )
• #### subproof

append x [] = x
• start[0.101s, "1"] append x [] = x
• induction on (functional ?)
:scheme (not (x <> []) ==> φ x) && (x <> [] && φ (List.tl x) ==> φ x)
• Split (let (_x_0 : bool) = x <> [] in
let (_x_1 : bool) = append x [] = x in
let (_x_2 : sko_ty_0 list) = List.tl x in
(_x_0 || _x_1) && (not (_x_0 && (append _x_2 [] = _x_2)) || _x_1)
:cases [x <> [] || (append x [] = x);
let (_x_0 : sko_ty_0 list) = List.tl x in
not (x <> []) || not (append _x_0 [] = _x_0)
|| (append x [] = x)])
• ##### subproof
let (_x_0 : sko_ty_0 list) = List.tl x in not (x <> []) || not (append _x_0 [] = _x_0) || (append x [] = x)
• start[0.054s, "1.1"]
let (_x_0 : sko_ty_0 list) = List.tl x in
not (x <> []) || not (append _x_0 [] = _x_0) || (append x [] = x)
• ###### simplify
 into: true expansions: append rewrite_steps: forward_chaining:
• ##### subproof
x <> [] || (append x [] = x)
• start[0.054s, "1.2"] x <> [] || (append x [] = x)
• ###### simplify
 into: true expansions: append rewrite_steps: forward_chaining:

Success! Now, let's return to our main goal and see if we progress any further (still with #max_induct 1) now that we have append_nil available as a rewrite rule:

In [28]:
verify (fun x y -> reverse (append x y) = append (reverse y) (reverse x)) [@@induct]

Out[28]:
- : 'a list -> 'a list -> bool = <fun>
Goal:

reverse (append x y) = append (reverse y) (reverse x).

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
reverse (append x y) = append (reverse y) (reverse x)

Must try induction.

The recursive terms in the conjecture suggest 3 inductions.
Subsumption and merging reduces this to 2.

Only 1 of those schemes are unflawed.
We shall induct according to a scheme derived from append.

Induction scheme:

(not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse (append x y) = append (reverse y) (reverse x)

But simplification reduces this to true, using the definitions of append and
reverse, and the rewrite rule append_nil.

Subgoal 1.1:

H0. x <> []
H1. reverse (append (List.tl x) y)
= append (reverse y) (reverse (List.tl x))
|---------------------------------------------------------------------------
reverse (append x y) = append (reverse y) (reverse x)

This simplifies, using the definitions of append and reverse to:

Subgoal 1.1':

H0. x <> []
H1. reverse (append (List.tl x) y)
= append (reverse y) (reverse (List.tl x))
|---------------------------------------------------------------------------
append (reverse (append (List.tl x) y)) [List.hd x]
= append (reverse y) (append (reverse (List.tl x)) [List.hd x])

We can eliminate destructors by the following substitution:
x -> x1 :: x2

This produces the modified subgoal:

Subgoal 1.1'':

H0. reverse (append x2 y) = append (reverse y) (reverse x2)
|---------------------------------------------------------------------------
append (reverse (append x2 y)) [x1]
= append (reverse y) (append (reverse x2) [x1])

Cross-fertilizing with:

reverse (append x2 y) = append (reverse y) (reverse x2)

This produces the modified subgoal:

Subgoal 1.1''':

|---------------------------------------------------------------------------
append (append (reverse y) (reverse x2)) [x1]
= append (reverse y) (append (reverse x2) [x1])

Candidates for generalization:

reverse x2
reverse y

This produces the modified subgoal:

Subgoal 1.1'''':

|---------------------------------------------------------------------------
append (append gen_2 gen_1) [x1] = append gen_2 (append gen_1 [x1])

Must try induction.

⚠  Aborting proof attempt for _verify_target.

ⓘ  Rules:
(:def append)
(:def reverse)
(:rw append_nil)
(:induct append)

Checkpoints:

|---------------------------------------------------------------------------
append (append gen_2 gen_1) [x1] = append gen_2 (append gen_1 [x1])

Error:
Maximum induction depth reached (1). You can set this with #max_induct.
At <none>:1


Yes! If we inspect Imandra's aborted proof, we see that our rewrite rule append_nil was applied exactly where we wanted it, and we now see another interesting subgoal which looks quite familiar:

append (append gen_1 gen_2) (x1 :: []) = append gen_1 (append gen_2 (x1 :: []))


That is, Imandra has derived, as a subgoal to be proved, an instance of the associativity of append!

If we set our #max_induct to 2, then Imandra would finish this proof automatically.

But, this fact about append seems very useful and of general purpose, so let's prove it as a rewrite rule and make it available to our subsequent proof efforts.

In [29]:
theorem assoc_append x y z = append x (append y z) = append (append x y) z [@@induct] [@@rewrite]

Out[29]:
val assoc_append : 'a list -> 'a list -> 'a list -> bool = <fun>
Goal:

append x (append y z) = append (append x y) z.

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
append x (append y z) = append (append x y) z

Must try induction.

The recursive terms in the conjecture suggest 3 inductions.
Subsumption and merging reduces this to 2.

Only 1 of those schemes are unflawed.
We shall induct according to a scheme derived from append.

Induction scheme:

(not (x <> []) ==> φ x y z) && (x <> [] && φ (List.tl x) y z ==> φ x y z).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. append x (append y z) = append (append x y) z

But simplification reduces this to true, using the definition of append.

Subgoal 1.1:

H0. x <> []
H1. append (List.tl x) (append y z) = append (append (List.tl x) y) z
|---------------------------------------------------------------------------
append x (append y z) = append (append x y) z

But simplification reduces this to true, using the definition of append.

ⓘ  Rules:
(:def append)
(:induct append)


Proved
proof
 ground_instances: 0 definitions: 5 inductions: 1 search_time: 0.074s
Expand
• start[0.074s, "Goal"]
append ( :var_0: ) (append ( :var_1: ) ( :var_2: ))
= append (append ( :var_0: ) ( :var_1: )) ( :var_2: )
• #### subproof

append x (append y z) = append (append x y) z
• start[0.074s, "1"] append x (append y z) = append (append x y) z
• induction on (functional ?)
:scheme (not (x <> []) ==> φ x y z)
&& (x <> [] && φ (List.tl x) y z ==> φ x y z)
• Split (let (_x_0 : bool) = x <> [] in
let (_x_1 : sko_ty_0 list) = append y z in
let (_x_2 : bool) = append x _x_1 = append (append x y) z in
let (_x_3 : sko_ty_0 list) = List.tl x in
(_x_0 || _x_2)
&& (not (_x_0 && (append _x_3 _x_1 = append (append _x_3 y) z))
|| _x_2)
:cases [x <> [] || (append x (append y z) = append (append x y) z);
let (_x_0 : sko_ty_0 list) = List.tl x in
let (_x_1 : sko_ty_0 list) = append y z in
not (x <> [])
|| not (append _x_0 _x_1 = append (append _x_0 y) z)
|| (append x _x_1 = append (append x y) z)])
• ##### subproof
let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = append y z in not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z) || (append x _x_1 = append (append x y) z)
• start[0.040s, "1.1"]
let (_x_0 : sko_ty_0 list) = List.tl x in
let (_x_1 : sko_ty_0 list) = append y z in
not (x <> []) || not (append _x_0 _x_1 = append (append _x_0 y) z)
|| (append x _x_1 = append (append x y) z)
• ###### simplify
 into: true expansions: [append, append, append] rewrite_steps: forward_chaining:
• ##### subproof
x <> [] || (append x (append y z) = append (append x y) z)
• start[0.040s, "1.2"]
x <> [] || (append x (append y z) = append (append x y) z)
• ###### simplify
 into: true expansions: [append, append] rewrite_steps: forward_chaining:

Success! Now, let's go back to our original goal and see if we can prove it with the help of our proved rules (still with max_induct 1):

In [30]:
verify (fun x y -> reverse (append x y) = append (reverse y) (reverse x)) [@@induct]

Out[30]:
- : 'a list -> 'a list -> bool = <fun>
Goal:

reverse (append x y) = append (reverse y) (reverse x).

1 nontautological subgoal.

Subgoal 1:

|---------------------------------------------------------------------------
reverse (append x y) = append (reverse y) (reverse x)

Must try induction.

The recursive terms in the conjecture suggest 3 inductions.
Subsumption and merging reduces this to 2.

Only 1 of those schemes are unflawed.
We shall induct according to a scheme derived from append.

Induction scheme:

(not (x <> []) ==> φ x y) && (x <> [] && φ (List.tl x) y ==> φ x y).

2 nontautological subgoals.

Subgoal 1.2:

|---------------------------------------------------------------------------
C0. x <> []
C1. reverse (append x y) = append (reverse y) (reverse x)

But simplification reduces this to true, using the definitions of append and
reverse, and the rewrite rule append_nil.

Subgoal 1.1:

H0. x <> []
H1. reverse (append (List.tl x) y)
= append (reverse y) (reverse (List.tl x))
|---------------------------------------------------------------------------
reverse (append x y) = append (reverse y) (reverse x)

This simplifies, using the definitions of append and reverse to:

Subgoal 1.1':

H0. x <> []
H1. reverse (append (List.tl x) y)
= append (reverse y) (reverse (List.tl x))
|---------------------------------------------------------------------------
append (reverse (append (List.tl x) y)) [List.hd x]
= append (reverse y) (append (reverse (List.tl x)) [List.hd x])

But simplification reduces this to true, using the rewrite rule assoc_append.

ⓘ  Rules:
(:def append)
(:def reverse)
(:rw append_nil)
(:rw assoc_append)
(:induct append)


Proved
proof
 ground_instances: 0 definitions: 6 inductions: 1 search_time: 0.166s
Expand
• start[0.166s, "Goal"]
reverse (append ( :var_0: ) ( :var_1: ))
= append (reverse ( :var_1: )) (reverse ( :var_0: ))
• #### subproof

reverse (append x y) = append (reverse y) (reverse x)
• start[0.166s, "1"] reverse (append x y) = append (reverse y) (reverse x)
• induction on (functional ?)
:scheme (not (x <> []) ==> φ x y)
&& (x <> [] && φ (List.tl x) y ==> φ x y)
• Split (let (_x_0 : bool) = x <> [] in
let (_x_1 : sko_ty_0 list) = reverse y in
let (_x_2 : bool) = reverse (append x y) = append _x_1 (reverse x) in
let (_x_3 : sko_ty_0 list) = List.tl x in
(_x_0 || _x_2)
&& (not
(_x_0 && (reverse (append _x_3 y) = append _x_1 (reverse _x_3)))
|| _x_2)
:cases [x <> []
|| (reverse (append x y) = append (reverse y) (reverse x));
let (_x_0 : sko_ty_0 list) = List.tl x in
let (_x_1 : sko_ty_0 list) = reverse y in
not (x <> [])
|| not (reverse (append _x_0 y) = append _x_1 (reverse _x_0))
|| (reverse (append x y) = append _x_1 (reverse x))])
• ##### subproof
let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse y in not (x <> []) || not (reverse (append _x_0 y) = append _x_1 (reverse _x_0)) || (reverse (append x y) = append _x_1 (reverse x))
• start[0.118s, "1.1"]
let (_x_0 : sko_ty_0 list) = List.tl x in
let (_x_1 : sko_ty_0 list) = reverse y in
not (x <> []) || not (reverse (append _x_0 y) = append _x_1 (reverse _x_0))
|| (reverse (append x y) = append _x_1 (reverse x))
• ###### simplify
 into: let (_x_0 : sko_ty_0 list) = List.tl x in let (_x_1 : sko_ty_0 list) = reverse (append _x_0 y) in let (_x_2 : sko_ty_0 list) = reverse y in let (_x_3 : sko_ty_0 list) = reverse _x_0 in let (_x_4 : sko_ty_0 list) = [List.hd x] in not (x <> []) || not (_x_1 = append _x_2 _x_3) || (append _x_1 _x_4 = append _x_2 (append _x_3 _x_4)) expansions: [reverse, reverse, append] rewrite_steps: forward_chaining:
• ###### simplify
 into: true expansions: [] rewrite_steps: assoc_append forward_chaining:
• ##### subproof
x <> [] || (reverse (append x y) = append (reverse y) (reverse x))
• start[0.118s, "1.2"]
x <> [] || (reverse (append x y) = append (reverse y) (reverse x))
• ###### simplify
 into: true expansions: [append, reverse, append] rewrite_steps: append_nil forward_chaining:

Success! And our rewrite rules were used exactly where we'd hoped.

Happy proving!