//
// ccsd_r12_e.cc
//
// Copyright (C) 2009 Toru Shiozaki
//
// Author: Toru Shiozaki <shiozaki.toru@gmail.com>
// Maintainer: TS
//
// This file is part of the SC Toolkit.
//
// The SC Toolkit is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// The SC Toolkit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with the SC Toolkit; see the file COPYING.LIB.  If not, write to
// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
//
// The U.S. Government is granted a limited license as per AL 91-7.
//

// This is a C++ code generated by SMITH

#include <algorithm>
#include <chemistry/qc/ccr12/ccsd_r12_e.h>
#include <chemistry/qc/ccr12/tensor.h>
#include <chemistry/qc/ccr12/mtensor.h>
using namespace sc;


CCSD_R12_E::CCSD_R12_E(CCR12_Info* info):z(info){};
CCSD_R12_E::~CCSD_R12_E(){};



void CCSD_R12_E::compute_amp(Ref<Tensor>& out){ //k_i0_offset,z->gt2(),z->f1(),z->in.at(0),z->t1(),z->t2(),z->v2()=>z->vr2()

in.resize(8);

in.at(1)=new Tensor("ccsd_r12_e_1_0",z->mem());
offset_smith_0_1();
smith_0_1_0(); //z->f1()=>in.at(1)
smith_1_2(); //z->t1(),z->v2()=>in.at(1)
smith_0_1(out); //z->t1(),in.at(1)=>out
delete in.at(1);
smith_0_3(out); //z->t2(),z->v2()=>out
smith_0_4(out); //z->gt2(),z->vr2()=>out

}

void CCSD_R12_E::smith_0_1(Ref<Tensor>& out){

long tileoffset;
if (out->is_this_local(0L)) {
 if (z->irrep_e()==(z->irrep_t()^z->irrep_f())) {
  long dimc=1;
  double* k_c_sort=z->mem()->malloc_local_double(dimc);
  std::fill(k_c_sort,k_c_sort+(size_t)dimc,0.0);
  for (long h2b=0L;h2b<z->noab();++h2b) {
   for (long p1b=z->noab();p1b<z->noab()+z->nvab();++p1b) {
    if (z->get_spin(p1b)==z->get_spin(h2b)) {
     if ((z->get_sym(p1b)^z->get_sym(h2b))==z->irrep_t()) {
      long p1b_0,h2b_0;
      z->restricted_2(p1b,h2b,p1b_0,h2b_0);
      long h2b_1,p1b_1;
      z->restricted_2(h2b,p1b,h2b_1,p1b_1);
      long dim_common=z->get_range(h2b)*z->get_range(p1b);
      long dima0_sort=1L;
      long dima0=dim_common*dima0_sort;
      long dima1_sort=1L;
      long dima1=dim_common*dima1_sort;
      if (dima0>0L && dima1>0L) {
       double* k_a0_sort=z->mem()->malloc_local_double(dima0);
       double* k_a0=z->mem()->malloc_local_double(dima0);
       z->t1()->get_block(h2b_0+z->noab()*(p1b_0-z->noab()),k_a0);
       z->sort_indices2(k_a0,k_a0_sort,z->get_range(p1b),z->get_range(h2b),0,1,+1.0);
       z->mem()->free_local_double(k_a0);
       double* k_a1_sort=z->mem()->malloc_local_double(dima1);
       double* k_a1=z->mem()->malloc_local_double(dima1);
       in[1]->get_block(p1b_1-z->noab()+z->nvab()*(h2b_1),k_a1);
       z->sort_indices2(k_a1,k_a1_sort,z->get_range(h2b),z->get_range(p1b),1,0,+1.0);
       z->mem()->free_local_double(k_a1);
       double factor=1.0;
       z->smith_dgemm(dima0_sort,dima1_sort,dim_common,factor,k_a0_sort,dim_common,k_a1_sort,dim_common,1.0,k_c_sort,dima0_sort);
       z->mem()->free_local_double(k_a1_sort);
       z->mem()->free_local_double(k_a0_sort);
      }
     }
    }
   }
  }
  double* k_c=z->mem()->malloc_local_double(dimc);
  z->sort_indices0(k_c_sort,k_c,1.0);
  out->add_block((0),k_c);
  z->mem()->free_local_double(k_c);
  z->mem()->free_local_double(k_c_sort);
 }
}
z->mem()->sync();
}

