/*---------------------------Pij.c---------------------------
  This software uses polynomial interpolation to calculate
  values of Pij, as in equation 8 of DW Brenner J Cond Mat
  14 (2002) 783-802.  It was written by Stephen Jordan and
  modeled on code by Zyvex as well as D.W. Brenner's original
  Fortran.
  -----------------------------------------------------------*/

#include "pij.h"
#include <math.h>
#include <stdio.h>

#define MAX_BC_NEIGHBORS 32

/*IN2 stores exponents for the 16 terms of a 2D cubic polynomial.
  CLM stores the coefficients for the polynomial interpolation.
  xh stores the values of Pij where i and j are integers. */

static int IN2[16+1][2+1];
static double CLM[2+1][MAX_BC_NEIGHBORS+1][MAX_BC_NEIGHBORS+1][16+1];
static double xh[3][5][5];
static int in2_initialized = 0;

//load coefficients from file
void init_pij() {
  int IC=0;
  int I, J, K, L, M;
  double xhh;
  double I2D;
  FILE *inter2D;
  inter2D = fopen("inter2d_iv.d", "r");
  if(inter2D == NULL) printf("Unable to open inter2d_iv.d\n");
  fscanf(inter2D, "%d\n",&I2D); //discard the first number
  //--------------initialize xh-----------
  do {
    fscanf(inter2D, "%d %d %d %lf\n",&I,&J,&K,&xhh);
    if (I > 0) xh[I][J][K] = xhh;
  }while(I > 0);
  //---------------initialize CLM---------
  for(I=1; I<5; I++) {
    for(J=1; J<5; J++) {
      IC=IC+1;
      IN2[IC][1]=I-1;
      IN2[IC][2]=J-1;
    }
  }
  //zero the coefficients
  for(I=1; I<3; I++)
    for(L=1; L <= 32; L++)
      for(M=1; M <= 32; M++)
	for(J=1; J<17; J++)
	  CLM[I][L][M][J]=0.0;
  for(K=1; K<73; K++) {
    fscanf(inter2D, "%d %d %d\n",&I,&L,&M);
    for(J=1; J<16; J+=4) {
      double j0, j1, j2, j3;
      fscanf(inter2D, "%lf %lf %lf %lf\n", &j0, &j1, &j2, &j3);
      CLM[I][L][M][J] = (double) j0;
      CLM[I][L][M][J+1] = (double) j1;
      CLM[I][L][M][J+2] = (double) j2;
      CLM[I][L][M][J+3] = (double) j3;
    }
  }
  in2_initialized = 1;
}


//follows normal rounding rules
double round(double input) {
  double a;
  a = floor(input);
  if((input - a) > 0.5) return a + 1.0;
  //otherwise
  return a;
}


//interpolates Pij using a bicubic spline
double spline(int KJ, double Nih, double Nic) {
  int J, NH, NC;
  double X, ANSY;
  double xx1_pow[4];
  double xx2_pow[4];
  double XX1, XX2;
  const double *clm_ptr;
  XX1 = Nih + 1.0;  //to make it match the paper
  XX2 = Nic + 1.0;  //likewise
  //bicubic interpolation or perhaps bicubic spline
  if (XX1 < 1) XX1 = 1;
  if (XX2 < 1) XX2 = 1;
  NH=(int)floor(XX1);
  NC=(int)floor(XX2);
  if(NH > MAX_BC_NEIGHBORS || NC > MAX_BC_NEIGHBORS) {
    fprintf(stderr, "Invalid neighbor numbers in Pij.\n");
    return(0.0);
  }
  if (KJ==0) {
    fprintf(stderr, "Invalid atom type in Pij.\n");
    return(0.0);
  }
  xx1_pow[0] = 1.0;
  xx1_pow[1] = XX1;
  xx1_pow[2] = XX1*XX1;
  xx1_pow[3] = XX1*XX1*XX1;
  xx2_pow[0] = 1.0;
  xx2_pow[1] = XX2;
  xx2_pow[2] = XX2*XX2;
  xx2_pow[3] = XX2*XX2*XX2;
  clm_ptr = &CLM[KJ][NH][NC][0];
  ANSY=0;
  for (J=1; J<17; J++) {
    X = *++clm_ptr*xx1_pow[IN2[J][1]]*xx2_pow[IN2[J][2]];
    ANSY += X;
  }
  return ANSY;
}


/* Compute P sub i j, which is referenced in equation 8 on page 789.
   (of J. Phys. Cond. Mat vol 14 (2002) 783-802)  
   KJ is the identity of the "j" atom in P sub i j.
   The i atom must always be a carbon.
   If i is hydrogen don't call Pij, just use 0.
   Nih is N super H sub i and Nic is N super C sub i.
   For KJ, 1 = carbon, 2 = hydrogen.*/

double Pij(int KJ, double Nih, double Nic) {
  double rNih, rNic;
  int ih, ic;
  if(in2_initialized == 0) init_pij();
  rNih = round(Nih);
  rNic = round(Nic);
  ih = (int)rNih;
  ic = (int)rNic;
  if(fabs(rNih - Nih) < 1.0e-8 && fabs(rNic - Nic) < 1.0e-8)
    return xh[KJ][ih+1][ic+1];
  //otherwise:
  return spline(KJ, Nih, Nic);
}


//for debugging purposes
void disp() {
  int type;
  int Nih, Nic, coefnum;
  int i;
  for(type = 1; type <= 2; type++) {
    for(Nih = 0; Nih <= 6; Nih++) {
      for(Nic = 0; Nic <= 6; Nic++) {
        for(coefnum = 1; coefnum <= 16; coefnum++) {
          printf("%d\t%d\t%d\t%d\t%f\n", type, Nih, Nic, coefnum,
                  CLM[type][Nih][Nic][coefnum]);
        }
      }
    }
  }
  for(i = 1; i <= 16; i++) {
    printf("%d\t%d\t%d\n", i, IN2[i][1], IN2[i][2]);
  }
}

