#include <stdio.h>
#include <libciomr/libciomr.h>
#include <libqt/qt.h>
#include "structs.h"
#define EXTERN
#include "globals.h"

#define INDEX(i,j) ((i>j) ? (ioff[(i)]+(j)) : (ioff[(j)]+(i)))


/*
** S3_BLOCK_BZ1()
**
** Calculate a block of the sigma3 vector in equation (9c) of
** Olsen, Roos, et al.  For diagonal blocks of sigma.
**
** Try routine from Bendazzoli and Evangelisti
**
** Pass in Ialist, Iblist, Jalist, Jblist.  Need to convert these
** to irreps for now; later we can make even more general.
**
*/
void s3_block_bz1(int Ialist, int Iblist, int Jalist, int Jblist, 
      int nas, int nbs,
      int cnbs, double *tei, double **C, double **S, 
      double **Cprime, double **Sprime)
{

   int Iasym, Jasym, Ibsym, Jbsym;
   int norbs, *orbsym;
   int i, j, k, l, ij, fullij, fullji, kl, fullkl, fulllk, tmpi, tmpj;
   int I, J, I1, I2, J1, J2, S2;   /* also try S2 as double */
   int ilen, jlen;
   double V, VS;
   double *Tptr;
   int *OVptr, *OVptr2;
   int signmask, nsignmask;
   double *SprimeI0, *SprimeI1, *SprimeI2, *SprimeI3;
   double *SprimeI4, *SprimeI5, *SprimeI6, *SprimeI7;
   double *CprimeI0, *CprimeI1, *CprimeI2, *CprimeI3;
   double *CprimeI4, *CprimeI5, *CprimeI6, *CprimeI7;

   orbsym = CalcInfo.orbsym + CalcInfo.num_fzc_orbs;
   Iasym = Ialist;
   Jasym = Jalist;
   Ibsym = Iblist;
   Jbsym = Jblist;

   norbs = CalcInfo.num_ci_orbs;

   signmask = 1 << (sizeof(int)*8-1);
   nsignmask = ~signmask;

   /* loop over k,l */
   for (k=0; k<norbs; k++) {
      for (l=0; l<norbs; l++) {
         if ((orbsym[k] ^ orbsym[l] ^ Jasym ^ Iasym) != 0) continue;

         kl = INDEX(k,l);
         Tptr = tei + ioff[kl];
         fullkl = k * norbs + l;
         fulllk = l * norbs + k;

         ilen = OV[Jalist][fulllk][0];

         if (ilen == 0) continue;

         for (I=0,OVptr=OV[Jalist][fulllk]+1; I<ilen; I++) {
            tmpi = *OVptr++;
            I2 = tmpi & nsignmask;
            S2 = (tmpi & signmask) ? -1 : 1;
            for (J=0; J<cnbs; J++) {
               Cprime[I][J] = C[I2][J] * S2;
               }
            zero_arr(Sprime[I], nbs);
            } 

         for (i=0; i<norbs; i++) {
            for (j=0; j<norbs; j++) {
               if ((orbsym[i] ^ orbsym[j] ^ Jbsym ^ Ibsym) != 0) continue;
               ij = INDEX(i,j);
               if (ij > kl) continue;
               V = Tptr[ij];
               if (ij==kl) V = V/2.0;
               fullij = i * norbs + j;
               fullji = j * norbs + i;
               jlen = OV[Jblist][fullji][0];
               OVptr = OV[Jblist][fullji] + 1;
               OVptr2 = OV[Iblist][fullij] + 1;

        
               for (J=0; J<jlen; J++) {
                  tmpi = OVptr[J];
                  tmpj = OVptr2[J];
                  J1 = tmpi & nsignmask;
                  J2 = tmpj & nsignmask;
                  VS = (tmpj & signmask) ? -V : V;
                  for (I=0; I<ilen%8; I++) {
                     Sprime[I][J2] += Cprime[I][J1] * VS;
                     }
                  }

               for (; I<ilen; I+=8) {

                  SprimeI0=Sprime[I];    SprimeI1=Sprime[I+1];
                  SprimeI2=Sprime[I+2];  SprimeI3=Sprime[I+3];
                  SprimeI4=Sprime[I+4];  SprimeI5=Sprime[I+5];
                  SprimeI6=Sprime[I+6];  SprimeI7=Sprime[I+7];
                  CprimeI0=Cprime[I];    CprimeI1=Cprime[I+1];
                  CprimeI2=Cprime[I+2];  CprimeI3=Cprime[I+3];
                  CprimeI4=Cprime[I+4];  CprimeI5=Cprime[I+5];
                  CprimeI6=Cprime[I+6];  CprimeI7=Cprime[I+7];

                  for (J=0; J<jlen; J++) {
                     tmpi = OVptr[J];
                     tmpj = OVptr2[J];
                     J1 = tmpi & nsignmask;
                     J2 = tmpj & nsignmask;
                     VS = (tmpj & signmask) ? -V : V;
                   
                     SprimeI0[J2] += CprimeI0[J1] * VS;
                     SprimeI1[J2] += CprimeI1[J1] * VS;
                     SprimeI2[J2] += CprimeI2[J1] * VS;
                     SprimeI3[J2] += CprimeI3[J1] * VS;
                     SprimeI4[J2] += CprimeI4[J1] * VS;
                     SprimeI5[J2] += CprimeI5[J1] * VS;
                     SprimeI6[J2] += CprimeI6[J1] * VS;
                     SprimeI7[J2] += CprimeI7[J1] * VS;
                     }
                  }
                 
               } /* end loop over j */
            } /* end loop over i */

         for (I=0,OVptr=OV[Ialist][fullkl]+1; I<ilen; I++) {
            tmpi = *OVptr++;
            I1 = tmpi & nsignmask; 
            for (J=0; J<nbs; J++) {
               S[I1][J] += Sprime[I][J];
               }
            }

         } /* end loop over l */
      } /* end loop over k */
}

