
use bitmasks
use omp_lib

BEGIN_PROVIDER [ integer(omp_lock_kind), pert_2rdm_lock]
  use f77_zmq
  implicit none
  call omp_init_lock(pert_2rdm_lock)
END_PROVIDER

BEGIN_PROVIDER [integer, n_orb_pert_rdm]
 implicit none
 n_orb_pert_rdm = n_act_orb
END_PROVIDER 

BEGIN_PROVIDER [integer, list_orb_reverse_pert_rdm, (mo_num)]
 implicit none
 list_orb_reverse_pert_rdm = list_act_reverse

END_PROVIDER 

BEGIN_PROVIDER [integer, list_orb_pert_rdm, (n_orb_pert_rdm)]
 implicit none
 list_orb_pert_rdm = list_act

END_PROVIDER

BEGIN_PROVIDER [double precision, pert_2rdm_provider, (n_orb_pert_rdm,n_orb_pert_rdm,n_orb_pert_rdm,n_orb_pert_rdm)]
 implicit none
 pert_2rdm_provider = 0.d0

END_PROVIDER

subroutine fill_buffer_double_rdm(i_generator, sp, h1, h2, bannedOrb, banned, fock_diag_tmp, E0, pt2_data, mat, buf, psi_det_connection, psi_coef_connection_reverse, n_det_connection)
  use bitmasks
  use selection_types
  implicit none
  
  integer, intent(in)           :: n_det_connection
  double precision, intent(in)  :: psi_coef_connection_reverse(N_states,n_det_connection)
  integer(bit_kind), intent(in) :: psi_det_connection(N_int,2,n_det_connection)
  integer, intent(in) :: i_generator, sp, h1, h2
  double precision, intent(in) :: mat(N_states, mo_num, mo_num)
  logical, intent(in) :: bannedOrb(mo_num, 2), banned(mo_num, mo_num)
  double precision, intent(in)           :: fock_diag_tmp(mo_num)
  double precision, intent(in)    :: E0(N_states)
  type(pt2_type), intent(inout)   :: pt2_data
  type(selection_buffer), intent(inout) :: buf
  logical :: ok
  integer :: s1, s2, p1, p2, ib, j, istate, jstate
  integer(bit_kind) :: mask(N_int, 2), det(N_int, 2)
  double precision :: e_pert, delta_E, val, Hii, sum_e_pert, tmp, alpha_h_psi, coef(N_states)
  double precision, external :: diag_H_mat_elem_fock
  double precision :: E_shift

  logical, external :: detEq
  double precision, allocatable :: values(:)
  integer, allocatable          :: keys(:,:)
  integer                       :: nkeys
  integer :: sze_buff
  sze_buff = 5 * mo_num ** 2 
  allocate(keys(4,sze_buff),values(sze_buff))
  nkeys = 0
  if(sp == 3) then
    s1 = 1
    s2 = 2
  else
    s1 = sp
    s2 = sp
  end if
  call apply_holes(psi_det_generators(1,1,i_generator), s1, h1, s2, h2, mask, ok, N_int)
  E_shift = 0.d0

  if (h0_type == 'CFG') then
    j = det_to_configuration(i_generator)
    E_shift = psi_det_Hii(i_generator) - psi_configuration_Hii(j)
  endif

  do p1=1,mo_num
    if(bannedOrb(p1, s1)) cycle
    ib = 1
    if(sp /= 3) ib = p1+1

    do p2=ib,mo_num

! -----
! /!\ Generating only single excited determinants doesn't work because a
! determinant generated by a single excitation may be doubly excited wrt
! to a determinant of the future. In that case, the determinant will be
! detected as already generated when generating in the future with a
! double excitation.
!     
!      if (.not.do_singles) then
!        if ((h1 == p1) .or. (h2 == p2)) then
!          cycle
!        endif
!      endif
!
!      if (.not.do_doubles) then
!        if ((h1 /= p1).and.(h2 /= p2)) then
!          cycle
!        endif
!      endif
! -----

      if(bannedOrb(p2, s2)) cycle
      if(banned(p1,p2)) cycle


      if( sum(abs(mat(1:N_states, p1, p2))) == 0d0) cycle
      call apply_particles(mask, s1, p1, s2, p2, det, ok, N_int)

      if (do_only_cas) then
        integer, external :: number_of_holes, number_of_particles
        if (number_of_particles(det)>0) then
          cycle
        endif
        if (number_of_holes(det)>0) then
          cycle
        endif
      endif

      if (do_ddci) then
        logical, external  :: is_a_two_holes_two_particles
        if (is_a_two_holes_two_particles(det)) then
          cycle
        endif
      endif

      if (do_only_1h1p) then
        logical, external :: is_a_1h1p
        if (.not.is_a_1h1p(det)) cycle
      endif


      Hii = diag_H_mat_elem_fock(psi_det_generators(1,1,i_generator),det,fock_diag_tmp,N_int)

      sum_e_pert = 0d0
      integer :: degree
      call get_excitation_degree(det,HF_bitmask,degree,N_int)
      if(degree == 2)cycle
      do istate=1,N_states
        delta_E = E0(istate) - Hii + E_shift
        alpha_h_psi = mat(istate, p1, p2)
        val = alpha_h_psi + alpha_h_psi
        tmp = dsqrt(delta_E * delta_E + val * val)
        if (delta_E < 0.d0) then
            tmp = -tmp
        endif
        e_pert = 0.5d0 * (tmp - delta_E)
        coef(istate) = e_pert / alpha_h_psi
        print*,e_pert,coef,alpha_h_psi
        pt2_data % pt2(istate) += e_pert
        pt2_data % variance(istate) += alpha_h_psi * alpha_h_psi
      enddo

      do istate=1,N_states
        alpha_h_psi = mat(istate, p1, p2)
        e_pert = coef(istate) * alpha_h_psi
        do jstate=1,N_states
          pt2_data % overlap(jstate,jstate) = coef(istate) * coef(jstate)
        enddo

        if (weight_selection /= 5) then
          ! Energy selection
          sum_e_pert = sum_e_pert + e_pert * selection_weight(istate)

        else
          ! Variance selection
          sum_e_pert = sum_e_pert - alpha_h_psi * alpha_h_psi * selection_weight(istate)
        endif
      end do
      call give_2rdm_pert_contrib(det,coef,psi_det_connection,psi_coef_connection_reverse,n_det_connection,nkeys,keys,values,sze_buff)

      if(sum_e_pert <= buf%mini) then
        call add_to_selection_buffer(buf, det, sum_e_pert)
      end if
    end do
  end do
  call update_keys_values(keys,values,nkeys,n_orb_pert_rdm,pert_2rdm_provider,pert_2rdm_lock)
end


