(** Module for handling distributed parallelism *)

val size : int
(** Number of distributed processes. *)

val rank : Mpi.rank
(** Rank of the current distributed processe. *)

val master : bool
(** True if [rank = 0]. *)

val barrier : unit -> unit
(** Wait for all processes to reach this point. *)

val broadcast : 'a lazy_t -> 'a
(** Broadcasts data to all processes. *)

val broadcast_int : int -> int
(** Broadcasts an [int] to all processes. *)

val broadcast_float : float -> float
(** Broadcasts a [float] to all processes. *)

val broadcast_int_array : int array -> int array
(** Broadcasts an [int array] to all processes. *)

val broadcast_float_array : float array -> float array
(** Broadcasts a [float array] to all processes. *)

val broadcast_vec : Lacaml.D.vec -> Lacaml.D.vec
(** Broadcasts a Lacaml vector to all processes. *)


(** {5 Intra-node  operations} *)
module Node : sig
  (** This module contains parallel primitives among processes
      within the same compute node.
    *)

  val name : string
  (** Name of the current host *)

  val comm : Mpi.communicator 
  (** MPI Communicator containing the processes of the current node *)

  val rank : Mpi.rank
  (** Rank of the current process in the node *)

  val master : bool
  (** If true, master process of the node *)

  val broadcast : 'a lazy_t -> 'a
  (** Broadcasts data to all the processes of the current node. *)

  val barrier : unit -> unit
  (** Wait for all processes among the node to reach this point. *)
end


(** {5 Inter-node  operations} *)
module InterNode : sig

  val comm : Mpi.communicator 
  (** MPI Communicator among the master processes of the each node *)

  val rank : Mpi.rank
  (** Rank of the current process in the inter-node communicator *)

  val master : bool
  (** If true, master process of the inter-node communicator *)

  val broadcast : 'a lazy_t -> 'a
  (** Broadcasts data to all the processes of the inter-node communicator. *)

  val barrier : unit -> unit
  (** Wait for all processes among the node to reach this point. *)
end

(** {5 Vector operations} *)
module Vec : sig

  type t = private
  { 
    global_first : int ; (* Lower  index in the global array *)
    global_last  : int ; (* Higher index in the global array *)
    local_first  : int ; (* Lower  index in the local array *)
    local_last   : int ; (* Higher index in the local array *)
    data         : Lacaml.D.vec ; (* Lacaml vector containing the data *)
  }

  val pp : Format.formatter -> t -> unit

  (** {6 Creation/conversion of vectors} *)

  val create : int -> t
  (** [create n] @return a distributed vector with [n] rows (not initialized). *)

  val make : int -> float -> t
  (** [make n x] @return a distributed vector with [n] rows initialized with value [x]. *)

  val make0 : int -> t
  (** [make0 n x] @return a distributed vector with [n] rows initialized with the zero
      element. *)

  val init : int -> (int -> float) -> t
  (** [init n f] @return a distributed vector containing [n] elements, where
      each element at position [i] is initialized by the result of calling [f i].  *)

  val of_array : float array -> t
  (** [of_array ar] @return a distributed vector initialized from array [ar]. *)

  val to_array : t -> float array
  (** [to_array v] @return an array initialized from vector [v]. *)

  val of_vec : Lacaml.D.vec -> t
  (** [of_vec vec] @return a distributed vector initialized from Lacaml vector [vec]. *)

  val to_vec : t -> Lacaml.D.vec 
  (** [to_vec v] @return a Lacaml vector initialized from vector [v]. *)


  (** {6 Accessors } *)

  val dim : t -> int
  (** [dim v] @return the dimension of the vector [v]. *)

  val global_first : t -> int
  (** [global_first v] @return the index of the first element of [v]. *)

  val global_last  : t -> int
  (** [global_last v] @return the index of the last element of [v]. *)

  val local_first : t -> int
  (** [local_first v] @return the index of the first element of the local piece of [v]. *)

  val global_last  : t -> int
  (** [local_last v] @return the index of the last element of the local piece of [v]. *)

  val data : t -> Lacaml.D.vec
  (** [data v] @return the local Lacaml vector in which the piece of the vector [v] is stored. *)

end


(*
module Mat : sig

  type t =
  { 
    global_first_row   : int ; (* Lower  row index in the global array *)
    global_last_row     : int ; (* Higher row index in the global array *)
    global_first_col   : int ; (* Lower  column index in the global array *)
    global_last_col     : int ; (* Higher column index in the global array *)
    local_first_row    : int ; (* Lower  row index in the local array *)
    local_last_row      : int ; (* Higher row index in the local array *)
    local_first_col    : int ; (* Lower  column index in the local array *)
    local_last_col      : int ; (* Higher column index in the local array *)
    data               : Lacaml.D.mat ; (* Lacaml matrix containing the data *)
  }

end

val gemm : Mat.t -> Mat.t -> Mat.t
(* Distributed matrix-matrix product. The result is a distributed matrix. *)
*)

val dot : Vec.t -> Vec.t-> float
(* Dot product between distributed vectors. *)