void CCSD_R12_E::smith_0_1_0(){

for (long h2b=0L;h2b<z->noab();++h2b) {
 for (long p1b=z->noab();p1b<z->noab()+z->nvab();++p1b) {
  long tileoffset;
  tileoffset=(p1b-z->noab()+z->nvab()*(h2b));
  if (in[1]->is_this_local(tileoffset)) {
   if (!z->restricted() || z->get_spin(h2b)+z->get_spin(p1b)!=4L) {
    if (z->get_spin(h2b)==z->get_spin(p1b)) {
     if ((z->get_sym(h2b)^z->get_sym(p1b))==z->irrep_f()) {
      long dimc=z->get_range(h2b)*z->get_range(p1b);
      long h2b_0,p1b_0;
      z->restricted_2(h2b,p1b,h2b_0,p1b_0);
      long dim_common=1L;
      long dima0_sort=z->get_range(h2b)*z->get_range(p1b);
      long dima0=dim_common*dima0_sort;
      if (dima0>0L) {
       double* k_a0_sort=z->mem()->malloc_local_double(dima0);
       double* k_a0=z->mem()->malloc_local_double(dima0);
       z->f1()->get_block(p1b_0+(z->nab())*(h2b_0),k_a0);
       z->sort_indices2(k_a0,k_a0_sort,z->get_range(h2b),z->get_range(p1b),0,1,+1.0);
       z->mem()->free_local_double(k_a0);
       double* k_c=z->mem()->malloc_local_double(dimc);
       z->sort_indices2(k_a0_sort,k_c,z->get_range(h2b),z->get_range(p1b),0,1,+1.0);
       in[1]->add_block(p1b-z->noab()+z->nvab()*(h2b),k_c);
       z->mem()->free_local_double(k_c);
       z->mem()->free_local_double(k_a0_sort);
      }
     }
    }
   }
  }
 }
}
z->mem()->sync();
}

void CCSD_R12_E::offset_smith_0_1(){

long size=0L;
for (long h2b=0L;h2b<z->noab();++h2b) {
 for (long p1b=z->noab();p1b<z->noab()+z->nvab();++p1b) {
  if (z->get_spin(h2b)==z->get_spin(p1b)) {
   if ((z->get_sym(h2b)^z->get_sym(p1b))==(z->irrep_t()^z->irrep_v())) {
    if (!z->restricted() || z->get_spin(h2b)+z->get_spin(p1b)!=4L) {
     in[1]->input_offset(p1b-z->noab()+z->nvab()*(h2b),size);
     size+=z->get_range(h2b)*z->get_range(p1b);
    }
   }
  }
 }
}
in[1]->set_filesize(size);
in[1]->createfile();
z->mem()->sync();
}

