(* * Copyright (c) 2015-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. *) open! IStd (* [take_append ~max list ~tail] takes the first max elements of [list] and appends [tail] to the result. Does not create garbage for [max] <= 1000. *) let take_append = let rec take_append_non_tailrec ~max list ~tail = match list with | hd :: tl when max > 0 -> hd :: take_append_non_tailrec ~max:(max - 1) tl ~tail | _ -> tail in let rec take_append_tailrec ~max list acc ~tail = match list with | hd :: tl when max > 0 -> (take_append_tailrec [@tailcall]) ~max:(max - 1) tl (hd :: acc) ~tail | _ -> List.rev_append acc tail in fun list ~max ~tail -> if max <= 1000 then take_append_non_tailrec ~max list ~tail else take_append_tailrec ~max list [] ~tail (** like map, but returns the original list if unchanged *) let map_changed ~equal ~f l = let rec aux unchanged_prefix_length = function | [] -> l | x :: tl -> let x' = f x in if not (equal x x') then take_append ~max:unchanged_prefix_length ~tail:(x' :: List.map ~f tl) l else aux (unchanged_prefix_length + 1) tl in aux 0 l (** like filter, but returns the original list if unchanged *) let filter_changed ~f l = let res_rev, changed = List.fold_left l ~init:([], false) ~f:(fun (l, changed) x -> if f x then (x :: l, changed) else (l, true) ) in if changed then List.rev res_rev else l (** Remove consecutive equal irrelevant elements from a list (according to the given comparison and relevance functions) *) let remove_irrelevant_duplicates ~equal ~f l = let rec remove acc = function | [] -> List.rev acc | [x] -> List.rev (x :: acc) | x :: (y :: l'' as l') -> if equal x y then match (f x, f y) with | false, _ -> remove acc l' | true, false -> remove acc (x :: l'') | true, true -> remove (x :: acc) l' else remove (x :: acc) l' in remove [] l (** The function works on sorted lists without duplicates, and keeps only one copy of elements that appear in both lists. *) let rec merge_sorted_nodup ~cmp ~res xs1 xs2 = match (xs1, xs2) with | [], _ -> List.rev_append res xs2 | _, [] -> List.rev_append res xs1 | x1 :: xs1', x2 :: xs2' -> let n = cmp x1 x2 in if Int.equal n 0 then merge_sorted_nodup ~cmp ~res:(x1 :: res) xs1' xs2' else if n < 0 then merge_sorted_nodup ~cmp ~res:(x1 :: res) xs1' xs2 else merge_sorted_nodup ~cmp ~res:(x2 :: res) xs1 xs2' let inter ~cmp xs ys = let rev_sort xs = List.dedup_and_sort ~compare:(fun x y -> cmp y x) xs in let rev_xs = rev_sort xs in let rev_ys = rev_sort ys in let rec inter_ is rev_xxs rev_yys = match (rev_xxs, rev_yys) with | [], _ | _, [] -> is | x :: rev_xs, y :: rev_ys -> let c = cmp x y in if Int.equal c 0 then inter_ (x :: is) rev_xs rev_ys else if c > 0 then inter_ is rev_xs rev_yys else inter_ is rev_xxs rev_ys in inter_ [] rev_xs rev_ys (** like fold, but apply [f_last] to the last element *) let rec fold_last l ~init ~f ~f_last = match l with | [] -> init | [last] -> f_last init last | hd :: tl -> fold_last tl ~init:(f init hd) ~f ~f_last let split_last_rev l = let result, _rev_prefix = fold_last l ~init:(None, []) ~f:(fun (_, rev_prefix) x -> (None, x :: rev_prefix)) ~f_last:(fun (_, rev_prefix) last -> (Some (last, rev_prefix), rev_prefix)) in result let append_no_duplicates (type a) ~(cmp : a -> a -> int) = (* roughly based on [Core.List.stable_dedup_staged] but also takes care of the append and takes into account the invariant that [list1] and [list2] do not contain duplicates individually *) let module Set = Set.Make (struct type t = a let compare = cmp (* we never calls these *) let t_of_sexp _ = assert false let sexp_of_t _ = assert false end) in Staged.stage (fun (list1 : a list) (list2 : a list) -> let set1 = Set.of_list list1 in let res_rev = List.fold_left list2 ~init:(List.rev list1) ~f:(fun res_rev x -> if Set.mem set1 x then res_rev else x :: res_rev ) in List.rev res_rev ) let merge_dedup l1 l2 ~compare = let rec loop acc l1 l2 = match (l1, l2) with | [], l2 -> List.rev_append acc l2 | l1, [] -> List.rev_append acc l1 | h1 :: t1, h2 :: t2 -> let cmp = compare h1 h2 in if Int.equal cmp 0 then loop (h1 :: acc) t1 t2 else if cmp < 0 then loop (h1 :: acc) t1 l2 else loop (h2 :: acc) l1 t2 in loop [] l1 l2 (* Remove when Base.List.drop perf is fixed *) let rec drop list index = match list with _ :: tl when index > 0 -> drop tl (index - 1) | _ -> list let opt_cons opt list = match opt with Some x -> x :: list | None -> list let remove_first = let rec aux list ~f rev_front = match list with | [] -> None | hd :: tl -> if f hd then Some (List.rev_append rev_front tl) else aux tl ~f (hd :: rev_front) in fun list ~f -> aux list ~f [] let pp_print_list ~max ?(pp_sep = Format.pp_print_cut) pp_v ppf = let rec aux n = function | [] -> () | v :: rest -> if n >= max then Format.fprintf ppf " ..." else ( pp_sep ppf () ; pp_v ppf v ; aux (n + 1) rest ) in function [] -> () | [v] -> pp_v ppf v | v :: rest -> pp_v ppf v ; aux 1 rest let fold2_result ~init ~f l1 l2 = List.fold2 l1 l2 ~init:(Ok init) ~f:(fun result x1 x2 -> Result.bind result ~f:(fun acc -> f acc x1 x2) )