(** Fixed-length, mutable vector of elements with O(1) [get] and [set] operations. *)

open! Import

type 'a t = 'a array [@@deriving_inline compare, globalize, sexp, sexp_grammar]

include Ppx_compare_lib.Comparable.S1 with type 'a t := 'a t

val globalize : (('a[@ocaml.local]) -> 'a) -> ('a t[@ocaml.local]) -> 'a t

include Sexplib0.Sexpable.S1 with type 'a t := 'a t

val t_sexp_grammar : 'a Sexplib0.Sexp_grammar.t -> 'a t Sexplib0.Sexp_grammar.t

[@@@end]

include Binary_searchable.S1 with type 'a t := 'a t
include Indexed_container.S1_with_creators with type 'a t := 'a t
include Invariant.S1 with type 'a t := 'a t

(** Maximum length of a normal array.  The maximum length of a float array is
    [max_length/2] on 32-bit machines and [max_length] on 64-bit machines. *)
val max_length : int

(*_ Declared as externals so that the compiler skips the caml_apply_X wrapping even when
  compiling without cross library inlining. *)

external length : ('a t[@local_opt]) -> int = "%array_length"

(** [Array.get a n] returns the element number [n] of array [a].
    The first element has number 0.
    The last element has number [Array.length a - 1].
    You can also write [a.(n)] instead of [Array.get a n].

    Raise [Invalid_argument "index out of bounds"]
    if [n] is outside the range 0 to [(Array.length a - 1)]. *)
external get : ('a t[@local_opt]) -> (int[@local_opt]) -> 'a = "%array_safe_get"

(** [Array.set a n x] modifies array [a] in place, replacing
    element number [n] with [x].
    You can also write [a.(n) <- x] instead of [Array.set a n x].

    Raise [Invalid_argument "index out of bounds"]
    if [n] is outside the range 0 to [Array.length a - 1]. *)
external set : ('a t[@local_opt]) -> (int[@local_opt]) -> 'a -> unit = "%array_safe_set"

(** Unsafe version of [get].  Can cause arbitrary behavior when used for an out-of-bounds
    array access. *)
external unsafe_get : ('a t[@local_opt]) -> (int[@local_opt]) -> 'a = "%array_unsafe_get"

(** Unsafe version of [set].  Can cause arbitrary behavior when used for an out-of-bounds
    array access. *)
external unsafe_set
  :  ('a t[@local_opt])
  -> (int[@local_opt])
  -> 'a
  -> unit
  = "%array_unsafe_set"

(** [create ~len x] creates an array of length [len] with the value [x] populated in
    each element. *)
val create : len:int -> 'a -> 'a t

(** [create_local ~len x] is like [create]. It allocates the array on the local stack. The
    array's elements are still global. *)
val create_local : len:int -> 'a -> ('a t[@local])

(** [create_float_uninitialized ~len] creates a float array of length [len] with
    uninitialized elements -- that is, they may contain arbitrary, nondeterministic float
    values. This can be significantly faster than using [create], when unboxed float array
    representations are enabled. *)
val create_float_uninitialized : len:int -> float t

(** [Array.make_matrix dimx dimy e] returns a two-dimensional array (an array of arrays)
    with first dimension [dimx] and second dimension [dimy]. All the elements of this new
    matrix are initially physically equal to [e].  The element ([x,y]) of a matrix [m] is
    accessed with the notation [m.(x).(y)].

    Raise [Invalid_argument] if [dimx] or [dimy] is negative or greater than
    [Array.max_length].

    If the value of [e] is a floating-point number, then the maximum size is only
    [Array.max_length / 2]. *)
val make_matrix : dimx:int -> dimy:int -> 'a -> 'a t t

(** [Array.copy_matrix t] returns a fresh copy of the array of arrays [t].  This is
    typically used when [t] is a matrix created by [Array.make_matrix]. *)
val copy_matrix : 'a t t -> 'a t t

(** Like [Array.append], but concatenates a list of arrays. *)
val concat : 'a t list -> 'a t

(** [Array.copy a] returns a copy of [a], that is, a fresh array
    containing the same elements as [a]. *)
val copy : 'a t -> 'a t

(** [Array.fill a ofs len x] modifies the array [a] in place, storing [x] in elements
    number [ofs] to [ofs + len - 1].

    Raise [Invalid_argument "Array.fill"] if [ofs] and [len] do not designate a valid
    subarray of [a]. *)
val fill : 'a t -> pos:int -> len:int -> 'a -> unit

(** [Array.blit v1 o1 v2 o2 len] copies [len] elements from array [v1], starting at
    element number [o1], to array [v2], starting at element number [o2].  It works
    correctly even if [v1] and [v2] are the same array, and the source and destination
    chunks overlap.

    Raise [Invalid_argument "Array.blit"] if [o1] and [len] do not designate a valid
    subarray of [v1], or if [o2] and [len] do not designate a valid subarray of [v2].

    [int_blit] and [float_blit] provide fast bound-checked blits for immediate
    data types.  The unsafe versions do not bound-check the arguments. *)
include Blit.S1 with type 'a t := 'a t

(** [folding_map] is a version of [map] that threads an accumulator through calls to
    [f]. *)
val folding_map : 'a t -> init:'acc -> f:(('acc -> 'a -> 'acc * 'b)[@local]) -> 'b t

val folding_mapi
  :  'a t
  -> init:'acc
  -> f:((int -> 'acc -> 'a -> 'acc * 'b)[@local])
  -> 'b t

(** [Array.fold_map] is a combination of [Array.fold] and [Array.map] that threads an
    accumulator through calls to [f]. *)
val fold_map : 'a t -> init:'acc -> f:(('acc -> 'a -> 'acc * 'b)[@local]) -> 'acc * 'b t

val fold_mapi
  :  'a t
  -> init:'acc
  -> f:((int -> 'acc -> 'a -> 'acc * 'b)[@local])
  -> 'acc * 'b t

(** [Array.fold_right f a ~init] computes [f a.(0) (f a.(1) ( ... (f a.(n-1) init) ...))],
    where [n] is the length of the array [a]. *)
val fold_right : 'a t -> f:(('a -> 'acc -> 'acc)[@local]) -> init:'acc -> 'acc


(** All sort functions in this module sort in increasing order by default.  *)

(** [sort] uses constant heap space. [stable_sort] uses linear heap space.

    To sort only part of the array, specify [pos] to be the index to start sorting from
    and [len] indicating how many elements to sort. *)
val sort : ?pos:int -> ?len:int -> 'a t -> compare:(('a -> 'a -> int)[@local]) -> unit

val stable_sort : 'a t -> compare:('a -> 'a -> int) -> unit
val is_sorted : 'a t -> compare:(('a -> 'a -> int)[@local]) -> bool

(** [is_sorted_strictly xs ~compare] iff [is_sorted xs ~compare] and no two
    consecutive elements in [xs] are equal according to [compare]. *)
val is_sorted_strictly : 'a t -> compare:(('a -> 'a -> int)[@local]) -> bool

(** Merges two arrays: assuming that [a1] and [a2] are sorted according to the comparison
    function [compare], [merge a1 a2 ~compare] will return a sorted array containing all
    the elements of [a1] and [a2]. If several elements compare equal, the elements of [a1]
    will be before the elements of [a2]. *)
val merge : 'a t -> 'a t -> compare:(('a -> 'a -> int)[@local]) -> 'a t

val partitioni_tf : 'a t -> f:((int -> 'a -> bool)[@local]) -> 'a t * 'a t
val cartesian_product : 'a t -> 'b t -> ('a * 'b) t

(** [transpose] in the sense of a matrix transpose.  It returns [None] if the arrays are
    not all the same length. *)
val transpose : 'a t t -> 'a t t option

val transpose_exn : 'a t t -> 'a t t

(** [filter_opt array] returns a new array where [None] entries are omitted and [Some x]
    entries are replaced with [x]. Note that this changes the index at which elements
    will appear. *)
val filter_opt : 'a option t -> 'a t

(** Functions with the 2 suffix raise an exception if the lengths of the two given arrays
    aren't the same. *)

val iter2_exn : 'a t -> 'b t -> f:(('a -> 'b -> unit)[@local]) -> unit
val map2_exn : 'a t -> 'b t -> f:(('a -> 'b -> 'c)[@local]) -> 'c t

val fold2_exn
  :  'a t
  -> 'b t
  -> init:'acc
  -> f:(('acc -> 'a -> 'b -> 'acc)[@local])
  -> 'acc

(** [for_all2_exn t1 t2 ~f] fails if [length t1 <> length t2]. *)
val for_all2_exn : 'a t -> 'b t -> f:(('a -> 'b -> bool)[@local]) -> bool

(** [exists2_exn t1 t2 ~f] fails if [length t1 <> length t2]. *)
val exists2_exn : 'a t -> 'b t -> f:(('a -> 'b -> bool)[@local]) -> bool

(** [swap arr i j] swaps the value at index [i] with that at index [j]. *)
val swap : 'a t -> int -> int -> unit

(** [rev_inplace t] reverses [t] in place. *)
val rev_inplace : 'a t -> unit

(** [rev t] returns a reversed copy of [t] *)
val rev : 'a t -> 'a t

(** [of_list_rev l] converts from list then reverses in place. *)
val of_list_rev : 'a list -> 'a t

(** [of_list_map l ~f] is the same as [of_list (List.map l ~f)]. *)
val of_list_map : 'a list -> f:(('a -> 'b)[@local]) -> 'b t

(** [of_list_mapi l ~f] is the same as [of_list (List.mapi l ~f)]. *)
val of_list_mapi : 'a list -> f:((int -> 'a -> 'b)[@local]) -> 'b t

(** [of_list_rev_map l ~f] is the same as [of_list (List.rev_map l ~f)]. *)
val of_list_rev_map : 'a list -> f:(('a -> 'b)[@local]) -> 'b t

(** [of_list_rev_mapi l ~f] is the same as [of_list (List.rev_mapi l ~f)]. *)
val of_list_rev_mapi : 'a list -> f:((int -> 'a -> 'b)[@local]) -> 'b t

(** Modifies an array in place, applying [f] to every element of the array *)
val map_inplace : 'a t -> f:(('a -> 'a)[@local]) -> unit


(** [find_exn f t] returns the first [a] in [t] for which [f t.(i)] is true.  It raises
    [Stdlib.Not_found] or [Not_found_s] if there is no such [a]. *)
val find_exn : 'a t -> f:(('a -> bool)[@local]) -> 'a

(** Returns the first evaluation of [f] that returns [Some].  Raises [Stdlib.Not_found] or
    [Not_found_s] if [f] always returns [None].  *)
val find_map_exn : 'a t -> f:(('a -> 'b option)[@local]) -> 'b

(** [findi_exn t f] returns the first index [i] of [t] for which [f i t.(i)] is true.  It
    raises [Stdlib.Not_found] or [Not_found_s] if there is no such element. *)
val findi_exn : 'a t -> f:((int -> 'a -> bool)[@local]) -> int * 'a

(** [find_mapi_exn] is like [find_map_exn] but passes the index as an argument. *)
val find_mapi_exn : 'a t -> f:((int -> 'a -> 'b option)[@local]) -> 'b

(** [find_consecutive_duplicate t ~equal] returns the first pair of consecutive elements
    [(a1, a2)] in [t] such that [equal a1 a2].  They are returned in the same order as
    they appear in [t]. *)
val find_consecutive_duplicate
  :  'a t
  -> equal:(('a -> 'a -> bool)[@local])
  -> ('a * 'a) option

(** [reduce f [a1; ...; an]] is [Some (f (... (f (f a1 a2) a3) ...) an)].  Returns [None]
    on the empty array. *)
val reduce : 'a t -> f:(('a -> 'a -> 'a)[@local]) -> 'a option