void CCSD_R12_E::smith_0_3(Ref<Tensor>& out){

long tileoffset;
if (out->is_this_local(0L)) {
 if (z->irrep_e()==(z->irrep_t()^z->irrep_v())) {
  long dimc=1;
  double* k_c_sort=z->mem()->malloc_local_double(dimc);
  std::fill(k_c_sort,k_c_sort+(size_t)dimc,0.0);
  for (long h3b=0L;h3b<z->noab();++h3b) {
   for (long h4b=h3b;h4b<z->noab();++h4b) {
    for (long p1b=z->noab();p1b<z->noab()+z->nvab();++p1b) {
     for (long p2b=p1b;p2b<z->noab()+z->nvab();++p2b) {
      if (z->get_spin(p1b)+z->get_spin(p2b)==z->get_spin(h3b)+z->get_spin(h4b)) {
       if ((z->get_sym(p1b)^(z->get_sym(p2b)^(z->get_sym(h3b)^z->get_sym(h4b))))==z->irrep_t()) {
        long p1b_0,p2b_0,h3b_0,h4b_0;
        z->restricted_4(p1b,p2b,h3b,h4b,p1b_0,p2b_0,h3b_0,h4b_0);
        long h3b_1,h4b_1,p1b_1,p2b_1;
        z->restricted_4(h3b,h4b,p1b,p2b,h3b_1,h4b_1,p1b_1,p2b_1);
        long dim_common=z->get_range(h3b)*z->get_range(h4b)*z->get_range(p1b)*z->get_range(p2b);
        long dima0_sort=1L;
        long dima0=dim_common*dima0_sort;
        long dima1_sort=1L;
        long dima1=dim_common*dima1_sort;
        if (dima0>0L && dima1>0L) {
         double* k_a0_sort=z->mem()->malloc_local_double(dima0);
         double* k_a0=z->mem()->malloc_local_double(dima0);
         z->t2()->get_block(h4b_0+z->noab()*(h3b_0+z->noab()*(p2b_0-z->noab()+z->nvab()*(p1b_0-z->noab()))),k_a0);
         z->sort_indices4(k_a0,k_a0_sort,z->get_range(p1b),z->get_range(p2b),z->get_range(h3b),z->get_range(h4b),1,0,3,2,+1.0);
         z->mem()->free_local_double(k_a0);
         double* k_a1_sort=z->mem()->malloc_local_double(dima1);
         double* k_a1=z->mem()->malloc_local_double(dima1);
         z->v2()->get_block(p2b_1+(z->nab())*(p1b_1+(z->nab())*(h4b_1+(z->nab())*(h3b_1))),k_a1);
         z->sort_indices4(k_a1,k_a1_sort,z->get_range(h3b),z->get_range(h4b),z->get_range(p1b),z->get_range(p2b),3,2,1,0,+1.0);
         z->mem()->free_local_double(k_a1);
         double factor=1.0;
         if (h3b==h4b) {
          factor=factor/2.0;
         }
         if (p1b==p2b) {
          factor=factor/2.0;
         }
         z->smith_dgemm(dima0_sort,dima1_sort,dim_common,factor,k_a0_sort,dim_common,k_a1_sort,dim_common,1.0,k_c_sort,dima0_sort);
         z->mem()->free_local_double(k_a1_sort);
         z->mem()->free_local_double(k_a0_sort);
        }
       }
      }
     }
    }
   }
  }
  double* k_c=z->mem()->malloc_local_double(dimc);
  z->sort_indices0(k_c_sort,k_c,1.0);
  out->add_block((0),k_c);
  z->mem()->free_local_double(k_c);
  z->mem()->free_local_double(k_c_sort);
 }
}
z->mem()->sync();
}

