

! ---

subroutine ppLR_GW_HR_calc(ispin, nOrb, nC, nO, nR, nOO, nVV, nS, lambda, e, eF, n_states_diag, &
                           ERI, eta, rho, Om, U, W)


  implicit none

  integer,          intent(in)  :: ispin
  integer,          intent(in)  :: n_states_diag
  integer,          intent(in)  :: nOO, nVV, nOrb, nC, nO, nR, nS
  double precision, intent(in)  :: lambda, eF, eta
  double precision, intent(in)  :: e(nOrb)
  double precision, intent(in)  :: ERI(nOrb,nOrb,nOrb,nOrb)
  double precision, intent(in)  :: rho(nS,nOrb,nOrb), Om(nS)
  double precision, intent(in)  :: U(nOO+nVV,n_states_diag)
  double precision, intent(out) :: W(nOO+nVV,n_states_diag)

  integer                       :: i, j, ij
  integer                       :: ab
  integer                       :: m
  integer                       :: state
  double precision              :: eta2
  double precision              :: t1, t2
  double precision              :: diff_tot, diff_loc
  double precision, allocatable :: M_ref(:,:)
  double precision, allocatable :: Bpp_ref(:,:), Cpp_ref(:,:), Dpp_ref(:,:)
  double precision, allocatable :: KB_sta(:,:), KC_sta(:,:), KD_sta(:,:)
  double precision, allocatable :: W_ref(:,:)
  double precision, allocatable :: rho_t(:,:,:)

  call wall_time(t1)

  if((nOO+nVV) .le. 30000) then

    call ppLR_GW_HR_calc_oneshot(ispin, nOrb, nC, nO, nR, nOO, nVV, nS, lambda, e(1), eF, n_states_diag, &
                                 ERI(1,1,1,1), eta, rho(1,1,1), Om(1), U(1,1), W(1,1))

  else

    call ppLR_GW_HR_calc_batches(ispin, nOrb, nC, nO, nR, nOO, nVV, nS, lambda, e(1), eF, n_states_diag, &
                                 ERI(1,1,1,1), eta, rho(1,1,1), Om(1), U(1,1), W(1,1))

  endif


!  print*, ' debug ppLR_GW_H_diag:'
!  allocate(M_ref(nOO+nVV,nOO+nVV))
!  allocate(Bpp_ref(nVV,nOO), Cpp_ref(nVV,nVV), Dpp_ref(nOO,nOO))
!  allocate(KB_sta(nVV,nOO), KC_sta(nVV,nVV), KD_sta(nOO,nOO))
!  allocate(rho_t(nOrb,nOrb,nS))
!  allocate(W_ref(nOO+nVV,n_states_diag))
!
!  call ppLR_C(ispin, nOrb, nC, nO, nOrb-nO, nR, nVV,      1d0, e, ERI, Cpp_ref)
!  call ppLR_D(ispin, nOrb, nC, nO, nOrb-nO, nR, nOO,      1d0, e, ERI, Dpp_ref)
!  call ppLR_B(ispin, nOrb, nC, nO, nOrb-nO, nR, nOO, nVV, 1d0,    ERI, Bpp_ref)
!
!  do j = 1, nOrb
!    do i = 1, nOrb
!      do m = 1, nS
!        rho_t(i,j,m) = rho(m,i,j) 
!      enddo
!    enddo
!  enddo
!
!  call RGW_ppBSE_static_kernel_C(ispin, eta, nOrb, nC, nO, nOrb-nO, nR, nS, nVV,      1.d0, ERI, Om, rho_t, KC_sta)
!  call RGW_ppBSE_static_kernel_D(ispin, eta, nOrb, nC, nO, nOrb-nO, nR, nS, nOO,      1.d0, ERI, Om, rho_t, KD_sta)
!  call RGW_ppBSE_static_kernel_B(ispin, eta, nOrb, nC, nO, nOrb-nO, nR, nS, nOO, nVV, 1.d0, ERI, Om, rho_t, KB_sta)
!
!  Cpp_ref = Cpp_ref + KC_sta
!  Dpp_ref = Dpp_ref + KD_sta
!  Bpp_ref = Bpp_ref + KB_sta
!
!  M_ref = 0.d0
!  M_ref(    1:nVV    ,    1:nVV)     = + Cpp_ref(1:nVV,1:nVV)
!  M_ref(nVV+1:nVV+nOO,nVV+1:nVV+nOO) = - Dpp_ref(1:nOO,1:nOO)
!  M_ref(    1:nVV    ,nVV+1:nOO+nVV) = - Bpp_ref(1:nVV,1:nOO)
!  M_ref(nVV+1:nOO+nVV,    1:nVV)     = + transpose(Bpp_ref(1:nVV,1:nOO))
!
!  call dgemm('N', 'N', nOO+nVV, n_states_diag, nOO+nVV, 1.d0, &
!             M_ref(1,1), size(M_ref, 1), U(1,1), size(U, 1),  &
!             0.d0, W_ref(1,1), size(W_ref, 1))
!
!  diff_tot = 0.d0
!  do state = 1, n_states_diag
!    do ab = 1, nOO
!      diff_loc = dabs(W(ab,state) - W_ref(ab,state))
!      if(diff_loc .gt. 1d-10) then
!        print*, ' important diff on:', ab, state
!        print*, W(ab,state), W_ref(ab,state)
!        stop
!      endif
!      diff_tot = diff_tot + diff_loc
!    enddo
!    do ij = nVV+1, nVV+nOO
!      diff_loc = dabs(W(ij,state) - W_ref(ij,state))
!      if(diff_loc .gt. 1d-10) then
!        print*, ' important diff on:', ij, state
!        print*, W(ij,state), W_ref(ij,state)
!        stop
!      endif
!      diff_tot = diff_tot + diff_loc
!    enddo
!  enddo
!  print*, 'diff_tot = ', diff_tot
!
!  deallocate(M_ref)
!  deallocate(Bpp_ref, Cpp_ref, Dpp_ref)
!  deallocate(KB_sta, KC_sta, KD_sta)
!  deallocate(W_ref)
!  deallocate(rho_t)


  call wall_time(t2)
  write(*,'(A50, F12.4)') 'total wall time for ppLR_GW_HR_calc (sec): ', t2-t1

  return
