/*
 * (C) Copyright 1996-2016 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

/*! \file Akima760.cc
    Implementation of Akima760 class.
    
    Magics Team - ECMWF 2004
   
    Created: Fri 12-Mar-2004
    
*/

#include "MagLog.h"
#include "Akima760Method.h"
#include "Timer.h"

using namespace magics;


Akima760::Akima760(const  AbstractMatrix& matrix, const Akima760MethodAttributes& attr) :
    MatrixHandler(matrix), attr_(attr)
{
	MagLog::debug() << "Akima760 Constructor" << "\n";

       int j;
//     monoColumns_ = MatrixHandler::columns(); //syntax also valid
       monoColumns_ = this->matrix_.columns();
       monoRows_ = this->matrix_.rows();

      // Compute matrix output sizes
       double aux = (this->matrix_.regular_column( monoColumns_-1) - this->matrix_.regular_column(0)) / attr_.resolutionX_;
       if ( (double)(int(aux)) != aux ) aux += 1.; //next integer number
       ncols_ = int(aux + 1.);                  //must include the first and the last input coordinates

       aux = (this->matrix_.regular_row( monoRows_-1) - this->matrix_.regular_row(0)) / attr_.resolutionY_;
       if ( (double)(int(aux)) != aux ) aux += 1.; //next integer number
       nrows_ = int(aux + 1.);                  //must include the first and the last input coordinates

      // Allocate 3 auxiliary arrays where the estimated zx, zy, and zxy 
      // values at the input-grid data points are to be stored.
      WKZX_  = new double* [ monoRows_];
      WKZY_  = new double* [ monoRows_];
      WKZXY_ = new double* [ monoRows_];
      for (j = 0; j <  monoRows_; j++)
      {
	      WKZX_[j]  = new double [monoColumns_];
	      WKZY_[j]  = new double [monoColumns_];
	      WKZXY_[j] = new double [monoColumns_];
      }

      // Check for missing values
      missingValues_ = this->matrix_.hasMissingValues();

      // Estimates partial derivatives at all input-grid data points
      //{
      Timer timer("Akima", "Time spent in interpolation");
      rgpd3p();
      //}
      
      double from = this->matrix_.regular_row(0);
      for ( int i = 0; i < nrows_; i++) {
    	  double pos = from + i *attr_.resolutionY_;
    	  rowsMap_.insert(make_pair(pos, i));
    	  rows_.push_back(pos);
      }
      from = this->matrix_.regular_column(0);
      for ( int i = 0; i < ncols_; i++) {
    	  double pos = from + i *attr_.resolutionX_;
          	 columnsMap_.insert(make_pair(pos, i));
          	columns_.push_back(pos);
      }
         
          
#if 0
      // Print partial derivatives
      int i;
      MagLog::debug() << "WKZX" << "\n";;
      for (i = 0; i < this->matrix_.rows(); i++)
      {
	      MagLog::debug() << "\n";;
	      for (j = 0; j < this->matrix_.columns(); j++)
		      MagLog::debug() << WKZX_[i][j] << " ";
       }
      MagLog::debug() << "\n" << "WKZY" << "\n";;
      for (i = 0; i < this->matrix_.rows(); i++)
      {
	      MagLog::debug() << "\n";;
	      for (j = 0; j < this->matrix_.columns(); j++)
	      MagLog::debug() << WKZY_[i][j] << " ";
       }
      MagLog::debug() << "\n" << "WKZXY" << "\n";;
      for (i = 0; i < this->matrix_.rows(); i++)
      {
	      MagLog::debug() << "\n";;
	      for (j = 0; j < this->matrix_.columns(); j++)
	      MagLog::debug() << WKZXY_[i][j] << " ";
       }
#endif
}


Akima760::~Akima760()
{
      int j;

      // release memory
      for (j = 0; j < monoRows_; j++)
      {
	      delete [] WKZX_[j];
	      delete [] WKZY_[j];
	      delete [] WKZXY_[j];
	      WKZX_[j]  = NULL;
	      WKZY_[j]  = NULL;
	      WKZXY_[j] = NULL;
      }

      delete [] WKZX_;
      delete [] WKZY_;
      delete [] WKZXY_;
      WKZX_  = NULL;
      WKZY_  = NULL;
      WKZXY_ = NULL; 
}


double Akima760::regular_row(int i) const
{
//Remove later. Why this function is called so many times ???
//static long itest=0;
//MagLog::debug() << "Akima760 row=" << itest++ << "\n";

	// Check if the coordinate does not go beyond the upper limit
	double aux = i*attr_.resolutionY_ + this->matrix_.regular_row(0);
        return (aux > this->matrix_.regular_row(monoRows_-1) ? this->matrix_.regular_row(monoRows_-1) : aux);
}


double Akima760::row(int i, int j) const {
	return regular_row(i);
}


double Akima760::regular_column(int j) const
{ 
//Remove later. Why this function is called so many times ???
//static long jtest=0;
//MagLog::debug() << "Akima760 column=" << jtest++ << "\n";

	// Check if the coordinate does not go beyond the upper limit
	double aux = j*attr_.resolutionX_ + this->matrix_.regular_column(0);
        return (aux > this->matrix_.regular_column(monoColumns_-1) ? this->matrix_.regular_column(monoColumns_-1) : aux);
}

double Akima760::column(int i, int j) const {
	
	return regular_column(j);
	
}

double Akima760::operator()(int  i, int  j) const
{
	double value =  rgbi3p(regular_column(j), regular_row(i));
	
	return value;
}