void CCSD_R12_E::smith_0_4(Ref<Tensor>& out){

  const int debug_print = 0;

long tileoffset;
if (out->is_this_local(0L)) {
 if (z->irrep_e()==(z->irrep_t()^z->irrep_e())) {
  long dimc=1;
  double* k_c_sort=z->mem()->malloc_local_double(dimc);
  std::fill(k_c_sort,k_c_sort+(size_t)dimc,0.0);
  for (long h1b=0L;h1b<z->noab();++h1b) {
   for (long h2b=h1b;h2b<z->noab();++h2b) {
    for (long h3b=0L;h3b<z->noab();++h3b) {
     for (long h4b=h3b;h4b<z->noab();++h4b) {
      if (z->get_spin(h1b)+z->get_spin(h2b)==z->get_spin(h3b)+z->get_spin(h4b)) {
       if ((z->get_sym(h1b)^(z->get_sym(h2b)^(z->get_sym(h3b)^z->get_sym(h4b))))==z->irrep_t()) {
        long h1b_0,h2b_0,h3b_0,h4b_0;
        z->restricted_4(h1b,h2b,h3b,h4b,h1b_0,h2b_0,h3b_0,h4b_0);
        long h3b_1,h4b_1,h1b_1,h2b_1;
        z->restricted_4(h3b,h4b,h1b,h2b,h3b_1,h4b_1,h1b_1,h2b_1);
        long dim_common=z->get_range(h1b)*z->get_range(h2b)*z->get_range(h3b)*z->get_range(h4b);
        long dima0_sort=1L;
        long dima0=dim_common*dima0_sort;
        long dima1_sort=1L;
        long dima1=dim_common*dima1_sort;
        if (dima0>0L && dima1>0L) {
         double* k_a0_sort=z->mem()->malloc_local_double(dima0);
         double* k_a0=z->mem()->malloc_local_double(dima0);
         z->gt2()->get_block(h4b_0+z->noab()*(h3b_0+z->noab()*(h2b_0+z->noab()*(h1b_0))),k_a0);
         if (debug_print) {
         ExEnv::out0() << "GT2" << std::endl;
         print_tile(k_a0, z->get_range(h1b)*z->get_range(h2b), z->get_range(h3b)*z->get_range(h4b));
         }
         z->sort_indices4(k_a0,k_a0_sort,z->get_range(h1b),z->get_range(h2b),z->get_range(h3b),z->get_range(h4b),3,2,1,0,+1.0);
         z->mem()->free_local_double(k_a0);
         double* k_a1_sort=z->mem()->malloc_local_double(dima1);
         double* k_a1=z->mem()->malloc_local_double(dima1);
         z->vr2()->get_block(h2b_1+z->noab()*(h1b_1+z->noab()*(h4b_1+(z->nab())*(h3b_1))),k_a1);
         if (debug_print) {
         ExEnv::out0() << "VR2" << std::endl;
         print_tile(k_a1, z->get_range(h1b)*z->get_range(h2b), z->get_range(h3b)*z->get_range(h4b));
         }
         z->sort_indices4(k_a1,k_a1_sort,z->get_range(h3b),z->get_range(h4b),z->get_range(h1b),z->get_range(h2b),1,0,3,2,+1.0);
         z->mem()->free_local_double(k_a1);
         double factor=1.0;
         if (h1b==h2b) {
          factor=factor/2.0;
         }
         if (h3b==h4b) {
          factor=factor/2.0;
         }
         z->smith_dgemm(dima0_sort,dima1_sort,dim_common,factor,k_a0_sort,dim_common,k_a1_sort,dim_common,1.0,k_c_sort,dima0_sort);
         z->mem()->free_local_double(k_a1_sort);
         z->mem()->free_local_double(k_a0_sort);

         if (debug_print) {
         ExEnv::out0() << "tiles = " << h1b << "," << h2b << "," << h3b << "," << h4b << " energy = " << k_c_sort[0] << std::endl;
         }
        }
       }
      }
     }
    }
   }
  }
  double* k_c=z->mem()->malloc_local_double(dimc);
  z->sort_indices0(k_c_sort,k_c,1.0);
  out->add_block((0),k_c);
  z->mem()->free_local_double(k_c);
  z->mem()->free_local_double(k_c_sort);
 }
}
z->mem()->sync();
}

