/*------------------------------------------------------------------------------

Routines common to Visual/surface.cpp and MeanField/ICM/icm.cpp
   written by G. Samsonidze (October 2008)
   refactored into common file by DAS

------------------------------------------------------------------------------*/

#include "wfn_utils.h"

const double EPS9 = 1.0E-9;
const double INF9 = 1.0E+9;
const double BOHR = 0.52917721092;

/* AAH! global variables! --DAS */
int *as = NULL;
CARTESIAN *ap = NULL;
double ***scalar = NULL;

const int vertex[8][3] = {
   {0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1},
   {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}};

void lowercase(char *s)
{
   int i, n;

   n = (int)strlen(s);
   for (i = 0; i < n; i++)
   {
      if (s[i] >= 'A' && s[i] <= 'Z')
         s[i] = s[i] + 32;
   }
}

void erasechar(char *s, char c)
{
   int i, j, n;

   n = (int)strlen(s);
   for (i = n - 1; i >= 0; i--)
      if (s[i] == c)
      {
         n--;
         for (j = i; j < n; j++)
            s[j] = s[j + 1];
         s[n] = '\0';
      }
}

void parse(char *s1, char *s2, char *s3)
{
   int i;
   char s4[MAXCHAR], s5[MAXCHAR], s6[MAXCHAR], s7[MAXCHAR];

   i = (int)strspn(s1, " \t");
   strncpy(s4, &s1[i], MAXCHAR - i);
   s4[MAXCHAR - 1 - i] = '\0';
   i = (int)strcspn(s4, " \t");
   strncpy(s5, s4, i);
   s5[i] = '\0';
   erasechar(s5, '\n');
   erasechar(s5, '\r');
   strncpy(s6, &s4[i], MAXCHAR - i);
   s6[MAXCHAR - 1 - i] = '\0';
   i = (int)strspn(s6, " \t");
   strncpy(s7, &s6[i], MAXCHAR - i);
   s7[MAXCHAR - 1 - i] = '\0';
   erasechar(s7, '\n');
   erasechar(s7, '\r');
   strncpy(s2, s5, MAXCHAR);
   s2[MAXCHAR - 1] = '\0';
   strncpy(s3, s7, MAXCHAR);
   s3[MAXCHAR - 1] = '\0';
}