end

! ---

subroutine ppLR_GW_H_diag(ispin, nOrb, nC, nO, nR, nOO, nVV, nS, lambda, e, eF, ERI, eta, rho, Om, H_diag)

  implicit none

  integer,          intent(in)  :: ispin
  integer,          intent(in)  :: nOO, nVV, nOrb, nC, nO, nR, nS
  double precision, intent(in)  :: lambda, eF, eta
  double precision, intent(in)  :: e(nOrb)
  double precision, intent(in)  :: ERI(nOrb,nOrb,nOrb,nOrb)
  double precision, intent(in)  :: rho(nS,nOrb,nOrb), Om(nS)
  double precision, intent(out) :: H_diag(nOO+nVV)

  integer                       :: i, j, ij, k, l, kl
  integer                       :: a, b, c, d, ab, cd
  integer                       :: m
  double precision              :: chi, eps
  double precision              :: t1, t2

  double precision, external    :: Kronecker_delta

  call wall_time(t1)

  if(ispin .eq. 1) then

    ab = 0
    do a = nO+1, nOrb-nR
      do b = a, nOrb-nR
        ab = ab + 1
        cd = 0
        do c = nO+1, nOrb-nR
          do d = c, nOrb-nR
            cd = cd + 1
            if(a .ne. c) cycle
            if(b .ne. d) cycle
            H_diag(ab) = e(a) + e(b) - eF &
                       + lambda * (ERI(a,b,c,d) + ERI(a,b,d,c)) / dsqrt( (1.d0 + Kronecker_delta(a, b)) &
                                                                       * (1.d0 + Kronecker_delta(c, d)))
            chi = 0.d0
            do m = 1, nS
              eps = Om(m)**2 + eta**2
              chi = chi - rho(m,a,c) * rho(m,b,d) * Om(m) / eps &
                        - rho(m,a,d) * rho(m,b,c) * Om(m) / eps
            end do
            H_diag(ab) = H_diag(ab) + 4.d0 * lambda * chi / dsqrt( (1.d0 + Kronecker_delta(a, b)) &
                                                                 * (1.d0 + Kronecker_delta(c, d)))
          enddo
        enddo
      enddo ! b
    enddo ! a

    ij = nVV
    do i = nC+1, nO
      do j = i, nO
        ij = ij + 1
        kl = 0
        do k = nC+1, nO
          do l = k, nO
            kl = kl + 1
            if(i .ne. k) cycle
            if(j .ne. l) cycle
            H_diag(ij) = e(i) + e(j) - eF &
                       - lambda * (ERI(i,j,k,l) + ERI(i,j,l,k)) / dsqrt( (1.d0 + Kronecker_delta(i, j)) &
                                                                       * (1.d0 + Kronecker_delta(k, l)))
            chi = 0.d0
            do m = 1, nS
              eps = Om(m)**2 + eta**2
              chi = chi - rho(m,i,k) * rho(m,j,l) * Om(m) / eps &
                        - rho(m,i,l) * rho(m,j,k) * Om(m) / eps
            enddo
            H_diag(ij) = H_diag(ij) - 4.d0 * lambda * chi / dsqrt( (1.d0 + Kronecker_delta(i, j)) &
                                                                 * (1.d0 + Kronecker_delta(k, l)))
          enddo
        enddo
      enddo ! j
    enddo ! i

  elseif(ispin .eq. 2) then

    ab = 0
    do a = nO+1, nOrb-nR
      do b = a+1, nOrb-nR
        ab = ab + 1
        cd = 0
        do c = nO+1, nOrb-nR
          do d = c+1, nOrb-nR
            cd = cd + 1
            if(a .ne. c) cycle
            if(b .ne. d) cycle
            H_diag(ab) = e(a) + e(b) - eF + lambda * (ERI(a,b,c,d) - ERI(a,b,d,c))
            chi = 0.d0
            do m = 1, nS
              eps = Om(m)**2 + eta**2
              chi = chi - rho(m,a,c) * rho(m,b,d) * Om(m) / eps &
                        + rho(m,a,d) * rho(m,b,c) * Om(m) / eps
            enddo
            H_diag(ab) = H_diag(ab) + 4.d0 * lambda * chi
          enddo
        enddo
      enddo ! b
    enddo ! a
  
    ij = nVV
    do i = nC+1, nO
      do j = i+1, nO
        ij = ij + 1
        kl = 0
        do k = nC+1, nO
          do l = k+1, nO
            kl = kl + 1
            if(i .ne. k) cycle
            if(j .ne. l) cycle
            H_diag(ij) = e(i) + e(j) - eF - lambda * (ERI(i,j,k,l) - ERI(i,j,l,k))
            chi = 0.d0
            do m = 1, nS
              eps = Om(m)**2 + eta**2
              chi = chi - rho(m,i,k) * rho(m,j,l) * Om(m) / eps &
                        + rho(m,i,l) * rho(m,j,k) * Om(m) / eps
            end do
            H_diag(ij) = H_diag(ij) - 4.d0 * lambda * chi
          enddo
        enddo
      enddo ! j
    enddo ! i

  else

    print*, ' Error in ppLR_GW_H_diag'
    print*, ' ispin is not supported'
    print*, ' ispin = ', ispin
    stop

  endif

  call wall_time(t2)
  write(*,'(A50, F12.4)') 'total wall time for ppLR_GW_H_diag (sec): ', t2-t1

  return