void CCSD_R12_E::smith_1_2(){

for (long h2b=0L;h2b<z->noab();++h2b) {
 for (long p1b=z->noab();p1b<z->noab()+z->nvab();++p1b) {
  long tileoffset;
  tileoffset=(p1b-z->noab()+z->nvab()*(h2b));
  if (in[1]->is_this_local(tileoffset)) {
   if (!z->restricted() || z->get_spin(h2b)+z->get_spin(p1b)!=4L) {
    if (z->get_spin(h2b)==z->get_spin(p1b)) {
     if ((z->get_sym(h2b)^z->get_sym(p1b))==(z->irrep_t()^z->irrep_v())) {
      long dimc=z->get_range(h2b)*z->get_range(p1b);
      double* k_c_sort=z->mem()->malloc_local_double(dimc);
      std::fill(k_c_sort,k_c_sort+(size_t)dimc,0.0);
      for (long h4b=0L;h4b<z->noab();++h4b) {
       for (long p3b=z->noab();p3b<z->noab()+z->nvab();++p3b) {
        if (z->get_spin(p3b)==z->get_spin(h4b)) {
         if ((z->get_sym(p3b)^z->get_sym(h4b))==z->irrep_t()) {
          long p3b_0,h4b_0;
          z->restricted_2(p3b,h4b,p3b_0,h4b_0);
          long h2b_1,h4b_1,p1b_1,p3b_1;
          z->restricted_4(h2b,h4b,p1b,p3b,h2b_1,h4b_1,p1b_1,p3b_1);
          long dim_common=z->get_range(h4b)*z->get_range(p3b);
          long dima0_sort=1L;
          long dima0=dim_common*dima0_sort;
          long dima1_sort=z->get_range(h2b)*z->get_range(p1b);
          long dima1=dim_common*dima1_sort;
          if (dima0>0L && dima1>0L) {
           double* k_a0_sort=z->mem()->malloc_local_double(dima0);
           double* k_a0=z->mem()->malloc_local_double(dima0);
           z->t1()->get_block(h4b_0+z->noab()*(p3b_0-z->noab()),k_a0);
           z->sort_indices2(k_a0,k_a0_sort,z->get_range(p3b),z->get_range(h4b),0,1,+1.0);
           z->mem()->free_local_double(k_a0);
           double* k_a1_sort=z->mem()->malloc_local_double(dima1);
           double* k_a1=z->mem()->malloc_local_double(dima1);
           if (h2b<h4b && p1b<p3b) {
            z->v2()->get_block(p3b_1+(z->nab())*(p1b_1+(z->nab())*(h4b_1+(z->nab())*(h2b_1))),k_a1);
            z->sort_indices4(k_a1,k_a1_sort,z->get_range(h2b),z->get_range(h4b),z->get_range(p1b),z->get_range(p3b),2,0,3,1,+1.0);
           }
           else if (h2b<h4b && p3b<=p1b) {
            z->v2()->get_block(p1b_1+(z->nab())*(p3b_1+(z->nab())*(h4b_1+(z->nab())*(h2b_1))),k_a1);
            z->sort_indices4(k_a1,k_a1_sort,z->get_range(h2b),z->get_range(h4b),z->get_range(p3b),z->get_range(p1b),3,0,2,1,-1.0);
           }
           else if (h4b<=h2b && p1b<p3b) {
            z->v2()->get_block(p3b_1+(z->nab())*(p1b_1+(z->nab())*(h2b_1+(z->nab())*(h4b_1))),k_a1);
            z->sort_indices4(k_a1,k_a1_sort,z->get_range(h4b),z->get_range(h2b),z->get_range(p1b),z->get_range(p3b),2,1,3,0,-1.0);
           }
           else if (h4b<=h2b && p3b<=p1b) {
            z->v2()->get_block(p1b_1+(z->nab())*(p3b_1+(z->nab())*(h2b_1+(z->nab())*(h4b_1))),k_a1);
            z->sort_indices4(k_a1,k_a1_sort,z->get_range(h4b),z->get_range(h2b),z->get_range(p3b),z->get_range(p1b),3,1,2,0,+1.0);
           }
           z->mem()->free_local_double(k_a1);
           double factor=1.0;
           z->smith_dgemm(dima0_sort,dima1_sort,dim_common,factor,k_a0_sort,dim_common,k_a1_sort,dim_common,1.0,k_c_sort,dima0_sort);
           z->mem()->free_local_double(k_a1_sort);
           z->mem()->free_local_double(k_a0_sort);
          }
         }
        }
       }
      }
      double* k_c=z->mem()->malloc_local_double(dimc);
      z->sort_indices2(k_c_sort,k_c,z->get_range(p1b),z->get_range(h2b),1,0,+0.5);
      in[1]->add_block(p1b-z->noab()+z->nvab()*(h2b),k_c);
      z->mem()->free_local_double(k_c);
      z->mem()->free_local_double(k_c_sort);
     }
    }
   }
  }
 }
}
z->mem()->sync();
}

