type t = {
  charge            : Charge.t;
  electrons         : Electrons.t;
  nuclei            : Nuclei.t;
  basis             : Basis.t;
  ao_basis          : AOBasis.t;
  nuclear_repulsion : float;
}

let nuclei            t = t.nuclei
let charge            t = t.charge
let electrons         t = t.electrons
let basis             t = t.basis
let ao_basis          t = t.ao_basis
let nuclear_repulsion t = t.nuclear_repulsion
let range_separation  t = Basis.range_separation t.basis 
let f12               t = Basis.f12 t.basis

let make ?cartesian:(cartesian=false)
      ?multiplicity:(multiplicity=1)
      ?charge:(charge=0)
      ~nuclei
      basis
  =

  (* Tune Garbage Collector *)
  let gc = Gc.get () in
  Gc.set { gc with space_overhead = 1000  };

  let electrons =
    Electrons.make ~multiplicity ~charge nuclei
  in

  let charge = 
    Charge.(Nuclei.charge nuclei + Electrons.charge electrons)
  in

  let ao_basis =
    AOBasis.make ~basis ~cartesian nuclei
  in

  let nuclear_repulsion =
    Nuclei.repulsion nuclei
  in

  {
    charge ; basis ; nuclei ; electrons ; ao_basis ; 
    nuclear_repulsion ; 
  }


let of_filenames ?(cartesian=false) ?(multiplicity=1) ?(charge=0) ?f12 ?range_separation ~nuclei ?(aux_basis_filenames=[]) basis_filename =
  let nuclei =
    Nuclei.of_filename nuclei
  in
  let basis =
    Basis.of_nuclei_and_basis_filenames ?f12 ?range_separation ~nuclei (basis_filename :: aux_basis_filenames)
  in

  lazy (make ~cartesian ~charge ~multiplicity ~nuclei basis)
  |> Parallel.broadcast