int cub_read(char *ifn, int *na, int *ni, int *nj, int *nk, CARTESIAN *sfo, CARTESIAN *sfv, CARTESIAN *sfs)
{
   int i, j, k, ierr;
   double ac;
   char s1[MAXCHAR], s2[MAXCHAR];
   FILE *h;

   h = fopen(ifn, "r");
   if (h == NULL)
      return -1;

   if (!fgets(s1, MAXCHAR, h)) return -1;
   if (!fgets(s2, MAXCHAR, h)) return -1;
   ierr = fscanf(h, "%i%le%le%le\n", &(*na), &sfo->x, &sfo->y, &sfo->z);
   ierr = fscanf(h, "%i%le%le%le\n", &(*ni), &sfs[0].x, &sfs[0].y, &sfs[0].z);
   ierr = fscanf(h, "%i%le%le%le\n", &(*nj), &sfs[1].x, &sfs[1].y, &sfs[1].z);
   ierr = fscanf(h, "%i%le%le%le\n", &(*nk), &sfs[2].x, &sfs[2].y, &sfs[2].z);

   if (*na < 0 || *ni < 1 || *nj < 1 || *nk < 1)
      return -1;

   as = new int[MAX(1,*na)];
   ap = new CARTESIAN[MAX(1,*na)];

   for (i = 0; i < *na; i++)
      ierr = fscanf(h, "%i%le%le%le%le\n", &as[i], &ac, &ap[i].x, &ap[i].y, &ap[i].z);

   for (i = 0; i < *na; i++)
      if (as[i] < 0 || as[i] > 118)
         return -1;

   scalar = new double**[*ni];
   for (i = 0; i < *ni; i++)
      scalar[i] = new double*[*nj];
   for (i = 0; i < *ni; i++)
   for (j = 0; j < *nj; j++)
      scalar[i][j] = new double[*nk];

   for (i = 0; i < *ni; i++)
   for (j = 0; j < *nj; j++)
   for (k = 0; k < *nk; k++)
      ierr = fscanf(h, "%le", &scalar[i][j][k]);

   ierr = fclose(h);
   if (ierr != 0)
      return -1;

   sfo->x = sfo->x * BOHR;
   sfo->y = sfo->y * BOHR;
   sfo->z = sfo->z * BOHR;
   for (i = 0; i < 3; i++)
   {
      sfs[i].x = sfs[i].x * BOHR;
      sfs[i].y = sfs[i].y * BOHR;
      sfs[i].z = sfs[i].z * BOHR;
   }
   for (i = 0; i < *na; i++)
   {
      ap[i].x = ap[i].x * BOHR;
      ap[i].y = ap[i].y * BOHR;
      ap[i].z = ap[i].z * BOHR;
   }

   sfv[0].x = sfs[0].x * double(*ni);
   sfv[0].y = sfs[0].y * double(*ni);
   sfv[0].z = sfs[0].z * double(*ni);
   sfv[1].x = sfs[1].x * double(*nj);
   sfv[1].y = sfs[1].y * double(*nj);
   sfv[1].z = sfs[1].z * double(*nj);
   sfv[2].x = sfs[2].x * double(*nk);
   sfv[2].y = sfs[2].y * double(*nk);
   sfv[2].z = sfs[2].z * double(*nk);

   return 0;
}

int xsf_read(char *ifn, int *na, int *ni, int *nj, int *nk, CARTESIAN *sfo, CARTESIAN *sfv, CARTESIAN *sfs)
{
   int i, j, k, p, ierr;
   int num_elements = get_num_elements();
   double sf;
   char s[MAXCHAR];
   char dummy[MAXCHAR];
   char symbol[4];
   FILE *h;

   h = fopen(ifn, "r");
   if (h == NULL)
      return -1;

   do
   {
      if (!fgets(s, MAXCHAR, h)) return -1;
      lowercase(s);
      erasechar(s, ' ');
   }
   while (strncmp(s, "primcoord", 9) != 0);

   ierr = fscanf(h, "%i%i\n", &(*na), &p);

   if (*na < 0 || p != 1)
      return -1;

   as = new int[MAX(1,*na)];
   ap = new CARTESIAN[MAX(1,*na)];

   for (i = 0; i < *na; i++)
   {
      // read string to buffer first, then read atomic species and
      // positions from the buffer, skip reading optional atomic forces,
      // if atomic species are given by symbols convert them to numbers
      if (!fgets(s, MAXCHAR, h)) return -1;
      ierr = sscanf(s, "%s%le%le%le", dummy, &ap[i].x, &ap[i].y, &ap[i].z);
      as[i] = 0;
      ierr = sscanf(dummy, "%i", &as[i]);
      if (as[i] == 0)
      {
         lowercase(dummy);
         erasechar(dummy, ' ');
         for (j = 0; j < num_elements; j++)
         {
            strncpy(symbol, periodic_table[j].symbol, 4);
            symbol[3] = '\0';
            lowercase(symbol);
            if (strlen(symbol) == strlen(dummy) && strncmp(symbol, dummy, 4) == 0)
            {
               as[i] = j;
               break;
            }
         }
      }
   }

   for (i = 0; i < *na; i++)
      if (as[i] < 0 || as[i] > 118)
         return -1;

   do
   {
      if (!fgets(s, MAXCHAR, h)) return -1;
      lowercase(s);
      erasechar(s, ' ');
   }
   while (strncmp(s, "begin_datagrid_3d", 17) != 0 && strncmp(s, "datagrid_3d", 11) != 0);

   ierr = fscanf(h, "%i%i%i\n", &(*ni), &(*nj), &(*nk));
   ierr = fscanf(h, "%le%le%le\n", &sfo->x, &sfo->y, &sfo->z);
   ierr = fscanf(h, "%le%le%le\n", &sfv[0].x, &sfv[0].y, &sfv[0].z);
   ierr = fscanf(h, "%le%le%le\n", &sfv[1].x, &sfv[1].y, &sfv[1].z);
   ierr = fscanf(h, "%le%le%le\n", &sfv[2].x, &sfv[2].y, &sfv[2].z);

   // general to periodic grid conversion
   // http://www.xcrysden.org/doc/XSF.html
   *ni -= 1;
   *nj -= 1;
   *nk -= 1;

   if (*ni < 1 || *nj < 1 || *nk < 1)
      return -1;

   scalar = new double**[*ni];
   for (i = 0; i < *ni; i++)
      scalar[i] = new double*[*nj];
   for (i = 0; i < *ni; i++)
   for (j = 0; j < *nj; j++)
      scalar[i][j] = new double[*nk];

   for (k = 0; k <= *nk; k++)
   for (j = 0; j <= *nj; j++)
   for (i = 0; i <= *ni; i++)
   {
      ierr = fscanf(h, "%le", &sf);
      if (i < *ni && j < *nj && k < *nk)
         scalar[i][j][k] = sf;
   }

   ierr = fclose(h);
   if (ierr != 0)
      return -1;

   sfs[0].x = sfv[0].x / double(*ni);
   sfs[0].y = sfv[0].y / double(*ni);
   sfs[0].z = sfv[0].z / double(*ni);
   sfs[1].x = sfv[1].x / double(*nj);
   sfs[1].y = sfv[1].y / double(*nj);
   sfs[1].z = sfv[1].z / double(*nj);
   sfs[2].x = sfv[2].x / double(*nk);
   sfs[2].y = sfv[2].y / double(*nk);
   sfs[2].z = sfv[2].z / double(*nk);

   return 0;
}