end

! ---

subroutine ppLR_GW_HR_calc_oneshot(ispin, nOrb, nC, nO, nR, nOO, nVV, nS, lambda, e, eF, n_states_diag, &
                                   ERI, eta, rho, Om, U, W)

  implicit none

  integer,          intent(in)  :: ispin
  integer,          intent(in)  :: n_states_diag
  integer,          intent(in)  :: nOO, nVV, nOrb, nC, nO, nR, nS
  double precision, intent(in)  :: lambda, eF, eta
  double precision, intent(in)  :: e(nOrb)
  double precision, intent(in)  :: ERI(nOrb,nOrb,nOrb,nOrb)
  double precision, intent(in)  :: rho(nS,nOrb,nOrb), Om(nS)
  double precision, intent(in)  :: U(nOO+nVV,n_states_diag)
  double precision, intent(out) :: W(nOO+nVV,n_states_diag)

  integer                       :: p, q, r, s
  integer                       :: i, j, ij, k, l, kl
  integer                       :: a, b, c, d, ab, cd
  integer                       :: a0, aa, i0, ii
  integer                       :: m
  logical                       :: a_eq_c, i_eq_k
  double precision              :: mat_tmp
  double precision              :: eta2
  double precision              :: tmp_e, tmp_ab, tmp_ij
  double precision, allocatable :: Om_tmp(:), H_mat(:,:)
  double precision, allocatable :: rho_tmp(:,:,:)
  double precision, allocatable :: rho_Om_rho(:,:,:,:)


  if(ispin .eq. 1) then

    a0 = nOrb - nR - nO

    eta2 = eta * eta

    allocate(Om_tmp(nS))
    do m = 1, nS
      Om_tmp(m) = Om(m) / (Om(m) * Om(m) + eta2)
    enddo

    allocate(rho_tmp(nOrb,nOrb,nS))
    !$OMP PARALLEL       &
    !$OMP DEFAULT(NONE)  &
    !$OMP PRIVATE(p,q,m) &
    !$OMP SHARED(nOrb, nS, rho_tmp, Om_tmp, rho)
    !$OMP DO
    do p = 1, nOrb
      do q = 1, nOrb
        do m = 1, nS
          rho_tmp(p,q,m) = Om_tmp(m) * rho(m,p,q)
        enddo
      enddo
    enddo
    !$OMP END DO
    !$OMP END PARALLEL
    deallocate(Om_tmp)

    allocate(rho_Om_rho(nOrb,nOrb,nOrb,nOrb))

    !$OMP PARALLEL         &
    !$OMP DEFAULT(NONE)    &
    !$OMP PRIVATE(p,q,r,s) &
    !$OMP SHARED(nOrb, rho_Om_rho, ERI)
    !$OMP DO
    do p = 1, nOrb
      do q = 1, nOrb
        do r = 1, nOrb
          do s = 1, nOrb
            rho_Om_rho(s,r,q,p) = ERI(s,r,q,p)
          enddo
        enddo
      enddo
    enddo
    !$OMP END DO
    !$OMP END PARALLEL

    call dgemm("N", "N", nOrb*nOrb, nOrb*nOrb, nS, -4.d0, &
               rho_tmp(1,1,1), nOrb*nOrb, rho(1,1,1), nS, &
               1.d0, rho_Om_rho(1,1,1,1), nOrb*nOrb)
    deallocate(rho_tmp)

    allocate(H_mat(nVV,nOO+nVV))

    !$OMP PARALLEL                                  &
    !$OMP DEFAULT(NONE)                             &
    !$OMP PRIVATE(a, b, aa, ab, c, d, cd, i, j, ij, &
    !$OMP         a_eq_c, tmp_e, tmp_ab, mat_tmp)   &
    !$OMP SHARED(a0, nC, nO, nOrb, nR, nVV, eF,     &
    !$OMP        lambda, e, rho_Om_rho, H_mat)
    !$OMP DO SCHEDULE(GUIDED)
    do a = nO+1, nOrb-nR
      aa = a0 * (a - nO - 1) - (a - nO - 1) * (a - nO) / 2 - nO
      do b = a, nOrb-nR
        ab = aa + b

        tmp_e = e(a) + e(b) - eF

        tmp_ab = lambda
        if(a .eq. b) then
          tmp_ab = 0.7071067811865475d0 * tmp_ab
        endif

        cd = 0
        do c = nO+1, nOrb-nR
          a_eq_c = a .eq. c
          do d = c, nOrb-nR
            cd = cd + 1

            mat_tmp = tmp_ab
            if(c .eq. d) then
              mat_tmp = 0.7071067811865475d0 * mat_tmp
            endif

            mat_tmp = mat_tmp * (rho_Om_rho(d,b,a,c) + rho_Om_rho(d,a,b,c))

            if(a_eq_c) then
              if(b .eq. d) mat_tmp = mat_tmp + tmp_e
            endif

            H_mat(ab,cd) = mat_tmp
          enddo ! d
        enddo ! c

        ij = nVV
        do i = nC+1, nO
          do j = i, nO
            ij = ij + 1

            mat_tmp = tmp_ab
            if(i .eq. j) then
              mat_tmp = 0.7071067811865475d0 * mat_tmp
            endif

            mat_tmp = mat_tmp * (rho_Om_rho(j,b,a,i) + rho_Om_rho(j,a,b,i))

            H_mat(ab,ij) = -mat_tmp
          enddo ! j
        enddo ! i

      enddo ! b
    enddo ! a
    !$OMP END DO
    !$OMP END PARALLEL

    call dgemm("N", "N", nVV, n_states_diag, nOO+nVV, 1.d0,    &
               H_mat(1,1), size(H_mat, 1), U(1,1), size(U, 1), &
               0.d0, W(1,1), size(W, 1))

    deallocate(H_mat)

    ! ---

    allocate(H_mat(nOO,nOO+nVV))

    i0 = nO - nC

    !$OMP PARALLEL                                  &
    !$OMP DEFAULT(NONE)                             &
    !$OMP PRIVATE(i, j, ii, ij, a, b, ab, k, l, kl, &
    !$OMP         i_eq_k,tmp_e, tmp_ij, mat_tmp)    &
    !$OMP SHARED(i0, nC, nO, nOrb, nR, nVV, eF,     &
    !$OMP        lambda, e, rho_Om_rho, H_mat)
    !$OMP DO SCHEDULE(GUIDED)
    do i = nC+1, nO
      ii = i0 * (i - nC - 1) - (i - nC - 1) * (i - nC) / 2 - nC
      do j = i, nO
        ij = ii + j

        tmp_e = e(i) + e(j) - eF

        tmp_ij = lambda
        if(i .eq. j) then
          tmp_ij = 0.7071067811865475d0 * tmp_ij
        endif
  
        ab = 0
        do a = nO+1, nOrb-nR
          do b = a, nOrb-nR
            ab = ab + 1

            mat_tmp = tmp_ij
            if(a .eq. b) then
              mat_tmp = 0.7071067811865475d0 * mat_tmp
            endif
    
            mat_tmp = mat_tmp * (rho_Om_rho(b,i,j,a) + rho_Om_rho(b,j,i,a))

            H_mat(ij,ab) = mat_tmp
          enddo ! b
        enddo ! a

        kl = nVV
        do k = nC+1, nO
          i_eq_k = i .eq. k
          do l = k, nO
            kl = kl + 1
    
            mat_tmp = tmp_ij
            if(k .eq. l) then
              mat_tmp = 0.7071067811865475d0 * mat_tmp
            endif

            mat_tmp = mat_tmp * (rho_Om_rho(l,j,i,k) + rho_Om_rho(l,i,j,k))

            if(i_eq_k) then
              if(j .eq. l) mat_tmp = mat_tmp - tmp_e
            endif

            H_mat(ij,kl) = -mat_tmp
          enddo ! l
        enddo ! k
      enddo ! j
    enddo ! i
    !$OMP END DO
    !$OMP END PARALLEL

    call dgemm("N", "N", nOO, n_states_diag, nOO+nVV, 1.d0,    &
               H_mat(1,1), size(H_mat, 1), U(1,1), size(U, 1), &
               0.d0, W(nVV+1,1), size(W, 1))

    deallocate(H_mat)
    deallocate(rho_Om_rho)

    ! ---

  elseif(ispin .eq. 2) then

    ! ---

    allocate(Om_tmp(nS))

    a0 = nOrb - nR - nO - 1

    eta2 = eta * eta
    do m = 1, nS
      Om_tmp(m) = Om(m) / (Om(m) * Om(m) + eta2)
    enddo

    allocate(rho_tmp(nOrb,nOrb,nS))
    !$OMP PARALLEL       &
    !$OMP DEFAULT(NONE)  &
    !$OMP PRIVATE(p,q,m) &
    !$OMP SHARED(nOrb, nS, rho_tmp, Om_tmp, rho)
    !$OMP DO
    do p = 1, nOrb
      do q = 1, nOrb
        do m = 1, nS
          rho_tmp(p,q,m) = Om_tmp(m) * rho(m,p,q)
        enddo
      enddo
    enddo
    !$OMP END DO
    !$OMP END PARALLEL
    deallocate(Om_tmp)

    allocate(rho_Om_rho(nOrb,nOrb,nOrb,nOrb))
    !$OMP PARALLEL         &
    !$OMP DEFAULT(NONE)    &
    !$OMP PRIVATE(p,q,r,s) &
    !$OMP SHARED(nOrb, rho_Om_rho, ERI)
    !$OMP DO
    do p = 1, nOrb
      do q = 1, nOrb
        do r = 1, nOrb
          do s = 1, nOrb
            rho_Om_rho(s,r,q,p) = ERI(s,r,q,p)
          enddo
        enddo
      enddo
    enddo
    !$OMP END DO
    !$OMP END PARALLEL
    call dgemm("N", "N", nOrb*nOrb, nOrb*nOrb, nS, 4.d0*lambda, &
               rho_tmp(1,1,1), nOrb*nOrb, rho(1,1,1), nS,       &
               lambda, rho_Om_rho(1,1,1,1), nOrb*nOrb)
    deallocate(rho_tmp)

    allocate(H_mat(nVV,nOO+nVV))

    !$OMP PARALLEL                                  &
    !$OMP DEFAULT(NONE)                             &
    !$OMP PRIVATE(a, b, aa, ab, c, d, cd, i, j, ij, &
    !$OMP         a_eq_c, tmp_e, tmp_ab, mat_tmp)   &
    !$OMP SHARED(a0, nC, nO, nOrb, nR, nS, nVV, eF, &
    !$OMP        e, rho_Om_rho, H_mat)
    !$OMP DO SCHEDULE(GUIDED)
    do a = nO+1, nOrb-nR
      aa = a0 * (a - nO - 1) - (a - nO - 1) * (a - nO) / 2 - nO - 1
      do b = a+1, nOrb-nR
        ab = aa + b

        tmp_e = e(a) + e(b) - eF

        cd = 0
        do c = nO+1, nOrb-nR
          a_eq_c = a .eq. c
          do d = c+1, nOrb-nR
            cd = cd + 1
    
            mat_tmp = rho_Om_rho(d,a,b,c) - rho_Om_rho(d,b,a,c)

            if(a_eq_c) then
              if(b .eq. d) mat_tmp = mat_tmp + tmp_e
            endif

            H_mat(ab,cd) = mat_tmp
          enddo ! d
        enddo ! c

        ij = nVV
        do i = nC+1, nO
          do j = i+1, nO
            ij = ij + 1

            mat_tmp = rho_Om_rho(j,a,b,i) - rho_Om_rho(j,b,a,i)

            H_mat(ab,ij) = -mat_tmp
          enddo ! j
        enddo ! i
      enddo ! b
    enddo ! a
    !$OMP END DO
    !$OMP END PARALLEL

    call dgemm("N", "N", nVV, n_states_diag, nOO+nVV, 1.d0,    &
               H_mat(1,1), size(H_mat, 1), U(1,1), size(U, 1), &
               0.d0, W(1,1), size(W, 1))

    deallocate(H_mat)

    ! ---

    i0 = nO - nC - 1
    allocate(H_mat(nOO,nOO+nVV))

    !$OMP PARALLEL                                  &
    !$OMP DEFAULT(NONE)                             &
    !$OMP PRIVATE(i, j, ii, ij, a, b, ab, k, l, kl, &
    !$OMP         i_eq_k, tmp_e, tmp_ij, mat_tmp)   &
    !$OMP SHARED(i0, nC, nO, nOrb, nR, nVV, eF,     &
    !$OMP        e, rho_Om_rho, H_mat)
    !$OMP DO SCHEDULE(GUIDED)
    do i = nC+1, nO
      ii = i0 * (i - nC - 1) - (i - nC - 1) * (i - nC) / 2 - nC - 1
      do j = i+1, nO
        ij = ii + j

        tmp_e = e(i) + e(j) - eF
  
        ab = 0
        do a = nO+1, nOrb-nR
          do b = a+1, nOrb-nR
            ab = ab + 1
    
            mat_tmp = rho_Om_rho(b,i,j,a) - rho_Om_rho(b,j,i,a)

            H_mat(ij,ab) = mat_tmp
          enddo ! b
        enddo ! a

        kl = nVV
        do k = nC+1, nO
          i_eq_k = i .eq. k
          do l = k+1, nO
            kl = kl + 1
   
            mat_tmp = rho_Om_rho(l,i,j,k) - rho_Om_rho(l,j,i,k)

            if(i_eq_k) then
              if(j .eq. l) mat_tmp = mat_tmp - tmp_e
            endif

            H_mat(ij,kl) = -mat_tmp
          enddo ! l
        enddo ! k
      enddo ! j
    enddo ! i
    !$OMP END DO
    !$OMP END PARALLEL

    call dgemm("N", "N", nOO, n_states_diag, nOO+nVV, 1.d0,    &
               H_mat(1,1), size(H_mat, 1), U(1,1), size(U, 1), &
               0.d0, W(nVV+1,1), size(W, 1))

    deallocate(H_mat)
    deallocate(rho_Om_rho)

    ! ---

  else

    print*, ' Error in ppLR_GW_HR_calc_oneshot'
    print*, ' ispin is not supported'
    print*, ' ispin = ', ispin
    stop

  endif

  return