void Akima760::rgpd3p()
{
//*
//* Partial derivatives of a bivariate function on a rectangular grid
//* (a supporting subroutine of the RGBI3P/RGSF3P subroutine package)
//*
//* Hiroshi Akima
//* U.S. Department of Commerce, NTIA/ITS
//* Version of 1995/08
//*
//* This subroutine estimates three partial derivatives, zx, zy, and
//* zxy, of a bivariate function, z(x,y), on a rectangular grid in
//* the x-y plane.  It is based on the revised Akima method that has
//* the accuracy of a bicubic polynomial.
//*
//* The input arguments are
//*   NXDF = number of the input-grid data points in the x
//*         coordinate (must be 2 or greater),
//*   NYDF = number of the input-grid data points in the y
//*         coordinate (must be 2 or greater),
//*   XD  = array of dimension NXDF containing the x coordinates
//*         of the input-grid data points (must be in a
//*         monotonic increasing order),
//*   YD  = array of dimension NYDF containing the y coordinates
//*         of the input-grid data points (must be in a
//*         monotonic increasing order),
//*   ZD  = two-dimensional array of dimension NXDF*NYDF
//*         containing the z(x,y) values at the input-grid data
//*         points.
//*
//* The output argument is
//*   PDD = three-dimensional array of dimension 3*NXDF*NYDF,
//*         where the estimated zx, zy, and zxy values at the
//*         input-grid data points are to be stored.
//*
//* This routine was adapted to C++ by Fernando Ii, 02/04
//*

      double  B00=0., B00X=0., B00Y=0., B01=0., B10=0., B11=0., CX1=0., CX2=0., CX3=0., CY1=0., CY2=0.,
              CY3=0., DISF =0., DNM =0., DZ00 =0., DZ01 =0., DZ02 = 0.,DZ03 =0., DZ10 =0., DZ11 =0., DZ12 = 0.,
              DZ13 =0., DZ20 =0., DZ21 =0., DZ22 =0., DZ23 =0., DZ30 = 0.,DZ31 =0., DZ32 = 0.,DZ33 = 0.,
              DZX10 =0., DZX20 = 0.,DZX30 =0., DZXY11 =0., DZXY12 =0., DZXY13 =0., DZXY21 = 0.,
              DZXY22 =0., DZXY23 =0., DZXY31 =0., DZXY32 =0., DZXY33 =0., DZY01 =0., DZY02 = 0.,
              DZY03 =0., EPSLN =0., PEZX =0., PEZXY =0., PEZY = 0.,SMPEF =0., SMPEI =0., SMWTF = 0.,
              SMWTI = 0.,SX =0., SXX =0., SXXY =0., SXXYY =0., SXY =0., SXYY =0., SXYZ =0., SXZ =0., SY =0., SYY =0., 
//              SYZ,SZ,VOLF,WT,X0,X1,X2,X3,XX1,XX2,XX3,Y0,Y1,Y2,
              SYZ =0., SZ =0., VOLF =0., WT = 0.,X0 =0., X1 = 0.,X2 =0., X3 =0., Y0 =0., Y1 =0., Y2 =0., 
              Y3 =0., Z00 = 0.,Z01 =0., Z02 = 0.,Z03 =0., Z10 =0., Z11 =0., Z12 =0., Z13 = 0.,Z20 =0., Z21 =0., Z22 = 0.,
//              Z23,Z30,Z31,Z32,Z33,ZXDI,ZXYDI,ZYDI,ZZ0,ZZ1,ZZ2;
              Z23 =0., Z30 =0., Z31 =0., Z32 =0., Z33 = 0.,ZXDI =0., ZXYDI =0., ZYDI = 0.0;

      int  IPEX,IPEY,IX0,IX1,IX2,IX3,IY0,IY1,IY2,IY3,NX0,NY0;

      int  NXDF,NYDF;       // input matrix dimensions

      //*     .. Local Arrays .
      double  B00XA[4],B00YA[4],B01A[4],B10A[4],CXA[3][4],
              CYA[3][4],SXA[4],SXXA[4],SYA[4],SYYA[4],XA[3][4],
              YA[3][4],Z0IA[3][4],ZI0A[3][4];

      int     IDLT[3][4] = {{-3,-2,-1,1},{-2,-1,1,2},{-1,1,2,3}};

      //* Statement Function definitions 
#define Z2F(XX1,XX2,ZZ0,ZZ1) ((ZZ1-ZZ0)*XX2/XX1 + ZZ0)
#define Z3F(XX1,XX2,XX3,ZZ0,ZZ1,ZZ2) (((ZZ2-ZZ0) * (XX3-XX1)/XX2  - (ZZ1-ZZ0) * (XX3-XX2)/XX1) * (XX3/ (XX2-XX1)) + ZZ0)

      //* Initial setting of some local variables
      NXDF = this->matrix_.columns();
      NYDF = this->matrix_.rows();
      NX0 = std::max((int)4,NXDF);
      NY0 = std::max((int)4,NYDF);

      //* Double DO-loop with respect to the input grid points
      for (IY0 = 0; IY0 < NYDF; IY0++)
      {
	  for (IX0 = 0; IX0 < NXDF; IX0++)
	  {
	      // Check for missing values
	      if ( missingValues_ )
	      {
		      if (CheckMissingValues(IX0,IY0) == 0)
		      {
			    WKZX_[IY0][IX0]  = missing();
			    WKZY_[IY0][IX0]  = missing();
			    WKZXY_[IY0][IX0] = missing();
			    continue;
		      }
	      }

              X0  = this->matrix_.regular_column(IX0);
              Y0  = this->matrix_.regular_row(IY0);
              Z00 = this->matrix_(IY0,IX0);
//            Z00 = this->matrix_.operator()(IY0,IX0);  //also valid

	      //* Part 1.  Estimation of ZXDI
	      //* Initial setting
              SMPEF = 0.0;
              SMWTF = 0.0;
              SMPEI = 0.0;
              SMWTI = 0.0;

	      //* DO-loop with respect to the primary estimate
              for (IPEX = 0; IPEX < 4; IPEX++)
	      {
	          //* Selects necessary grid points in the x direction
                  IX1 = IX0 + IDLT[0][IPEX];
                  IX2 = IX0 + IDLT[1][IPEX];
                  IX3 = IX0 + IDLT[2][IPEX];
                  if ((IX1 < 0) || (IX2 < 0) || (IX3 < 0) ||
                      (IX1 >= NX0) || (IX2 >= NX0) || (IX3 >= NX0)) continue;

		  //* Selects and/or supplements the x and z values
                  X1  = this->matrix_.regular_column(IX1) - X0;
                  Z10 = this->matrix_(IY0,IX1);
                  if (NXDF >= 4)
		  {
                      X2  = this->matrix_.regular_column(IX2) - X0;
                      X3  = this->matrix_.regular_column(IX3) - X0;
                      Z20 = this->matrix_(IY0,IX2);
                      Z30 = this->matrix_(IY0,IX3);
		  }
                  else if (NXDF == 3)
		  {
                      X2  = this->matrix_.regular_column(IX2) - X0;
                      Z20 = this->matrix_(IY0,IX2);
                      X3  = 2*this->matrix_.regular_column(2) - this->matrix_.regular_column(1) - X0;
                      Z30 = Z3F(X1,X2,X3,Z00,Z10,Z20);
		  }
                  else if (NXDF == 2)
		  {
                      X2  = 2*this->matrix_.regular_column(1) - this->matrix_.regular_column(0) - X0;
                      Z20 = Z2F(X1,X2,Z00,Z10);
                      X3  = 2*this->matrix_.regular_column(0) - this->matrix_.regular_column(1) - X0;
                      Z30 = Z2F(X1,X3,Z00,Z10);
		  }

                  DZX10 = (Z10-Z00)/X1;
                  DZX20 = (Z20-Z00)/X2;
                  DZX30 = (Z30-Z00)/X3;

		  //* Calculates the primary estimate of partial derivative zx as
		  //* the coefficient of the bicubic polynomial
                  CX1 = X2*X3/ ((X1-X2)* (X1-X3));
                  CX2 = X3*X1/ ((X2-X3)* (X2-X1));
                  CX3 = X1*X2/ ((X3-X1)* (X3-X2));
                  PEZX = CX1*DZX10 + CX2*DZX20 + CX3*DZX30;

		  //* Calculates the volatility factor and distance factor in the x
		  //* direction for the primary estimate of zx
                  SX   = X1 + X2 + X3;
                  SZ   = Z00 + Z10 + Z20 + Z30;
                  SXX  = X1*X1 + X2*X2 + X3*X3;
                  SXZ  = X1*Z10 + X2*Z20 + X3*Z30;
                  DNM  = 4.0*SXX - SX*SX;
                  B00  = (SXX*SZ-SX*SXZ)/DNM;
                  B10  = (4.0*SXZ-SX*SZ)/DNM;
                  DZ00 = Z00 - B00;
                  DZ10 = Z10 - (B00+B10*X1);
                  DZ20 = Z20 - (B00+B10*X2);
                  DZ30 = Z30 - (B00+B10*X3);
                  VOLF = DZ00*DZ00 + DZ10*DZ10 + DZ20*DZ20 + DZ30*DZ30;
                  DISF = SXX;

		  //* Calculates the EPSLN value, which is used to decide whether or
		  //* not the volatility factor is essentially zero
                  EPSLN = (Z00*Z00 + Z10*Z10 + Z20*Z20 + Z30*Z30) * 1.0E-12;

		  //* Accumulates the weighted primary estimates of zx and their weights
                  if (VOLF > EPSLN)
		  {
		      //* - For a finite weight
                      WT = 1.0/ (VOLF*DISF);
                      SMPEF = SMPEF + WT*PEZX;
                      SMWTF = SMWTF + WT;
		  }
                  else
		  {
		      //* - For an infinite weight
                      SMPEI = SMPEI + PEZX;
                      SMWTI = SMWTI + 1.0;
		  }

		  //* Saves the necessary values for estimating zxy
                  XA[0][IPEX] = X1;
                  XA[1][IPEX] = X2;
                  XA[2][IPEX] = X3;
                  ZI0A[0][IPEX] = Z10;
                  ZI0A[1][IPEX] = Z20;
                  ZI0A[2][IPEX] = Z30;
                  CXA[0][IPEX] = CX1;
                  CXA[1][IPEX] = CX2;
                  CXA[2][IPEX] = CX3;
                  SXA[IPEX] = SX;
                  SXXA[IPEX] = SXX;
                  B00XA[IPEX] = B00;
                  B10A[IPEX] = B10;
              } //end for IPX

	      //* Calculates the final estimate of zx
              if (SMWTI < 0.5) ZXDI = SMPEF/SMWTF;  //When no infinite weights exist
              else	       ZXDI = SMPEI/SMWTI;  //When infinite weights exist

	      //* End of Part 1
	      //* Part 2.  Estimation of ZYDI
	      //* Initial setting
              SMPEF = 0.0;
              SMWTF = 0.0;
              SMPEI = 0.0;
              SMWTI = 0.0;

	      //* DO-loop with respect to the primary estimate
              for (IPEY = 0; IPEY < 4; IPEY++)
	      {
		  //* Selects necessary grid points in the y direction
                  IY1 = IY0 + IDLT[0][IPEY];
                  IY2 = IY0 + IDLT[1][IPEY];
                  IY3 = IY0 + IDLT[2][IPEY];
                  if ((IY1 < 0) || (IY2 < 0) || (IY3 < 0) ||
                      (IY1 >= NY0) || (IY2 >= NY0) || (IY3 >= NY0)) continue;

		  //* Selects and/or supplements the y and z values
                  Y1  = this->matrix_.regular_row(IY1) - Y0;
                  Z01 = this->matrix_(IY1,IX0);
                  if (NYDF >= 4)
		  {
                      Y2 = this->matrix_.regular_row(IY2) - Y0;
                      Y3 = this->matrix_.regular_row(IY3) - Y0;
                      Z02 = this->matrix_(IY2,IX0);
                      Z03 = this->matrix_(IY3,IX0);
		  }
                  else if (NYDF == 3)
		  {
                      Y2 = this->matrix_.regular_row(IY2) - Y0;
                      Z02 = this->matrix_(IY2,IX0);
                      Y3 = 2*this->matrix_.regular_row(2) - this->matrix_.regular_row(1) - Y0;
                      Z03 = Z3F(Y1,Y2,Y3,Z00,Z01,Z02);
		  }
                  else if (NYDF == 2)
		  {
                      Y2 = 2*this->matrix_.regular_row(1) - this->matrix_.regular_row(0) - Y0;
                      Z02 = Z2F(Y1,Y2,Z00,Z01);
                      Y3 = 2*this->matrix_.regular_row(0) - this->matrix_.regular_row(1) - Y0;
                      Z03 = Z2F(Y1,Y3,Z00,Z01);
		  }

                  DZY01 = (Z01-Z00)/Y1;
                  DZY02 = (Z02-Z00)/Y2;
                  DZY03 = (Z03-Z00)/Y3;

		  //* Calculates the primary estimate of partial derivative zy as
		  //* the coefficient of the bicubic polynomial
                  CY1 = Y2*Y3/ ((Y1-Y2)* (Y1-Y3));
                  CY2 = Y3*Y1/ ((Y2-Y3)* (Y2-Y1));
                  CY3 = Y1*Y2/ ((Y3-Y1)* (Y3-Y2));
                  PEZY = CY1*DZY01 + CY2*DZY02 + CY3*DZY03;

		  //* Calculates the volatility factor and distance factor in the y
		  //* direction for the primary estimate of zy
                  SY = Y1 + Y2 + Y3;
                  SZ = Z00 + Z01 + Z02 + Z03;
                  SYY = Y1*Y1 + Y2*Y2 + Y3*Y3;
                  SYZ = Y1*Z01 + Y2*Z02 + Y3*Z03;
                  DNM = 4.0*SYY - SY*SY;
                  B00 = (SYY*SZ-SY*SYZ)/DNM;
                  B01 = (4.0*SYZ-SY*SZ)/DNM;
                  DZ00 = Z00 - B00;
                  DZ01 = Z01 - (B00+B01*Y1);
                  DZ02 = Z02 - (B00+B01*Y2);
                  DZ03 = Z03 - (B00+B01*Y3);
                  VOLF = DZ00*DZ00 + DZ01*DZ01 + DZ02*DZ02 + DZ03*DZ03;
                  DISF = SYY;

		  //* Calculates the EPSLN value, which is used to decide whether or
		  //* not the volatility factor is essentially zero
                  EPSLN = (Z00*Z00 + Z01*Z01 + Z02*Z02 + Z03*Z03) * 1.0E-12;

		  //* Accumulates the weighted primary estimates of zy and their weights
                  if (VOLF > EPSLN)
		  {
		      //* - For a finite weight
                      WT = 1.0/ (VOLF*DISF);
                      SMPEF = SMPEF + WT*PEZY;
                      SMWTF = SMWTF + WT;
		  }
                  else
		  {
		      //* - For an infinite weight
                      SMPEI = SMPEI + PEZY;
                      SMWTI = SMWTI + 1.0;
		  }

		  //* Saves the necessary values for estimating zxy
                  YA[0][IPEY] = Y1;
                  YA[1][IPEY] = Y2;
                  YA[2][IPEY] = Y3;
                  Z0IA[0][IPEY] = Z01;
                  Z0IA[1][IPEY] = Z02;
                  Z0IA[2][IPEY] = Z03;
                  CYA[0][IPEY] = CY1;
                  CYA[1][IPEY] = CY2;
                  CYA[2][IPEY] = CY3;
                  SYA[IPEY] = SY;
                  SYYA[IPEY] = SYY;
                  B00YA[IPEY] = B00;
                  B01A[IPEY] = B01;
              } //end for IPEY

	      //* Calculates the final estimate of zy
              if (SMWTI < 0.5)	        //When no infinite weights exist
                  ZYDI = SMPEF/SMWTF;
              else                      //When infinite weights exist
                  ZYDI = SMPEI/SMWTI;

	      //* End of Part 2
	      //* Part 3.  Estimation of ZXYDI
	      //* Initial setting
              SMPEF = 0.0;
              SMWTF = 0.0;
              SMPEI = 0.0;
              SMWTI = 0.0;

	      //* Outer DO-loops with respect to the primary estimates in the x direction
              for (IPEX = 0; IPEX < 4; IPEX++)
	      {
                  IX1 = IX0 + IDLT[0][IPEX];
                  IX2 = IX0 + IDLT[1][IPEX];
                  IX3 = IX0 + IDLT[2][IPEX];
                  if ((IX1 < 0) || (IX2 < 0) || (IX3 < 0) ||
                      (IX1 >= NX0) || (IX2 >= NX0) || (IX3 >= NX0)) continue;

		  //* Retrieves the necessary values for estimating zxy in the x direction
                  X1 = XA[0][IPEX];
                  X2 = XA[1][IPEX];
                  X3 = XA[2][IPEX];
                  Z10 = ZI0A[0][IPEX];
                  Z20 = ZI0A[1][IPEX];
                  Z30 = ZI0A[2][IPEX];
                  CX1 = CXA[0][IPEX];
                  CX2 = CXA[1][IPEX];
                  CX3 = CXA[2][IPEX];
                  SX = SXA[IPEX];
                  SXX = SXXA[IPEX];
                  B00X = B00XA[IPEX];
                  B10 = B10A[IPEX];

		  //* Inner DO-loops with respect to the primary estimates in the y direction
                  for (IPEY = 0; IPEY < 4; IPEY++)
		  {
                      IY1 = IY0 + IDLT[0][IPEY];
                      IY2 = IY0 + IDLT[1][IPEY];
                      IY3 = IY0 + IDLT[2][IPEY];
                      if ((IY1 < 0) || (IY2 < 0) || (IY3 < 0) ||
		          (IY1 >= NY0) || (IY2 >= NY0) || (IY3 >= NY0)) continue;

		      //* Retrieves the necessary values for estimating zxy in the y direction.
                      Y1 = YA[0][IPEY];
                      Y2 = YA[1][IPEY];
                      Y3 = YA[2][IPEY];
                      Z01 = Z0IA[0][IPEY];
                      Z02 = Z0IA[1][IPEY];
                      Z03 = Z0IA[2][IPEY];
                      CY1 = CYA[0][IPEY];
                      CY2 = CYA[1][IPEY];
                      CY3 = CYA[2][IPEY];
                      SY = SYA[IPEY];
                      SYY = SYYA[IPEY];
                      B00Y = B00YA[IPEY];
                      B01 = B01A[IPEY];

		      //* Selects and/or supplements the z values
                      if (NYDF >= 4)
		      {
                          Z11 = this->matrix_(IY1,IX1);
                          Z12 = this->matrix_(IY2,IX1);
                          Z13 = this->matrix_(IY3,IX1);
                          if (NXDF >= 4)
			  {
                              Z21 = this->matrix_(IY1,IX2);
                              Z22 = this->matrix_(IY2,IX2);
                              Z23 = this->matrix_(IY3,IX2);
                              Z31 = this->matrix_(IY1,IX3);
                              Z32 = this->matrix_(IY2,IX3);
                              Z33 = this->matrix_(IY3,IX3);
			  }
                          else if (NXDF == 3)
			  {
                              Z21 = this->matrix_(IY1,IX2);
                              Z22 = this->matrix_(IY2,IX2);
                              Z23 = this->matrix_(IY3,IX2);
                              Z31 = Z3F(X1,X2,X3,Z01,Z11,Z21);
                              Z32 = Z3F(X1,X2,X3,Z02,Z12,Z22);
                              Z33 = Z3F(X1,X2,X3,Z03,Z13,Z23);
			  }
                          else if (NXDF == 2)
			  {
                              Z21 = Z2F(X1,X2,Z01,Z11);
                              Z22 = Z2F(X1,X2,Z02,Z12);
                              Z23 = Z2F(X1,X2,Z03,Z13);
                              Z31 = Z2F(X1,X3,Z01,Z11);
                              Z32 = Z2F(X1,X3,Z02,Z12);
                              Z33 = Z2F(X1,X3,Z03,Z13);
			  }
		      }
                      else if (NYDF == 3)
		      {
                          Z11 = this->matrix_(IY1,IX1);
                          Z12 = this->matrix_(IY2,IX1);
                          Z13 = Z3F(Y1,Y2,Y3,Z10,Z11,Z12);
                          if (NXDF >= 4)
			  {
                              Z21 = this->matrix_(IY1,IX2);
                              Z22 = this->matrix_(IY2,IX2);
                              Z31 = this->matrix_(IY1,IX3);
                              Z32 = this->matrix_(IY2,IX3);
			  }
                          else if (NXDF == 3)
			  {
                              Z21 = this->matrix_(IY1,IX2);
                              Z22 = this->matrix_(IY2,IX2);
                              Z31 = Z3F(X1,X2,X3,Z01,Z11,Z21);
                              Z32 = Z3F(X1,X2,X3,Z02,Z12,Z22);
			  }
                          else if (NXDF == 2)
			  {
                              Z21 = Z2F(X1,X2,Z01,Z11);
                              Z22 = Z2F(X1,X2,Z02,Z12);
                              Z31 = Z2F(X1,X3,Z01,Z11);
                              Z32 = Z2F(X1,X3,Z02,Z12);
                          }

                          Z23 = Z3F(Y1,Y2,Y3,Z20,Z21,Z22);
                          Z33 = Z3F(Y1,Y2,Y3,Z30,Z31,Z32);
		      }
                      else if (NYDF == 2)
		      {
                          Z11 = this->matrix_(IY1,IX1);
                          Z12 = Z2F(Y1,Y2,Z10,Z11);
                          Z13 = Z2F(Y1,Y3,Z10,Z11);
                          if (NXDF >= 4)
			  {
                              Z21 = this->matrix_(IY1,IX2);
                              Z31 = this->matrix_(IY1,IX3);
			  }
                          else if (NXDF == 3)
			  {
                              Z21 = this->matrix_(IY1,IX2);
                              Z31 = Z3F(X1,X2,X3,Z01,Z11,Z21);
			  }
                          else if (NXDF == 2)
			  {
                              Z21 = Z2F(X1,X2,Z01,Z11);
                              Z31 = Z2F(X1,X3,Z01,Z11);
			  }

                          Z22 = Z2F(Y1,Y2,Z20,Z21);
                          Z23 = Z2F(Y1,Y3,Z20,Z21);
                          Z32 = Z2F(Y1,Y2,Z30,Z31);
                          Z33 = Z2F(Y1,Y3,Z30,Z31);
                      }

		      //* Calculates the primary estimate of partial derivative zxy as
		      //* the coefficient of the bicubic polynomial
                      DZXY11 = (Z11-Z10-Z01+Z00)/ (X1*Y1);
                      DZXY12 = (Z12-Z10-Z02+Z00)/ (X1*Y2);
                      DZXY13 = (Z13-Z10-Z03+Z00)/ (X1*Y3);
                      DZXY21 = (Z21-Z20-Z01+Z00)/ (X2*Y1);
                      DZXY22 = (Z22-Z20-Z02+Z00)/ (X2*Y2);
                      DZXY23 = (Z23-Z20-Z03+Z00)/ (X2*Y3);
                      DZXY31 = (Z31-Z30-Z01+Z00)/ (X3*Y1);
                      DZXY32 = (Z32-Z30-Z02+Z00)/ (X3*Y2);
                      DZXY33 = (Z33-Z30-Z03+Z00)/ (X3*Y3);
                      PEZXY = CX1* (CY1*DZXY11+CY2*DZXY12+CY3*DZXY13) +
                              CX2* (CY1*DZXY21+CY2*DZXY22+CY3*DZXY23) +
                              CX3* (CY1*DZXY31+CY2*DZXY32+CY3*DZXY33);

		      //* Calculates the volatility factor and distance factor in the x
		      //* and y directions for the primary estimate of zxy
                      B00 = (B00X+B00Y)/2.0;
                      SXY = SX*SY;
                      SXXY = SXX*SY;
                      SXYY = SX*SYY;
                      SXXYY = SXX*SYY;
                      SXYZ = X1* (Y1*Z11+Y2*Z12+Y3*Z13) +
                             X2* (Y1*Z21+Y2*Z22+Y3*Z23) +
                             X3* (Y1*Z31+Y2*Z32+Y3*Z33);
                      B11 = (SXYZ-B00*SXY-B10*SXXY-B01*SXYY)/SXXYY;
                      DZ00 = Z00 - B00;
                      DZ01 = Z01 - (B00+B01*Y1);
                      DZ02 = Z02 - (B00+B01*Y2);
                      DZ03 = Z03 - (B00+B01*Y3);
                      DZ10 = Z10 - (B00+B10*X1);
                      DZ11 = Z11 - (B00+B01*Y1+X1* (B10+B11*Y1));
                      DZ12 = Z12 - (B00+B01*Y2+X1* (B10+B11*Y2));
                      DZ13 = Z13 - (B00+B01*Y3+X1* (B10+B11*Y3));
                      DZ20 = Z20 - (B00+B10*X2);
                      DZ21 = Z21 - (B00+B01*Y1+X2* (B10+B11*Y1));
                      DZ22 = Z22 - (B00+B01*Y2+X2* (B10+B11*Y2));
                      DZ23 = Z23 - (B00+B01*Y3+X2* (B10+B11*Y3));
                      DZ30 = Z30 - (B00+B10*X3);
                      DZ31 = Z31 - (B00+B01*Y1+X3* (B10+B11*Y1));
                      DZ32 = Z32 - (B00+B01*Y2+X3* (B10+B11*Y2));
                      DZ33 = Z33 - (B00+B01*Y3+X3* (B10+B11*Y3));
                      VOLF = DZ00*DZ00 + DZ01*DZ01 + DZ02*DZ02 + DZ03*DZ03 +
                             DZ10*DZ10 + DZ11*DZ11 + DZ12*DZ12 + DZ13*DZ13 +
                             DZ20*DZ20 + DZ21*DZ21 + DZ22*DZ22 + DZ23*DZ23 +
                             DZ30*DZ30 + DZ31*DZ31 + DZ32*DZ32 + DZ33*DZ33;
                      DISF = SXX*SYY;

		      //* Calculates EPSLN
                      EPSLN = (Z00*Z00 + Z01*Z01 + Z02*Z02 + Z03*Z03 + 
			       Z10*Z10 + Z11*Z11 + Z12*Z12 + Z13*Z13 + 
                               Z20*Z20 + Z21*Z21 + Z22*Z22 + Z23*Z23 + 
			       Z30*Z30 + Z31*Z31 + Z32*Z32 + Z33*Z33) *
                               1.0E-12;

		      //* Accumulates the weighted primary estimates of zxy and their weights
                      if (VOLF > EPSLN)
		      {
		          //* - For a finite weight
                          WT = 1.0/ (VOLF*DISF);
                          SMPEF = SMPEF + WT*PEZXY;
                          SMWTF = SMWTF + WT;
		      }
                      else
		      {
		          //* - For an infinite weight
                          SMPEI = SMPEI + PEZXY;
                          SMWTI = SMWTI + 1.0;
		      }

                  } //end for IPEY
              } // end for IPEX

	      //* Calculates the final estimate of zxy
              if (SMWTI < 0.5)         //When no infinite weights exist
                  ZXYDI = SMPEF/SMWTF;
              else		       //When infinite weights exist
                  ZXYDI = SMPEI/SMWTI;

	      //* End of Part 3

              WKZX_[IY0][IX0]  = ZXDI;
              WKZY_[IY0][IX0]  = ZYDI;
              WKZXY_[IY0][IX0] = ZXYDI;

          } //end for IX0
      } //end for IY0
      return;
}