void cell_set(CARTESIAN sfo, CARTESIAN *sfv, bool uc, CARTESIAN *uco, CARTESIAN *ucv, int ucf, bool sc, CARTESIAN *sco, CARTESIAN *scv, int scf)
{
   int i;
   double c1, c2, c3;

   if (uc)
   {
      if (ucf == 0)
      {
         uco->x = uco->x * BOHR;
         uco->y = uco->y * BOHR;
         uco->z = uco->z * BOHR;
         for (i = 0; i < 3; i++)
         {
            ucv[i].x = ucv[i].x * BOHR;
            ucv[i].y = ucv[i].y * BOHR;
            ucv[i].z = ucv[i].z * BOHR;
         }
      }
      else if (ucf == 2)
      {
         c1 = uco->x;
         c2 = uco->y;
         c3 = uco->z;
         uco->x = c1 * sfv[0].x + c2 * sfv[1].x + c3 * sfv[2].x;
         uco->y = c1 * sfv[0].y + c2 * sfv[1].y + c3 * sfv[2].y;
         uco->z = c1 * sfv[0].z + c2 * sfv[1].z + c3 * sfv[2].z;
         for (i = 0; i < 3; i++)
         {
            c1 = ucv[i].x;
            c2 = ucv[i].y;
            c3 = ucv[i].z;
            ucv[i].x = c1 * sfv[0].x + c2 * sfv[1].x + c3 * sfv[2].x;
            ucv[i].y = c1 * sfv[0].y + c2 * sfv[1].y + c3 * sfv[2].y;
            ucv[i].z = c1 * sfv[0].z + c2 * sfv[1].z + c3 * sfv[2].z;
         }
      }
   }
   else
   {
      uco->x = sfo.x;
      uco->y = sfo.y;
      uco->z = sfo.z;
      for (i = 0; i < 3; i++)
      {
         ucv[i].x = sfv[i].x;
         ucv[i].y = sfv[i].y;
         ucv[i].z = sfv[i].z;
      }
   }

   if (sc)
   {
      if (scf == 0)
      {
         sco->x = sco->x * BOHR;
         sco->y = sco->y * BOHR;
         sco->z = sco->z * BOHR;
         for (i = 0; i < 3; i++)
         {
            scv[i].x = scv[i].x * BOHR;
            scv[i].y = scv[i].y * BOHR;
            scv[i].z = scv[i].z * BOHR;
         }
      }
      else if (scf == 2)
      {
         c1 = sco->x;
         c2 = sco->y;
         c3 = sco->z;
         sco->x = c1 * ucv[0].x + c2 * ucv[1].x + c3 * ucv[2].x;
         sco->y = c1 * ucv[0].y + c2 * ucv[1].y + c3 * ucv[2].y;
         sco->z = c1 * ucv[0].z + c2 * ucv[1].z + c3 * ucv[2].z;
         for (i = 0; i < 3; i++)
         {
            c1 = scv[i].x;
            c2 = scv[i].y;
            c3 = scv[i].z;
            scv[i].x = c1 * ucv[0].x + c2 * ucv[1].x + c3 * sfv[2].x;
            scv[i].y = c1 * ucv[0].y + c2 * ucv[1].y + c3 * sfv[2].y;
            scv[i].z = c1 * ucv[0].z + c2 * sfv[1].z + c3 * sfv[2].z;
         }
      }
   }
   else
   {
      sco->x = uco->x;
      sco->y = uco->y;
      sco->z = uco->z;
      for (i = 0; i < 3; i++)
      {
         scv[i].x = ucv[i].x;
         scv[i].y = ucv[i].y;
         scv[i].z = ucv[i].z;
      }
   }
}