end

! ---

subroutine ppLR_GW_HR_calc_batches(ispin, nOrb, nC, nO, nR, nOO, nVV, nS, lambda, e, eF, n_states_diag, &
                                   ERI, eta, rho, Om, U, W)


  use omp_lib

  implicit none

  integer,          intent(in)  :: ispin
  integer,          intent(in)  :: n_states_diag
  integer,          intent(in)  :: nOO, nVV, nOrb, nC, nO, nR, nS
  double precision, intent(in)  :: lambda, eF, eta
  double precision, intent(in)  :: e(nOrb)
  double precision, intent(in)  :: ERI(nOrb,nOrb,nOrb,nOrb)
  double precision, intent(in)  :: rho(nS,nOrb,nOrb), Om(nS)
  double precision, intent(in)  :: U(nOO+nVV,n_states_diag)
  double precision, intent(out) :: W(nOO+nVV,n_states_diag)

  integer                       :: i, j, ij, k, l, kl
  integer                       :: a, b, c, d, ab, cd
  integer                       :: a0, aa, i0, ii, bb
  integer                       :: m
  integer                       :: state
  logical                       :: a_eq_c, i_eq_k
  double precision              :: mat_tmp, chi, eps
  double precision              :: eta2
  double precision              :: tmp_e, tmp_ab, tmp_ij
  double precision, allocatable :: Om_tmp(:), H_mat(:,:)

  double precision, external    :: Kronecker_delta


  if(ispin .eq. 1) then

    call omp_set_max_active_levels(1)

    allocate(Om_tmp(nS))

    a0 = nOrb - nR - nO

    eta2 = eta * eta
    do m = 1, nS
      Om_tmp(m) = Om(m) / (Om(m) * Om(m) + eta2)
    enddo

    !$OMP PARALLEL                                            &
    !$OMP DEFAULT(NONE)                                       &
    !$OMP PRIVATE(a, b, aa, ab, c, d, cd, i, j, ij, m, state, &
    !$OMP         bb, a_eq_c, tmp_e, tmp_ab, chi, mat_tmp,    &
    !$OMP         H_mat)                                      &
    !$OMP SHARED(nC, nO, nOrb, nR, nS, n_states_diag, nVV,    &
    !$OMP        nOO, a0, eF, lambda, e, Om_tmp, rho, ERI, U, W)

    allocate(H_mat(nOO+nVV,a0))

    !$OMP DO SCHEDULE(GUIDED)
    do a = nO+1, nOrb-nR
      aa = a0 * (a - nO - 1) - (a - nO - 1) * (a - nO) / 2 - nO
      do b = a, nOrb-nR
        ab = aa + b

        bb = b - a + 1

        tmp_e = e(a) + e(b) - eF

        tmp_ab = lambda
        if(a .eq. b) then
          tmp_ab = 0.7071067811865475d0 * tmp_ab
        endif

        cd = 0
        do c = nO+1, nOrb-nR
          a_eq_c = a .eq. c
          do d = c, nOrb-nR
            cd = cd + 1

            chi = 0.d0
            do m = 1, nS
              chi = chi - Om_tmp(m) * (rho(m,a,c) * rho(m,b,d) + rho(m,a,d) * rho(m,b,c))
            enddo

            mat_tmp = tmp_ab
            if(c .eq. d) then
              mat_tmp = 0.7071067811865475d0 * mat_tmp
            endif

            mat_tmp = mat_tmp * (4.d0 * chi + ERI(d,c,b,a) + ERI(d,c,a,b))

            if(a_eq_c) then
              if(b .eq. d) mat_tmp = mat_tmp + tmp_e
            endif

            H_mat(cd,bb) = mat_tmp
          enddo ! d
        enddo ! c

        ij = nVV
        do i = nC+1, nO
          do j = i, nO
            ij = ij + 1

            chi = 0.d0
            do m = 1, nS
              chi = chi - Om_tmp(m) * (rho(m,i,a) * rho(m,j,b) + rho(m,i,b) * rho(m,a,j))
            enddo

            mat_tmp = tmp_ab
            if(i .eq. j) then
              mat_tmp = 0.7071067811865475d0 * mat_tmp
            endif

            mat_tmp = mat_tmp * (4.d0 * chi + ERI(j,i,b,a) + ERI(j,i,a,b))

            H_mat(ij,bb) = -mat_tmp
          enddo ! j
        enddo ! i
      enddo ! b

      call dgemm("T", "N", nOrb-nR-a+1, n_states_diag, nOO+nVV, 1.d0, &
                 H_mat(1,1), size(H_mat, 1), U(1,1), size(U, 1),      &
                 0.d0, W(aa+a,1), size(W, 1))

    enddo ! a
    !$OMP END DO

    deallocate(H_mat)

    !$OMP END PARALLEL

    ! ---

    i0 = nO - nC
    allocate(H_mat(nOO,nOO+nVV))

    !$OMP PARALLEL                                            &
    !$OMP DEFAULT(NONE)                                       &
    !$OMP PRIVATE(i, j, ii, ij, a, b, ab, k, l, kl, m, state, &
    !$OMP         i_eq_k, tmp_e, tmp_ij, chi, mat_tmp)        &
    !$OMP SHARED(nC, nO, nOrb, nR, nS, nVV, i0,               &
    !$OMP        eF, lambda, e, Om_tmp, rho, ERI, U, H_mat)
    !$OMP DO SCHEDULE(GUIDED)
    do i = nC+1, nO
      ii = i0 * (i - nC - 1) - (i - nC - 1) * (i - nC) / 2 - nC
      do j = i, nO
        ij = ii + j

        tmp_e = e(i) + e(j) - eF

        tmp_ij = lambda
        if(i .eq. j) then
          tmp_ij = 0.7071067811865475d0 * tmp_ij
        endif
  
        ab = 0
        do a = nO+1, nOrb-nR
          do b = a, nOrb-nR
            ab = ab + 1

            chi = 0.d0
            do m = 1, nS
              chi = chi - Om_tmp(m) * (rho(m,i,a) * rho(m,j,b) + rho(m,i,b) * rho(m,a,j))
            enddo

            mat_tmp = tmp_ij
            if(a .eq. b) then
              mat_tmp = 0.7071067811865475d0 * mat_tmp
            endif
    
            mat_tmp = mat_tmp * (4.d0 * chi + ERI(b,a,j,i) + ERI(b,a,i,j))

            H_mat(ij,ab) = mat_tmp
          enddo ! b
        enddo ! a

        kl = nVV
        do k = nC+1, nO
          i_eq_k = i .eq. k
          do l = k, nO
            kl = kl + 1
    
            chi = 0.d0
            do m = 1, nS
              chi = chi - Om_tmp(m) * (rho(m,i,k) * rho(m,j,l) + rho(m,i,l) * rho(m,j,k))
            enddo

            mat_tmp = tmp_ij
            if(k .eq. l) then
              mat_tmp = 0.7071067811865475d0 * mat_tmp
            endif

            mat_tmp = mat_tmp * (4.d0 * chi + ERI(l,k,j,i) + ERI(l,k,i,j))

            if(i_eq_k) then
              if(j .eq. l) mat_tmp = mat_tmp - tmp_e
            endif

            H_mat(ij,kl) = -mat_tmp
          enddo ! l
        enddo ! k
      enddo ! j
    enddo ! i
    !$OMP END DO
    !$OMP END PARALLEL

    call dgemm("N", "N", nOO, n_states_diag, nOO+nVV, 1.d0,    &
               H_mat(1,1), size(H_mat, 1), U(1,1), size(U, 1), &
               0.d0, W(nVV+1,1), size(W, 1))
    deallocate(H_mat)

    deallocate(Om_tmp)

    ! ---

  elseif(ispin .eq. 2) then

    call omp_set_max_active_levels(1)

    allocate(Om_tmp(nS))

    a0 = nOrb - nR - nO - 1

    eta2 = eta * eta
    do m = 1, nS
      Om_tmp(m) = Om(m) / (Om(m) * Om(m) + eta2)
    enddo

    ! ---

    !$OMP PARALLEL                                            &
    !$OMP DEFAULT(NONE)                                       &
    !$OMP PRIVATE(a, b, aa, ab, c, d, cd, i, j, ij, m, state, &
    !$OMP         bb, a_eq_c, tmp_e, tmp_ab, chi, mat_tmp,    &
    !$OMP         H_mat)                                      &
    !$OMP SHARED(nC, nO, nOrb, nR, nS, n_states_diag, nVV,    &
    !$OMP        nOO, a0, eF, lambda, e, Om_tmp, rho, ERI, U, W)

    allocate(H_mat(nOO+nVV,a0))

    !$OMP DO SCHEDULE(GUIDED)
    do a = nO+1, nOrb-nR
      aa = a0 * (a - nO - 1) - (a - nO - 1) * (a - nO) / 2 - nO - 1
      do b = a+1, nOrb-nR
        ab = aa + b

        bb = b - a

        tmp_e = e(a) + e(b) - eF

        cd = 0
        do c = nO+1, nOrb-nR
          a_eq_c = a .eq. c
          do d = c+1, nOrb-nR
            cd = cd + 1
    
            mat_tmp = lambda * (ERI(d,c,b,a) - ERI(d,c,a,b))

            chi = 0.d0
            do m = 1, nS
              chi = chi - Om_tmp(m) * (rho(m,a,c) * rho(m,b,d) - rho(m,a,d) * rho(m,b,c))
            enddo
            mat_tmp = mat_tmp + 4.d0 * lambda * chi

            if(a_eq_c) then
              if(b .eq. d) mat_tmp = mat_tmp + tmp_e
            endif

            H_mat(cd,bb) = mat_tmp
          enddo ! d
        enddo ! c

        ij = nVV
        do i = nC+1, nO
          do j = i+1, nO
            ij = ij + 1

            mat_tmp = lambda * (ERI(j,i,b,a) - ERI(j,i,a,b))

            chi = 0.d0
            do m = 1, nS
              chi = chi - Om_tmp(m) * (rho(m,i,a) * rho(m,j,b) - rho(m,i,b) * rho(m,a,j))
            end do
            mat_tmp = mat_tmp + 4.d0 * lambda * chi

            H_mat(ij,bb) = -mat_tmp
          enddo ! j
        enddo ! i
      enddo ! b

      call dgemm("T", "N", nOrb-nR-a, n_states_diag, nOO+nVV, 1.d0, &
                 H_mat(1,1), size(H_mat, 1), U(1,1), size(U, 1),    &
                 0.d0, W(aa+a+1,1), size(W, 1))

    enddo ! a
    !$OMP END DO

    deallocate(H_mat)

    !$OMP END PARALLEL

    ! ---

    i0 = nO - nC - 1
    allocate(H_mat(nOO,nOO+nVV))

    !$OMP PARALLEL                                            &
    !$OMP DEFAULT(NONE)                                       &
    !$OMP PRIVATE(i, j, ii, ij, a, b, ab, k, l, kl, m, state, &
    !$OMP         i_eq_k, tmp_e, tmp_ij, chi, mat_tmp)        &
    !$OMP SHARED(nC, nO, nOrb, nR, nS, nVV, i0,               &
    !$OMP        eF, lambda, e, Om_tmp, rho, ERI, U, H_mat)
    !$OMP DO SCHEDULE(GUIDED)
    do i = nC+1, nO
      ii = i0 * (i - nC - 1) - (i - nC - 1) * (i - nC) / 2 - nC - 1
      do j = i+1, nO
        ij = ii + j

        tmp_e = e(i) + e(j) - eF
  
        ab = 0
        do a = nO+1, nOrb-nR
          do b = a+1, nOrb-nR
            ab = ab + 1
    
            mat_tmp = lambda * (ERI(b,a,j,i) - ERI(b,a,i,j))

            chi = 0.d0
            do m = 1, nS
              chi = chi - Om_tmp(m) * (rho(m,i,a) * rho(m,j,b) - rho(m,i,b) * rho(m,a,j))
            enddo
            mat_tmp = mat_tmp + 4.d0 * lambda * chi

            H_mat(ij,ab) = mat_tmp
          enddo ! b
        enddo ! a

        kl = nVV
        do k = nC+1, nO
          i_eq_k = i .eq. k
          do l = k+1, nO
            kl = kl + 1
   
            mat_tmp = lambda * (ERI(l,k,j,i) - ERI(l,k,i,j))

            chi = 0.d0
            do m = 1, nS
              chi = chi - Om_tmp(m) * (rho(m,i,k) * rho(m,j,l) - rho(m,i,l) * rho(m,j,k))
            enddo
            mat_tmp = mat_tmp + 4.d0 * lambda * chi

            if(i_eq_k) then
              if(j .eq. l) mat_tmp = mat_tmp - tmp_e
            endif

            H_mat(ij,kl) = -mat_tmp
          enddo ! l
        enddo ! k
      enddo ! j
    enddo ! i
    !$OMP END DO
    !$OMP END PARALLEL

    call dgemm("N", "N", nOO, n_states_diag, nOO+nVV, 1.d0,    &
               H_mat(1,1), size(H_mat, 1), U(1,1), size(U, 1), &
               0.d0, W(nVV+1,1), size(W, 1))
    deallocate(H_mat)

    deallocate(Om_tmp)

    ! ---

  else

    print*, ' Error in ppLR_GW_HR_calc_batches'
    print*, ' ispin is not supported'
    print*, ' ispin = ', ispin
    stop

  endif

  return
end

! ---