val reduce_exn : 'a t -> f:(('a -> 'a -> 'a)[@local]) -> 'a

(** [permute ?random_state ?pos ?len t] randomly permutes [t] in place.

    To permute only part of the array, specify [pos] to be the index to start permuting
    from and [len] indicating how many elements to permute.

    [permute] side-effects [random_state] by repeated calls to [Random.State.int].  If
    [random_state] is not supplied, [permute] uses [Random.State.default]. *)
val permute : ?random_state:Random.State.t -> ?pos:int -> ?len:int -> 'a t -> unit

(** [random_element ?random_state t] is [None] if [t] is empty, else it is [Some x] for
    some [x] chosen uniformly at random from [t].

    [random_element] side-effects [random_state] by calling [Random.State.int]. If
    [random_state] is not supplied, [random_element] uses [Random.State.default]. *)
val random_element : ?random_state:Random.State.t -> 'a t -> 'a option

val random_element_exn : ?random_state:Random.State.t -> 'a t -> 'a

(** [zip] is like [List.zip], but for arrays. *)
val zip : 'a t -> 'b t -> ('a * 'b) t option

val zip_exn : 'a t -> 'b t -> ('a * 'b) t

(** [unzip] is like [List.unzip], but for arrays. *)
val unzip : ('a * 'b) t -> 'a t * 'b t