void normal_make(CARTESIAN *vector, CARTESIAN *normal, double *weight)
{
   int i;

   normal[0].x = vector[1].y * vector[2].z - vector[1].z * vector[2].y;
   normal[0].y = vector[1].z * vector[2].x - vector[1].x * vector[2].z;
   normal[0].z = vector[1].x * vector[2].y - vector[1].y * vector[2].x;
   normal[1].x = vector[2].y * vector[0].z - vector[2].z * vector[0].y;
   normal[1].y = vector[2].z * vector[0].x - vector[2].x * vector[0].z;
   normal[1].z = vector[2].x * vector[0].y - vector[2].y * vector[0].x;
   normal[2].x = vector[0].y * vector[1].z - vector[0].z * vector[1].y;
   normal[2].y = vector[0].z * vector[1].x - vector[0].x * vector[1].z;
   normal[2].z = vector[0].x * vector[1].y - vector[0].y * vector[1].x;

   for (i = 0; i < 3; i ++)
      weight[i] = sqrt(normal[i].x * normal[i].x + normal[i].y * normal[i].y + normal[i].z * normal[i].z);
   for (i = 0; i < 3; i ++)
      if (weight[i] < EPS9)
         weight[i] = 1.0;
   for (i = 0; i < 3; i ++)
   {
      normal[i].x = normal[i].x / weight[i];
      normal[i].y = normal[i].y / weight[i];
      normal[i].z = normal[i].z / weight[i];
   }

   for (i = 0; i < 3; i ++)
      weight[i] = vector[i].x * normal[i].x + vector[i].y * normal[i].y + vector[i].z * normal[i].z;
   for (i = 0; i < 3; i ++)
   {
      normal[i].x = normal[i].x * weight[i];
      normal[i].y = normal[i].y * weight[i];
      normal[i].z = normal[i].z * weight[i];
   }

   for (i = 0; i < 3; i ++)
      weight[i] = normal[i].x * normal[i].x + normal[i].y * normal[i].y + normal[i].z * normal[i].z;
   for (i = 0; i < 3; i ++)
      if (weight[i] < EPS9)
         weight[i] = 1.0;
}