// A 7x7 area surround the point is searched

int Akima760::CheckMissingValues (int col, int lin) const
{
      int i,j,NXDF,NYDF;

      //* Initial setting of some local variables
      NXDF = std::max(4,this->matrix_.columns());
      NYDF = std::max(4,this->matrix_.rows());

      // Check a 7x7 area
      for (i = lin-3; i <= lin+3; i++)
      {
	      if ( i < 0 || i >= NYDF ) continue;

	      for (j = col-3; j <= col+3; j++)
	      {
		      if ( j < 0 || j >= NXDF ) continue;

		      if ( this->matrix_(i,j) == this->matrix_.missing() )
			      return 0;
	      }
      }

      return 1;
}

#if 0
// This subroutine is obsolet, no longer needed

void Akima760::rglctn(double XII, double YII, int& INXI, int& INYI) const
{
//*
//* Location of the desired points in a rectangular grid
//* (a supporting subroutine of the RGBI3P/RGSF3P subroutine package)
//*
//* Hiroshi Akima
//* U.S. Department of Commerce, NTIA/ITS
//* Version of 1995/08
//*
//* This subroutine locates the desired point in a rectangular grid
//* in the x-y plane.
//*
//* The grid lines can be unevenly spaced.
//*
//* The input arguments are
//*   NXD  = number of the input-grid data points in the x
//*          coordinate (must be 2 or greater),
//*   NYDF  = number of the input-grid data points in the y
//*          coordinate (must be 2 or greater),
//*   XD   = array of dimension NXD containing the x coordinates
//*          of the input-grid data points (must be in a
//*          monotonic increasing order),
//*   YD   = array of dimension NYDF containing the y coordinates
//*          of the input-grid data points (must be in a
//*          monotonic increasing order),
//*   XI   = x coordinate of the output point to be located
//*   YI   = y coordinate of the output point to be located
//*
//* The output arguments are
//*   INXI = integer where the interval number of the element is to be stored
//*   INYI = integer where the interval number of the element is to be stored
//*
//* The external arguments are
//*   NXD          = number of the input-grid data points in the x
//*                  coordinate (must be 2 or greater),
//*   NYDF         = number of the input-grid data points in the y
//*                  coordinate (must be 2 or greater),
//*   column       = array of dimension NXD containing the x coordinates
//*                  of the input-grid data points (must be in a
//*                  monotonic increasing order),
//*   row          = array of dimension NYDF containing the y coordinates
//*                  of the input-grid data points (must be in a
//*                  monotonic increasing order)
//*
//* The interval numbers are between 0 and NXD and between 0 and NYDF,
//* respectively.
//*
//* This routine was adapted to C++ by Fernando Ii, 02/04
//*
//*

      int    NXDF,NYDF;        // input matrix dimensions
      int       IMD,IMN,IMX;    // aixiliary variables

 
      //* Locates the output point by binary search. Determines INXI
      //* for which XII lies between XD(INXI) and XD(INXI+1)

//???????????????????????????
// IMPORTANT IMPORTANT IMPORTANT
      // THE OLD FORTRAN STYLE CODE ROUTINE IS RUNNING IN
      // PARALLEL WITH THE C++ CODE, TO MAKE SURE THAT THE
      // C++ CODE IS CORRECT. REMOVE THE FORTRAN STYLE CODE
      // LATER.
//???????????????????????????

      // IX/IY POINT TO 2 POSITIONS AHEAD IN THE ARRAY BECAUSE:
      // 1. FORTRAN STYLE INDEX (START WITH 1, INSTEAD OF 0)
      //    THIS IS COMPENSATE LATER IN THE CODE.
      // 2. ROUTINE 'lowerColumn' AND 'lowerRow' RETURNS THE
      //    'LOWER' VALUE INDEX, WHICH MEANS X(IX-1) OR Y(IY-1),
      //    INSTEAD OF X(IX) OR Y(IY)

      NXDF = this->matrix_.columns();
      NYDF = this->matrix_.rows();
      int ji;
      if (XII <= this->matrix_.regular_column(0))
              INXI = -1;
      else if (XII < this->matrix_.regular_column(NXDF-1))
      {
#if 1  //REMOVE LATER
              IMN = 0;
              IMX = NXDF-1;
              IMD = (IMN+IMX)/2;
ll:
              if (XII >= this->matrix_.regular_column(IMD))
		      IMN = IMD;
              else
                      IMX = IMD;

              IMD = (IMN+IMX)/2;
              if (IMD > IMN) goto ll;
	      INXI = IMD;
#endif
	      ji = this->matrix_.lowerColumn(XII);

#if 0  //REMOVE LATER
     if (JX1 != IX){
       MagLog::dev()<< "SERIOUS ERROR FINDING INDEXES" << endl;
       return -9999999.;
     }
#endif
//     MagLog::dev()<< XII << " " << INXI << " " << ji << endl;
       }
       else
              INXI = NXDF-1;

       //* Locates the output point by binary search. Determines INYI 
       //*for which YII lies between YD(INYI) and YD(INYI+1)
       if (YII <= this->matrix_.regular_row(0))
               INYI = -1;
       else if (YII < this->matrix_.regular_row(NYDF-1))
       {
#if 1  //REMOVE LATER
               IMN = 0;
               IMX = NYDF-1;
               IMD = (IMN+IMX)/2;
ll1:
	       if (YII >= this->matrix_.regular_row(IMD))
                      IMN = IMD;
               else
                      IMX = IMD;

               IMD = (IMN+IMX)/2;
               if (IMD > IMN) goto ll1;
               INYI = IMD;
#endif
	      ji = this->matrix_.lowerRow(YII);
#if 1  //REMOVE LATER
     if (ji != INYI){
       MagLog::dev()<< "SERIOUS ERROR FINDING INDEXES" << ji << " " <<  INYI << endl;
       return -9999999.;
     }
#endif
//      MagLog::dev()<< YII << " " << INYI << " " << ji << endl;
      	}
        else
               INYI = NYDF-1;

      return;
}
#endif


