Source file tree.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
type 'a t = Tree of 'a * 'a t Seq.t
let root (Tree (x, _)) = x
let children (Tree (_, xs)) = xs
let pure x = Tree (x, Seq.empty)
let rec map f (Tree (x, xs)) =
let y = f x in
let ys () = Seq.map (map f) xs () in
Tree (y, ys)
let ( >|= ) t f = map f t
let rec ap (Tree (f, fs)) (Tree (x, xs)) =
let y = f x in
let ys () =
Seq.append
(Seq.map (fun f' -> ap f' (Tree (x, xs))) fs)
(Seq.map (fun x' -> ap (Tree (f, fs)) x') xs)
()
in
Tree (y, ys)
let ( <*> ) = ap
let liftA2 f a b = map f a <*> b
let rec bind (Tree (x, xs)) f =
let (Tree (y, ys_of_x)) = f x in
let ys_of_xs () = Seq.map (fun x' -> bind x' f) xs () in
let ys () = Seq.append ys_of_xs ys_of_x () in
Tree (y, ys)
let ( >>= ) = bind
let rec make_primitive shrink x =
let shrink_trees () = Seq.map (make_primitive shrink) (shrink x) () in
Tree (x, shrink_trees)
let rec add_shrink_invariant p (Tree (x, xs)) =
let xs' () =
Seq.filter_map
(fun (Tree (x', _) as t) ->
if p x' then Some (add_shrink_invariant p t) else None)
xs ()
in
Tree (x, xs')
let rec opt (Tree (x, xs)) =
let shrinks () = Seq.cons (pure None) (Seq.map opt xs) () in
Tree (Some x, shrinks)
let rec sequence_list = function
| [] -> pure []
| hd :: tl -> liftA2 List.cons hd (sequence_list tl)
let rec drop_at i = function
| [] -> []
| _ :: xs when i = 0 -> xs
| x :: xs -> x :: drop_at (i - 1) xs
let list_split l len =
let rec go acc n = function
| [] -> (List.rev acc, [])
| xs when n = 0 -> (List.rev acc, xs)
| x :: xs -> go (x :: acc) (n - 1) xs
in
go [] len l
let rec build_list_shrink_tree l =
match l with
| [] -> Seq.empty
| _ :: _ ->
fun () ->
let len = List.length l in
if len < 4 then
let rec drop_each i () =
if i >= len then (children (sequence_list l)) ()
else
let dropped = drop_at i l in
Seq.Cons
( Tree (List.map root dropped, build_list_shrink_tree dropped),
drop_each (i + 1) )
in
drop_each 0 ()
else
let half = (len + 1) / 2 in
let xs, ys = list_split l half in
let xs_roots = List.map root xs in
let ys_roots = List.map root ys in
Seq.Cons
( Tree (xs_roots, build_list_shrink_tree xs),
fun () ->
Seq.Cons
( Tree (ys_roots, build_list_shrink_tree ys),
fun () ->
let rest = List.tl l in
let rest_roots = List.map root rest in
Seq.Cons
( Tree (rest_roots, build_list_shrink_tree rest),
fun () ->
if List.length ys > 1 then
let shorter = xs @ List.tl ys in
Seq.Cons
( Tree
( List.map root shorter,
build_list_shrink_tree shorter ),
fun () -> (children (sequence_list l)) () )
else (children (sequence_list l)) () ) ) )