bool box_check(int i, int j, int k, CARTESIAN *gso, CARTESIAN *sfs, CARTESIAN *sco, CARTESIAN *normal, int *n)
{
   bool flag;
   int l, m;
   CARTESIAN point;
   double weight[3], proj[3];

   flag = true;

   point.x = gso[0].x + sfs[0].x * double(i) + sfs[1].x * double(j) + sfs[2].x * double(k) - sco[0].x;
   point.y = gso[0].y + sfs[0].y * double(i) + sfs[1].y * double(j) + sfs[2].y * double(k) - sco[0].y;
   point.z = gso[0].z + sfs[0].z * double(i) + sfs[1].z * double(j) + sfs[2].z * double(k) - sco[0].z;

   for (l = 0; l < 3; l++)
      weight[l] = normal[l].x * normal[l].x + normal[l].y * normal[l].y + normal[l].z * normal[l].z;
   for (l = 0; l < 3; l++)
      proj[l] = (point.x * normal[l].x + point.y * normal[l].y + point.z * normal[l].z) / weight[l];
   for (l = 0; l < 3; l++)
   {
// this is similar to double_to_int but we want to convert
// the interval (-1,0] to -1, (0,1] to 0, (1,2] to 1, etc.
      m = double_to_int(proj[l] - 0.5);
      if (proj[l] > 1.0 + EPS9 || proj[l] < -EPS9)
         flag = false;
      n[l] = m;
   }

   return flag;
}

