(* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. *) open! Core (* Invariants: - elements of Concat are not empty (nor singletons) - arg of Rev is not empty, nor singleton, nor Rev - arg of Snoc is not empty ...ensure that: - an empty list is always represented by Empty, - a singleton is always represented by Cons(_, Empty) - the memory footprint is in Theta(length) Potential constructors to add later: - OfArray of 'a Array.t - Flatten of 'a t t *) type +'a t = Empty | Cons of 'a * 'a t | Snoc of 'a t * 'a | Concat of 'a t * 'a t | Rev of 'a t let () = (* This is a pretty simple test to check that the runtime representation of OCaml lists is compatible with our lists and hence using [Obj.magic] for [of_list] is legit *) let exception RuntimeValue in assert (Poly.(Caml.Obj.repr [RuntimeValue] = Caml.Obj.repr (Cons (RuntimeValue, Empty)))) (* Constructors *) let of_list = Caml.Obj.magic let empty = Empty let singleton e = Cons (e, Empty) let cons e l = Cons (e, l) let snoc l e = match l with Empty -> singleton e | _ -> Snoc (l, e) let append l1 l2 = match (l1, l2) with | Empty, l | l, Empty -> l | Cons (e, Empty), l2 -> Cons (e, l2) | l1, Cons (e, Empty) -> Snoc (l1, e) | l1, l2 -> Concat (l1, l2) let rev l = match l with Empty | Cons (_, Empty) -> l | Rev l -> l | l -> Rev l (* Deconstructors *) let is_empty = function Empty -> true | _ -> false let is_singleton = function Cons (e, Empty) -> Some e | _ -> None let is_singleton_or_more = function | Empty -> IContainer.Empty | Cons (e, Empty) -> IContainer.Singleton e | _ -> IContainer.More let rec hd_tl_exn : 'a t -> 'a * 'a t = function | Empty -> raise Caml.Not_found | Cons (hd, tl) -> (hd, tl) | Snoc (front, last) -> let hd, tl = hd_tl_exn front in (hd, snoc tl last) | Concat (l1, l2) -> let hd, tl1 = hd_tl_exn l1 in (hd, append tl1 l2) | Rev l -> let rev_tl, hd = front_last_exn l in (hd, rev rev_tl) and front_last_exn : 'a t -> 'a t * 'a = function | Empty -> raise Caml.Not_found | Cons (hd, tl) -> let front, last = front_last_exn tl in (cons hd front, last) | Snoc (front, last) -> (front, last) | Concat (l1, l2) -> let front2, last = front_last_exn l2 in (append l1 front2, last) | Rev l -> let last, rev_front = hd_tl_exn l in (rev rev_front, last) let rec hd_exn : 'a t -> 'a = function | Empty -> raise Caml.Not_found | Cons (hd, _) -> hd | Snoc (front, _) | Concat (front, _) -> hd_exn front | Rev l -> last_exn l and last_exn : 'a t -> 'a = function | Empty -> raise Caml.Not_found | Snoc (_, last) -> last | Cons (_, tl) | Concat (_, tl) -> last_exn tl | Rev l -> hd_exn l let hd = function Empty -> None | l -> Some (hd_exn l) let last = function Empty -> None | l -> Some (last_exn l) (* Traversing *) let rec fold_left_tailrec l rem ~init ~f = match l with | Empty -> ( match rem with Empty -> init | rem -> (fold_left_tailrec [@tailcall]) rem Empty ~init ~f ) | Cons (hd, tl) -> let init = f init hd in (fold_left_tailrec [@tailcall]) tl rem ~init ~f | Snoc (front, last) -> (fold_left_tailrec [@tailcall]) front (cons last rem) ~init ~f | Concat (l1, l2) -> (fold_left_tailrec [@tailcall]) l1 (append l2 rem) ~init ~f | Rev l -> (fold_right_tailrec [@tailcall]) l (rev rem) ~init ~f and fold_right_tailrec l rem ~init ~f = match l with | Empty -> ( match rem with Empty -> init | rem -> (fold_right_tailrec [@tailcall]) rem Empty ~init ~f ) | Cons (hd, tl) -> (fold_right_tailrec [@tailcall]) tl (snoc rem hd) ~init ~f | Snoc (front, last) -> let init = f init last in (fold_right_tailrec [@tailcall]) front rem ~init ~f | Concat (l1, l2) -> (fold_right_tailrec [@tailcall]) l2 (append rem l1) ~init ~f | Rev l -> (fold_left_tailrec [@tailcall]) l (rev rem) ~init ~f let max_recursion = 1000 let rec fold_left_count l depth ~init ~f = match l with | Empty -> init | Cons (hd, tl) -> let init = f init hd in (fold_left_count [@tailcall]) tl depth ~init ~f | Snoc (front, last) -> let init = if depth < max_recursion then fold_left_count front (depth + 1) ~init ~f else fold_left_tailrec l Empty ~init ~f in f init last | Concat (l1, l2) -> if depth < max_recursion then let init = fold_left_count l1 (depth + 1) ~init ~f in (fold_left_count [@tailcall]) l2 depth ~init ~f else fold_left_tailrec l1 l2 ~init ~f | Rev l -> (fold_right_count [@tailcall]) l depth ~init ~f and fold_right_count l depth ~init ~f = match l with | Empty -> init | Cons (hd, tl) -> let init = if depth < max_recursion then fold_right_count tl (depth + 1) ~init ~f else fold_right_tailrec tl Empty ~init ~f in f init hd | Snoc (front, last) -> let init = f init last in (fold_right_count [@tailcall]) front depth ~init ~f | Concat (l1, l2) -> if depth < max_recursion then let init = fold_right_count l2 (depth + 1) ~init ~f in (fold_right_count [@tailcall]) l1 depth ~init ~f else fold_right_tailrec l2 l1 ~init ~f | Rev l -> (fold_left_count [@tailcall]) l depth ~init ~f let fold_left l ~init ~f = fold_left_count l 0 ~init ~f let fold_right l ~init ~f = fold_right_count l 0 ~init ~f let rec fold_unordered_tailrec l rem ~init ~f = match l with | Empty -> ( match rem with Empty -> init | rem -> (fold_unordered_tailrec [@tailcall]) rem Empty ~init ~f ) | Cons (e, l) | Snoc (l, e) -> let init = f init e in (fold_unordered_tailrec [@tailcall]) l rem ~init ~f | Concat (l1, l2) -> (fold_unordered_tailrec [@tailcall]) l1 (append l2 rem) ~init ~f | Rev l -> (fold_unordered_tailrec [@tailcall]) l rem ~init ~f let rec fold_unordered_count l depth ~init ~f = match l with | Empty -> init | Cons (e, l) | Snoc (l, e) -> let init = f init e in (fold_unordered_count [@tailcall]) l depth ~init ~f | Concat (l1, l2) -> if depth < max_recursion then let init = fold_unordered_count l1 (depth + 1) ~init ~f in (fold_unordered_count [@tailcall]) l2 depth ~init ~f else fold_unordered_tailrec l1 l2 ~init ~f | Rev l -> (fold_unordered_count [@tailcall]) l depth ~init ~f let fold_unordered l ~init ~f = fold_unordered_count l 0 ~init ~f