(** [sorted_copy ar compare] returns a shallow copy of [ar] that is sorted. Similar to
    List.sort *)
val sorted_copy : 'a t -> compare:(('a -> 'a -> int)[@local]) -> 'a t

val last : 'a t -> 'a
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool


(** The input array is copied internally so that future modifications of it do not change
    the sequence. *)
val to_sequence : 'a t -> 'a Sequence.t

(** The input array is shared with the sequence and modifications of it will result in
    modification of the sequence. *)
val to_sequence_mutable : 'a t -> 'a Sequence.t

(**/**)

(*_ See the Jane Street Style Guide for an explanation of [Private] submodules:

  https://opensource.janestreet.com/standards/#private-submodules *)
module Private : sig
  module Sort : sig
    module type Sort = sig
      val sort
        :  'a t
        -> compare:(('a -> 'a -> int)[@local])
        -> left:int
        -> right:int
        -> unit
    end

    module Insertion_sort : Sort
    module Heap_sort : Sort

    module Intro_sort : sig
      include Sort

      val five_element_sort
        :  'a t
        -> compare:(('a -> 'a -> int)[@local])
        -> int
        -> int
        -> int
        -> int
        -> int
        -> unit
    end
  end

  module Sorter (S : sig
      type 'a t

      val get : 'a t -> int -> 'a
      val set : 'a t -> int -> 'a -> unit
      val length : 'a t -> int
    end) : sig
    val sort
      :  ?pos:int
      -> ?len:int
      -> 'a S.t
      -> compare:(('a -> 'a -> int)[@local])
      -> unit
  end
end