int scalar_clone(int *ni, int *nj, int *nk, CARTESIAN *sfo, CARTESIAN *sfs, CARTESIAN uco, CARTESIAN *ucv, CARTESIAN sco, CARTESIAN *scv)
{
   int i, j, k, l, di, dj, dk, ui, uj, uk, sni, snj, snk;
   int n[3], nmin[3], nmax[3], nuc[3], nsc[3];
   double sfsw[3], ucvw[3], scvw[3], p;
   CARTESIAN sfsn[3], ucvn[3], scvn[3], ssfo, sccenter, sccorner[8], point;
   double ***scscalar = NULL;

   normal_make(sfs, sfsn, sfsw);
   normal_make(ucv, ucvn, ucvw);
   normal_make(scv, scvn, scvw);

   sccenter.x = sco.x + scv[0].x * 0.5 + scv[1].x * 0.5 + scv[2].x * 0.5;
   sccenter.y = sco.y + scv[0].y * 0.5 + scv[1].y * 0.5 + scv[2].y * 0.5;
   sccenter.z = sco.z + scv[0].z * 0.5 + scv[1].z * 0.5 + scv[2].z * 0.5;

   for (i = 0; i < 8; i++)
   {
      sccorner[i].x = sccenter.x;
      sccorner[i].y = sccenter.y;
      sccorner[i].z = sccenter.z;
      for (j = 0; j < 3; j++)
      {
         sccorner[i].x += scv[j].x * (double(vertex[i][j]) - 0.5);
         sccorner[i].y += scv[j].y * (double(vertex[i][j]) - 0.5);
         sccorner[i].z += scv[j].z * (double(vertex[i][j]) - 0.5);
      }
   }

   for (i = 0; i < 3; i++)
   {
      nmin[i] = INT_MAX;
      nmax[i] = INT_MIN;
      for (j = 0; j < 8; j++)
      {
         p = ((sccorner[j].x - sfo->x) * sfsn[i].x + (sccorner[j].y - sfo->y) * sfsn[i].y + (sccorner[j].z - sfo->z) * sfsn[i].z) / sfsw[i];
         k = double_to_int(p);
         if (k < nmin[i])
            nmin[i] = k;
         if (k > nmax[i])
            nmax[i] = k;
      }
   }

   di = -nmin[0];
   dj = -nmin[1];
   dk = -nmin[2];
   sni = nmax[0] - nmin[0] + 1;
   snj = nmax[1] - nmin[1] + 1;
   snk = nmax[2] - nmin[2] + 1;
   ssfo.x = sfo->x - sfs[0].x * double(di) - sfs[1].x * double(dj) - sfs[2].x * double(dk);
   ssfo.y = sfo->y - sfs[0].y * double(di) - sfs[1].y * double(dj) - sfs[2].y * double(dk);
   ssfo.z = sfo->z - sfs[0].z * double(di) - sfs[1].z * double(dj) - sfs[2].z * double(dk);

   if (sni < 1 || snj < 1 || snk < 1)
      return -1;

   scscalar = new double**[sni];
   for (i = 0; i < sni; i++)
      scscalar[i] = new double*[snj];
   for (i = 0; i < sni; i++)
   for (j = 0; j < snj; j++)
      scscalar[i][j] = new double[snk];

   for (i = 0; i < sni; i++)
   for (j = 0; j < snj; j++)
   for (k = 0; k < snk; k++)
   {
      if (!box_check(i, j, k, &ssfo, sfs, &sco, scvn, nsc))
         scscalar[i][j][k] = 0.0;
      else if (i >= di && i <= di + *ni && j >= dj && j <= dj + *nj && k >= dk && k <= dk + *nk)
      {
         ui = i - di;
         uj = j - dj;
         uk = k - dk;
         if (ui == *ni)
            ui = 0;
         if (uj == *nj)
            uj = 0;
         if (uk == *nk)
            uk = 0;
         scscalar[i][j][k] = scalar[ui][uj][uk];
      }
      else if (box_check(i, j, k, &ssfo, sfs, &uco, ucvn, nuc))
         scscalar[i][j][k] = 0.0;
      else
      {
         point.x = ssfo.x - sfo->x + sfs[0].x * double(i) + sfs[1].x * double(j) + sfs[2].x 
	   * double(k) - ucv[0].x * double(nuc[0]) - ucv[1].x * double(nuc[1]) - ucv[2].x * double(nuc[2]);
         point.y = ssfo.y - sfo->y + sfs[0].y * double(i) + sfs[1].y * double(j) + sfs[2].y 
	   * double(k) - ucv[0].y * double(nuc[0]) - ucv[1].y * double(nuc[1]) - ucv[2].y * double(nuc[2]);
         point.z = ssfo.z - sfo->z + sfs[0].z * double(i) + sfs[1].z * double(j) + sfs[2].z 
	   * double(k) - ucv[0].z * double(nuc[0]) - ucv[1].z * double(nuc[1]) - ucv[2].z * double(nuc[2]);
         for (l = 0; l < 3; l++)
         {
            p = (point.x * sfsn[l].x + point.y * sfsn[l].y + point.z * sfsn[l].z) / sfsw[l];
	    n[l] = double_to_int(p);
         }
         if (n[0] >= 0 && n[0] <= *ni && n[1] >= 0 && n[1] <= *nj && n[2] >= 0 && n[2] <= *nk)
         {
            ui = n[0];
            uj = n[1];
            uk = n[2];
            if (ui == *ni)
               ui = 0;
            if (uj == *nj)
               uj = 0;
            if (uk == *nk)
               uk = 0;
            scscalar[i][j][k] = scalar[ui][uj][uk];
         }
         else
            scscalar[i][j][k] = 0.0;
      }
   }

   if (scalar != NULL)
   {
      for (i = 0; i < *ni; i++)
      for (j = 0; j < *nj; j++)
         delete [] scalar[i][j];
      for (i = 0; i < *ni; i++)
         delete [] scalar[i];
      delete [] scalar;
   }
   scalar = scscalar;

   *ni = sni;
   *nj = snj;
   *nk = snk;

   sfo->x = ssfo.x;
   sfo->y = ssfo.y;
   sfo->z = ssfo.z;

   return 0;
}

