(*
 * ExtList - additional and modified functions for lists.
 * Copyright (C) 2003 Brian Hurt
 * Copyright (C) 2003 Nicolas Cannasse
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version,
 * with the special exception on linking described in file LICENSE.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *)

(** Additional and modified functions for lists.

  The OCaml standard library provides a module for list functions.
  This ExtList module can be used to override the List module or
  as a standalone module. It provides new functions and modify
  the behavior of some other ones (in particular all functions
  are now {b tail-recursive}).
*)

module List :
    sig

  (** {6 New functions} *)

  val init : int -> (int -> 'a) -> 'a list
  (** Similar to [Array.init], [init n f] returns the list containing
   the results of (f 0),(f 1).... (f (n-1)).
   Raise [Invalid_arg "ExtList.init"] if n < 0.
   Uses stdlib implementation in OCaml 4.06.0 and newer.
  *)

  val make : int -> 'a -> 'a list
    (** Similar to [String.make], [make n x] returns a
      * list containing [n] elements [x].
          *)

  val first : 'a list -> 'a
  (** Returns the first element of the list, or raise [Empty_list] if
   the list is empty (similar to [hd]). *)

  val last : 'a list -> 'a
  (** Returns the last element of the list, or raise [Empty_list] if
   the list is empty. This function takes linear time. *)

  val iteri : (int -> 'a -> unit) -> 'a list -> unit
  (** [iteri f l] will call [(f 0 a0);(f 1 a1) ... (f n an)] where
   [a0..an] are the elements of the list [l]. *)

  val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list
  (** [mapi f l] will build the list containing
   [(f 0 a0);(f 1 a1) ... (f n an)] where [a0..an] are the elements of
   the list [l]. *)

  val rfind : ('a -> bool) -> 'a list -> 'a
  (** [rfind p l] returns the last element [x] of [l] such as [p x] returns
   [true] or raises [Not_found] if such element as not been found. *)

  val find_exc : ('a -> bool) -> exn -> 'a list -> 'a
  (** [find_exc p e l] returns the first element of [l] such as [p x]
   returns [true] or raises [e] if such element as not been found. *)

  val findi : (int -> 'a -> bool) -> 'a list -> (int * 'a)
  (** [findi p e l] returns the first element [ai] of [l] along with its
   index [i] such that [p i ai] is true, or raises [Not_found] if no
   such element has been found. *)

  val unique : ?cmp:('a -> 'a -> bool) -> 'a list -> 'a list
  (** [unique cmp l] returns the list [l] without any duplicate element.
   Default comparator ( = ) is used if no comparison function specified. *)

  val filter_map : ('a -> 'b option) -> 'a list -> 'b list
  (** [filter_map f l] call [(f a0) (f a1).... (f an)] where [a0..an] are
   the elements of [l]. It returns the list of elements [bi] such as
   [f ai = Some bi] (when [f] returns [None], the corresponding element of
   [l] is discarded). *)

  val find_map : ('a -> 'b option) -> 'a list -> 'b
  (** [find_map pred list] finds the first element of [list] for which
      [pred element] returns [Some r].  It returns [r] immediately
      once found or raises [Not_found] if no element matches the
      predicate.  See also {!filter_map}. *)

  val split_nth : int -> 'a list -> 'a list * 'a list
  (** [split_nth n l] returns two lists [l1] and [l2], [l1] containing the
   first [n] elements of [l] and [l2] the others. Raise [Invalid_index] if
   [n] is outside of [l] size bounds. *)

  val remove : 'a list -> 'a -> 'a list
  (** [remove l x] returns the list [l] without the first element [x] found
   or returns  [l] if no element is equal to [x]. Elements are compared
   using ( = ). *)

  val remove_if : ('a -> bool) -> 'a list -> 'a list
  (** [remove_if cmp l] is similar to [remove], but with [cmp] used
   instead of ( = ). *)

  val remove_all : 'a list -> 'a -> 'a list
  (** [remove_all l x] is similar to [remove] but removes all elements that
   are equal to [x] and not only the first one. *)

  val take : int -> 'a list -> 'a list
  (** [take n l] returns up to the [n] first elements from list [l], if
   available. *)

  val drop : int -> 'a list -> 'a list
  (** [drop n l] returns [l] without the first [n] elements, or the empty
   list if [l] have less than [n] elements. *)

  val takewhile : ('a -> bool) -> 'a list -> 'a list
    (** [takewhile f xs] returns the first elements of list [xs]
        which satisfy the predicate [f]. *)

  val dropwhile : ('a -> bool) -> 'a list -> 'a list
    (** [dropwhile f xs] returns the list [xs] with the first
        elements satisfying the predicate [f] dropped. *)

  (** {6 Enum functions} *)

  (** Enumerations are important in ExtLib, they are a good way to work with
   abstract enumeration of elements, regardless if they are located in a list,
   an array, or a file. *)

  val enum : 'a list -> 'a Enum.t
  (** Returns an enumeration of the elements of a list. *)

  val of_enum : 'a Enum.t -> 'a list
  (** Build a list from an enumeration. *)

  (** {6 Compatibility functions} *)

  val cons : 'a -> 'a list -> 'a list

  val assoc_opt : 'a -> ('a * 'b) list -> 'b option
  val assq_opt : 'a -> ('a * 'b) list -> 'b option

  val find_opt : ('a -> bool) -> 'a list -> 'a option
  val nth_opt : 'a list -> int -> 'a option

  val compare_lengths : 'a list -> 'b list -> int
  val compare_length_with : 'a list -> int -> int

  (** {6 Modified functions} *)

  (** Some minor modifications have been made to the specification of some
   functions, especially concerning exceptions raised. *)

  val hd : 'a list -> 'a
  (** Returns the first element of the list or raise [Empty_list] if the
   list is empty. *)

  val tl : 'a list -> 'a list
  (** Returns the list without its first elements or raise [Empty_list] if
   the list is empty. *)

  val nth : 'a list -> int -> 'a
  (** [nth l n] returns the n-th element of the list [l] or raise
   [Invalid_index] is the index is outside of [l] bounds. *)

  val sort : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list
  (** Sort the list using optional comparator (by default [compare]). *)

  (** The following functions have been improved so all of them are
   tail-recursive. They have also been modified so they no longer
   raise [Invalid_arg] but [Different_list_size] when used on two
   lists having a different number of elements. *)

  val map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list
  val rev_map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list
  val iter2 : ('a -> 'b -> unit) -> 'a list -> 'b list -> unit
  val fold_left2 : ('a -> 'b -> 'c -> 'a) -> 'a -> 'b list -> 'c list -> 'a
  val fold_right2 : ('a -> 'b -> 'c -> 'c) -> 'a list -> 'b list -> 'c -> 'c
  val for_all2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool
  val exists2 : ('a -> 'b -> bool) -> 'a list -> 'b list -> bool
  val combine : 'a list -> 'b list -> ('a * 'b) list


  (** {6 Improved functions} *)

  (** The following functions have the same behavior as the [List]
    module ones but are tail-recursive. That means they will not
    cause a [Stack_overflow] when used on very long list.

    The implementation might be a little more slow in bytecode,
    but compiling in native code will not affect performances. *)

  val map : ('a -> 'b) -> 'a list -> 'b list
  val append : 'a list -> 'a list -> 'a list
  val flatten : 'a list list -> 'a list
  val concat : 'a list list -> 'a list
  val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
  val remove_assoc : 'a -> ('a * 'b) list -> ('a * 'b) list
  val remove_assq : 'a -> ('a * 'b) list -> ('a * 'b) list
  val split : ('a * 'b) list -> 'a list * 'b list

  (** The following functions were already tail-recursive in the [List]
    module but were using [List.rev] calls. The new implementations
    have better performances. *)

  val filter : ('a -> bool) -> 'a list -> 'a list
  val find_all : ('a -> bool) -> 'a list -> 'a list
  val partition : ('a -> bool) -> 'a list -> 'a list * 'a list

  (** {6 Older functions} *)

  (** These functions are already part of the Ocaml standard library
    and have not been modified. Please refer to the Ocaml Manual for
    documentation. *)

  val length : 'a list -> int
  val rev_append : 'a list -> 'a list -> 'a list
  val rev : 'a list -> 'a list
  val rev_map : ('a -> 'b) -> 'a list -> 'b list
  val iter : ('a -> unit) -> 'a list -> unit
  val fold_left : ('b -> 'a -> 'b) -> 'b -> 'a list -> 'b
  val for_all : ('a -> bool) -> 'a list -> bool
  val exists : ('a -> bool) -> 'a list -> bool
  val find : ('a -> bool) -> 'a list -> 'a

  val mem : 'a -> 'a list -> bool
  val memq : 'a -> 'a list -> bool
  val assoc : 'a -> ('a * 'b) list -> 'b
  val assq : 'a -> ('a * 'b) list -> 'b
  val mem_assoc : 'a -> ('a * 'b) list -> bool
  val mem_assq : 'a -> ('a * 'b) list -> bool


  val stable_sort : ('a -> 'a -> int) -> 'a list -> 'a list
  val fast_sort : ('a -> 'a -> int) -> 'a list -> 'a list
  val merge : ('a -> 'a -> int) -> 'a list -> 'a list -> 'a list

#if OCAML >= 402
  val sort_uniq : ('a -> 'a -> int) -> 'a list -> 'a list
  (** Same as {!List.sort}, but also remove duplicates.
      @since 4.02.0 *)
#endif

#if OCAML >= 407
  (** [*_seq] functions were introduced in OCaml 4.07.0, and are _not_ implemented in extlib for older OCaml versions *)
  val to_seq : 'a list -> 'a Seq.t
  val of_seq : 'a Seq.t -> 'a list
#endif

  (** {6 Exceptions} *)

  exception Empty_list
  (** [Empty_list] is raised when an operation applied on an empty list
    is invalid : [hd] for example. *)

  exception Invalid_index of int
  (** [Invalid_index] is raised when an indexed access on a list is
    out of list bounds. *)

  exception Different_list_size of string
  (** [Different_list_size] is raised when applying functions such as
    [iter2] on two lists having different size. *)


end

val ( @ ) : 'a list -> 'a list -> 'a list
(** the new implementation for ( @ ) operator, see [List.append]. *)