/*
** S3_BLOCK_BZ2()
**
** Calculate a block of the sigma3 vector in equation (9c) of
** Olsen, Roos, et al.  For diagonal blocks of sigma.
**
** Try routine from Bendazzoli and Evangelisti
**
** This one reverses the direction of vectorization
**
** Pass in Ialist, Iblist, Jalist, Jblist.  Need to convert these
** to irreps for now; later we can make even more general.
**
*/
void s3_block_bz(int Ialist, int Iblist, int Jalist, int Jblist, 
      int nas, int nbs,
      int cnas, double *tei, double **C, double **S, 
      double **Cprime, double **Sprime)
{

   int Iasym, Jasym, Ibsym, Jbsym;
   int norbs, *orbsym;
   int i, j, k, l, ij, fullij, fullji, kl, fullkl, fulllk, tmpi, tmpj;
   int I, J, I1, I2, J1, J2, S2;   /* also try S2 as double */
   int ilen, jlen;
   double V, VS;
   double *Tptr;
   int *OVptr, *OVptr2;
   int signmask, nsignmask;
   double *SprimeI, *CprimeI;
   double *SprimeI0,*SprimeI1,*SprimeI2,*SprimeI3;
   double *SprimeI4,*SprimeI5,*SprimeI6,*SprimeI7;
   double *SI0, *SI1, *SI2, *SI3, *SI4, *SI5, *SI6, *SI7;

   orbsym = CalcInfo.orbsym + CalcInfo.num_fzc_orbs;
   Iasym = Ialist;
   Jasym = Jalist;
   Ibsym = Iblist;
   Jbsym = Jblist;

   norbs = CalcInfo.num_ci_orbs;

   signmask = 1 << (sizeof(int)*8-1);
   nsignmask = ~signmask;

   /* loop over i,j */
   for (i=0; i<norbs; i++) {
      for (j=0; j<norbs; j++) {
         if ((orbsym[i] ^ orbsym[j] ^ Jbsym ^ Ibsym) != 0) continue;

         ij = INDEX(i,j);
         Tptr = tei + ioff[ij];
         fullij = i * norbs + j;
         fullji = j * norbs + i;

         jlen = OV[Jblist][fullji][0];

         if (jlen == 0) continue;

         for (J=0,OVptr=OV[Jblist][fullji]+1; J<jlen; J++) {
            tmpi = *OVptr++;
            J2 = tmpi & nsignmask;
            S2 = (tmpi & signmask) ? -1 : 1;
            for (I=0; I<cnas%8; I++) {
               Cprime[I][J] = C[I][J2] * S2;
               }
            } 

         OVptr = OV[Jblist][fullji]+1;
         for (; I<cnas; I+=8) {
            SprimeI0 = Cprime[I];    SI0 = C[I];
            SprimeI1 = Cprime[I+1];  SI1 = C[I+1];
            SprimeI2 = Cprime[I+2];  SI2 = C[I+2];
            SprimeI3 = Cprime[I+3];  SI3 = C[I+3];
            SprimeI4 = Cprime[I+4];  SI4 = C[I+4];
            SprimeI5 = Cprime[I+5];  SI5 = C[I+5];
            SprimeI6 = Cprime[I+6];  SI6 = C[I+6];
            SprimeI7 = Cprime[I+7];  SI7 = C[I+7];

            for (J=0; J<jlen; J++) {
               tmpi = OVptr[J];
               J2 = tmpi & nsignmask;
               S2 = (tmpi & signmask) ? -1 : 1;

               SprimeI0[J] = SI0[J2] * S2;               
               SprimeI1[J] = SI1[J2] * S2;
               SprimeI2[J] = SI2[J2] * S2;
               SprimeI3[J] = SI3[J2] * S2;
               SprimeI4[J] = SI4[J2] * S2;
               SprimeI5[J] = SI5[J2] * S2;
               SprimeI6[J] = SI6[J2] * S2;
               SprimeI7[J] = SI7[J2] * S2;
               }
            }

         zero_mat(Sprime, nas, nbs);

         for (k=0; k<norbs; k++) {
            for (l=0; l<norbs; l++) {
               if ((orbsym[k] ^ orbsym[l] ^ Jasym ^ Iasym) != 0) continue;
               kl = INDEX(k,l);
               if (kl > ij) continue;
               V = Tptr[kl];
               if (ij==kl) V = V/2.0;
               fullkl = k * norbs + l;
               fulllk = l * norbs + k;
               ilen = OV[Jalist][fulllk][0];
               OVptr = OV[Jalist][fulllk] + 1;
               OVptr2 = OV[Ialist][fullkl] + 1;

               for (I=0; I<ilen; I++) {
                  tmpi = *OVptr++;
                  tmpj = *OVptr2++;
                  I1 = tmpi & nsignmask;
                  I2 = tmpj & nsignmask;
                  VS = (tmpj & signmask) ? -V : V;

               #ifdef USE_BLAS
                  C_DAXPY(jlen, VS, Cprime[I1], 1, Sprime[I2], 1);
               #else
                  SprimeI = Sprime[I2];
                  CprimeI = Cprime[I1];

                  for (J=0; J<jlen%8; J++) {
                     SprimeI[J] += CprimeI[J] * VS; 
                     }

                  for (; J<jlen; J+=8) {
                     SprimeI[J] += CprimeI[J] * VS; 
                     SprimeI[J+1] += CprimeI[J+1] * VS; 
                     SprimeI[J+2] += CprimeI[J+2] * VS; 
                     SprimeI[J+3] += CprimeI[J+3] * VS; 
                     SprimeI[J+4] += CprimeI[J+4] * VS; 
                     SprimeI[J+5] += CprimeI[J+5] * VS; 
                     SprimeI[J+6] += CprimeI[J+6] * VS; 
                     SprimeI[J+7] += CprimeI[J+7] * VS; 
                     }
               #endif

                  } /* end loop over I */
               } /* end loop over l */
            } /* end loop over k */

         for (J=0,OVptr=OV[Iblist][fullij]+1; J<jlen; J++) {
            tmpi = *OVptr++;
            J1 = tmpi & nsignmask; 
            for (I=0; I<nas%8; I++) {
               S[I][J1] += Sprime[I][J];
               }
            }

         OVptr = OV[Iblist][fullij]+1;

         for (; I<nas; I+=8) {
            SI0 = S[I];  SprimeI0 = Sprime[I];
            SI1 = S[I+1];  SprimeI1 = Sprime[I+1];
            SI2 = S[I+2];  SprimeI2 = Sprime[I+2];
            SI3 = S[I+3];  SprimeI3 = Sprime[I+3];
            SI4 = S[I+4];  SprimeI4 = Sprime[I+4];
            SI5 = S[I+5];  SprimeI5 = Sprime[I+5];
            SI6 = S[I+6];  SprimeI6 = Sprime[I+6];
            SI7 = S[I+7];  SprimeI7 = Sprime[I+7];

            for (J=0; J<jlen; J++) {
               tmpi = OVptr[J];
               J1 = tmpi & nsignmask;
               SI0[J1] += SprimeI0[J];
               SI1[J1] += SprimeI1[J];
               SI2[J1] += SprimeI2[J];
               SI3[J1] += SprimeI3[J];
               SI4[J1] += SprimeI4[J];
               SI5[J1] += SprimeI5[J];
               SI6[J1] += SprimeI6[J];
               SI7[J1] += SprimeI7[J];
               }
            }

         } /* end loop over j */
      } /* end loop over i */
}