double inversion(int n, double *x, double *y, double z)
{
   int i, j;
   double a, b, c, s, t, v, w, u = INF9;

   for (i = 0; i < n - 2; i++)
   {
      v = (y[i] - z) * (y[i] - z) + (y[i + 1] - z) * (y[i + 1] - z) + (y[i + 2] - z) * (y[i + 2] - z);
      if (v < u)
      {
         j = i;
         u = v;
      }
   }

   a = ((x[j + 1] - x[j]) * (y[j + 2] - y[j + 1]) - (x[j + 2] - x[j + 1]) * (y[j + 1] - y[j])) / ((x[j + 2] - x[j]) * (x[j + 2] - x[j + 1]) * (x[j + 1] - x[j]));
   b = ((x[j + 1] - x[j]) * (y[j + 2] - y[j + 1]) + (x[j + 2] - x[j + 1]) * (y[j + 1] - y[j])) / (2.0 * (x[j + 2] - x[j + 1]) * (x[j + 1] - x[j])) - a * (x[j] + 2.0 * x[j + 1] + x[j + 2]) / 2.0;
   c = (y[j] + y[j + 1] + y[j + 2] - b * (x[j] + x[j + 1] + x[j + 2]) - a * (x[j] * x[j] + x[j + 1] * x[j + 1] + x[j + 2] * x[j + 2])) / 3.0;

   u = (-b + sqrt(b * b - 4.0 * a * (c - z))) / (2.0 * a);
   v = (-b - sqrt(b * b - 4.0 * a * (c - z))) / (2.0 * a);

   s = (x[j] - u) * (x[j] - u) + (x[j + 1] - u) * (x[j + 1] - u) + (x[j + 2] - u) * (x[j + 2] - u);
   t = (x[j] - v) * (x[j] - v) + (x[j + 1] - v) * (x[j + 1] - v) + (x[j + 2] - v) * (x[j + 2] - v);

   if (s < t)
      w = u;
   else
      w = v;

   return w;
}

double isovalue_scale(int ni, int nj, int nk, int power, double isovalue)
{
   const int ns = 64;
   int i, j, k, p, s;
   double samp, smin = INF9, smax = -INF9;
   double sa[ns], sb[ns], sc[ns], sd, se, sf;

   for (i = 0; i < ni; i++)
   for (j = 0; j < nj; j++)
   for (k = 0; k < nk; k++)
   {
      if (scalar[i][j][k] < smin)
         smin = scalar[i][j][k];
      if (scalar[i][j][k] > smax)
         smax = scalar[i][j][k];
   }
   if (fabs(smax) > fabs(smin))
      samp = fabs(smax);
   else
      samp = fabs(smin);

   if (power > 0)
   {
      for (s = 0; s < ns; s++)
         sa[s] = double(s) / double(ns - 1);
      for (s = 0; s < ns; s++)
         sb[s] = sa[s] * samp;
      sb[0] = sb[0] - EPS9;
      sb[ns - 1] = sb[ns - 1] + EPS9;
      for (s = 0; s < ns; s++)
         sc[s] = 0.0;
      for (i = 0; i < ni; i++)
      for (j = 0; j < nj; j++)
      for (k = 0; k < nk; k++)
      {
         sd = fabs(scalar[i][j][k]);
         se = 1.0;
         for (p = 0; p < power; p++)
            se = se * sd;
         for (s = 0; s < ns; s++)
            if (sb[s] < sd)
               sc[s] = sc[s] + se;
      }
      sf = sc[0];
      if (sf > EPS9)
      {
         for (s = 0; s < ns; s++)
            sc[s] = sc[s] / sf;
         isovalue = inversion(ns, sa, sc, isovalue);
      }
      else
         isovalue = 0.0;
   }
   isovalue = isovalue * samp;

   return isovalue;
}

// round to nearest integer
int double_to_int(double dd)
{
   if (dd < 0.0)
      return int(dd - 0.5);
   else
      return int(dd + 0.5);
}
