open Util
open Constants

exception Null_contribution

type t =
{
  contracted_shell_pairs : ContractedShellPair.t list;
  atomic_shell_a         : AtomicShell.t;
  atomic_shell_b         : AtomicShell.t;
}


module Am  = AngularMomentum
module As  = AtomicShell
module Co  = Coordinate
module Cs  = ContractedShell
module Csp = ContractedShellPair

(** Creates an atomic shell pair : an array of pairs of contracted shells.
*)
let make ?(cutoff=Constants.epsilon) atomic_shell_a atomic_shell_b =

  let l_a = Array.to_list (As.contracted_shells atomic_shell_a)
  and l_b = Array.to_list (As.contracted_shells atomic_shell_b)
  in

  let contracted_shell_pairs =
    List.concat_map (fun s_a -> 
      List.map (fun s_b ->
        if Cs.index s_b <= Cs.index s_a then 
          Csp.make ~cutoff s_a s_b
        else
          None
      ) l_b
    ) l_a
    |> list_some
  in
  match contracted_shell_pairs with
  | [] -> None
  | _  -> Some { atomic_shell_a ; atomic_shell_b ; contracted_shell_pairs }


let atomic_shell_a  x = x.atomic_shell_a
let atomic_shell_b  x = x.atomic_shell_b
let contracted_shell_pairs x = x.contracted_shell_pairs

let monocentric x = Csp.monocentric @@ List.hd x.contracted_shell_pairs

let a_minus_b x = Csp.a_minus_b @@ List.hd x.contracted_shell_pairs

let a_minus_b_sq x = Csp.a_minus_b_sq @@ List.hd x.contracted_shell_pairs

let ang_mom x = Csp.ang_mom @@ List.hd x.contracted_shell_pairs

let norm_scales x = Csp.norm_scales @@ List.hd x.contracted_shell_pairs

(** The array of all shell pairs with their correspondance in the list
    of contracted shells.
*)
let of_atomic_shell_array ?(cutoff=Constants.epsilon) basis =
  Array.mapi (fun i shell_a ->
    Array.mapi (fun j shell_b -> 
      make ~cutoff shell_a shell_b)
    (Array.sub basis 0 (i+1)) 
  ) basis



