ocaml-containers/src/data/CCRAL.ml
2024-05-13 20:57:53 -04:00

528 lines
12 KiB
OCaml

(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Random-Access Lists} *)
(** A complete binary tree *)
type +'a tree =
| Leaf of 'a
| Node of 'a * 'a tree * 'a tree
and +'a t =
| Nil
| Cons of int * 'a tree * 'a t (** Functional array of complete trees *)
(** {2 Functions on trees} *)
(** {2 Functions on lists of trees} *)
let empty = Nil
let return x = Cons (1, Leaf x, Nil)
let is_empty = function
| Nil -> true
| Cons _ -> false
let rec get_exn l i =
match l with
| Nil -> invalid_arg "RAL.get"
| Cons (size, t, _) when i < size -> tree_lookup_ size t i
| Cons (size, _, l') -> get_exn l' (i - size)
and tree_lookup_ size t i =
match t, i with
| Leaf x, 0 -> x
| Leaf _, _ -> invalid_arg "RAL.get"
| Node (x, _, _), 0 -> x
| Node (_, t1, t2), _ ->
let size' = size / 2 in
if i <= size' then
tree_lookup_ size' t1 (i - 1)
else
tree_lookup_ size' t2 (i - 1 - size')
let get l i = try Some (get_exn l i) with Invalid_argument _ -> None
let rec set l i v =
match l with
| Nil -> invalid_arg "RAL.set"
| Cons (size, t, l') when i < size -> Cons (size, tree_update_ size t i v, l')
| Cons (size, t, l') -> Cons (size, t, set l' (i - size) v)
and tree_update_ size t i v =
match t, i with
| Leaf _, 0 -> Leaf v
| Leaf _, _ -> invalid_arg "RAL.set"
| Node (_, t1, t2), 0 -> Node (v, t1, t2)
| Node (x, t1, t2), _ ->
let size' = size / 2 in
if i <= size' then
Node (x, tree_update_ size' t1 (i - 1) v, t2)
else
Node (x, t1, tree_update_ size' t2 (i - 1 - size') v)
let cons x l =
match l with
| Cons (size1, t1, Cons (size2, t2, l')) when size1 = size2 ->
Cons (1 + size1 + size2, Node (x, t1, t2), l')
| _ -> Cons (1, Leaf x, l)
let cons' l x = cons x l
let hd l =
match l with
| Nil -> invalid_arg "RAL.hd"
| Cons (_, Leaf x, _) -> x
| Cons (_, Node (x, _, _), _) -> x
let tl l =
match l with
| Nil -> invalid_arg "RAL.tl"
| Cons (_, Leaf _, l') -> l'
| Cons (size, Node (_, t1, t2), l') ->
let size' = size / 2 in
Cons (size', t1, Cons (size', t2, l'))
let front l =
match l with
| Nil -> None
| Cons (_, Leaf x, tl) -> Some (x, tl)
| Cons (size, Node (x, t1, t2), l') ->
let size' = size / 2 in
Some (x, Cons (size', t1, Cons (size', t2, l')))
let front_exn l =
match l with
| Nil -> invalid_arg "RAL.front_exn"
| Cons (_, Leaf x, tl) -> x, tl
| Cons (size, Node (x, t1, t2), l') ->
let size' = size / 2 in
x, Cons (size', t1, Cons (size', t2, l'))
let rec _remove prefix l i =
let x, l' = front_exn l in
if i = 0 then
List.fold_left (fun l x -> cons x l) l' prefix
else
_remove (x :: prefix) l' (i - 1)
let remove l i = _remove [] l i
let rec _get_and_remove_exn prefix l i =
let x, l' = front_exn l in
if i = 0 then
x, List.fold_left (fun l x -> cons x l) l' prefix
else
_get_and_remove_exn (x :: prefix) l' (i - 1)
let get_and_remove_exn l i = _get_and_remove_exn [] l i
let rec _map_tree f t =
match t with
| Leaf x -> Leaf (f x)
| Node (x, l, r) -> Node (f x, _map_tree f l, _map_tree f r)
let rec map ~f l =
match l with
| Nil -> Nil
| Cons (i, t, tl) -> Cons (i, _map_tree f t, map ~f tl)
let mapi ~f l =
let rec aux f i l =
match l with
| Nil -> Nil
| Cons (size, t, tl) -> Cons (size, aux_t f ~size i t, aux f (i + size) tl)
and aux_t f ~size i t =
match t with
| Leaf x -> Leaf (f i x)
| Node (x, l, r) ->
let x = f i x in
let l = aux_t f ~size:(size / 2) (i + 1) l in
Node (x, l, aux_t f ~size:(size / 2) (i + 1 + (size / 2)) r)
in
aux f 0 l
let rec length l =
match l with
| Nil -> 0
| Cons (size, _, l') -> size + length l'
let rec iter ~f l =
match l with
| Nil -> ()
| Cons (_, Leaf x, l') ->
f x;
iter ~f l'
| Cons (_, t, l') ->
iter_tree t f;
iter ~f l'
and iter_tree t f =
match t with
| Leaf x -> f x
| Node (x, t1, t2) ->
f x;
iter_tree t1 f;
iter_tree t2 f
let iteri ~f l =
let rec aux f i l =
match l with
| Nil -> ()
| Cons (size, t, l') ->
aux_t ~size f i t;
aux f (i + size) l'
and aux_t f ~size i t =
match t with
| Leaf x -> f i x
| Node (x, l, r) ->
f i x;
let size' = size / 2 in
aux_t ~size:size' f (i + 1) l;
aux_t ~size:size' f (i + 1 + size') r
in
aux f 0 l
let rec fold ~f ~x:acc l =
match l with
| Nil -> acc
| Cons (_, Leaf x, l') -> fold ~f ~x:(f acc x) l'
| Cons (_, t, l') ->
let acc' = fold_tree t acc f in
fold ~f ~x:acc' l'
and fold_tree t acc f =
match t with
| Leaf x -> f acc x
| Node (x, t1, t2) ->
let acc = f acc x in
let acc = fold_tree t1 acc f in
fold_tree t2 acc f
let rec fold_rev ~f ~x:acc l =
match l with
| Nil -> acc
| Cons (_, Leaf x, l') -> f (fold_rev ~f ~x:acc l') x
| Cons (_, t, l') ->
let acc = fold_rev ~f ~x:acc l' in
fold_tree_rev t acc f
and fold_tree_rev t acc f =
match t with
| Leaf x -> f acc x
| Node (x, t1, t2) ->
let acc = fold_tree_rev t2 acc f in
let acc = fold_tree_rev t1 acc f in
f acc x
let rev_map ~f l = fold ~f:(fun acc x -> cons (f x) acc) ~x:empty l
let rev l = fold ~f:cons' ~x:empty l
let append l1 l2 = fold_rev ~f:(fun l2 x -> cons x l2) ~x:l2 l1
let append_tree_ t l = fold_tree_rev t l cons'
let filter ~f l =
fold_rev
~f:(fun acc x ->
if f x then
cons x acc
else
acc)
~x:empty l
let filter_map ~f l =
fold_rev ~x:empty l ~f:(fun acc x ->
match f x with
| None -> acc
| Some y -> cons y acc)
let flat_map f l =
fold_rev ~x:empty l ~f:(fun acc x ->
let l = f x in
append l acc)
let flatten l = fold_rev ~f:(fun acc l -> append l acc) ~x:empty l
let app funs l =
fold_rev ~x:empty funs ~f:(fun acc f ->
fold_rev ~x:acc l ~f:(fun acc x -> cons (f x) acc))
type 'a stack =
| St_nil
| St_list of 'a t * 'a stack
| St_tree of 'a tree * 'a stack
let rec stack_to_list = function
| St_nil -> Nil
| St_list (l, st') -> append l (stack_to_list st')
| St_tree (t, st') -> append_tree_ t (stack_to_list st')
let rec take n l =
match l with
| Nil -> Nil
| Cons (size, t, tl) ->
if size <= n then
append_tree_ t (take (n - size) tl)
else
take_tree_ ~size n t
and take_tree_ ~size n t =
match t with
| _ when n = 0 -> Nil
| Leaf x -> cons x Nil
| Node (x, l, r) ->
let size' = size / 2 in
if size' <= n - 1 then
cons x (append_tree_ l (take_tree_ ~size:size' (n - size' - 1) r))
else
cons x (take_tree_ ~size:size' (n - 1) l)
let take_while ~f l =
(* st: stack of subtrees *)
let rec aux p st =
match st with
| St_nil -> Nil
| St_list (Nil, st') -> aux p st'
| St_list (Cons (_, t, tl), st') -> aux p (St_tree (t, St_list (tl, st')))
| St_tree (Leaf x, st') ->
if p x then
cons x (aux p st')
else
Nil
| St_tree (Node (x, l, r), st') ->
if p x then
cons x (aux p (St_tree (l, St_tree (r, st'))))
else
Nil
in
aux f (St_list (l, St_nil))
(* drop [n < size] elements from [t] *)
let rec drop_tree_ ~size n t tail =
match t with
| _ when n = 0 -> tail
| Leaf _ ->
assert (n = 1);
tail
| Node (_, left, right) ->
if n = 1 then
append_tree_ left (append_tree_ right tail)
else (
assert (size mod 2 = 1);
let size_sub = size / 2 in
(* size of subtrees *)
let n = n - 1 in
if n = size_sub then
append_tree_ right tail
(* drop element and left tree *)
else if n < size_sub then
(* drop element and part of left tree *)
drop_tree_ ~size:size_sub n left (append_tree_ right tail)
else
(* drop element, left tree, and part of right tree *)
drop_tree_ ~size:size_sub (n - size_sub) right tail
)
let rec drop n l =
match l with
| _ when n = 0 -> l
| Nil -> Nil
| Cons (size, t, tl) ->
if n >= size then
drop (n - size) tl
else
drop_tree_ ~size n t tl
let drop_while ~f l =
let rec aux p st =
match st with
| St_nil -> Nil
| St_list (Nil, st') -> aux p st'
| St_list (Cons (_, t, tail), st') ->
aux p (St_tree (t, St_list (tail, st')))
| St_tree (Leaf x, st') ->
if p x then
aux p st'
else
cons x (stack_to_list st')
| St_tree ((Node (x, l, r) as tree), st') ->
if p x then
aux p (St_tree (l, St_tree (r, st')))
else
append_tree_ tree (stack_to_list st')
in
aux f (St_list (l, St_nil))
let take_drop n l = take n l, drop n l
let equal ~eq l1 l2 =
let rec aux ~eq l1 l2 =
match l1, l2 with
| Nil, Nil -> true
| Cons (size1, t1, l1'), Cons (size2, t2, l2') ->
size1 = size2 && aux_t ~eq t1 t2 && aux ~eq l1' l2'
| Nil, Cons _ | Cons _, Nil -> false
and aux_t ~eq t1 t2 =
match t1, t2 with
| Leaf x, Leaf y -> eq x y
| Node (x1, l1, r1), Node (x2, l2, r2) ->
eq x1 x2 && aux_t ~eq l1 l2 && aux_t ~eq r1 r2
| Leaf _, Node _ | Node _, Leaf _ -> false
in
aux ~eq l1 l2
(** {2 Utils} *)
let make n x =
let rec aux n acc x =
if n <= 0 then
acc
else
aux (n - 1) (cons x acc) x
in
aux n empty x
let repeat n l =
let rec aux n l acc =
if n <= 0 then
acc
else
aux (n - 1) l (append l acc)
in
aux n l empty
let range i j =
let rec aux i j acc =
if i = j then
cons i acc
else if i < j then
aux i (j - 1) (cons j acc)
else
aux i (j + 1) (cons j acc)
in
aux i j empty
let range_r_open_ i j =
if i = j then
empty
else if i < j then
range i (j - 1)
else
range i (j + 1)
(** {2 Conversions} *)
type 'a iter = ('a -> unit) -> unit
type 'a gen = unit -> 'a option
let add_list l l2 = List.fold_left (fun acc x -> cons x acc) l (List.rev l2)
let of_list l = add_list empty l
let to_list l = fold_rev ~f:(fun acc x -> x :: acc) ~x:[] l
let add_array l a = Array.fold_right cons a l
let of_array a = add_array empty a
let to_array l =
match l with
| Nil -> [||]
| Cons (_, Leaf x, _) | Cons (_, Node (x, _, _), _) ->
let len = length l in
let arr = Array.make len x in
iteri ~f:(fun i x -> Array.set arr i x) l;
arr
let of_iter s =
let l = ref empty in
s (fun x -> l := cons x !l);
rev !l
let add_iter l s =
let l1 = ref empty in
s (fun x -> l1 := cons x !l1);
fold ~f:(fun acc x -> cons x acc) ~x:l !l1
let to_iter l yield = iter ~f:yield l
let rec gen_iter_ f g =
match g () with
| None -> ()
| Some x ->
f x;
gen_iter_ f g
let add_gen l g =
let l1 = ref empty in
gen_iter_ (fun x -> l1 := cons x !l1) g;
fold ~f:(fun acc x -> cons x acc) ~x:l !l1
let of_gen g = add_gen empty g
let to_gen l =
let st = Stack.create () in
(* stack for tree *)
let l = ref l in
(* tail of list *)
let rec next () =
if Stack.is_empty st then (
match !l with
| Nil -> None
| Cons (_, t, tl) ->
l := tl;
Stack.push t st;
next ()
) else (
match Stack.pop st with
| Leaf x -> Some x
| Node (x, l, r) ->
Stack.push r st;
Stack.push l st;
Some x
)
in
next
let rec of_list_map ~f l =
match l with
| [] -> empty
| x :: l' ->
let y = f x in
cons y (of_list_map ~f l')
let compare ~cmp l1 l2 =
let rec cmp_gen ~cmp g1 g2 =
match g1 (), g2 () with
| None, None -> 0
| Some _, None -> 1
| None, Some _ -> -1
| Some x, Some y ->
let c = cmp x y in
if c <> 0 then
c
else
cmp_gen ~cmp g1 g2
in
cmp_gen ~cmp (to_gen l1) (to_gen l2)
(** {2 Infix} *)
module Infix = struct
let ( @+ ) = cons
let ( >>= ) l f = flat_map f l
let ( >|= ) l f = map ~f l
let ( <*> ) = app
let ( -- ) = range
let ( --^ ) = range_r_open_
end
include Infix
(** {2 IO} *)
type 'a printer = Format.formatter -> 'a -> unit
let pp ?(pp_sep = fun fmt () -> Format.fprintf fmt ",@ ") pp_item fmt l =
let first = ref true in
iter l ~f:(fun x ->
if !first then
first := false
else
pp_sep fmt ();
pp_item fmt x);
()