/***************************************************************************
 *  PSIMRCC : Copyright (C) 2007 by Francesco Evangelista and Andrew Simmonett
 *  frank@ccc.uga.edu   andysim@ccc.uga.edu
 *  A multireference coupled cluster code
 ***************************************************************************/
#include <libmoinfo/libmoinfo.h>
#include "mrcc.h"
#include "matrix.h"
#include "blas.h"
#include "debugging.h"
#include <libutil/libutil.h>

extern FILE* outfile;

namespace psi{ namespace psimrcc{

using namespace std;

void CCMRCC::build_t1_amplitudes()
{
  build_t1_ia_amplitudes();
  build_t1_IA_amplitudes();
}

void CCMRCC::build_t1_ia_amplitudes()
{
  Timer timer;
  DEBUGGING(1,
    fprintf(outfile,"\n\tBuilding the t1_ia Amplitudes     ...");
    fflush(outfile);
  )
  // Closed-shell
  blas->append("t1_eqns[o][v]{c} = fock[o][v]{c}");
  blas->append("t1_eqns[o][v]{c} += t1[o][v]{c} 2@2 F_ae[v][v]{c}");
  blas->append("t1_eqns[o][v]{c} += - F_mi[o][o]{c} 1@1 t1[o][v]{c}");
  blas->append("t1_eqns[o][v]{c} += #12# t2[ov][ov]{c} 2@1 F_me[ov]{c}");
  blas->append("t1_eqns[o][v]{c} += #12# t2[ov][OV]{c} 2@1 F_me[ov]{c}");

  blas->append("t1_eqns[o][v]{c} += #12# - <[ov]|[ov]> 2@1 t1[ov]{c}");
  blas->append("t1_eqns[o][v]{c} += #21# 2 ([ov]|[vo]) 1@1 t1[ov]{c}");

  blas->append("t1_eqns[o][v]{c} += 1/2 t2[o][ovv]{c} 2@2 <[v]:[ovv]>");
  blas->append("t1_eqns[o][v]{c} +=     t2[o][OvV]{c} 2@2 <[v]|[ovv]>");

  blas->append("t1_eqns[o][v]{c} += -1/2 <[o]:[voo]> 2@2 t2[v][voo]{c}");
  blas->append("t1_eqns[o][v]{c} += - <[o]|[voo]> 2@2 t2[v][VoO]{c}");

  // Open-shell
  blas->append("t1_eqns[o][v]{o} = fock[o][v]{o}");
  blas->append("t1_eqns[o][v]{o} += t1[o][v]{o} 2@2 F_ae[v][v]{o}");
  blas->append("t1_eqns[o][v]{o} += - F_mi[o][o]{o} 1@1 t1[o][v]{o}");
  blas->append("t1_eqns[o][v]{o} += #12# t2[ov][ov]{o} 2@1 F_me[ov]{o}");
  blas->append("t1_eqns[o][v]{o} += #12# t2[ov][OV]{o} 2@1 F_ME[OV]{o}");

  blas->append("t1_eqns[o][v]{o} += #12# - <[ov]|[ov]> 2@1 t1[ov]{o}");
  blas->append("t1_eqns[o][v]{o} += #21#  ([ov]|[vo]) 1@1 t1[ov]{o}");
  blas->append("t1_eqns[o][v]{o} += #21#  ([ov]|[vo]) 1@1 t1[OV]{o}");

  blas->append("t1_eqns[o][v]{o} += 1/2 t2[o][ovv]{o} 2@2 <[v]:[ovv]>");
  blas->append("t1_eqns[o][v]{o} +=     t2[o][OvV]{o} 2@2 <[v]|[ovv]>");

  blas->append("t1_eqns[o][v]{o} += -1/2 <[o]:[voo]> 2@2 t2[v][voo]{o}");
  blas->append("t1_eqns[o][v]{o} += - <[o]|[voo]> 2@2 t2[v][VoO]{o}");

  DEBUGGING(3,blas->print("t1_eqns[o][v]{u}"););


  DEBUGGING(1,
    fprintf(outfile," done. Timing %20.6f s",timer.get());
    fflush(outfile);
  );
}

void CCMRCC::build_t1_IA_amplitudes()
{
  Timer timer;
  DEBUGGING(1,
    fprintf(outfile,"\n\tBuilding the t1_IA Amplitudes     ...");
    fflush(outfile);
  );
  // Closed-shell
  blas->append("t1_eqns[O][V]{c} = t1_eqns[o][v]{c}");

  // Open-shell
  blas->append("t1_eqns[O][V]{o} = fock[O][V]{o}");
  blas->append("t1_eqns[O][V]{o} += t1[O][V]{o} 2@2 F_AE[V][V]{o}");
  blas->append("t1_eqns[O][V]{o} += - F_MI[O][O]{o} 1@1 t1[O][V]{o}");
  blas->append("t1_eqns[O][V]{o} += #12# t2[OV][OV]{o} 2@1 F_ME[OV]{o}");
  blas->append("t1_eqns[O][V]{o} += #12# t2[ov][OV]{o} 1@1 F_me[ov]{o}");

  blas->append("t1_eqns[O][V]{o} += #12# - <[ov]|[ov]> 2@1 t1[OV]{o}");
  blas->append("t1_eqns[O][V]{o} += #21#  ([ov]|[vo]) 1@1 t1[OV]{o}");
  blas->append("t1_eqns[O][V]{o} += #21#  ([ov]|[vo]) 1@1 t1[ov]{o}");

  blas->append("t1_eqns[O][V]{o} += 1/2 t2[O][OVV]{o} 2@2 <[v]:[ovv]>");
  blas->append("t1_eqns[O][V]{o} +=     t2[O][oVv]{o} 2@2 <[v]|[ovv]>");

  blas->append("t1_eqns[O][V]{o} += -1/2 <[o]:[voo]> 2@2 t2[V][VOO]{o}");
  blas->append("t1_eqns[O][V]{o} += - <[o]|[voo]> 2@2 t2[V][vOo]{o}");

  DEBUGGING(3,blas->print("t1_eqns[O][V]{u}"););

  DEBUGGING(1,
    fprintf(outfile," done. Timing %20.6f s",timer.get());
    fflush(outfile);
  );
}


void CCMRCC::build_t1_amplitudes_triples()
{
  build_t1_ia_amplitudes_triples();
  build_t1_IA_amplitudes_triples();
}

/**
 * @brief Computes the contraction
 * \f[ \frac{1}{4} \sum_{mnef} t_{imn}^{aef} <mn||ef> \f]
 * as described in Fig. 1 of Phys. Chem. Chem. Phys. vol. 2, pg. 2047 (2000).
 * This algorithm hence performs
 * \f[ \frac{1}{4} \sum_{mnef} t_{imn}^{aef} <mn||ef> + \sum_{mNeF} t_{imN}^{aeF} <mN|eF> + \frac{1}{4} \sum_{MNEF} t_{iMN}^{aEF} <MN||EF>\rightarrow  \{ \bar{H}_{i}^{a} \} \f]
 */
void CCMRCC::build_t1_ia_amplitudes_triples()
{
  // Loop over references
  for(int ref=0;ref<moinfo->get_nunique();ref++){
    int unique_ref  = moinfo->get_ref_number("u",ref);

    // Grab the temporary matrices
    CCMatTmp  HiaMatTmp     = blas->get_MatTmp("t1_eqns[o][v]",unique_ref,none);
    CCMatTmp  TijkabcMatTmp = blas->get_MatTmp("t3[ooo][vvv]",unique_ref,none);
    CCMatTmp  TijKabCMatTmp = blas->get_MatTmp("t3[ooO][vvV]",unique_ref,none);
    CCMatTmp  TiJKaBCMatTmp = blas->get_MatTmp("t3[oOO][vVV]",unique_ref,none);
    CCMatTmp  ImnefMatTmp   = blas->get_MatTmp("<[oo]:[vv]>",none);
    CCMatTmp  ImNeFMatTmp   = blas->get_MatTmp("<[oo]|[vv]>",none);

    // Grab the indexing for t3[ijk][abc]
    short**   i_tuples = HiaMatTmp->get_left()->get_tuples();
    short**   a_tuples = HiaMatTmp->get_right()->get_tuples();
    short**   mn_tuples = ImnefMatTmp->get_left()->get_tuples();
    short**   ef_tuples = ImnefMatTmp->get_right()->get_tuples();

    double*** Tijkabc_matrix = TijkabcMatTmp->get_matrix();
    double*** TijKabC_matrix = TijKabCMatTmp->get_matrix();
    double*** TiJKaBC_matrix = TiJKaBCMatTmp->get_matrix();
    double*** Hia_matrix     = HiaMatTmp->get_matrix();
    double*** Imnef_matrix   = ImnefMatTmp->get_matrix();
    double*** ImNeF_matrix   = ImNeFMatTmp->get_matrix();
    CCIndex*  ijkIndex       = blas->get_index("[ooo]");
    CCIndex*  abcIndex       = blas->get_index("[vvv]");

    for(int h =0; h < moinfo->get_nirreps();h++){
      size_t i_offset  = HiaMatTmp->get_left()->get_first(h);
      size_t a_offset = HiaMatTmp->get_right()->get_first(h);     
      for(int a = 0;a <HiaMatTmp->get_right_pairpi(h);a++){
        int a_abs = a + a_offset;
        for(int i = 0;i<HiaMatTmp->get_left_pairpi(h);i++){
          int i_abs = i + i_offset;
          // <mn||ef> contribution
          for(int mn_sym =0; mn_sym < moinfo->get_nirreps();mn_sym++){
            size_t mn_offset = ImnefMatTmp->get_left()->get_first(mn_sym);
            size_t ef_offset = ImnefMatTmp->get_right()->get_first(mn_sym);
            for(int ef = 0;ef <ImnefMatTmp->get_right_pairpi(mn_sym);ef++){
              int e = ef_tuples[ef_offset + ef][0];
              int f = ef_tuples[ef_offset + ef][1];
              size_t aef  = abcIndex->get_tuple_index(a_abs,e,f);
              int aef_sym = abcIndex->get_tuple_irrep(a_abs,e,f);
              for(int mn = 0;mn <ImnefMatTmp->get_left_pairpi(mn_sym);mn++){
                int m = mn_tuples[mn_offset + mn][0];
                int n = mn_tuples[mn_offset + mn][1];
                size_t imn  = ijkIndex->get_tuple_index(i_abs,m,n);
                Hia_matrix[h][i][a] += 0.25 * Tijkabc_matrix[aef_sym][imn][aef] * Imnef_matrix[mn_sym][mn][ef];
                Hia_matrix[h][i][a] += 0.25 * TiJKaBC_matrix[aef_sym][imn][aef] * Imnef_matrix[mn_sym][mn][ef];
                Hia_matrix[h][i][a] +=        TijKabC_matrix[aef_sym][imn][aef] * ImNeF_matrix[mn_sym][mn][ef];
              }
            }
          }
        }
      }
    }
  }
//   blas->print("t1_eqns[o][v]{u}");
//   blas->solve("ERROR{u} = 100000000000.0 t1_eqns[o][v]{u} . t1_eqns[o][v]{u}");
//   blas->print("ERROR{u}");
}

/**
 * @brief Computes the contraction
 * \f[ \frac{1}{4} \sum_{mnef} t_{imn}^{aef} <mn||ef> \f]
 * as described in Fig. 1 of Phys. Chem. Chem. Phys. vol. 2, pg. 2047 (2000).
 * This algorithm hence performs
 * \f[ \frac{1}{4} \sum_{mnef} t_{mnI}^{efA} <mn||ef> + \sum_{mNeF} t_{mNI}^{eFA} <mN|eF> + \frac{1}{4} \sum_{MNEF} t_{MNI}^{EFA} <MN||EF>\rightarrow  \{ \bar{H}_{I}^{A} \} \f]
 */
void CCMRCC::build_t1_IA_amplitudes_triples()
{
  // Loop over references
  for(int ref=0;ref<moinfo->get_nunique();ref++){
    int unique_ref  = moinfo->get_ref_number("u",ref);

    // Grab the temporary matrices
    CCMatTmp  HIAMatTmp     = blas->get_MatTmp("t1_eqns[O][V]",unique_ref,none);
    CCMatTmp  TijKabCMatTmp = blas->get_MatTmp("t3[ooO][vvV]",unique_ref,none);
    CCMatTmp  TiJKaBCMatTmp = blas->get_MatTmp("t3[oOO][vVV]",unique_ref,none);
    CCMatTmp  TIJKABCMatTmp = blas->get_MatTmp("t3[OOO][VVV]",unique_ref,none);
    CCMatTmp  ImnefMatTmp   = blas->get_MatTmp("<[oo]:[vv]>",none);
    CCMatTmp  ImNeFMatTmp   = blas->get_MatTmp("<[oo]|[vv]>",none);

    // Grab the indexing for t3[ijk][abc]
    short**   I_tuples = HIAMatTmp->get_left()->get_tuples();
    short**   A_tuples = HIAMatTmp->get_right()->get_tuples();
    short**   mn_tuples = ImnefMatTmp->get_left()->get_tuples();
    short**   ef_tuples = ImnefMatTmp->get_right()->get_tuples();

    double*** TijKabC_matrix = TijKabCMatTmp->get_matrix();
    double*** TiJKaBC_matrix = TiJKaBCMatTmp->get_matrix();
    double*** TIJKABC_matrix = TIJKABCMatTmp->get_matrix();
    double*** HIA_matrix     = HIAMatTmp->get_matrix();
    double*** Imnef_matrix   = ImnefMatTmp->get_matrix();
    double*** ImNeF_matrix   = ImNeFMatTmp->get_matrix();
    CCIndex*  ijkIndex       = blas->get_index("[ooo]");
    CCIndex*  abcIndex       = blas->get_index("[vvv]");

    for(int h =0; h < moinfo->get_nirreps();h++){
      size_t i_offset  = HIAMatTmp->get_left()->get_first(h);
      size_t a_offset = HIAMatTmp->get_right()->get_first(h);     
      for(int a = 0;a <HIAMatTmp->get_right_pairpi(h);a++){
        int a_abs = a + a_offset;
        for(int i = 0;i<HIAMatTmp->get_left_pairpi(h);i++){
          int i_abs = i + i_offset;
          // <mn||ef> contribution
          for(int mn_sym =0; mn_sym < moinfo->get_nirreps();mn_sym++){
            size_t mn_offset = ImnefMatTmp->get_left()->get_first(mn_sym);
            size_t ef_offset = ImnefMatTmp->get_right()->get_first(mn_sym);
            for(int ef = 0;ef <ImnefMatTmp->get_right_pairpi(mn_sym);ef++){
              int e = ef_tuples[ef_offset + ef][0];
              int f = ef_tuples[ef_offset + ef][1];
              size_t efa  = abcIndex->get_tuple_index(e,f,a_abs);
              int efa_sym = abcIndex->get_tuple_irrep(e,f,a_abs);
              for(int mn = 0;mn <ImnefMatTmp->get_left_pairpi(mn_sym);mn++){
                int m = mn_tuples[mn_offset + mn][0];
                int n = mn_tuples[mn_offset + mn][1];
                size_t mni  = ijkIndex->get_tuple_index(m,n,i_abs);
                HIA_matrix[h][i][a] += 0.25 * TijKabC_matrix[efa_sym][mni][efa] * Imnef_matrix[mn_sym][mn][ef];
                HIA_matrix[h][i][a] += 0.25 * TIJKABC_matrix[efa_sym][mni][efa] * Imnef_matrix[mn_sym][mn][ef];
                HIA_matrix[h][i][a] +=        TiJKaBC_matrix[efa_sym][mni][efa] * ImNeF_matrix[mn_sym][mn][ef];
              }
            }
          }
        }
      }
    }
  }
//   blas->print("t1_eqns[O][V]{u}");
//   blas->solve("ERROR{u} = 100000000000.0 t1_eqns[O][V]{u} . t1_eqns[O][V]{u}");
//   blas->print("ERROR{u}");
}

}} /* End Namespaces */