void Akima760::rgplnl(double XII, double YII, int IXDI, int IYDI, double& ZII) const
{

////*
////* Polynomials for rectangular-grid bivariate interpolation and
////* surface fitting
////* (a supporting subroutine of the RGBI3P/RGSF3P subroutine package)
////*
////* Hiroshi Akima
////* U.S. Department of Commerce, NTIA/ITS
////* Version of 1995/08
////*
////* This subroutine determines a polynomial in x and y for a rectangle
////* of the input grid in the x-y plane and calculates the z value for
////* the desired points by evaluating the polynomial for rectangular-
////* grid bivariate interpolation and surface fitting.
////*
////* The input arguments are
////*   NXD  = number of the input-grid data points in the x
////*          coordinate (must be 2 or greater),
////*   NYDF  = number of the input-grid data points in the y
////*          coordinate (must be 2 or greater),
////*   XD   = array of dimension NXDF containing the x coordinates
////*          of the input-grid data points (must be in a
////*          monotonic increasing order),
////*   YD   = array of dimension NYDF containing the y coordinates
////*          of the input-grid data points (must be in a
////*          monotonic increasing order),
////*   ZD   = two-dimensional array of dimension NXDF*NYDF
////*          containing the z(x,y) values at the input-grid data
////*          points,
////*   PDD  = three-dimensional array of dimension 3*NXDF*NYDF
////*          containing the estimated zx, zy, and zxy values
////*          at the input-grid data points,
////*   NIP  = number of the output points at which interpolation
////*          is to be performed,
////*   XII   = x coordinate of the output point,
////*   YII   = y coordinate of the output points,
////*   IXDXI = interval number of the input grid interval in the
////*          x direction where the x coordinate of the output
////*          point lie,
////*   IYDI = interval number of the input grid interval in the
////*          y direction where the y coordinate of the output
////*          point lie.
////*
////* The output argument is
////*   ZII   = interpolated z value at the output point.
////*
////* This routine was adapted to C++ by Fernando Ii, 02/04
////*
////*
//
//      
// Check for missing values: only need to check one of the three
// arrays (WKZX_,WKZY_,WKZXY_), since if there is a missing value
// within a 7x7 region surround the central point, all the three
// arrays point to a missing value.

      // Variables definition
      double   P00,P01,P02,P03,P10,P11;
      double   P12,P13,P20,P21,P22,P23,P30,P31,P32,P33,Q0,Q1,Q2;
      double   Q3,U,V,X0,Y0,Z00,Z01,Z0DX,Z0DY,Z10,Z11;
      double   Z1DX,Z1DY,ZDXDY,ZX00,ZX01,ZX0DY,ZX10,ZX11;
      double   ZX1DY,ZXY00,ZXY01,ZXY10,ZXY11,ZY00,ZY01,ZY0DX;
      double   ZY10,ZY11,ZY1DX;
      double   A;
      double   B;
      double   C;
      double   DF; 
      double   DX;
      double   DXSQ;
      double   DY; 
      double   DYSQ;
      int      IXD0,IXD1,IYD0,IYD1;
      int      NXDF,NYDF;

       //* Retrieves the z and partial derivative values at the origin of
       //* the coordinate for the rectangle
       NXDF = monoColumns_;
       NYDF = monoRows_;
       IXD0  = std::max(0,IXDI);
       IYD0  = std::max(0,IYDI);
       X0    = this->matrix_.regular_column(IXD0);
       Y0    = this->matrix_.regular_row(IYD0);
       Z00   = this->matrix_(IYD0,IXD0);
       ZX00  = WKZX_[IYD0][IXD0];
       ZY00  = WKZY_[IYD0][IXD0];
       ZXY00 = WKZXY_[IYD0][IXD0];
       if (ZX00 == missing()) 
       {
		ZII = missing();
		return;
       }

// CHECK THESE INDEXES LATER ????????????????????????????

       //* Case 1.  When the rectangle is inside the data area in both the
       //* x and y directions
       if (IXDI >= 0 && IXDI < NXDF-1 && IYDI >= 0 && IYDI < NYDF-1)
       {
	          //* Retrieves the z and partial derivative values at the other three
	          //* vertexes of the rectangle
                  IXD1 = IXD0 + 1;
                  DX = this->matrix_.regular_column(IXD1) - X0;
                  DXSQ = DX*DX;
                  IYD1 = IYD0 + 1;
                  DY = this->matrix_.regular_row(IYD1) - Y0;
                  DYSQ = DY*DY;
                  Z10 = this->matrix_(IYD0,IXD1);
                  Z01 = this->matrix_(IYD1,IXD0);
                  Z11 = this->matrix_(IYD1,IXD1);
                  ZX10 = WKZX_[IYD0][IXD1];
                  ZX01 = WKZX_[IYD1][IXD0];
                  ZX11 = WKZX_[IYD1][IXD1];
                  ZY10 = WKZY_[IYD0][IXD1];
                  ZY01 = WKZY_[IYD1][IXD0];
                  ZY11 = WKZY_[IYD1][IXD1];
                  ZXY10 = WKZXY_[IYD0][IXD1];
                  ZXY01 = WKZXY_[IYD1][IXD0];
                  ZXY11 = WKZXY_[IYD1][IXD1];
		  if (ZX10 == missing() || ZX01 == missing() || ZX11 == missing()) 
		  {
			 ZII = missing();
			 return;
		  }

		  //* Calculates the polynomial coefficients
                  Z0DX = (Z10-Z00)/DX;
                  Z1DX = (Z11-Z01)/DX;
                  Z0DY = (Z01-Z00)/DY;
                  Z1DY = (Z11-Z10)/DY;
                  ZX0DY = (ZX01-ZX00)/DY;
                  ZX1DY = (ZX11-ZX10)/DY;
                  ZY0DX = (ZY10-ZY00)/DX;
                  ZY1DX = (ZY11-ZY01)/DX;
                  ZDXDY = (Z1DY-Z0DY)/DX;
                  A = ZDXDY - ZX0DY - ZY0DX + ZXY00;
                  B = ZX1DY - ZX0DY - ZXY10 + ZXY00;
                  C = ZY1DX - ZY0DX - ZXY01 + ZXY00;
                  DF= ZXY11 - ZXY10 - ZXY01 + ZXY00;
                  P00 = Z00;
                  P01 = ZY00;
                  P02 = (2.0* (Z0DY-ZY00)+Z0DY-ZY01)/DY;
                  P03 = (-2.0*Z0DY+ZY01+ZY00)/DYSQ;
                  P10 = ZX00;
                  P11 = ZXY00;
                  P12 = (2.0* (ZX0DY-ZXY00)+ZX0DY-ZXY01)/DY;
                  P13 = (-2.0*ZX0DY+ZXY01+ZXY00)/DYSQ;
                  P20 = (2.0* (Z0DX-ZX00)+Z0DX-ZX10)/DX;
                  P21 = (2.0* (ZY0DX-ZXY00)+ZY0DX-ZXY10)/DX;
                  P22 = (3.0* (3.0*A-B-C)+DF)/ (DX*DY);
                  P23 = (-6.0*A+2.0*B+3.0*C-DF)/ (DX*DYSQ);
                  P30 = (-2.0*Z0DX+ZX10+ZX00)/DXSQ;
                  P31 = (-2.0*ZY0DX+ZXY10+ZXY00)/DXSQ;
                  P32 = (-6.0*A+3.0*B+2.0*C-DF)/ (DXSQ*DY);
                  P33 = (2.0* (2.0*A-B-C)+DF)/ (DXSQ*DYSQ);

	      //* Evaluates the polynomial
              U = XII - X0;
              V = YII - Y0;
              Q0 = P00 + V* (P01+V* (P02+V*P03));
              Q1 = P10 + V* (P11+V* (P12+V*P13));
              Q2 = P20 + V* (P21+V* (P22+V*P23));
              Q3 = P30 + V* (P31+V* (P32+V*P33));
              ZII = Q0 + U* (Q1+U* (Q2+U*Q3));
	  }
	  //* End of Case 1

	  //* Case 2.  When the rectangle is inside the data area in the x
	  //* direction but outside in the y direction
          else if ((IXDI >= 0 && IXDI < NXDF-1) && (IYDI < 0 || IYDI >= NYDF-1))
	  {
	      //* Retrieves the z and partial derivative values at the other
	      //* vertex of the semi-infinite rectangle
                  IXD1 = IXD0 + 1;
                  DX = this->matrix_.regular_column(IXD1) - X0;
                  DXSQ = DX*DX;
                  Z10 = this->matrix_(IYD0,IXD1);
                  ZX10 = WKZX_[IYD0][IXD1];
                  ZY10 = WKZY_[IYD0][IXD1];
                  ZXY10 = WKZXY_[IYD0][IXD1];
		  if (ZX10 == missing())
		  {
			 ZII = missing();
			 return;
		  }

		  //* Calculates the polynomial coefficients
                  Z0DX = (Z10-Z00)/DX;
                  ZY0DX = (ZY10-ZY00)/DX;
                  P00 = Z00;
                  P01 = ZY00;
                  P10 = ZX00;
                  P11 = ZXY00;
                  P20 = (2.0* (Z0DX-ZX00)+Z0DX-ZX10)/DX;
                  P21 = (2.0* (ZY0DX-ZXY00)+ZY0DX-ZXY10)/DX;
                  P30 = (-2.0*Z0DX+ZX10+ZX00)/DXSQ;
                  P31 = (-2.0*ZY0DX+ZXY10+ZXY00)/DXSQ;

	      //* Evaluates the polynomial
              U = XII - X0;
              V = YII - Y0;
              Q0 = P00 + V*P01;
              Q1 = P10 + V*P11;
              Q2 = P20 + V*P21;
              Q3 = P30 + V*P31;
              ZII = Q0 + U* (Q1+U* (Q2+U*Q3));
	  }
	  //* End of Case 2

	  //* Case 3.  When the rectangle is outside the data area in the x
	  //* direction but inside in the y direction
          else if ((IXDI < 0 || IXDI >= NXDF-1) && (IYDI >= 0 && IYDI < NYDF-1))
	  {
	      //* Retrieves the z and partial derivative values at the other
	      //* vertex of the semi-infinite rectangle
                  IYD1 = IYD0 + 1;
                  DY = this->matrix_.regular_row(IYD1) - Y0;
                  DYSQ = DY*DY;
                  Z01 = this->matrix_(IYD1,IXD0);
                  ZX01 = WKZX_[IYD1][IXD0];
                  ZY01 = WKZY_[IYD1][IXD0];
                  ZXY01 = WKZXY_[IYD1][IXD0];
		  if (ZX01 == missing())
		  {
			  ZII = missing();
			  return;
		  }

		  //* Calculates the polynomial coefficients
                  Z0DY = (Z01-Z00)/DY;
                  ZX0DY = (ZX01-ZX00)/DY;
                  P00 = Z00;
                  P01 = ZY00;
                  P02 = (2.0* (Z0DY-ZY00)+Z0DY-ZY01)/DY;
                  P03 = (-2.0*Z0DY+ZY01+ZY00)/DYSQ;
                  P10 = ZX00;
                  P11 = ZXY00;
                  P12 = (2.0* (ZX0DY-ZXY00)+ZX0DY-ZXY01)/DY;
                  P13 = (-2.0*ZX0DY+ZXY01+ZXY00)/DYSQ;

	      //* Evaluates the polynomial
              U = XII - X0;
              V = YII - Y0;
              Q0 = P00 + V* (P01+V* (P02+V*P03));
              Q1 = P10 + V* (P11+V* (P12+V*P13));
              ZII = Q0 + U*Q1;
	  }
	  //* End of Case 3

	  //* Case 4.  When the rectangle is outside the data area in both the
	  //* x and y direction
          else if ((IXDI < 0 || IXDI >= NXDF-1) && (IYDI < 0 || IYDI >= NYDF-1))
	  {
	      //* Calculates the polynomial coefficients
              P00 = Z00;
              P01 = ZY00;
              P10 = ZX00;
              P11 = ZXY00;

	      //* Evaluates the polynomial
              U = XII - X0;
              V = YII - Y0;
              Q0 = P00 + V*P01;
              Q1 = P10 + V*P11;
              ZII = Q0 + U*Q1;
	  }
	  //* End of Case 4

      return;
}



