(*
 * Copyright (c) 2018-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.
 *)

(** Qset - Set with (signed) rational multiplicity for each element *)

open Base

type ('elt, 'cmp) t

type ('elt, 'cmp) comparator =
  (module Comparator.S with type t = 'elt and type comparator_witness = 'cmp)

module M (Elt : sig
  type t
  type comparator_witness
end) : sig
  type nonrec t = (Elt.t, Elt.comparator_witness) t
end

module type Sexp_of_m = sig
  type t [@@deriving sexp_of]
end

module type M_of_sexp = sig
  type t [@@deriving of_sexp]

  include Comparator.S with type t := t
end

module type Compare_m = sig end
module type Hash_fold_m = Hasher.S

val sexp_of_m__t :
  (module Sexp_of_m with type t = 'elt) -> ('elt, 'cmp) t -> Sexp.t

val m__t_of_sexp :
     (module M_of_sexp with type t = 'elt and type comparator_witness = 'cmp)
  -> Sexp.t
  -> ('elt, 'cmp) t

val compare_m__t :
  (module Compare_m) -> ('elt, 'cmp) t -> ('elt, 'cmp) t -> int

val equal_m__t :
  (module Compare_m) -> ('elt, 'cmp) t -> ('elt, 'cmp) t -> bool

val hash_fold_m__t :
     (module Hash_fold_m with type t = 'elt)
  -> Hash.state
  -> ('elt, _) t
  -> Hash.state

val hash_m__t :
  (module Hash_fold_m with type t = 'elt) -> ('elt, _) t -> Hash.hash_value

val empty : ('elt, 'cmp) comparator -> ('elt, 'cmp) t
(** The empty multiset over the provided order. *)

val add : ('a, 'c) t -> 'a -> Q.t -> ('a, 'c) t
(** Add to multiplicity of single element. [O(log n)] *)

val remove : ('a, 'c) t -> 'a -> ('a, 'c) t
(** Set the multiplicity of an element to zero. [O(log n)] *)

val union : ('a, 'c) t -> ('a, 'c) t -> ('a, 'c) t
(** Sum multiplicities pointwise. [O(n + m)] *)

val length : _ t -> int
(** Number of elements with non-zero multiplicity. [O(1)]. *)

val count : ('a, _) t -> 'a -> Q.t
(** Multiplicity of an element. [O(log n)]. *)

val count_and_remove : ('a, 'c) t -> 'a -> (Q.t * ('a, 'c) t) option
(** Multiplicity of an element, and remove it. [O(log n)]. *)

val map : ('a, 'c) t -> f:('a -> Q.t -> 'a * Q.t) -> ('a, 'c) t
(** Map over the elements in ascending order. Preserves physical equality if
    [f] does. *)

val map_counts : ('a, 'c) t -> f:('a -> Q.t -> Q.t) -> ('a, 'c) t
(** Map over the multiplicities of the elements in ascending order. *)

val fold : ('a, _) t -> f:('a -> Q.t -> 's -> 's) -> init:'s -> 's
(** Fold over the elements in ascending order. *)

val fold_map :
     ('a, 'c) t
  -> f:('a -> Q.t -> 's -> 'a * Q.t * 's)
  -> init:'s
  -> ('a, 'c) t * 's
(** Folding map over the elements in ascending order. Preserves physical
    equality if [f] does. *)

val for_all : ('a, _) t -> f:('a -> Q.t -> bool) -> bool
(** Universal property test. [O(n)] but returns as soon as a violation is
    found, in ascending order. *)

val iter : ('a, _) t -> f:('a -> Q.t -> unit) -> unit
(** Iterate over the elements in ascending order. *)

val min_elt : ('a, _) t -> ('a * Q.t) option
(** Minimum element. *)

val min_elt_exn : ('a, _) t -> 'a * Q.t
(** Minimum element. *)

val to_list : ('a, _) t -> ('a * Q.t) list
(** Convert to a list of elements in ascending order. *)
