
let png_image = print_endline ;;


open Lacaml.D


let basis_filename  = "/home/scemama/qp2/data/basis/6-31g" 
let aux_basis_filename = "/home/scemama/qp2/data/basis/cc-pvdz" 
let nuclei = Nuclei.of_zmt_string "li" 
let frozen_core = false
let multiplicity = 2
let state = 1

let f12 = F12factor.gaussian_geminal 1.0 
let basis =  Basis.of_nuclei_and_basis_filenames ~f12 ~nuclei [basis_filename] 
let charge = 0 


let simulation =
    Simulation.make 
        ~charge ~multiplicity ~nuclei
        ~cartesian:true
        basis


let n_elec_alfa, n_elec_beta, n_elec = 
  let e =  Simulation.electrons simulation in
  Electrons.(n_alfa e, n_beta e, n_elec e)

let hf =  HartreeFock.make ~guess:`Hcore ~max_scf:1 simulation  ;;

let mo_basis = MOBasis.of_hartree_fock hf

let f12 = Util.of_some @@ Simulation.f12 simulation 

let mo_num = MOBasis.size mo_basis 

let pp_spindet = Spindeterminant.pp mo_num

let pp_det = Determinant.pp mo_num

;;



let simulation_aux = 
    let charge        = Charge.to_int @@ Simulation.charge simulation 
    and multiplicity  = Electrons.multiplicity @@ Simulation.electrons simulation
    and nuclei        = Simulation.nuclei simulation
    in
    let general_basis = 
      Basis.general_basis @@ Simulation.basis simulation
    in
    GeneralBasis.combine [
      general_basis ; GeneralBasis.read aux_basis_filename
    ]
    |> Basis.of_nuclei_and_general_basis ~f12 nuclei
    |> Simulation.make ~charge ~multiplicity ~nuclei 


let aux_basis = 
  MOBasis.of_mo_basis simulation_aux mo_basis

let aux_num = 
  MOBasis.size aux_basis


let () = ignore @@ MOBasis.f12_ints   aux_basis
let () = ignore @@ MOBasis.two_e_ints aux_basis

let cancel_singles = false 

let mos_cabs = 
  Util.list_range (mo_num+1) aux_num
  
let mos_abs = 
  Util.list_range 1 aux_num
  
let mos_in = 
  Util.list_range 1 mo_num

let mos_a k =
  Determinant.alfa k
  |> Spindeterminant.to_list
  
let mos_b k =
  Determinant.beta k
  |> Spindeterminant.to_list
  

let h_one =
  let h = 
    MOBasis.one_e_ints aux_basis
  in fun i j _ -> h.{i,j}
  
let h_two = 
  let two_e_ints = MOBasis.two_e_ints aux_basis in
  let h2 i j k l (s:Spin.t) (s':Spin.t) =
    if s' <> s then
        ERI.get_phys two_e_ints i j k l
    else
        (ERI.get_phys two_e_ints i j k l) -.                                                 
        (ERI.get_phys two_e_ints i j l k)
  in
  h2

    
  

let f_two = 
  let two_e_ints = MOBasis.f12_ints aux_basis in
  let f2 i j k l (s:Spin.t) (s':Spin.t) =
    if s' <> s then
        0.5 *. F12.get_phys two_e_ints i j k l
    else
        0.5 *. (
         (F12.get_phys two_e_ints i j k l) -.                                                 
         (F12.get_phys two_e_ints i j l k) )
  in
  let f3 i j k l (s:Spin.t) (s':Spin.t) = 
      if (i=k && j<>l) || (j=l && i<>k) then
        0.
      else
        f2 i j k l s s'
  in
  if cancel_singles then f3 else f2

let f_one = fun _ _ _ -> 0.

(*
let f_two = h_two

let f_one = h_one
*)

let f12_integrals mo_basis =
  ( f_one, f_two, None )

let h_ij mo_basis ki kj =
  let integrals =
    List.map (fun f -> f mo_basis)
      [ CI.h_integrals ]
  in
  CIMatrixElement.make integrals ki kj 
  |> List.hd


let f_ij mo_basis ki kj =
  let integrals =
    List.map (fun f -> f mo_basis)
      [ f12_integrals ]
  in
  CIMatrixElement.make integrals ki kj 
  |> List.hd                                                                                  


let hf_ij mo_basis ki kj =
  let integrals =
    List.map (fun f -> f mo_basis)
      [ CI.h_integrals ; f12_integrals ]
  in
  CIMatrixElement.make integrals ki kj




let is_a_double det_space =
    let mo_class = DeterminantSpace.mo_class det_space in
    let mo_num = Array.length @@ MOClass.mo_class_array mo_class in
    let m l =
      List.fold_left (fun accu i ->
          let j = i-1 in Bitstring.logor accu (Bitstring.shift_left_one mo_num j)
        ) (Bitstring.zero mo_num) l
    in
    let aux_mask   = m (MOClass.auxiliary_mos mo_class) in
    fun k ->                                                                                  
      let alfa =
        Determinant.alfa k
        |> Spindeterminant.bitstring
      in
      let beta =
        Determinant.beta k
        |> Spindeterminant.bitstring
      in
      let a = Bitstring.logand aux_mask alfa
      and b = Bitstring.logand aux_mask beta
      in
      match Bitstring.popcount a + Bitstring.popcount b with
      | 2 | 1 -> true
      | 0 | _ -> false



let in_space = 
    DeterminantSpace.fci_f12_of_mo_basis aux_basis ~frozen_core mo_num
    
let aux_space = 
    DeterminantSpace.fci_of_mo_basis aux_basis ~frozen_core 

let det_space_in () =
    DeterminantSpace.determinant_stream in_space

let det_space_out () =
    let s = 
        DeterminantSpace.determinant_stream aux_space
    in
    Stream.from (fun _ ->
        try
          let is_a_double = is_a_double in_space in
          let rec result () =
            let ki = Stream.next s in
            if is_a_double ki then
              Some (ki,ki)
            else
              result ()
          in
          result ()
        with Stream.Failure -> None
      )
    

let ci = CI.make ~n_states:state in_space

let ci_coef, ci_energy = Lazy.force ci.eigensystem 

let _ = print_newline () 

let p12 det_space =
    let mo_class = DeterminantSpace.mo_class det_space in
    let mo_num = Array.length @@ MOClass.mo_class_array mo_class in
    let m l =
      List.fold_left (fun accu i ->
          let j = i-1 in Bitstring.logor accu (Bitstring.shift_left_one mo_num j)
        ) (Bitstring.zero mo_num) l
    in
    let aux_mask = m (MOClass.auxiliary_mos mo_class) in
    let not_aux_mask =
      Bitstring.(shift_left_one mo_num (mo_num-1) |> minus_one |> logxor aux_mask)
    in
    fun k ->
      let alfa =
        Determinant.alfa k
        |> Spindeterminant.bitstring
      in
      let beta =
        Determinant.beta k
        |> Spindeterminant.bitstring
      in
      let a = Bitstring.logand aux_mask alfa
      and b = Bitstring.logand aux_mask beta
      in
      match Bitstring.popcount a, Bitstring.popcount b with
      | 2, 0  
      | 0, 2 -> Some (Determinant.negate_phase k)
      | 1, 1 -> Some (Determinant.of_spindeterminants
                  (Spindeterminant.of_bitstring  @@
                    Bitstring.(logor b (logand not_aux_mask alfa)) )
                  (Spindeterminant.of_bitstring  @@
                    Bitstring.(logor a (logand not_aux_mask beta))
                  ) )
      | 1, 0 
      | 0, 1 -> Some k
      | _ -> None
                    
        

let out_list =
    Util.stream_to_list (det_space_out ())
    
let in_list =
    Util.stream_to_list (det_space_in ())
    
let det_a = Array.of_list out_list
            |> Array.map (fun (i,_) -> i)

let det_I =
    let n = 123456789 in
    in_list
    |> List.map (fun k -> (Random.int n, k))
    |> List.sort compare
    |> List.map  (fun (_,k) -> k)
    |> Array.of_list


let generate_alphas ki kj exc cabs l r =
  (* Check input excitation degree *)
  let d = Determinant.degree ki kj in
  if d <> exc then
    Printf.sprintf "Invalid excitation degree. Expected %d and found %d." exc d
    |> failwith;
    
  (* Generate single excitations *)
  let all_singles ki =
      let mos_a, mos_b = Determinant.to_lists ki in
      [ List.map (fun hole -> 
          List.map (fun particle ->
               if hole = particle then None else
                  Some (Determinant.single_excitation Spin.Alfa hole particle ki)
              ) mos_abs
          ) mos_a
      ;
        List.map (fun hole -> 
          List.map (fun particle ->
               if hole = particle then None else
                 Some (Determinant.single_excitation Spin.Beta hole particle ki)
              ) mos_abs
          ) mos_b 
      ]
      |> List.concat
      |> List.concat
      |> Util.list_some
      |> List.filter (fun x -> Determinant.is_none x = false)
      |> List.map (fun x -> Determinant.set_phase Phase.Pos x)
  in
  
  (* Generate double excitations *)
  let all_doubles ki =
      let mos_a, mos_b = Determinant.to_lists ki in
      [ List.map (fun hole ->  (* Alpha-Alpha *)
          List.map (fun particle ->
            List.map (fun hole' -> 
              List.map (fun particle' ->
                if hole = particle || hole' = particle' then None else
                if hole' > hole && particle' < particle then
                  Some (Determinant.double_excitation Spin.Alfa hole particle Spin.Alfa hole' particle' ki)
                else None
                      ) mos_abs
                  ) mos_a 
              ) mos_abs
          ) mos_a
        ; 
        List.map (fun hole ->  (* Beta-Beta *)
          List.map (fun particle ->
            List.map (fun hole' -> 
              List.map (fun particle' ->
                if hole' > hole && particle' < particle && hole <> particle && hole' <> particle' then
                  Some (Determinant.double_excitation Spin.Beta hole particle Spin.Beta hole' particle' ki)
                else None
                      ) mos_abs
                  ) mos_b 
              ) mos_abs
          ) mos_b
        ;
        List.map (fun hole ->  (* Alpha-Beta *)
          List.map (fun particle ->
            List.map (fun hole' -> 
              List.map (fun particle' ->
                  if hole = particle || hole' = particle' then None else
                  Some (Determinant.double_excitation Spin.Alfa hole particle Spin.Beta hole' particle' ki)
                      ) mos_abs
                  ) mos_b 
              ) mos_abs
          ) mos_a
      ]
      |> List.concat
      |> List.concat
      |> List.concat
      |> List.concat
      |> Util.list_some
      |> List.filter (fun x -> Determinant.is_none x = false)
      |> List.map (fun x -> Determinant.set_phase Phase.Pos x)
  in

  (* Generate left and right excitations *)
  let al = 
      match l with
      | 1 -> all_singles ki
      | 2 -> all_doubles ki
      | _ -> assert false
  in
  let ar = 
      match r with
      | 1 -> all_singles kj
      | 2 -> all_doubles kj
      | _ -> assert false
  in
  
  let mo_class = DeterminantSpace.mo_class in_space in
  let m l =
    List.fold_left (fun accu i ->
        let j = i-1 in Bitstring.logor accu (Bitstring.shift_left_one mo_num j)
      ) (Bitstring.zero mo_num) l
  in
  let aux_mask = m (MOClass.auxiliary_mos mo_class) in
  let good_cabs k = 
      let alfa =
        Determinant.alfa k
        |> Spindeterminant.bitstring
      in
      let beta =
        Determinant.beta k
        |> Spindeterminant.bitstring
      in
      let a = Bitstring.logand aux_mask alfa
      and b = Bitstring.logand aux_mask beta
      in
      Bitstring.popcount a + Bitstring.popcount b = cabs
  in
  let good_lr k =
    Determinant.degree ki k = l &&
    Determinant.degree k kj = r 
  in


  
  (* Merge lists in a set of unique determinants *)
  List.concat [ al; ar ]
  |> List.sort_uniq compare
  
  (* Filter out all determinants with incorrect numbers of electrons in the CABS *)
  |> List.filter good_cabs 
  
  (* Filter out all determinants with incorrect excitation numbers *)
  |> List.filter good_lr 
  
  
let compute_HaaF ki alphas kj =
   List.fold_left (fun accu alpha -> accu
      +. h_ij aux_basis ki alpha
      *. f_ij aux_basis alpha kj
    ) 0. alphas


let check n integral_value exc cabs lexc rexc =
  let cpudet, cpuint = ref 0., ref 0. in
  let det_list =
      match n with
        | 0 -> det_I
        | n -> Array.sub det_I 0 n
  in
  let result =
        if Parallel.master then Printf.printf "Checking ... \n%!";
        let percent = ref 0 in
        let task (i,ki) =
            (let p = (10 * (i+1))/(Array.length det_list) in
            if p <> !percent then
                ( percent := p ; if Parallel.master then Printf.printf " - %3d %%\n%!" (p*10) ));
            Array.mapi (fun j kj -> 
                if i > j || Determinant.degree ki kj <> exc then
                    (0,0,0.,0.)
                else
                  begin
                    let alphas = generate_alphas ki kj exc cabs lexc rexc in
                    let det_value =
                      let t0 = Unix.gettimeofday () in
                      let result = compute_HaaF ki alphas kj in
                      cpudet := !cpudet +. Unix.gettimeofday () -. t0;
                      result
                    in
                    let int_value = 
                      let t0 = Unix.gettimeofday () in
                      let result = integral_value ki kj in
                      cpuint := !cpuint +. Unix.gettimeofday () -. t0;
                      result
                    in
(*   Printf.printf "%d %d %e %e\n%!" i j det_value int_value;  *)
                    (i,j,det_value,int_value)
                  end
            ) det_list
        in
        det_list
        |> Array.mapi (fun i ki -> (i,ki))
        |> Array.to_list
        |> Stream.of_list
        |> Farm.run ~f:task
        |> Util.stream_to_list
        |> Array.concat
  in
  let i,j,d,v = 
      let rec aux k imax jmax emax dmax vmax = 
        if k = -1 then
          imax, jmax, dmax, vmax
        else
          let i, j, d, v = result.(k) in
          let e = abs_float (d -. v) in
          if e >= emax then
            aux (k-1) i j e d v
          else
            aux (k-1) imax jmax emax dmax vmax
      in
      aux (Array.length result - 1) 0 0 0. 0. 0.
  in
  let error = abs_float (d -. v) in
  if error < epsilon_float then
  (*
      Printf.printf "OK: %e\n%!" error
      *)
      if Parallel.master then Printf.printf "OK: (%d, %d) | %e %e | %e  |  cpu : %f  %f\n%!" i j d v error !cpudet !cpuint
  else
      if Parallel.master then Printf.printf "Failed: (%d, %d) | %e %e | %e  |  cpu : %f %f\n%!" i j d v error !cpudet !cpuint

    
let sum l f = List.fold_left (fun accu i -> accu +. f i) 0. l

let array_3_init d1 d2 d3 f =
  let result = 
      Bigarray.(Array3.create Float64 fortran_layout) d1 d2 d3
  in
  for k=1 to d3 do
      for j=1 to d2 do
          for i=1 to d1 do
             result.{i,j,k} <- f i j k
          done
      done
  done;
  result


let array_4_init d1 d2 d3 d4 f =
  let result = 
      Bigarray.(Genarray.create Float64 fortran_layout) [| d1;d2;d3;d4 |]
  in
  for l=1 to d4 do
    for k=1 to d3 do
      for j=1 to d2 do
        for i=1 to d1 do
           result.{i,j,k,l} <- f i j k l
        done
      done
    done
  done;
  result


let array_5_init d1 d2 d3 d4 d5 f =
  let result = 
      Bigarray.(Genarray.create Float64 fortran_layout) [| d1;d2;d3;d4;d5 |]
  in
  for m=1 to d5 do
    for l=1 to d4 do
      for k=1 to d3 do
        for j=1 to d2 do
          for i=1 to d1 do
             result.{i,j,k,l,m} <- f i j k l m
          done
        done
      done
    done
  done;
  result

(* ----- *)

let _ = png_image "0_1_11.png"

let integral_value ki kj = 
   (* Alpha-Beta *)
   let s, s' = Spin.(Alfa, Beta) in 
   let mos_a, mos_b = mos_a ki, mos_b ki in
   
   sum mos_cabs (fun a ->
      sum mos_a (fun i -> 
           (h_one i a s  +.
               sum mos_a (fun j -> h_two i j a j s s ) +.
               sum mos_b (fun j -> h_two i j a j s s') ) *. 
           (f_one a i s  +.
               sum mos_a (fun j -> f_two a j i j s s ) +.
               sum mos_b (fun j -> f_two a j i j s s') )
         )
       +.
       sum mos_b (fun i -> 
           (h_one i a s  +.
               sum mos_b (fun j -> h_two i j a j s' s') +.
               sum mos_a (fun j -> h_two i j a j s' s ) ) *. 
           (f_one a i s  +.
               sum mos_a (fun j -> f_two a j i j s' s ) +.
               sum mos_b (fun j -> f_two a j i j s' s') )
         ) 
     ) 
     
let _ =
  check 0 integral_value 0 1 1 1
    

let m_0111_1H_1F = 
  Vec.init mo_num (fun i -> 
        sum mos_cabs (fun a ->
            h_one i a Spin.Alfa *. f_one a i Spin.Alfa ))

    
let m_0111_1H_2Fa = 
  Mat.init_cols mo_num mo_num (fun i j -> sum mos_cabs (fun a ->
    h_one i a Spin.Alfa *. f_two a j i j Spin.Alfa Spin.Alfa  ))

    
let m_0111_1H_2Fb = 
  Mat.init_cols mo_num mo_num (fun i j -> sum mos_cabs (fun a ->
    h_one i a Spin.Alfa *. f_two a j i j Spin.Alfa Spin.Beta  ))


let m_0111_2Ha_1F = 
  Mat.init_cols mo_num mo_num (fun i j -> sum mos_cabs (fun a ->
    h_two i j a j Spin.Alfa Spin.Alfa *. f_one a j Spin.Alfa ))

    
let m_0111_2Hb_1F = 
  Mat.init_cols mo_num mo_num (fun i j -> sum mos_cabs (fun a ->
    h_two i j a j Spin.Alfa Spin.Beta *. f_one a j Spin.Alfa ))


let m_0111_2Ha_2Fa =
  array_3_init mo_num mo_num mo_num (fun i j k ->
    sum mos_cabs (fun a ->
      h_two i j a j Spin.Alfa Spin.Alfa *.
      f_two a k i k Spin.Alfa Spin.Alfa 
    )
  )


let m_0111_2Hb_2Fa =
  array_3_init mo_num mo_num mo_num (fun i j k ->
    sum mos_cabs (fun a ->
      h_two i j a j Spin.Alfa Spin.Beta *.
      f_two a k i k Spin.Alfa Spin.Alfa 
    )
  )


let m_0111_2Ha_2Fb =
  array_3_init mo_num mo_num mo_num (fun i j k ->
    sum mos_cabs (fun a ->
      h_two i j a j Spin.Alfa Spin.Alfa *.
      f_two a k i k Spin.Alfa Spin.Beta 
    )
  )


let m_0111_2Hb_2Fb =
  array_3_init mo_num mo_num mo_num (fun i j k ->
    sum mos_cabs (fun a ->
      h_two i j a j Spin.Alfa Spin.Beta *.
      f_two a k i k Spin.Alfa Spin.Beta 
    )
  )



let integral_value ki kj = 

   let mos_a, mos_b = mos_a ki, mos_b ki in
   
   sum mos_a (fun i ->
     m_0111_1H_1F.{i} +. 
     sum mos_a (fun j ->
       m_0111_1H_2Fa.{i,j} +. m_0111_2Ha_1F.{i,j} +.
       sum mos_a (fun k -> m_0111_2Ha_2Fa.{i,j,k}) +.
       sum mos_b (fun k -> m_0111_2Ha_2Fb.{i,j,k})) +.
     sum mos_b (fun j ->
       m_0111_1H_2Fb.{i,j} +. m_0111_2Hb_1F.{i,j} +.
       sum mos_a (fun k -> m_0111_2Hb_2Fa.{i,j,k}) +.
       sum mos_b (fun k -> m_0111_2Hb_2Fb.{i,j,k}))
     )
   +.
   sum mos_b (fun i ->
     m_0111_1H_1F.{i} +. 
     sum mos_b (fun j ->
       m_0111_1H_2Fa.{i,j} +. m_0111_2Ha_1F.{i,j} +.
       sum mos_b (fun k -> m_0111_2Ha_2Fa.{i,j,k}) +.
       sum mos_a (fun k -> m_0111_2Ha_2Fb.{i,j,k})) +.
     sum mos_a (fun j ->
       m_0111_1H_2Fb.{i,j} +. m_0111_2Hb_1F.{i,j} +.
       sum mos_b (fun k -> m_0111_2Hb_2Fa.{i,j,k}) +.
       sum mos_a (fun k -> m_0111_2Hb_2Fb.{i,j,k}))
     )
   
let _ =
  check 0 integral_value 0 1 1 1
    

let _ = png_image "0_1_22.png"

let integral_value ki kj = 
   (* mos unoccupied in both I and J *)
   let mos_virt_a, mos_virt_b = 
     Array.init mo_num (fun i -> Some (i+1)) , 
     Array.init mo_num (fun i -> Some (i+1)) 
   in
   List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a ki);
   List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a kj);
   List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b ki);
   List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b kj);
   
   let mos_virt_a, mos_virt_b = 
     Array.to_list mos_virt_a |> Util.list_some,
     Array.to_list mos_virt_b |> Util.list_some
   in
   
   (* Alpha-Beta *)
   let s, s' = Spin.(Alfa, Beta) in
   sum mos_cabs   (fun a ->
   sum (mos_b ki) (fun j ->  
   sum (mos_a ki) (fun i ->  
   sum mos_virt_b (fun k ->  
       h_two i j a k s s'  *. f_two a k i j s s'
   )))
   +.
   sum (mos_a ki) (fun j ->  
   sum (mos_b ki) (fun i ->  
   sum mos_virt_a (fun k ->  
       h_two i j a k s s'  *. f_two a k i j s s'
   )))) 
   +.
   (* Alpha-Alpha / Beta-Beta *)
   List.fold_left (fun accu (mos_virt,mos,s) -> 
     let s' = s in accu +.
     sum mos (fun j ->  
     sum mos (fun i -> if i < j then 0. else
     sum mos_virt (fun k ->  
     sum mos_cabs (fun a -> 
       h_two i j a k s s'  *. f_two a k i j s s'
     )))) 
   ) 0. [ (mos_virt_a,mos_a ki,Spin.Alfa) ; (mos_virt_b,mos_b ki,Spin.Beta)]
     
let _ =
  check 0 integral_value 0 1 2 2
    

let m_0122_Haa = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
      sum mos_cabs (fun a ->
        h_two i j a k Spin.Alfa Spin.Alfa *. f_two a k i j Spin.Alfa Spin.Alfa
        ) )
      
let m_0122_Hab = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
      sum mos_cabs (fun a ->
         h_two i j a k Spin.Alfa Spin.Beta *. f_two a k i j Spin.Alfa Spin.Beta
      ) )


let integral_value ki kj = 
   (* mos unoccupied in both I and J *)
   let mos_virt_a, mos_virt_b = 
     Array.init mo_num (fun i -> Some (i+1)) , 
     Array.init mo_num (fun i -> Some (i+1)) 
   in
   List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a ki);
   List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a kj);
   List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b ki);
   List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b kj);
   
   let mos_virt_a, mos_virt_b = 
     Array.to_list mos_virt_a |> Util.list_some,
     Array.to_list mos_virt_b |> Util.list_some
   in
   
   let result = 
   sum (mos_a ki) (fun i ->  
   sum (mos_b ki) (fun j ->  
   sum mos_virt_b (fun k ->  m_0122_Hab.{i,j,k} )))
   +.
   sum (mos_b ki) (fun i ->  
   sum (mos_a ki) (fun j ->  
   sum mos_virt_a (fun k ->  m_0122_Hab.{i,j,k} )))
   +.
   (* Alpha-Alpha / Beta-Beta *)
   List.fold_left (fun accu (mos_virt,mos) -> 
     accu +.
     sum mos (fun j ->  
     sum mos (fun i -> if i <= j then 0. else
     sum mos_virt (fun k -> m_0122_Haa.{i,j,k} )))
   ) 0. [ (mos_virt_a,mos_a ki) ; (mos_virt_b,mos_b ki)]
   in 
   result
     
let _ =
  check 0 integral_value 0 1 2 2
    

let _ = png_image "0_2_22.png"

let integral_value ki kj = 
   (* Alpha-Beta *)
   let s, s' = Spin.(Alfa, Beta) in
   sum  mos_cabs  (fun a -> 
   sum  mos_cabs  (fun b ->  
   sum (mos_a ki) (fun i ->  
   sum (mos_b ki) (fun j ->  
       h_two i j a b s s'  *. f_two a b i j s s'
   ))))
   +.
   (* Alpha-Alpha / Beta-Beta *)
   List.fold_left (fun accu (mos,s) -> 
     let s' = s in accu +.
     sum mos_cabs (fun b ->  
     sum mos_cabs (fun a -> if b > a then 0. else
     sum   mos    (fun j ->  
     sum   mos    (fun i -> if i < j then 0. else
        h_two i j a b s s'  *. f_two a b i j s s'
     ))))
   ) 0. [ (mos_a ki,Spin.Alfa) ; (mos_b ki,Spin.Beta)]
     
let _ =
  check 0 integral_value 0 2 2 2
    

let m_0222_Haa = 
  Mat.init_cols mo_num mo_num (fun i j ->
      sum mos_cabs (fun a ->
      sum mos_cabs (fun b -> if b >= a then 0. else
        h_two i j a b Spin.Alfa Spin.Alfa *. f_two a b i j Spin.Alfa Spin.Alfa
        ) )
     )
        
let m_0222_Hab = 
  Mat.init_cols mo_num mo_num (fun i j ->
      sum mos_cabs (fun a ->
      sum mos_cabs (fun b ->
        h_two i j a b Spin.Alfa Spin.Beta *. f_two a b i j Spin.Alfa Spin.Beta
        ) )
     )
      
let integral_value ki kj = 
   (* Alpha-Beta *)
   sum (mos_a ki) (fun i ->  
       sum (mos_b ki) (fun j ->  
          m_0222_Hab.{i,j}  
       ))
   +.
   (* Alpha-Alpha / Beta-Beta *)
   List.fold_left (fun accu (mos) -> 
     accu +.
     sum mos (fun j ->  
         sum mos (fun i -> if i < j then 0. else
           m_0222_Haa.{i,j}  
     ))
   ) 0. [ (mos_a ki) ; (mos_b ki)]
     
let _ =
  check 0 integral_value 0 2 2 2
    

let _ = png_image "1_1_11.png"

let integral_value ki kj = 
    let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in

    let mos_i, mos_i' =
      match s with
      | Spin.Alfa -> mos_a ki, mos_b ki
      | Spin.Beta -> mos_b ki, mos_a ki
    in
    let mos_j, mos_j' =
      match s with
      | Spin.Alfa -> mos_a kj, mos_b kj
      | Spin.Beta -> mos_b kj, mos_a kj
    in

    let result = 
       let s' = Spin.other s in
       
       sum mos_cabs (fun a -> 
           (h_one i a s +. 
              sum mos_i  (fun j ->  h_two i j a j s s ) +.
              sum mos_i' (fun j ->  h_two i j a j s s') ) *.
           (f_one a k s +. 
              sum mos_j  (fun j ->  f_two a j k j s s ) +.
              sum mos_j' (fun j ->  f_two a j k j s s') )  
         )
    in
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 1 1 1 1

let m_1111_1H_1F = 
  Mat.init_cols mo_num mo_num (fun i k -> 
        sum mos_cabs (fun a ->
            h_one i a Spin.Alfa *. f_one a k Spin.Alfa ))


let m_1111_1H_2Fa = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
      sum mos_cabs (fun a ->
        h_one i a Spin.Alfa *. f_two a j k j Spin.Alfa Spin.Alfa ))

    
let m_1111_1H_2Fb = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
      sum mos_cabs (fun a ->
        h_one i a Spin.Alfa *. f_two a j k j Spin.Alfa Spin.Beta ))


let m_1111_2Ha_1F = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
   sum mos_cabs (fun a ->
    h_two i j a j Spin.Alfa Spin.Alfa *. f_one a k Spin.Alfa ))

    
let m_1111_2Hb_1F = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
   sum mos_cabs (fun a ->
    h_two i j a j Spin.Alfa Spin.Beta *. f_one a k Spin.Alfa ))


let m_1111_2Ha_2Fa =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
      h_two i j a j Spin.Alfa Spin.Alfa *.
      f_two a l k l Spin.Alfa Spin.Alfa 
    )
  )


let m_1111_2Hb_2Fa =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
      h_two i j a j Spin.Alfa Spin.Beta *.
      f_two a l k l Spin.Alfa Spin.Alfa 
    )
  )


let m_1111_2Ha_2Fb =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
      h_two i j a j Spin.Alfa Spin.Alfa *.
      f_two a l k l Spin.Alfa Spin.Beta 
    )
  )


let m_1111_2Hb_2Fb =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
      h_two i j a j Spin.Alfa Spin.Beta *.
      f_two a l k l Spin.Alfa Spin.Beta 
    )
  )


let integral_value ki kj = 
    let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in
    
    let mos_a, mos_b =
      match s with
      | Spin.Alfa -> mos_a ki, mos_b ki
      | Spin.Beta -> mos_b ki, mos_a ki
    in    
   
    let result = 
     m_1111_1H_1F.{i,k} +. 
     sum mos_a (fun j -> if j = i then 0. else
       m_1111_1H_2Fa.{i,j,k} +. m_1111_2Ha_1F.{i,j,k} +.
       sum mos_a (fun l -> if l = i then 0. else m_1111_2Ha_2Fa.{i,j,k,l}) +.
       sum mos_b (fun l -> m_1111_2Ha_2Fb.{i,j,k,l})) +.
     sum mos_b (fun j ->
       m_1111_1H_2Fb.{i,j,k} +. m_1111_2Hb_1F.{i,j,k} +.
       sum mos_a (fun l -> if l = i then 0. else m_1111_2Hb_2Fa.{i,j,k,l}) +.
       sum mos_b (fun l -> m_1111_2Hb_2Fb.{i,j,k,l}))
    in
    
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
   
let _ =
  check 0 integral_value 1 1 1 1
    

let _ = png_image "1_1_12.png"

let integral_value ki kj = 
    let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in

    let mos_j, mos_j' =
      let alfa = 
        let i = Spindeterminant.bitstring @@ Determinant.alfa ki in
        let j = Spindeterminant.bitstring @@ Determinant.alfa kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      let beta  = 
        let i = Spindeterminant.bitstring @@ Determinant.beta ki in
        let j = Spindeterminant.bitstring @@ Determinant.beta kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      match s with
      | Spin.Alfa -> alfa, beta
      | Spin.Beta -> beta, alfa
    in
    
    let mos_i, mos_i' =
      match s with
      | Spin.Alfa -> mos_a ki, mos_b ki
      | Spin.Beta -> mos_b ki, mos_a ki
    in

    let result = 
       let s' = Spin.other s in
       sum mos_cabs (fun a -> 
           sum mos_j (fun j -> 
               (h_one j a s +. 
                  sum mos_i  (fun m -> h_two j m a m s s )  +.
                  sum mos_i' (fun m -> h_two j m a m s s')  ) *.
               (f_two a i j k s s )
             ) 
           +.
           sum mos_j' (fun j -> 
               (h_one j a s +. 
                  sum mos_i  (fun m ->  h_two j m a m s' s ) +.
                  sum mos_i' (fun m ->  h_two j m a m s' s') ) *.
               (f_two a i j k s' s )
             ) 
         ) 
    in
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 1 1 1 2

let m_1112_1H_2Fa = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
      sum mos_cabs (fun a ->
        h_one j a Spin.Alfa *. f_two a i j k Spin.Alfa Spin.Alfa ))

    
let m_1112_1H_2Fb = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
      sum mos_cabs (fun a ->
        h_one j a Spin.Beta *. f_two a i j k Spin.Alfa Spin.Beta ))


let m_1112_2Ha_2Fa =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two j m a m Spin.Alfa Spin.Alfa *.
      f_two a i j k Spin.Alfa Spin.Alfa 
    )
  )


let m_1112_2Hb_2Fa =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two j m a m Spin.Alfa Spin.Beta *.
      f_two a i j k Spin.Alfa Spin.Alfa 
    )
  )

let m_1112_2Ha_2Fb =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two j m a m Spin.Alfa Spin.Alfa *.
      f_two a i j k Spin.Alfa Spin.Beta 
    )
  )


let m_1112_2Hb_2Fb =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two j m a m Spin.Alfa Spin.Beta *.
      f_two a i j k Spin.Alfa Spin.Beta 
    )
  )


let integral_value ki kj = 
    let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in
    
    let mos_j, mos_j' =
      let alfa = 
        let i = Spindeterminant.bitstring @@ Determinant.alfa ki in
        let j = Spindeterminant.bitstring @@ Determinant.alfa kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      let beta  = 
        let i = Spindeterminant.bitstring @@ Determinant.beta ki in
        let j = Spindeterminant.bitstring @@ Determinant.beta kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      match s with
      | Spin.Alfa -> alfa, beta
      | Spin.Beta -> beta, alfa
    in
    
    let mos_i, mos_i' =
      match s with
      | Spin.Alfa -> mos_a ki, mos_b ki
      | Spin.Beta -> mos_b ki, mos_a ki
    in
   
    let result = 
     sum mos_j (fun j -> 
       m_1112_1H_2Fa.{i,j,k} +. 
       sum mos_i  (fun m -> m_1112_2Ha_2Fa.{i,j,k,m}) +.
       sum mos_i' (fun m -> m_1112_2Hb_2Fa.{i,j,k,m})) +.
     sum mos_j' (fun j ->
       m_1112_1H_2Fb.{i,j,k} +. 
       sum mos_i  (fun m -> m_1112_2Hb_2Fb.{i,j,k,m}) +.
       sum mos_i' (fun m -> m_1112_2Ha_2Fb.{i,j,k,m}))
    in
    
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
   
   

let _ = check 0 integral_value 1 1 1 2

let _ = png_image "1_1_21.png"

let integral_value ki kj = 
    let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in

    let mos_j, mos_j' =
      let alfa = 
        let i = Spindeterminant.bitstring @@ Determinant.alfa ki in
        let j = Spindeterminant.bitstring @@ Determinant.alfa kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      let beta  = 
        let i = Spindeterminant.bitstring @@ Determinant.beta ki in
        let j = Spindeterminant.bitstring @@ Determinant.beta kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      match s with
      | Spin.Alfa -> alfa, beta
      | Spin.Beta -> beta, alfa
    in
    
    let mos_i, mos_i' =
      match s with
      | Spin.Alfa -> mos_a kj, mos_b kj
      | Spin.Beta -> mos_b kj, mos_a kj
    in

    let result = 
       let s' = Spin.other s in
       sum mos_cabs (fun a -> 
       sum mos_j    (fun j -> 
               (h_two i j k a s s ) *.
               (f_one a j s  +. 
                  sum mos_i  (fun m -> f_two m a m j s  s) +.
                  sum mos_i' (fun m -> f_two m a m j s' s) ) 
             ) 
       +.
       sum mos_j' (fun j -> 
               (h_two i j k a s s') *.
               (f_one a j s' +. 
                  sum mos_i  (fun m -> f_two m a m j s  s') +.
                  sum mos_i' (fun m -> f_two m a m j s' s') ) 
       )) 
    in
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 1 1 2 1

let m_1121_2Ha_1F = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
      sum mos_cabs (fun a ->
        h_two i j k a Spin.Alfa Spin.Alfa *. f_one a j Spin.Alfa))

    
let m_1121_2Hb_1F = 
  array_3_init mo_num mo_num mo_num (fun i j k ->
      sum mos_cabs (fun a ->
        h_two i j k a Spin.Alfa Spin.Beta *. f_one a j Spin.Beta))


let m_1121_2Ha_2Fa =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two i j k a Spin.Alfa Spin.Alfa *.
      f_two m a m j Spin.Alfa Spin.Alfa 
    )
  )


let m_1121_2Hb_2Fa =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two i j k a Spin.Alfa Spin.Beta *.
      f_two m a m j Spin.Alfa Spin.Alfa 
    )
  )

let m_1121_2Ha_2Fb =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two i j k a Spin.Alfa Spin.Alfa *.
      f_two m a m j Spin.Alfa Spin.Beta 
    )
  )


let m_1121_2Hb_2Fb =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two i j k a Spin.Alfa Spin.Beta *.
      f_two m a m j Spin.Alfa Spin.Beta 
    )
  )


let integral_value ki kj = 
    let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in
    
    let mos_j, mos_j' =
      let alfa = 
        let i = Spindeterminant.bitstring @@ Determinant.alfa ki in
        let j = Spindeterminant.bitstring @@ Determinant.alfa kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      let beta  = 
        let i = Spindeterminant.bitstring @@ Determinant.beta ki in
        let j = Spindeterminant.bitstring @@ Determinant.beta kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      match s with
      | Spin.Alfa -> alfa, beta
      | Spin.Beta -> beta, alfa
    in
    
    let mos_i, mos_i' =
      match s with
      | Spin.Alfa -> mos_a kj, mos_b kj
      | Spin.Beta -> mos_b kj, mos_a kj
    in
   
    let result = 
     sum mos_j (fun j -> 
       m_1121_2Ha_1F.{i,j,k} +. 
       sum mos_i  (fun m -> m_1121_2Ha_2Fa.{i,j,k,m}) +.
       sum mos_i' (fun m -> m_1121_2Ha_2Fb.{i,j,k,m})) +.
     sum mos_j' (fun j ->
       m_1121_2Hb_1F.{i,j,k} +. 
       sum mos_i  (fun m -> m_1121_2Hb_2Fb.{i,j,k,m}) +.
       sum mos_i' (fun m -> m_1121_2Hb_2Fa.{i,j,k,m}))
    in
    
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
   
   

let _ = check 0 integral_value 1 1 2 1

let _ = png_image "1_1_22.png"

(*
let ki = det_I.(6)
let kj = det_I.(58)

let _ = 
    Format.printf "|I> -> |J> : %a |\n@." Excitation.pp (Excitation.of_det ki kj) ;
generate_alphas ki kj 1 1 2 2
|> Array.of_list
|> Array.mapi (fun kk alpha -> 

(*
let _ = Determinant.to_lists ki
let _ = Determinant.to_lists alpha
let _ = Determinant.to_lists kj

   
let _ = 
    Format.printf "|I> -> |J> : %a\n@." Excitation.pp (Excitation.of_det ki kj);
    Format.printf "|I> -> |a> : %a\n@." Excitation.pp (Excitation.of_det ki alpha);
    Format.printf "|a> -> |J> : %a\n@." Excitation.pp (Excitation.of_det alpha kj)
*)

    let integral_value ki kj = 
    
      let exc0 = Array.init (aux_num+1) (fun _ -> [|"-";"-"|]) in
      let exc1 = Array.init (aux_num+1) (fun _ -> [|"-";"-"|]) in 
      let exc2 = Array.init (aux_num+1) (fun _ -> [|"-";"-"|]) in 
 
      let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin } )) ->
            hole, particle, spin, phase
          | _ -> assert false
      in
      let spin = function
        | Spin.Alfa -> 0
        | _ -> 1
      in
        exc0.(i).(spin s ) <- "i" 
      ; exc0.(k).(spin s ) <- "k" 
      ;
      let s0 = s in
    
      let i, j, k, l, s, s', p1 =
          match Excitation.of_det ki alpha with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
      in
      
      if exc0.(i).(spin s ) =  "-" then exc0.(i).(spin s ) <- "n";
      if exc0.(j).(spin s') =  "-" then exc0.(j).(spin s') <- "n";
      if exc0.(k).(spin s ) =  "-" then exc0.(k).(spin s ) <- if k > mo_num then "a" else "m";
      if exc0.(l).(spin s') =  "-" then exc0.(l).(spin s') <- if l > mo_num then "a" else "m";
      
      let string_h = 
          Printf.sprintf "h_two %s %s %s %s %s %s *.  " 
            exc0.(i).(spin s )
            exc0.(j).(spin s')
            exc0.(k).(spin s )
            exc0.(l).(spin s')
            (if s  = s0 then "s " else "s'")
            (if s' = s0 then "s " else "s'")
            (*
            (if exc0.(i).(spin s ) = "i" || exc0.(k).(spin s ) = "k" then "s  " else 
             if exc0.(i).(spin s ) = "j" || exc0.(k).(spin s ) = "l" then "s' " else "s''")
            (if exc0.(j).(spin s') = "i" || exc0.(l).(spin s') = "k" then "s  " else 
             if exc0.(j).(spin s') = "j" || exc0.(l).(spin s') = "l" then "s' " else "s''")
             *)
      in 
      
      let i, j, k, l, s, s', p2 =
          match Excitation.of_det alpha kj with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
      in
      
      let string_f = 
          Printf.sprintf "f_two %s %s %s %s %s %s" 
            exc0.(i).(spin s )
            exc0.(j).(spin s')
            exc0.(k).(spin s )
            exc0.(l).(spin s')
            (*
            (if exc0.(i).(spin s ) = "i" || exc0.(k).(spin s ) = "k" then "s  " else 
             if exc0.(i).(spin s ) = "j" || exc0.(k).(spin s ) = "l" then "s' " else 
             if exc0.(i).(spin s ) = "n" || exc0.(k).(spin s ) = "n" then "s''" else
             if s  = s0 then "s" else "s'")
            (if exc0.(j).(spin s') = "i" || exc0.(l).(spin s') = "k" then "s  " else 
             if exc0.(j).(spin s') = "j" || exc0.(l).(spin s') = "l" then "s' " else 
             if exc0.(j).(spin s') = "n" || exc0.(l).(spin s') = "n" then "s''" else
             if s' = s0 then "s" else "s'")
             *)
            (if s  = s0 then "s " else "s'")
            (if s' = s0 then "s " else "s'")
      in
      
    Format.printf "|I> -> |a> : %a | %s\n@." Excitation.pp (Excitation.of_det ki alpha) string_h ;
    Format.printf "|a> -> |J> : %a | %s\n@." Excitation.pp (Excitation.of_det alpha kj) string_f ;
    
    let sign = 
        if Phase.add p1 p2 = phase then "+. " else "-. "
    in
sign ^ string_h ^ string_f
(*
        
        let mos, mos' =
          match s with
          | Spin.Alfa -> mos_a ki, mos_b ki
          | Spin.Beta -> mos_b ki, mos_a ki
        in

        let result = 
           let k=4 and l=5 in
           let i=1 and j=2 in
           let n=2 and a=7 in
           let s   = Spin.Alfa
           and s'  = Spin.Alfa
           and s'' = Spin.Beta
           in
           (* 
           h_two j n l a s' s'' *. f_two a i n k s s''
           *)
           h_two i n k a s s'' *. f_two j a l n s s''
        in

        match phase with
        | Phase.Pos -> result
        | Phase.Neg -> -. result
        *)

in
integral_value ki kj
(*
let a = (compute_HaaF ki [alpha] kj)
and b = (integral_value ki kj)
in
if kk = 31 then
    Format.printf "%6d %e %e@.@." (kk) a b
    *)
)
|> Array.to_list
|> List.sort_uniq compare

let _ = compute_HaaF ki (generate_alphas ki kj 1 1 2 2) kj

let _ = integral_value ki kj

*)

let integral_value ki kj = 
    let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in
    
   (* MOs unoccupied in both I and J *)
   let mos_virt_a, mos_virt_b = 
     Array.init mo_num (fun i -> Some (i+1)) , 
     Array.init mo_num (fun i -> Some (i+1)) 
   in
   List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a ki);
   List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a kj);
   List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b ki);
   List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b kj);
   
   let mos_virt_a, mos_virt_b = 
     Array.to_list mos_virt_a |> Util.list_some,
     Array.to_list mos_virt_b |> Util.list_some
   in

   let mos_virt, mos_virt' =
      match s with
      | Spin.Alfa -> mos_virt_a, mos_virt_b
      | Spin.Beta -> mos_virt_b, mos_virt_a
   in
    
   let mos_j, mos_j' =
      let alfa = 
        let i = Spindeterminant.bitstring @@ Determinant.alfa ki in
        let j = Spindeterminant.bitstring @@ Determinant.alfa kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      let beta  = 
        let i = Spindeterminant.bitstring @@ Determinant.beta ki in
        let j = Spindeterminant.bitstring @@ Determinant.beta kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      match s with
      | Spin.Alfa -> alfa, beta
      | Spin.Beta -> beta, alfa
   in
   let result = 
       let s' = Spin.other s in
        sum mos_cabs (fun a -> 
        sum mos_j    (fun n -> 
        sum mos_virt (fun m -> 
             h_two n i a m s  s  *.  f_two m a k n s  s 
        ))) +.
        sum mos_cabs (fun a' -> 
        sum mos_j'   (fun n' -> 
        sum mos_virt (fun m -> 
             h_two i n' m a' s  s' *.  f_two m a' k n' s  s'
        ))) +.
        sum mos_cabs  (fun a -> 
        sum mos_j'    (fun n' -> 
        sum mos_virt' (fun m' -> 
             h_two i n' a m' s  s' *.  f_two a m' k n' s  s'
        ))) -. 
        sum mos_cabs (fun a -> 
        sum mos_j    (fun n -> 
        sum mos_j    (fun l -> if l <=n then 0. else
             h_two n l a k s  s  *.  f_two i a l n s  s 
        ))) -.  
        sum mos_cabs (fun a' -> 
        sum mos_j'   (fun n' -> 
        sum mos_j    (fun l -> 
             h_two l n' k a' s s'  *.  f_two i a' l n' s s'
        ))) 
    in
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     
let _ = check 0 integral_value 1 1 2 2



let m_1122_va =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two j i a m Spin.Alfa Spin.Alfa *.
      f_two m a k j Spin.Alfa Spin.Alfa 
    )
  )

let m_1122_v2 =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two i j m a Spin.Alfa Spin.Beta *.
      f_two m a k j Spin.Alfa Spin.Beta 
    )
  )

let m_1122_v3 =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k m ->
    sum mos_cabs (fun a ->
      h_two i j a m Spin.Alfa Spin.Beta *.
      f_two a m k j Spin.Alfa Spin.Beta 
    )
  )

let m_1122_oa =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
      h_two j l a k Spin.Alfa Spin.Alfa *.
      f_two i a l j Spin.Alfa Spin.Alfa 
    )
  )

let m_1122_o =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
      h_two l j k a Spin.Alfa Spin.Beta *.
      f_two i a l j Spin.Alfa Spin.Beta 
    )
  )


let integral_value ki kj = 
    let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in
    
   (* MOs unoccupied in both I and J *)
   let mos_virt_a, mos_virt_b = 
     Array.init mo_num (fun i -> Some (i+1)) , 
     Array.init mo_num (fun i -> Some (i+1)) 
   in
   List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a ki);
   List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a kj);
   List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b ki);
   List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b kj);
   
   let mos_virt_a, mos_virt_b = 
     Array.to_list mos_virt_a |> Util.list_some,
     Array.to_list mos_virt_b |> Util.list_some
   in

   let mos_virt, mos_virt' =
      match s with
      | Spin.Alfa -> mos_virt_a, mos_virt_b
      | Spin.Beta -> mos_virt_b, mos_virt_a
   in
    
   let mos_j, mos_j' =
      let alfa = 
        let i = Spindeterminant.bitstring @@ Determinant.alfa ki in
        let j = Spindeterminant.bitstring @@ Determinant.alfa kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      let beta  = 
        let i = Spindeterminant.bitstring @@ Determinant.beta ki in
        let j = Spindeterminant.bitstring @@ Determinant.beta kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      match s with
      | Spin.Alfa -> alfa, beta
      | Spin.Beta -> beta, alfa
   in
   
    let result = 
        sum mos_j    (fun j -> 
        sum mos_virt (fun m -> 
          m_1122_va.{i,j,k,m}
        )) +.
        sum mos_j'   (fun j -> 
        sum mos_virt (fun m -> 
          m_1122_v2.{i,j,k,m}
        ))   +.
        sum mos_j'    (fun j -> 
        sum mos_virt' (fun m -> 
          m_1122_v3.{i,j,k,m}
        ))   -.
        sum mos_j    (fun j -> 
        sum mos_j    (fun l -> if l <= j then 0. else
          m_1122_oa.{i,j,k,l}
        )) -. 
        sum mos_j'   (fun j -> 
        sum mos_j    (fun l -> 
          m_1122_o.{i,j,k,l}
        )) 
    in
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 1 1 2 2




let _ = png_image "1_2_22.png"

let integral_value ki kj = 
    let h, p, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in

    let mos, mos' =
      match s with
      | Spin.Alfa -> mos_a ki, mos_b ki
      | Spin.Beta -> mos_b ki, mos_a ki
    in
   
   

    let result = 
       (* Alpha-Beta *)
       let s' = Spin.other s in
       sum mos_cabs (fun b -> 
       sum mos_cabs (fun a -> 
       sum mos'     (fun j -> h_two h j a b s s'  *. f_two a b p j s s')
       ))
       +.
       (* Alpha-Alpha / Beta-Beta *)
       sum mos_cabs (fun b -> 
       sum mos_cabs (fun a -> if b >= a then 0. else
       sum mos  (fun j -> h_two h j a b s s  *. f_two a b p j s s)
       )) 
    in
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 1 2 2 2

let m_1222a =
  array_3_init mo_num mo_num mo_num (fun i j k ->
    sum mos_cabs (fun a ->
    sum mos_cabs (fun b -> if b > a then 0. else
      h_two i j a b Spin.Alfa Spin.Alfa *.
      f_two a b k j Spin.Alfa Spin.Alfa 
    )
    )
  )

let m_1222 =
  array_3_init mo_num mo_num mo_num (fun i j k ->
    sum mos_cabs (fun a ->
    sum mos_cabs (fun b ->
      h_two i j a b Spin.Alfa Spin.Beta *.
      f_two a b k j Spin.Alfa Spin.Beta 
    )
    )
  )
  

let integral_value ki kj = 
    let i, k, s, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Single (phase, { hole ; particle ; spin })) ->
            hole, particle, spin, phase
          | _ -> assert false
    in
    
   (* MOs unoccupied in both I and J *)
    let mos_j, mos_j' =
      let alfa = 
        let i = Spindeterminant.bitstring @@ Determinant.alfa ki in
        let j = Spindeterminant.bitstring @@ Determinant.alfa kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      let beta  = 
        let i = Spindeterminant.bitstring @@ Determinant.beta ki in
        let j = Spindeterminant.bitstring @@ Determinant.beta kj in
        Bitstring.to_list (Bitstring.logand i j)
      in
      match s with
      | Spin.Alfa -> alfa, beta
      | Spin.Beta -> beta, alfa
    in

    let result = 
        sum mos_j    (fun j ->  m_1222a.{i,j,k} ) +.
        sum mos_j'   (fun j ->  m_1222.{i,j,k} )
    in
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 1 2 2 2




let _ = png_image "2_1_12.png"

let integral_value ki kj = 
    let i, j, k, l, s, s', phase =
          match Excitation.of_det ki kj with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
    in

        let mos, mos' =
          match s with
          | Spin.Alfa -> mos_a ki, mos_b ki
          | Spin.Beta -> mos_b ki, mos_a ki
        in
        
        let mos2, mos2' =
          match s' with
          | Spin.Alfa -> mos_a ki, mos_b ki
          | Spin.Beta -> mos_b ki, mos_a ki
        in

        let result = 
       sum mos_cabs (fun a -> 
           let s'' = Spin.other s in
             ( h_one i a s +.
               sum mos  (fun n -> h_two i n a n s s  ) +.
               sum mos' (fun n -> h_two i n a n s s'') 
             ) *. f_two a j k l s s'
             ) +.
       sum mos_cabs (fun a -> 
           let s'' = Spin.other s' in
             ( h_one j a s' +.
               sum mos2 (fun n -> h_two j n a n s' s' ) +.
               sum mos2'  (fun n -> h_two j n a n s' s'') 
             ) *. f_two i a k l s s'
             ) 
        in

        match phase with
        | Phase.Pos -> result
        | Phase.Neg -> -. result


let _ = check 0 integral_value 2 1 1 2

let m_2112_1H_2Fa =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
        h_one i a Spin.Alfa *. f_two a j k l Spin.Alfa Spin.Alfa +.
        h_one j a Spin.Alfa *. f_two i a k l Spin.Alfa Spin.Alfa )
    )

let m_2112_1H_2Fb =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
        h_one i a Spin.Alfa *. f_two a j k l Spin.Alfa Spin.Beta +.
        h_one j a Spin.Alfa *. f_two i a k l Spin.Alfa Spin.Beta)
    )

let m_2112_2Ha_2Fa =
  array_5_init mo_num mo_num mo_num mo_num mo_num (fun i j k l n ->
    sum mos_cabs (fun a ->
        h_two i n a n Spin.Alfa Spin.Alfa *.
        f_two a j k l Spin.Alfa Spin.Alfa )
    )
    
let m_2112_2Hb_2Fa =
  array_5_init mo_num mo_num mo_num mo_num mo_num (fun i j k l n ->
    sum mos_cabs (fun a ->
        h_two i n a n Spin.Alfa Spin.Beta *.
        f_two a j k l Spin.Alfa Spin.Alfa )
    )
    
let m_2112_2Ha_2Fb =
  array_5_init mo_num mo_num mo_num mo_num mo_num (fun i j k l n ->
    sum mos_cabs (fun a ->
        h_two i n a n Spin.Alfa Spin.Alfa *.
        f_two a j k l Spin.Alfa Spin.Beta )
    )
    
let m_2112_2Hb_2Fb =
  array_5_init mo_num mo_num mo_num mo_num mo_num (fun i j k l n ->
    sum mos_cabs (fun a ->
        h_two i n a n Spin.Alfa Spin.Beta *.
        f_two a j k l Spin.Alfa Spin.Beta )
    )
    
let integral_value ki kj = 
    let i, j, k, l, s, s', phase =
          match Excitation.of_det ki kj with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
    in
    let mos, mos' =
       match s with
       | Spin.Alfa -> mos_a ki, mos_b ki
       | Spin.Beta -> mos_b ki, mos_a ki
     in
     
     let result = 
       if s = s' then
         m_2112_1H_2Fa.{i,j,k,l} +. 
         sum mos  (fun n ->
             m_2112_2Ha_2Fa.{i,j,k,l,n} +. m_2112_2Ha_2Fa.{j,i,l,k,n}
         ) +.
         sum mos' (fun n ->
             m_2112_2Hb_2Fa.{i,j,k,l,n} +. m_2112_2Hb_2Fa.{j,i,l,k,n}
         ) 
       else
         m_2112_1H_2Fb.{i,j,k,l} +.
         sum mos  (fun n ->
             m_2112_2Ha_2Fb.{i,j,k,l,n} +. m_2112_2Hb_2Fb.{j,i,l,k,n}
         ) +.
         sum mos' (fun n ->
             m_2112_2Hb_2Fb.{i,j,k,l,n} +. m_2112_2Ha_2Fb.{j,i,l,k,n}
         ) 
     in

    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 2 1 1 2




let _ = png_image "2_1_21.png"

let integral_value ki kj = 
    let i, j, k, l, s, s', phase =
          match Excitation.of_det ki kj with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
    in

        let result = 
        
        let mos, mos', s'' =
          match s with
          | Spin.Alfa -> mos_a kj, mos_b kj, Spin.Beta
          | Spin.Beta -> mos_b kj, mos_a kj, Spin.Alfa
        in
        sum mos_cabs (fun a -> 
             h_two i j a l s s' *.
             ( f_one a k s +.
               sum mos  (fun n -> f_two a n k n s s) +.
               sum mos' (fun n -> f_two a n k n s s'') 
             ) ) +.

        let mos, mos', s'' =
          match s' with
          | Spin.Alfa -> mos_a kj, mos_b kj, Spin.Beta
          | Spin.Beta -> mos_b kj, mos_a kj, Spin.Alfa
        in
        sum mos_cabs (fun a -> 
             h_two j i a k s' s *.
             ( f_one a l s' +.
               sum mos  (fun n -> f_two a n l n s' s') +.
               sum mos' (fun n -> f_two a n l n s' s'') 
             ) 
             ) 
        in

        match phase with
        | Phase.Pos -> result
        | Phase.Neg -> -. result


let _ = check 0 integral_value 2 1 2 1

let m_2121_2Ha_1F =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
        h_two i j a l Spin.Alfa Spin.Alfa *. f_one a k Spin.Alfa +.
        h_two i j k a Spin.Alfa Spin.Alfa *. f_one a l Spin.Alfa)
    )

let m_2121_2Hb_1F =
  array_4_init mo_num mo_num mo_num mo_num (fun i j k l ->
    sum mos_cabs (fun a ->
        h_two i j a l Spin.Alfa Spin.Beta *. f_one a k Spin.Alfa +.
        h_two i j k a Spin.Alfa Spin.Beta *. f_one a l Spin.Alfa)
    )

let m_2121_2Ha_2Fa =
  array_5_init mo_num mo_num mo_num mo_num mo_num (fun i j k l n ->
    sum mos_cabs (fun a ->
        h_two i j a l Spin.Alfa Spin.Alfa *.
        f_two a n k n Spin.Alfa Spin.Alfa )
    )
    
let m_2121_2Hb_2Fa =
  array_5_init mo_num mo_num mo_num mo_num mo_num (fun i j k l n ->
    sum mos_cabs (fun a ->
        h_two i j a l Spin.Alfa Spin.Beta *.
        f_two a n k n Spin.Alfa Spin.Alfa )
    )
    
let m_2121_2Ha_2Fb =
  array_5_init mo_num mo_num mo_num mo_num mo_num (fun i j k l n ->
    sum mos_cabs (fun a ->
        h_two i j a l Spin.Alfa Spin.Alfa *.
        f_two a n k n Spin.Alfa Spin.Beta )
    )
    
let m_2121_2Hb_2Fb =
  array_5_init mo_num mo_num mo_num mo_num mo_num (fun i j k l n ->
    sum mos_cabs (fun a ->
        h_two i j a l Spin.Alfa Spin.Beta *.
        f_two a n k n Spin.Alfa Spin.Beta )
    )
    
let integral_value ki kj = 
    let i, j, k, l, s, s', phase =
          match Excitation.of_det ki kj with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
    in
    let mos, mos' =
       match s with
       | Spin.Alfa -> mos_a kj, mos_b kj
       | Spin.Beta -> mos_b kj, mos_a kj
     in
     
     let result = 
       if s = s' then
         m_2121_2Ha_1F.{i,j,k,l} +. 
         sum mos  (fun n ->
             m_2121_2Ha_2Fa.{i,j,k,l,n} +. m_2121_2Ha_2Fa.{j,i,l,k,n}
         ) +.
         sum mos' (fun n ->
             m_2121_2Ha_2Fb.{i,j,k,l,n} +. m_2121_2Ha_2Fb.{j,i,l,k,n}
         ) 
       else
         m_2121_2Hb_1F.{i,j,k,l} +.
         sum mos  (fun n ->
             m_2121_2Hb_2Fa.{i,j,k,l,n} +. m_2121_2Hb_2Fb.{j,i,l,k,n}
         ) +.
         sum mos' (fun n ->
             m_2121_2Hb_2Fb.{i,j,k,l,n} +. m_2121_2Hb_2Fa.{j,i,l,k,n}
         ) 
     in

    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 2 1 2 1




let _ = png_image "2_1_22.png"

let integral_value ki kj = 
    let i, j, k, l, s, s', phase =
          match Excitation.of_det ki kj with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
    in

    let mos_virt_a, mos_virt_b = 
       Array.init mo_num (fun i -> Some (i+1)) , 
       Array.init mo_num (fun i -> Some (i+1)) 
    in
    List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a ki);
    List.iter (fun i -> mos_virt_a.(i-1) <- None) (mos_a kj);
    List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b ki);
    List.iter (fun i -> mos_virt_b.(i-1) <- None) (mos_b kj);

    let mos_virt_a, mos_virt_b = 
      Array.to_list mos_virt_a |> Util.list_some,
      Array.to_list mos_virt_b |> Util.list_some
    in
   
    let result = 
        
        let mos_virt, mos_virt' =
          match s with
          | Spin.Alfa -> mos_virt_a, mos_virt_b
          | Spin.Beta -> mos_virt_b, mos_virt_a
        in
        
        let mos, mos' =
          let alfa = 
            let i = Spindeterminant.bitstring @@ Determinant.alfa ki in
            let j = Spindeterminant.bitstring @@ Determinant.alfa kj in
            Bitstring.to_list (Bitstring.logand i j)
          in
          let beta  =  
            let i = Spindeterminant.bitstring @@ Determinant.beta ki in
            let j = Spindeterminant.bitstring @@ Determinant.beta kj in
            Bitstring.to_list (Bitstring.logand i j)
          in
          match s with
          | Spin.Alfa -> alfa, beta
          | Spin.Beta -> beta, alfa
        in

        if s = s' then            
            let s'' = Spin.other s' in
            sum mos_cabs (fun a -> 
            sum mos  (fun n ->
                 h_two i n a k s  s  *.  f_two j a n l s  s 
              +. h_two i n a l s  s  *.  f_two j a k n s  s 
              -. h_two j n a k s  s  *.  f_two i a n l s  s   
              -. h_two j n a l s  s  *.  f_two i a k n s  s
              )
            +. sum mos_virt (fun m ->
                -. h_two i j a m s  s  *.  f_two m a k l s  s )
            +. sum mos' (fun n ->
                 h_two i n k a s  s'' *.  f_two j a l n s  s''
              +. h_two j n l a s  s'' *.  f_two i a k n s  s''
              -. h_two i n l a s  s'' *.  f_two j a k n s  s''
              -. h_two j n k a s  s'' *.  f_two i a l n s  s''
              )
            )
        else
            sum mos_cabs (fun a ->
            sum mos_virt' (fun m ->
                  h_two i j a m s  s' *.  f_two a m k l s  s' ) +.
            sum mos_virt  (fun m ->
                  h_two i j m a s  s' *.  f_two m a k l s  s' ) +.
            sum mos  (fun n ->
                  h_two n i a k s  s  *.  f_two a j n l s  s'
               +. h_two n j a l s  s' *.  f_two i a k n s  s 
               -. h_two n j k a s  s' *.  f_two i a n l s  s'
               ) +.
            sum mos' (fun n -> if n >= j then 0. else 
                  h_two i n k a s  s' *.  f_two j a l n s' s'
               +. h_two n j a l s' s' *.  f_two i a k n s  s' ) +. 
            sum mos' (fun n -> if n <= j then 0. else 
               -. h_two i n k a s  s' *.  f_two j a n l s' s' 
               -. h_two j n a l s' s' *.  f_two i a k n s  s' ) +.
            sum mos' (fun n -> 
               -. h_two i n a l s  s' *.  f_two a j k n s  s'
               )
            )
    in

    match phase with
        | Phase.Pos -> result
        | Phase.Neg -> -. result


let _ = check 0 integral_value 2 1 2 2

let _ = png_image "2_2_22.png"

let integral_value ki kj = 
    let h, h', p, p', s, s', phase =
          match Excitation.of_det ki kj with
          | Excitation.(Double (phase,
              { hole=h ; particle=p ; spin=s },
              { hole=h'; particle=p'; spin=s'}) )  -> h, h', p, p', s, s', phase
          | _ -> assert false
    in

    let result = 
       if s <> s' then (* Alpha-Beta *)
           sum mos_cabs (fun b -> 
           sum mos_cabs (fun a -> 
               h_two h h' a b s s'  *. f_two a b p p' s s'
           )) 
       else (* Alpha-Alpha / Beta-Beta *)
           sum mos_cabs (fun b -> 
           sum mos_cabs (fun a ->  if b >= a then 0. else
               h_two h h' a b s s'  *. f_two a b p p' s s'
           )) 
    in
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 2 2 2 2

let _ = png_image "3_1_22.png"

let integral_value ki kj = 
    let i, j, m, k, l, n, s1, s2, s3, phase =
          match Excitation.of_det ki kj with
          | Excitation.(Triple (phase,
              { hole=h1 ; particle=p1 ; spin=s1 },
              { hole=h2 ; particle=p2 ; spin=s2 },
              { hole=h3 ; particle=p3 ; spin=s3 }) )  -> h1, h2, h3, p1, p2, p3, s1, s2, s3, phase
          | _ -> assert false
    in

    let result = 
       let open Spin in 
       match s1, s2, s3 with
       | Alfa, Alfa, Alfa
       | Beta, Beta, Beta -> 
           sum mos_cabs (fun a -> 
                h_two i j a k s1 s2 *.  f_two m a l n s3 s3 
             +. h_two i j a n s1 s2 *.  f_two m a k l s3 s2 
             +. h_two i m a l s1 s3 *.  f_two j a k n s2 s3 
             +. h_two j m a k s2 s3 *.  f_two i a l n s1 s3 
             +. h_two j m a n s2 s3 *.  f_two i a k l s1 s2 
             -. h_two i j a l s1 s2 *.  f_two m a k n s3 s3 
             -. h_two i m a k s1 s3 *.  f_two j a l n s2 s3 
             -. h_two i m a n s1 s3 *.  f_two j a k l s2 s2 
             -. h_two j m a l s2 s3 *.  f_two i a k n s1 s3 )
       | Alfa, Alfa, Beta
       | Beta, Beta, Alfa -> 
           sum mos_cabs (fun a -> 
                h_two i j a l s1 s2 *.  f_two a m k n s1 s3
             +. h_two i m k a s1 s3 *.  f_two j a l n s2 s3
             +. h_two j m a n s2 s3 *.  f_two i a k l s1 s2 
             +. h_two j m l a s2 s3 *.  f_two i a k n s1 s3
             -. h_two i j a k s1 s2 *.  f_two a m l n s1 s3
             -. h_two i m a n s1 s3 *.  f_two j a k l s2 s2 
             -. h_two i m l a s1 s3 *.  f_two j a k n s2 s3
             -. h_two j m k a s2 s3 *.  f_two i a l n s1 s3
           ) 
       | Alfa, Beta, Beta
       | Beta, Alfa, Alfa -> 
           sum mos_cabs (fun a -> 
                h_two i j a l s1 s2 *.  f_two a m k n s1 s3 
             +. h_two i m a n s1 s3 *.  f_two a j k l s1 s2 
             +. h_two i m k a s1 s3 *.  f_two j a l n s2 s3 
             +. h_two j m a n s2 s3 *.  f_two i a k l s1 s2 
             -. h_two i j a n s1 s2 *.  f_two a m k l s1 s2 
             -. h_two i j k a s1 s2 *.  f_two m a l n s2 s3 
             -. h_two i m a l s1 s3 *.  f_two a j k n s1 s3 
             -. h_two j m a l s2 s3 *.  f_two i a k n s1 s3 
           ) 
       | Beta, Alfa, Beta
       | Alfa, Beta, Alfa ->  0. (*TODO *)
    in
    match phase with
    | Phase.Pos -> result
    | Phase.Neg -> -. result
     

let _ = check 0 integral_value 3 1 2 2

let ki = det_I.(129)
let kj = det_I.(349)

let alpha_to_string alpha = 
   
      let exc0 = Array.init (aux_num+1) (fun _ -> [|"-";"-"|]) in
 
      let i, j, m, k, l, n,  s, s', s'',  phase =
          match Excitation.of_det ki kj with
          | Excitation.(Triple (phase,
              { hole ; particle ; spin },
              {hole=hole' ; particle=particle' ; spin=spin' }, 
              {hole=hole''; particle=particle''; spin=spin''}  )) ->
            hole, hole', hole'', particle, particle', particle'', spin, spin', spin'', phase
          | _ -> assert false
      in
      let spin = function
        | Spin.Alfa -> 0
        | _ -> 1
      in
        exc0.(i).(spin s  ) <- "i" 
      ; exc0.(j).(spin s' ) <- "j" 
      ; exc0.(k).(spin s  ) <- "k" 
      ; exc0.(l).(spin s' ) <- "l" 
      ; exc0.(m).(spin s'') <- "m" 
      ; exc0.(n).(spin s'') <- "n" 
      ;
      let s0, s0', s0'' = s, s', s'' in
    
      let i, j, k, l, s, s', p1 =
          match Excitation.of_det ki alpha with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
      in
      
      if exc0.(i).(spin s ) =  "-" then exc0.(i).(spin s ) <- "p";
      if exc0.(j).(spin s') =  "-" then exc0.(j).(spin s') <- "p";
      if exc0.(k).(spin s ) =  "-" then exc0.(k).(spin s ) <- if k > mo_num then "a" else "q";
      if exc0.(l).(spin s') =  "-" then exc0.(l).(spin s') <- if l > mo_num then "a" else "q";
      
      let string_h = 
          Printf.sprintf "h_two %s %s %s %s %s %s *.  " 
            exc0.(i).(spin s )
            exc0.(j).(spin s')
            exc0.(k).(spin s )
            exc0.(l).(spin s')
            (if s  = s0  then "s " else if s = s0'  then "s'"  else "s''")
            (if s' = s0' then "s'" else if s = s0'' then "s''" else "s"  )
      in 
      
      let i, j, k, l, s, s', p2 =
          match Excitation.of_det alpha kj with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
      in
      
      let string_f = 
          Printf.sprintf "f_two %s %s %s %s %s %s" 
            exc0.(i).(spin s )
            exc0.(j).(spin s')
            exc0.(k).(spin s )
            exc0.(l).(spin s')
            (if s  = s0  then "s " else if s = s0'  then "s'"  else "s''")
            (if s' = s0' then "s'" else if s = s0'' then "s''" else "s"  )
      in
        (*
        Format.printf "|I> -> |a> : %a | %s\n@." Excitation.pp (Excitation.of_det ki alpha) string_h ;
        Format.printf "|a> -> |J> : %a | %s\n@." Excitation.pp (Excitation.of_det alpha kj) string_f ;
        *)

    
        let sign = 
            if Phase.add p1 p2 = phase then "+." else "-."
        in
        sign ^ string_h ^ string_f
    

let alpha_debug alpha = 
   
      let i, j, k, l, s, s', p1 =
          match Excitation.of_det ki alpha with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
      in
      Printf.printf "%d %d %d %d " i j k l;
      
      let i, j, k, l, s, s', p2 =
          match Excitation.of_det alpha kj with
          | Excitation.(Double (phase, { hole ; particle ; spin }, {hole=hole' ; particle=particle' ; spin=spin' })) ->
            hole, hole', particle, particle', spin, spin', phase
          | _ -> assert false
      in
      (*
        Format.printf "|I> -> |a> : %a | \n@." Excitation.pp (Excitation.of_det ki alpha) ;
        Format.printf "|a> -> |J> : %a | \n@." Excitation.pp (Excitation.of_det alpha kj) ;
        *)
      Printf.printf "%d %d %d %d \n%!" i j k l

      
let strings = 
    Format.printf "|I> -> |J> : %a |\n@." Excitation.pp (Excitation.of_det ki kj) ;
    generate_alphas ki kj 3 1 2 2
    |> Array.of_list
    |> Array.mapi (fun kk alpha -> alpha_to_string alpha)
    |> Array.to_list
    |> List.sort_uniq compare
    |> Array.of_list

let _ = Array.iteri (fun i x -> Printf.printf "%d %s \n%!" i x) strings

let _ =
  let v =
    let alphas =
      generate_alphas ki kj 3 1 2 2
      (*
      |> List.filter (fun alpha ->
        let x =  alpha_to_string alpha in
           x = strings.(6)
        )
        *)
    in
    (*
    List.iter alpha_debug alphas ;
    Printf.printf "\n%!";
    *)
    compute_HaaF ki alphas kj 
  in
  let x = (integral_value ki kj) in
  Printf.printf "%20.8e %20.8e %20.8e\n%!" x v (v-. x)