double Akima760::rgbi3p(double XI, double YI) const
{
//*
//* Rectangular-grid bivariate interpolation
//* (a master subroutine of the RGBI3P/RGSF3P subroutine package)
//*
//* Hiroshi Akima
//* U.S. Department of Commerce, NTIA/ITS
//* Version of 1995/08
//*
//* This subroutine performs interpolation of a bivariate function,
//* z(x,y), on a rectangular grid in the x-y plane.  It is based on
//* the revised Akima method.
//*
//* In this subroutine, the interpolating function is a piecewise
//* function composed of a set of bicubic (bivariate third-degree)
//* polynomials, each applicable to a rectangle of the input grid
//* in the x-y plane.  Each polynomial is determined locally.
//*
//* This subroutine has the accuracy of a bicubic polynomial, i.e.,
//* it interpolates accurately when all data points lie on a
//* surface of a bicubic polynomial.
//*
//* The grid lines can be unevenly spaced.
//*
//* The input arguments are
//*   MD  = mode of computation
//*       = 1 for new XD, YD, or ZD data (default)
//*       = 2 for old XD, YD, and ZD data,
//*   NXDF = number of the input-grid data points in the x
//*         coordinate (must be 2 or greater),
//*   NYDF = number of the input-grid data points in the y
//*         coordinate (must be 2 or greater),
//*   XD  = array of dimension NXDF containing the x coordinates
//*         of the input-grid data points (must be in a
//*         monotonic increasing order),
//*   YD  = array of dimension NYDF containing the y coordinates
//*         of the input-grid data points (must be in a
//*         monotonic increasing order),
//*   ZD  = two-dimensional array of dimension NXDF*NYDF
//*         containing the z(x,y) values at the input-grid data
//*         points,
//*   NIP = number of the output points at which interpolation
//*         of the z value is desired (must be 1 or greater),
//*   XI  = array of dimension NIP containing the x coordinates
//*         of the output points,
//*   YI  = array of dimension NIP containing the y coordinates
//*         of the output points.
//*
//* The output arguments are
//*   ZI  = array of dimension NIP where the interpolated z
//*         values at the output points are to be stored,
//*   IER = error flag
//*       = 0 for no errors
//*       = 1 for NXDF = 1 or less
//*       = 2 for NYDF = 1 or less
//*       = 3 for identical XD values or
//*               XD values out of sequence
//*       = 4 for identical YD values or
//*               YD values out of sequence
//*       = 5 for NIP = 0 or less.
//*
//* The other argument is
//*   WK  = three dimensional array of dimension 3*NXDF*NYDF used
//*         internally as a work area.
//*
//* The very fisrt call to this subroutine and the call with a new
//* XD, YD, and ZD array must be made with MD=1.  The call with MD=2
//* must be preceded by another call with the same XD, YD, and ZD
//* arrays.  Between the call with MD=2 and its preceding call, the
//* WK array must not be disturbed.
//*
//* The constant in the PARAMETER statement below is
//*   NIPIMX = maximum number of output points to be processed
//*            at a time.
//* The constant value has been selected empirically.
//*
//* This subroutine calls the RGLCTN and RGPLNL subroutines.
//*
//* This routine was adapted to C++ by Fernando Ii, 02/04
//*
//* Specification statements
//*     .. Parameters ..
//

//*   .. Scalar Arguments ..
//      int          IER,MD,NIP,NXDF,NYDF;

//*   .. Array Arguments ..
//      double       WK[NYDF][NXDF][3],XD[NXDF],XI[NIP],YD[NYDF],YI[NIP],
//                   ZD[NYDF][NXDF],ZI[NIP];

      int    INXI,INYI;  // input point
      double ZI;         // interpolated value

      // Locates the output point
      // It is not called anymore, but it is kept here
      // for test purpose
      //int col, row;
      //rglctn(XI,YI,col,row );

      if (XI <= this->matrix_.regular_column(0)) INXI = -1;
      else if (XI < this->matrix_.regular_column(this->matrix_.columns()-1)) INXI = this->matrix_.lowerColumn(XI);
      else INXI = this->matrix_.columns()-1;

      if (YI <= this->matrix_.regular_row(0)) INYI = -1;
      else if (YI < this->matrix_.regular_row(this->matrix_.rows()-1)) INYI = this->matrix_.lowerRow(YI);
      else INYI = this->matrix_.rows()-1;

//MagLog::dev()<< XI << " " << YI << "   " << INXI << " " << INYI << "   " << col << " " << row << endl;

      // Calculates the z values at the output point
      rgplnl(XI,YI,INXI,INYI,ZI);

      return ZI;
}



