(* This file is free software, part of containers. See file "license" for more details. *)

(** Hash Tries

    Trie indexed by the hash of the keys, where the branching factor is fixed.
    The goal is to have a quite efficient functional structure with fast
    update and access {b if} the hash function is good.
    The trie is not binary, to improve cache locality and decrease depth.

    Preliminary benchmarks (see the "tbl" section of benchmarks) tend to show
    that this type is quite efficient for small data sets.

    {b status: unstable}

    @since 0.13
*)

type 'a iter = ('a -> unit) -> unit
type 'a gen = unit -> 'a option
type 'a printer = Format.formatter -> 'a -> unit
type 'a ktree = unit -> [ `Nil | `Node of 'a * 'a ktree list ]

(** {2 Transient Identifiers} *)
module Transient : sig
  type t
  (** Identifiers for transient modifications. A transient modification
      is uniquely identified by a [Transient.t]. Once [Transient.freeze r]
      is called, [r] cannot be used to modify the structure again. *)

  val create : unit -> t
  (** Create a new, active ID. *)

  val equal : t -> t -> bool
  (** Equality between IDs. *)

  val frozen : t -> bool
  (** [frozen i] returns [true] if [freeze i] was called before. In this case,
      the ID cannot be used for modifications again. *)

  val active : t -> bool
  (** [active i] is [not (frozen i)]. *)

  val freeze : t -> unit
  (** [freeze i] makes [i] unusable for new modifications. The values
      created with [i] will now be immutable. *)

  val with_ : (t -> 'a) -> 'a
  (** [with_ f] creates a transient ID [i], calls [f i],
      freezes the ID [i] and returns the result of [f i]. *)

  exception Frozen
  (** Raised when a frozen ID is used. *)
end

(** {2 Signature} *)
module type S = sig
  type key
  type 'a t

  val empty : 'a t
  val is_empty : _ t -> bool
  val singleton : key -> 'a -> 'a t
  val add : key -> 'a -> 'a t -> 'a t
  val mem : key -> _ t -> bool
  val get : key -> 'a t -> 'a option

  val get_exn : key -> 'a t -> 'a
  (** @raise Not_found if key not present. *)

  val remove : key -> 'a t -> 'a t
  (** Remove the key, if present. *)

  val update : key -> f:('a option -> 'a option) -> 'a t -> 'a t
  (** [update k ~f m] calls [f (Some v)] if [get k m = Some v], [f None]
      otherwise. Then, if [f] returns [Some v'] it binds [k] to [v'],
      if [f] returns [None] it removes [k]. *)

  val add_mut : id:Transient.t -> key -> 'a -> 'a t -> 'a t
  (** [add_mut ~id k v m] behaves like [add k v m], except it will mutate
      in place whenever possible. Changes done with an [id] might affect all
      versions of the structure obtained with the same [id] (but not
      other versions).
      @raise Transient.Frozen if [id] is frozen. *)

  val remove_mut : id:Transient.t -> key -> 'a t -> 'a t
  (** Same as {!remove}, but modifies in place whenever possible.
      @raise Transient.Frozen if [id] is frozen. *)

  val update_mut :
    id:Transient.t -> key -> f:('a option -> 'a option) -> 'a t -> 'a t
  (** Same as {!update} but with mutability.
      @raise Transient.Frozen if [id] is frozen. *)

  val cardinal : _ t -> int
  val choose : 'a t -> (key * 'a) option

  val choose_exn : 'a t -> key * 'a
  (** @raise Not_found if not pair was found. *)

  val iter : f:(key -> 'a -> unit) -> 'a t -> unit
  val fold : f:('b -> key -> 'a -> 'b) -> x:'b -> 'a t -> 'b

  (** {5 Conversions} *)

  val to_list : 'a t -> (key * 'a) list
  val add_list : 'a t -> (key * 'a) list -> 'a t

  val add_list_mut : id:Transient.t -> 'a t -> (key * 'a) list -> 'a t
  (** @raise Frozen if the ID is frozen. *)

  val of_list : (key * 'a) list -> 'a t
  val add_iter : 'a t -> (key * 'a) iter -> 'a t

  val add_iter_mut : id:Transient.t -> 'a t -> (key * 'a) iter -> 'a t
  (** @raise Frozen if the ID is frozen. *)

  val of_iter : (key * 'a) iter -> 'a t
  val to_iter : 'a t -> (key * 'a) iter
  val add_gen : 'a t -> (key * 'a) gen -> 'a t

  val add_gen_mut : id:Transient.t -> 'a t -> (key * 'a) gen -> 'a t
  (** @raise Frozen if the ID is frozen. *)

  val of_gen : (key * 'a) gen -> 'a t
  val to_gen : 'a t -> (key * 'a) gen

  (** {5 IO} *)

  val pp : key printer -> 'a printer -> 'a t printer
  (** Renamed from [val print].
      @since 2.0 *)

  val as_tree : 'a t -> [ `L of int * (key * 'a) list | `N ] ktree
  (** For debugging purpose: explore the structure of the tree,
      with [`L (h,l)] being a leaf (with shared hash [h])
      and [`N] an inner node. *)
end

(** {2 Type for keys} *)
module type KEY = sig
  type t

  val equal : t -> t -> bool
  val hash : t -> int
end

(** {2 Functors} *)
module Make (K : KEY) : S with type key = K.t

(**/**)

val popcount : int64 -> int

(**/**)