int Akima760::rowIndex(double row) const
{
     map<double, int>::const_iterator i = rowsMap_.find(row);
     if ( i != rowsMap_.end() ) return (*i).second;
     return -1;
}



int Akima760::columnIndex(double row) const
{
      map<double, int>::const_iterator i = columnsMap_.find(row);
      if ( i != columnsMap_.end() ) return (*i).second;
      return -1;
}



void Akima760::boundRow(double r, double& row1, int& index1, double& row2, int& index2) const
{
//	unsigned int  nb =  rows_.size();

	// first test if increasing!
	if ( rows_.back() - rows_.front() > 0 ) {
		index2 = 0;
		while ( index2 < rows_.size() && rows_[index2] < r ) {
			index2++;
		}
		index1 = (index2) ? index2-1 : 0;
		row1 = rows_[index1];
		row2 = rows_[index2];
	}
	else {
		index1 = 0;
		while ( index1 < rows_.size() && rows_[index1] >  r ) {
			index1++;
		}
		index2 = (index1 == rows_.size() -1 ) ? index1 :  index1+1;
		row1 = rows_[index1];
		row2 = rows_[index2];
	}
}



void Akima760::boundColumn(double r, double& column1, int& index1, double& column2, int& index2) const
{
//	unsigned int  nb =  rows_.size();

	// first test if increasing!
	if ( columns_.back() - columns_.front() > 0 )
	{
		index2 = 0;
		while ( index2 < columns_.size() && columns_[index2] < r )
		{
				index2++;
		}
		index1 = (index2) ? index2-1 : 0;
		column1 = columns_[index1];
		column2 = columns_[index2];
	}
	else {
		index1 = 0;
		while (  index1 < columns_.size() && columns_[index1] >  r )
		{
			index1++;
		}
		index2 = (index1 == columns_.size() -1 ) ? index1 : index1+1;
		column1 = columns_[index1];
		column2 = columns_[index2];
	}
}
