/*
 * (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 Akima474.cc
    Implementation of Akima474 class.

    Magics Team - ECMWF 2004

    Created: Wed 14-Apr-2004

*/

#include "Akima474Method.h"
#include "MagLog.h"
#include "Timer.h"
//#include "Filter.h"  //test, remove later

using namespace magics;


Akima474::Akima474(const AbstractMatrix& matrix, const Akima474MethodAttributes& attr) :
    MatrixHandler(matrix),
    mono_(matrix),
    //    matrix1_(mono_),
    //    mono_(matrix1_),

    attr_(attr) {
    // Compute matrix output sizes
    double aux = (mono_.regular_column(mono_.columns() - 1) - mono_.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 = (mono_.regular_row(mono_.rows() - 1) - mono_.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

    // Check for missing values
    missingValues_ = mono_.hasMissingValues() ? true : false;

// Test. If this is ok create a new function setRowAxis
#if 0
int ind;
double val;
rowsAxis_.reserve(nrows_);
for (ind = 0; ind < nrows_; ind++)
{
	val = ind*attr_.resolutionY_ + mono_.regular_row(0);
        rowsAxis_.push_back(val);
}
columnsAxis_.reserve(ncols_);
for (ind = 0; ind < ncols_; ind++)
{
	val = ind*attr_.resolutionX_ + mono_.regular_column(0);
        columnsAxis_.push_back(val);
}
#endif
}


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

    return (i * attr_.resolutionY_ + mono_.regular_row(0));
    //	return rowsAxis_[i];  //this could make it faster
}


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


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

    return (j * attr_.resolutionX_ + mono_.regular_column(0));
    //	return columnsAxis_[j];  //this could make it faster
}


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


double Akima474::operator()(int i, int j) const {
#if 0
         // Calculate coordinates of (l,c) in the output matrix
	 double col = (regular_column(j) - mono_.regular_column(0)) / Iresx_;
	 double lin = (regular_row(i) - mono_.regular_row(0)) / Iresy_;

	 // Compute interpolated value
	 return InterpolateBicubicAt(lin,col);
#else
    // MagLog::dev()<< i << " " << j << " " << regular_row(i) << " " << regular_column(j) << endl;
    return itplbv(regular_column(j), regular_row(i));

#endif
}


double Akima474::itplbv(double col, double lin) const {
    //     ALGORITHM 474 COLLECTED ALGORITHMS FROM ACM.
    //     ALGORITHM APPEARED IN COMM. ACM, VOL. VV, NO. NN,
    //     P. 000.
    //      SUBROUTINE ITPLBV(LX, LY, X, Y, Z, N, U, V, W)
    // BIVARIATE INTERPOLATION
    // THIS SUBROUTINE INTERPOLATES, FROM VALUES OF THE FUNCTION
    // GIVEN AT INPUT GRID POINTS IN AN X-Y PLANE AND FOR A GIVEN
    // SET OF POINTS IN THE PLANE, THE VALUES OF A SINGLE-VALUED
    // BIVARIATE FUNCTION Z = Z(X,Y).
    // THE METHOD IS BASED ON A PIECE-WISE FUNCTION COMPOSED OF
    // A SET OF BICUBIC POLYNOMIALS IN X AND Y.  EACH POLYNOMIAL
    // IS APPLICABLE TO A RECTANGLE OF THE INPUT GRID IN THE X-Y
    // PLANE.  EACH POLYNOMIAL IS DETERMINED LOCALLY.
    //
    // This routine was adapted to C++ by Fernando Ii, 05/04
    //
    // THE INPUT PARAMETERS ARE:
    // LX  = NUMBER OF INPUT GRID POINTS IN THE X COORDINATE
    //       (MUST BE 2 OR GREATER)
    // LY  = NUMBER OF INPUT GRID POINTS IN THE Y COORDINATE
    //       (MUST BE 2 OR GREATER)
    // X   = ARRAY OF DIMENSION LX STORING THE X COORDINATES
    //       OF INPUT GRID POINTS (IN ASCENDING ORDER)
    // Y   = ARRAY OF DIMENSION LY STORING THE Y COORDINATES
    //       OF INPUT GRID POINTS (IN ASCENDING ORDER)
    // Z   = DOUBLY-DIMENSIONED ARRAY OF DIMENSION (LX,LY)
    //       STORING THE VALUES OF THE FUNCTION (Z VALUES)
    //       AT INPUT GRID POINTS
    // N   = NUMBER OF POINTS AT WHICH INTERPOLATION OF THE
    //       Z VALUE IS DESIRED (MUST BE 1 OR GREATER)
    // U   = ARRAY OF DIMENSION N STORING THE X COORDINATES
    //       OF DESIRED POINTS
    // V   = ARRAY OF DIMENSION N STORING THE Y COORDINATES
    //       OF DESIRED POINTS
    // THE OUTPUT PARAMETER IS
    // W   = ARRAY OF DIMENSION N WHERE THE INTERPOLATED Z
    //       VALUES AT DESIRED POINTS ARE TO BE DISPLAYED
    //       SOME VARIABLES INTERNALLY USED ARE
    // ZA  = DIVIDED DIFFERENCE OF Z WITH RESPECT TO X
    // ZB  = DIVIDED DIFFERENCE OF Z WITH RESPECT TO Y
    // ZAB = SECOND ORDER DIVIDED DIFFERENCE OF Z WITH
    //       RESPECT TO X AND Y
    // ZX  = PARTIAL DERIVATIVE OF Z WITH RESPECT TO X
    // ZY  = PARTIAL DERIVATIVE OF Z WITH RESPECT TO Y
    // ZXY = SECOND ORDER PARTIAL DERIVATIVE OF Z WITH
    //       RESPECT TO X AND Y

    // DECLARATION STATEMENTS
    double a, b, c, d, e;
    double Z33;

    double ZA[2][5], ZB[5][2], ZAB[3][3], ZX[4][4], ZY[4][4], ZXY[4][4];
    double &Z3A1 = ZA[0][0], &Z3A2 = ZA[0][1], &Z3A3 = ZA[0][2], &Z3A4 = ZA[0][3], &Z3A5 = ZA[0][4], &Z4A1 = ZA[1][0],
           &Z4A2 = ZA[1][1], &Z4A3 = ZA[1][2], &Z4A4 = ZA[1][3], &Z4A5 = ZA[1][4];
    double &Z3B1 = ZB[0][0], &Z3B2 = ZB[1][0], &Z3B3 = ZB[2][0], &Z3B4 = ZB[3][0], &Z3B5 = ZB[4][0], &Z4B1 = ZB[0][1],
           &Z4B2 = ZB[1][1], &Z4B3 = ZB[2][1], &Z4B4 = ZB[3][1], &Z4B5 = ZB[4][1];

    double &ZA2B2 = ZAB[0][0], &ZA3B2 = ZAB[0][1], &ZA4B2 = ZAB[0][2], &ZA2B3 = ZAB[1][0], &ZA3B3 = ZAB[1][1],
           &ZA4B3 = ZAB[1][2], &ZA2B4 = ZAB[2][0], &ZA3B4 = ZAB[2][1], &ZA4B4 = ZAB[2][2];

    double &ZX33 = ZX[1][1], &ZX43 = ZX[1][2], &ZX34 = ZX[2][1], &ZX44 = ZX[2][2];
    double &ZY33 = ZY[1][1], &ZY43 = ZY[1][2], &ZY34 = ZY[2][1], &ZY44 = ZY[2][2];
    double &ZXY33 = ZXY[1][1], &ZXY43 = ZXY[1][2], &ZXY34 = ZXY[2][1], &ZXY44 = ZXY[2][2];

    double &P00 = Z33, &P01 = ZY33, &P10 = ZX33, &P11 = ZXY33;

    //      double LX0,ZX(1)), (LXM1,ZX(4)), (LXM2,ZX(13)),
    //     * (LXP1,ZX(16)), (LY0,ZY(1)), (LYM1,ZY(4)), (LYM2,ZY(13)),
    //     * (LYP1,ZY(16)), (IX,ZXY(1)), (IY,ZXY(4)), (IXPV,ZXY(13)),
    //     * (IYPV,ZXY(16))

    //      int (IMN,JX), (IMX,JY), (JXM2,JX1), (JYM2,JY1)
    //      double (UK,DX), (VK,DY)

    //      double (A1,A5,B1,B5,ZX(2),A,Q0),
    double A1, A5, B1, B5, Q0;

    //     * (A2,ZX(5),B,Q1),
    double A2 = 0., Q1;

    //    (A4,ZX(8),C,Q2)
    double A4 = 0., Q2;

    //    (B2,ZY(2),D,Q3),
    double B2 = 0., Q3;

    //     * (B4,ZY(14),E),
    double B4 = 0.;

    //    (X2,ZX(3),A3SQ),
    double X2 = 0., A3SQ;

    //    (X4,ZX(9)), (X5,ZX(12)),
    double X4, X5 = 0.;

    //     * (Y2,ZX(14)), (Y4,ZY(3),B3SQ), (Y5,ZX(15),P02),
    double Y2 = 0., Y4, B3SQ, Y5 = 0., P02;

    //     * (Z23,ZY(5),P03), (Z24,ZY(8),P12), (Z32,ZY(9),P13),
    double Z23 = 0., P03 = 0., Z24 = 0., P12 = 0., Z32 = 0., P13 = 0.;

    //     * (Z34,ZY(12),P20), (Z35,ZY(15),P21), (Z42,ZXY(2),P22),
    double Z34, P20, Z35 = 0., P21, Z42 = 0., P22;

    //     * (Z43,ZXY(5),P23), (Z44,ZXY(3),P30), (Z45,ZXY(8),P31),
    double Z43, P23, Z44, P30, Z45 = 0., P31;

    //     * (Z53,ZXY(9),P32), (Z54,ZXY(12),P33), (W2,WY2,W4),
    double Z53 = 0., P32, Z54 = 0., P33, W2, WY2, W4;

    //     * (W3,WY3,W1,W5), (WX2,ZXY(14)), (WX3,ZXY(15))
    double W3, WY3, W1, W5, WX2, WX3;

    //      DIMENSION X(LX), Y(LY), Z(LX,LY), U(N), V(N), W(N)

    // Auxiliary variables
    int IX, IY, JX, JY, JX1, JY1;
    int JXM2, JXML, JYM2, JYML;
    int LX, LY, LX0, LXM1, LXM2, LXP1, LY0, LYM1, LYM2, LYP1;
    int INXI, INYI;
    double UK, VK, DX, DY, SW;

    double X3, Y3, A3, B3, ZX3B3, ZX4B3, ZY3A3, ZY4A3;

    double WK;

    // PRELIMINARY PROCESSING
    // SETTING OF SOME INPUT PARAMETERS TO LOCAL VARIABLES
    LX   = mono_.columns();
    LY   = mono_.rows();
    LX0  = LX;
    LXM1 = LX0 - 1;
    LXM2 = LXM1 - 1;
    LXP1 = LX0 + 1;
    LY0  = LY;
    LYM1 = LY0 - 1;
    LYM2 = LYM1 - 1;
    LYP1 = LY0 + 1;

    // ERROR CHECK
    if (LXM2 < 0 || LYM2 < 0) {
        MagLog::debug() << "LX/LY = 1 OR LESS" << endl;
        MagLog::debug() << LX << " " << LY << endl;
        return mono_.missing();
    }

    // INITIAL SETTING OF PREVIOUS VALUES OF IX AND IY
    //      IXPV = 0;
    //      IYPV = 0;

    // ROUTINES TO LOCATE THE DESIRED POINT

    //???????????????????????????
    // 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)

    // TO FIND OUT THE IX VALUE FOR WHICH
    // (U(K).GE.X(IX-1)).AND.(U(K).LT.X(IX))

    UK = col;
    VK = lin;

    if (UK <= mono_.regular_column(0))
        INXI = -1;
    else
        INXI = mono_.lowerColumn(UK);

    if (VK <= mono_.regular_row(0))
        INYI = -1;
    else
        INYI = mono_.lowerRow(VK);

    if (LXM2 == 0)
        IX = 2;
    else if (UK >= mono_.regular_column(LX0 - 1))
        IX = LXP1;
    else if (UK < mono_.regular_column(0))
        IX = 1;
    else {
#if 0  // REMOVE LATER
	      int IMN,IMX;
	      IMN = 2;
	      IMX = LX0;
ll:
	      IX = (IMN+IMX)/2;
	      if (UK >= mono_.regular_column(IX-1))
	       IMN = IX + 1;
	      else
               IMX = IX;

	      if (IMX > IMN) goto ll;
	      IX = IMX;
	      JX1 = IX;
#endif
        IX = mono_.lowerColumn(UK) + 2;

#if 0  // REMOVE LATER
     if (JX1 != IX){
       MagLog::dev()<< "SERIOUS ERROR FINDING INDEXES" << endl;
       return -9999999.;
     }
#endif
        //      MagLog::dev()<< UK << " " << VK << "   " << IX << "   " << JX1 << endl;
    }
    // TO FIND OUT THE IY VALUE FOR WHICH
    // (V(K).GE.Y(IY-1)).AND.(V(K).LT.Y(IY))
    if (LYM2 == 0)
        IY = 2;
    else if (VK >= mono_.regular_row(LY0 - 1))
        IY = LYP1;
    else if (VK < mono_.regular_row(0))
        IY = 1;
    else {
#if 0  // REMOVE LATER
	      int IMN,IMX;
	      IMN = 2;
	      IMX = LY0;
ll1:
	      IY = (IMN+IMX)/2;
	      if (VK >= mono_.regular_row(IY-1))
	       IMN = IY + 1;
	      else
	       IMX = IY;

	      if (IMX > IMN) goto ll1;
	      IY = IMX;
	      JY1 = IY;
#endif
        IY = mono_.lowerRow(VK) + 2;
#if 0  // REMOVE LATER
     if (JY1 != IY){
       MagLog::dev()<< "SERIOUS ERROR FINDING INDEXES" << endl;
       return -9999999.;
     }
#endif
        //      MagLog::dev()<< UK << " " << VK << "   " << IY << "   " << JY1 << endl;
    }

    // TO CHECK IF THE DESIRED POINT IS IN THE SAME RECTANGLE
    // AS THE PREVIOUS POINT.  IF YES, SKIP TO THE COMPUTATION
    // OF THE POLYNOMIAL
    // maybe this is a good idea to increase performance
    //  160   IF (IX.EQ.IXPV .AND. IY.EQ.IYPV) GO TO 690
    //      IXPV = IX;
    //      IYPV = IY;

    // ROUTINES TO PICK UP NECESSARY X, Y, AND Z VALUES, TO
    // COMPUTE THE ZA, ZB, AND ZAB VALUES, AND TO ESTIMATE THEM
    // WHEN NECESSARY
    JX = IX;
    if (JX == 1)
        JX = 2;
    if (JX == LXP1)
        JX = LX0;
    JY = IY;
    if (JY == 1)
        JY = 2;
    if (JY == LYP1)
        JY = LY0;
    JXM2 = JX - 2;
    JXML = JX - LX0;
    JYM2 = JY - 2;
    JYML = JY - LY0;

    // CHECK FOR MISSING VALUES
    if (missingValues_)
        if (this->CheckMissingValues(JX, JY) == 0)
            return this->missing();

    // IN THE CORE AREA, I.E., IN THE RECTANGLE THAT CONTAINS
    // THE DESIRED POINT
    X3 = mono_.regular_column(JX - 2);
    X4 = mono_.regular_column(JX - 1);

    A3    = 1.0 / (X4 - X3);
    Y3    = mono_.regular_row(JY - 2);
    Y4    = mono_.regular_row(JY - 1);
    B3    = 1.0 / (Y4 - Y3);
    Z33   = mono_(JY - 2, JX - 2);
    Z43   = mono_(JY - 2, JX - 1);
    Z34   = mono_(JY - 1, JX - 2);
    Z44   = mono_(JY - 1, JX - 1);
    Z3A3  = (Z43 - Z33) * A3;
    Z4A3  = (Z44 - Z34) * A3;
    Z3B3  = (Z34 - Z33) * B3;
    Z4B3  = (Z44 - Z43) * B3;
    ZA3B3 = (Z4B3 - Z3B3) * A3;

    // IN THE X DIRECTION
    if (LXM2 == 0) {
        Z3A2 = Z3A3;
        Z4A2 = Z4A3;
        Z3A4 = Z3A3 + Z3A3 - Z3A2;
        Z4A4 = Z4A3 + Z4A3 - Z4A2;
        goto l190;
    }
    else if (JXM2 != 0) {
        X2   = mono_.regular_column(JX - 3);
        A2   = 1.0 / (X3 - X2);
        Z23  = mono_(JY - 2, JX - 3);
        Z24  = mono_(JY - 1, JX - 3);
        Z3A2 = (Z33 - Z23) * A2;
        Z4A2 = (Z34 - Z24) * A2;
        if (JXML == 0) {
            Z3A4 = Z3A3 + Z3A3 - Z3A2;
            Z4A4 = Z4A3 + Z4A3 - Z4A2;
            goto l190;
        }
    }
    X5   = mono_.regular_column(JX);
    A4   = 1.0 / (X5 - X4);
    Z53  = mono_(JY - 2, JX);
    Z54  = mono_(JY - 1, JX);
    Z3A4 = (Z53 - Z43) * A4;
    Z4A4 = (Z54 - Z44) * A4;
    if (JXM2 == 0) {
        Z3A2 = Z3A3 + Z3A3 - Z3A4;
        Z4A2 = Z4A3 + Z4A3 - Z4A4;
    }

l190:
    ZA2B3 = (Z4A2 - Z3A2) * B3;
    ZA4B3 = (Z4A4 - Z3A4) * B3;
    if (JX <= 3) {
        Z3A1 = Z3A2 + Z3A2 - Z3A3;
        Z4A1 = Z4A2 + Z4A2 - Z4A3;
    }
    else {
        A1   = 1.0 / (X2 - mono_.regular_column(JX - 4));
        Z3A1 = (Z23 - mono_(JY - 2, JX - 4)) * A1;
        Z4A1 = (Z24 - mono_(JY - 1, JX - 4)) * A1;
    }

    if (JX >= LXM1) {
        Z3A5 = Z3A4 + Z3A4 - Z3A3;
        Z4A5 = Z4A4 + Z4A4 - Z4A3;
    }
    else {
        A5   = 1.0 / (mono_.regular_column(JX + 1) - X5);
        Z3A5 = (mono_(JY - 2, JX + 1) - Z53) * A5;
        Z4A5 = (mono_(JY - 1, JX + 1) - Z54) * A5;
    }

    // IN THE Y DIRECTION
    if (LYM2 == 0) {
        Z3B2 = Z3B3;
        Z4B2 = Z4B3;
        Z3B4 = Z3B3 + Z3B3 - Z3B2;
        Z4B4 = Z4B3 + Z4B3 - Z4B2;
        goto l270;
    }
    else if (JYM2 != 0) {
        Y2   = mono_.regular_row(JY - 3);
        B2   = 1.0 / (Y3 - Y2);
        Z32  = mono_(JY - 3, JX - 2);
        Z42  = mono_(JY - 3, JX - 1);
        Z3B2 = (Z33 - Z32) * B2;
        Z4B2 = (Z43 - Z42) * B2;
        if (JYML == 0) {
            Z3B4 = Z3B3 + Z3B3 - Z3B2;
            Z4B4 = Z4B3 + Z4B3 - Z4B2;
            goto l270;
        }
    }
    Y5   = mono_.regular_row(JY);
    B4   = 1.0 / (Y5 - Y4);
    Z35  = mono_(JY, JX - 2);
    Z45  = mono_(JY, JX - 1);
    Z3B4 = (Z35 - Z34) * B4;
    Z4B4 = (Z45 - Z44) * B4;
    if (JYM2 == 0) {
        Z3B2 = Z3B3 + Z3B3 - Z3B4;
        Z4B2 = Z4B3 + Z4B3 - Z4B4;
    }

l270:
    ZA3B2 = (Z4B2 - Z3B2) * A3;
    ZA3B4 = (Z4B4 - Z3B4) * A3;
    if (JY <= 3) {
        Z3B1 = Z3B2 + Z3B2 - Z3B3;
        Z4B1 = Z4B2 + Z4B2 - Z4B3;
    }
    else {
        B1   = 1.0 / (Y2 - mono_.regular_row(JY - 4));
        Z3B1 = (Z32 - mono_(JY - 4, JX - 2)) * B1;
        Z4B1 = (Z42 - mono_(JY - 4, JX - 1)) * B1;
    }

    if (JY >= LYM1) {
        Z3B5 = Z3B4 + Z3B4 - Z3B3;
        Z4B5 = Z4B4 + Z4B4 - Z4B3;
    }
    else {
        B5   = 1.0 / (mono_.regular_row(JY + 1) - Y5);
        Z3B5 = (mono_(JY + 1, JX - 2) - Z35) * B5;
        Z4B5 = (mono_(JY + 1, JX - 1) - Z45) * B5;
    }

    // IN THE DIAGONAL DIRECTIONS
    if (LXM2 == 0) {
        ZA2B2 = ZA3B2;
        ZA4B2 = ZA3B2;
        ZA2B4 = ZA3B4;
        ZA4B4 = ZA3B4;
    }
    else if (LYM2 == 0) {
        ZA2B2 = ZA2B3;
        ZA2B4 = ZA2B3;
        ZA4B2 = ZA4B3;
        ZA4B4 = ZA4B3;
    }
    else if (JXML == 0) {
        if (JYM2 == 0) {
            ZA2B4 = (Z3B4 - (mono_(JY, JX - 3) - Z24) * B4) * A2;
            ZA2B2 = ZA2B3 + ZA2B3 - ZA2B4;
        }
        else {
            ZA2B2 = (Z3B2 - (Z23 - mono_(JY - 3, JX - 3)) * B2) * A2;
            if (JYML == 0)
                ZA2B4 = ZA2B3 + ZA2B3 - ZA2B2;
            else
                ZA2B4 = (Z3B4 - (mono_(JY, JX - 3) - Z24) * B4) * A2;
        }
        ZA4B2 = ZA3B2 + ZA3B2 - ZA2B2;
        ZA4B4 = ZA3B4 + ZA3B4 - ZA2B4;
    }
    else if (JYM2 == 0) {
        ZA4B4 = ((mono_(JY, JX) - Z54) * B4 - Z4B4) * A4;
        ZA4B2 = ZA4B3 + ZA4B3 - ZA4B4;
        if (JXM2 == 0) {
            ZA2B2 = ZA3B2 + ZA3B2 - ZA4B2;
            ZA2B4 = ZA3B4 + ZA3B4 - ZA4B4;
        }
        else {
            ZA2B4 = (Z3B4 - (mono_(JY, JX - 3) - Z24) * B4) * A2;
            ZA2B2 = ZA2B3 + ZA2B3 - ZA2B4;
        }
    }
    else {
        ZA4B2 = ((Z53 - mono_(JY - 3, JX)) * B2 - Z4B2) * A4;
        if (JYML == 0) {
            ZA4B4 = ZA4B3 + ZA4B3 - ZA4B2;
            if (JXM2 == 0) {
                ZA2B2 = ZA3B2 + ZA3B2 - ZA4B2;
                ZA2B4 = ZA3B4 + ZA3B4 - ZA4B4;
            }
            else {
                ZA2B2 = (Z3B2 - (Z23 - mono_(JY - 3, JX - 3)) * B2) * A2;
                ZA2B4 = ZA2B3 + ZA2B3 - ZA2B2;
            }
        }
        else {
            ZA4B4 = ((mono_(JY, JX) - Z54) * B4 - Z4B4) * A4;
            if (JXM2 == 0) {
                ZA2B2 = ZA3B2 + ZA3B2 - ZA4B2;
                ZA2B4 = ZA3B4 + ZA3B4 - ZA4B4;
            }
            else {
                ZA2B2 = (Z3B2 - (Z23 - mono_(JY - 3, JX - 3)) * B2) * A2;
                ZA2B4 = (Z3B4 - (mono_(JY, JX - 3) - Z24) * B4) * A2;
            }
        }
    }

    // NUMERICAL DIFFERENTIATION   ---   TO DETERMINE PARTIAL
    // DERIVATIVES ZX, ZY, AND ZXY AS WEIGHTED MEANS OF DIVIDED
    // DIFFERENCES ZA, ZB, AND ZAB, RESPECTIVELY
    for (JY = 2; JY <= 3; JY++) {
        for (JX = 2; JX <= 3; JX++) {
            W2 = abs(ZA[JY - 2][JX + 1] - ZA[JY - 2][JX]);
            W3 = abs(ZA[JY - 2][JX - 1] - ZA[JY - 2][JX - 2]);
            SW = W2 + W3;
            if (SW == 0.0) {
                WX2 = 0.5;
                WX3 = 0.5;
            }
            else {
                WX2 = W2 / SW;
                WX3 = W3 / SW;
            }

            ZX[JY - 1][JX - 1] = WX2 * ZA[JY - 2][JX - 1] + WX3 * ZA[JY - 2][JX];
            W2                 = abs(ZB[JY + 1][JX - 2] - ZB[JY][JX - 2]);
            W3                 = abs(ZB[JY - 1][JX - 2] - ZB[JY - 2][JX - 2]);
            SW                 = W2 + W3;
            if (SW == 0.0) {
                WY2 = 0.5;
                WY3 = 0.5;
            }
            else {
                WY2 = W2 / SW;
                WY3 = W3 / SW;
            }

            ZY[JY - 1][JX - 1]  = WY2 * ZB[JY - 1][JX - 2] + WY3 * ZB[JY][JX - 2];
            ZXY[JY - 1][JX - 1] = WY2 * (WX2 * ZAB[JY - 2][JX - 2] + WX3 * ZAB[JY - 2][JX - 1]) +
                                  WY3 * (WX2 * ZAB[JY - 1][JX - 2] + WX3 * ZAB[JY - 1][JX - 1]);
        }
    }

    // WHEN (U(K).LT.X(1)).OR.(U(K).GT.X(LX))
    if (IX == LXP1 || IX == 1) {
        if (IX == LXP1) {
            W4 = A2 * (3.0 * A3 + A2);
            W5 = 2.0 * A3 * (A3 - A2) + W4;
            for (JY = 2; JY <= 3; JY++) {
                ZX[JY - 1][3]  = (W4 * ZA[JY - 2][3] + W5 * ZA[JY - 2][4]) / (W4 + W5);
                ZY[JY - 1][3]  = ZY[JY - 1][2] + ZY[JY - 1][2] - ZY[JY - 1][1];
                ZXY[JY - 1][3] = ZXY[JY - 1][2] + ZXY[JY - 1][2] - ZXY[JY - 1][1];
                for (JX = 2; JX <= 3; JX++) {
                    ZX[JY - 1][JX - 1]  = ZX[JY - 1][JX];
                    ZY[JY - 1][JX - 1]  = ZY[JY - 1][JX];
                    ZXY[JY - 1][JX - 1] = ZXY[JY - 1][JX];
                }
            }

            X3  = X4;
            Z33 = Z43;
            for (JY = 1; JY <= 5; JY++)
                ZB[JY - 1][0] = ZB[JY - 1][1];

            A3 = A2;
            JX = 3;
        }
        else if (IX == 1) {
            W2 = A4 * (3.0 * A3 + A4);
            W1 = 2.0 * A3 * (A3 - A4) + W2;
            for (JY = 2; JY <= 3; JY++) {
                ZX[JY - 1][0]  = (W1 * ZA[JY - 2][0] + W2 * ZA[JY - 2][1]) / (W1 + W2);
                ZY[JY - 1][0]  = ZY[JY - 1][1] + ZY[JY - 1][1] - ZY[JY - 1][2];
                ZXY[JY - 1][0] = ZXY[JY - 1][1] + ZXY[JY - 1][1] - ZXY[JY - 1][2];
                for (JX1 = 2; JX1 <= 3; JX1++) {
                    JX                  = 5 - JX1;
                    ZX[JY - 1][JX - 1]  = ZX[JY - 1][JX - 2];
                    ZY[JY - 1][JX - 1]  = ZY[JY - 1][JX - 2];
                    ZXY[JY - 1][JX - 1] = ZXY[JY - 1][JX - 2];
                }
            }

            X3  = X3 - 1.0 / A4;
            Z33 = Z33 - Z3A2 / A4;
            for (JY = 1; JY <= 5; JY++)
                ZB[JY - 1][1] = ZB[JY - 1][0];

            for (JY = 2; JY <= 4; JY++)
                ZB[JY - 1][0] = ZB[JY - 1][0] - ZAB[JY - 2][0] / A4;

            A3 = A4;
            JX = 1;
        }

        ZA[0][2] = ZA[0][JX];
        for (JY = 1; JY <= 3; JY++)
            ZAB[JY - 1][1] = ZAB[JY - 1][JX - 1];
    }

    // WHEN (V(K).LT.Y(1)).OR.(V(K).GT.Y(LY))

    if (IY == LYP1 || IY == 1) {
        if (IY == LYP1) {
            W4 = B2 * (3.0 * B3 + B2);
            W5 = 2.0 * B3 * (B3 - B2) + W4;
            for (JX = 2; JX <= 3; JX++) {
                if (!((JX == 3 && IX == LXP1) || (JX == 2 && IX == 1))) {
                    ZY[3][JX - 1]  = (W4 * ZB[3][JX - 2] + W5 * ZB[4][JX - 2]) / (W4 + W5);
                    ZX[3][JX - 1]  = ZX[2][JX - 1] + ZX[2][JX - 1] - ZX[1][JX - 1];
                    ZXY[3][JX - 1] = ZXY[2][JX - 1] + ZXY[2][JX - 1] - ZXY[1][JX - 1];
                }

                for (JY = 2; JY <= 3; JY++) {
                    ZY[JY - 1][JX - 1]  = ZY[JY][JX - 1];
                    ZX[JY - 1][JX - 1]  = ZX[JY][JX - 1];
                    ZXY[JY - 1][JX - 1] = ZXY[JY][JX - 1];
                }
            }

            Y3    = Y4;
            Z33   = Z33 + Z3B3 / B3;
            Z3A3  = Z3A3 + ZA3B3 / B3;
            Z3B3  = Z3B4;
            ZA3B3 = ZA3B4;
            B3    = B2;
        }
        else if (IY == 1) {
            W2 = B4 * (3.0 * B3 + B4);
            W1 = 2.0 * B3 * (B3 - B4) + W2;
            for (JX = 2; JX <= 3; JX++) {
                if (!((JX == 3 && IX == LXP1) || (JX == 2 && IX == 1))) {
                    ZY[0][JX - 1]  = (W1 * ZB[0][JX - 2] + W2 * ZB[1][JX - 2]) / (W1 + W2);
                    ZX[0][JX - 1]  = ZX[1][JX - 1] + ZX[1][JX - 1] - ZX[2][JX - 1];
                    ZXY[0][JX - 1] = ZXY[1][JX - 1] + ZXY[1][JX - 1] - ZXY[2][JX - 1];
                }

                for (JY1 = 2; JY1 <= 3; JY1++) {
                    JY                  = 5 - JY1;
                    ZY[JY - 1][JX - 1]  = ZY[JY - 2][JX - 1];
                    ZX[JY - 1][JX - 1]  = ZX[JY - 2][JX - 1];
                    ZXY[JY - 1][JX - 1] = ZXY[JY - 2][JX - 1];
                }
            }

            Y3    = Y3 - 1.0 / B4;
            Z33   = Z33 - Z3B2 / B4;
            Z3A3  = Z3A3 - ZA3B2 / B4;
            Z3B3  = Z3B2;
            ZA3B3 = ZA3B2;
            B3    = B4;
        }

        if (IX == 1 || IX == LXP1) {
            JX                  = IX / LXP1 + 2;
            JX1                 = 5 - JX;
            JY                  = IY / LYP1 + 2;
            JY1                 = 5 - JY;
            ZX[JY - 1][JX - 1]  = ZX[JY - 1][JX1 - 1] + ZX[JY1 - 1][JX - 1] - ZX[JY1 - 1][JX1 - 1];
            ZY[JY - 1][JX - 1]  = ZY[JY - 1][JX1 - 1] + ZY[JY1 - 1][JX - 1] - ZY[JY1 - 1][JX1 - 1];
            ZXY[JY - 1][JX - 1] = ZXY[JY - 1][JX1 - 1] + ZXY[JY1 - 1][JX - 1] - ZXY[JY1 - 1][JX1 - 1];
        }
    }

    // DETERMINATION OF THE COEFFICIENTS OF THE POLYNOMIAL

    ZX3B3 = (ZX34 - ZX33) * B3;
    ZX4B3 = (ZX44 - ZX43) * B3;
    ZY3A3 = (ZY43 - ZY33) * A3;
    ZY4A3 = (ZY44 - ZY34) * A3;
    a     = ZA3B3 - ZX3B3 - ZY3A3 + ZXY33;
    b     = ZX4B3 - ZX3B3 - ZXY43 + ZXY33;
    c     = ZY4A3 - ZY3A3 - ZXY34 + ZXY33;
    d     = ZXY44 - ZXY43 - ZXY34 + ZXY33;
    e     = a + a - b - c;
    A3SQ  = A3 * A3;
    B3SQ  = B3 * B3;
    P02   = (2.0 * (Z3B3 - ZY33) + Z3B3 - ZY34) * B3;
    P03   = (-2.0 * Z3B3 + ZY34 + ZY33) * B3SQ;
    P12   = (2.0 * (ZX3B3 - ZXY33) + ZX3B3 - ZXY34) * B3;
    P13   = (-2.0 * ZX3B3 + ZXY34 + ZXY33) * B3SQ;
    P20   = (2.0 * (Z3A3 - ZX33) + Z3A3 - ZX43) * A3;
    P21   = (2.0 * (ZY3A3 - ZXY33) + ZY3A3 - ZXY43) * A3;
    P22   = (3.0 * (a + e) + d) * A3 * B3;
    P23   = (-3.0 * e - b - d) * A3 * B3SQ;
    P30   = (-2.0 * Z3A3 + ZX43 + ZX33) * A3SQ;
    P31   = (-2.0 * ZY3A3 + ZXY43 + ZXY33) * A3SQ;
    P32   = (-3.0 * e - c - d) * B3 * A3SQ;
    P33   = (d + e + e) * A3SQ * B3SQ;

    // COMPUTATION OF THE POLYNOMIAL
    DY = VK - Y3;
    Q0 = P00 + DY * (P01 + DY * (P02 + DY * P03));
    Q1 = P10 + DY * (P11 + DY * (P12 + DY * P13));
    Q2 = P20 + DY * (P21 + DY * (P22 + DY * P23));
    Q3 = P30 + DY * (P31 + DY * (P32 + DY * P33));
    DX = UK - X3;
    WK = Q0 + DX * (Q1 + DX * (Q2 + DX * Q3));

    return WK;
}


// The following points are searched:
//
//  -    -    X    X     -    -
//  -    X    X    X     X    -
//  X    X    X    X     X    X
//  X    X    X    X    col   X
//  -    X    X   lin    X    -
//  -    -    X    X     -    -
//

int Akima474::CheckMissingValues(int col, int lin) const {
    const int NIND = 24;
    int i, ix, iy;

    int ind[NIND][2] = {{-4, -2}, {-4, -1}, {-3, -3}, {-3, -2}, {-3, -1}, {-3, 0},  {-2, -4}, {-2, -3},
                        {-2, -2}, {-2, -1}, {-2, 0},  {-2, 1},  {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1},
                        {-1, 0},  {-1, 1},  {0, -3},  {0, -2},  {0, -1},  {0, 0},   {1, -2},  {1, -1}};

    for (i = 0; i < NIND; i++) {
        ix = col + ind[i][1];
        iy = lin + ind[i][0];
        MagLog::dev() << ix << " " << iy << " " << mono_.columns() << " " << mono_.rows() << endl;
        if (iy < 0 || iy >= mono_.rows() || ix < 0 || ix >= mono_.columns())
            continue;

        if (mono_(iy, ix) == mono_.missing()) {
            MagLog::debug() << mono_(iy, ix) << endl;
            return 0;
        }
    }

    return 1;
}

#if 0

double Akima474::InterpolateBicubicAt (double lin, double col) const
{
	int i,j;   // auxiliary variables

//remove later
#if 1
if ((col < 2.) || (col >= (float)(mono_.columns()-3)) || (lin < 2.) || (lin >= (float)(mono_.rows()-3)))
{
    int ll, cc;
    if (col < 1.)                        cc = 0;
    else if (col < 2.)                   cc = 1;
    else if (col >= mono_.columns()-2) cc = mono_.columns()-1;
    else if (col >= mono_.columns()-3) cc = mono_.columns()-2;
    else                                 cc = (int)col;
		
    if (lin < 1.)                      ll = 0;
    else if (lin < 2.)                 ll = 1;
    else if (lin >= mono_.rows()-2)  ll = mono_.rows()-1;
    else if (lin >= mono_.rows()-3)  ll = mono_.rows()-2;
    else                               ll = (int)lin;

    return mono_(ll,cc);
}
#endif

// put it back later
#if 0
	if ( (col < 0.) || (col >= mono_.columns()-1.) || (lin < 0.) || (lin >= mono_.rows()-1.) )
	{
		double ll, cc;
		if (col < 0.)                      cc = 0;
		else if (col >= mono_.columns()) cc = mono_.columns()-1;
		else                               cc = col;
		
		if (lin < 0.)                      ll = 0;
		else if (lin >= mono_.rows())    ll = mono_.rows()-1;
		else                               ll = lin;

		return mono_(ll,cc);
	}
#endif

	// Set coordinates
	int u = (int)col;
	int v = (int)lin;
	double dx = col - (double)u;
//F	dy = (double)v - y;
	double dy = lin - (double)v;

        // Set the vx and vy values
//F1
#if 0 
	double vx[6], vy[6];
	for(i=0; i<6; i++)
	{
		vx[i] = Iresx_/2. + (double)(u-2+i)*Iresx_;
//F		vy[i] = (float)(Y2() - ResolutionY()/2. - (float)(v-2+i)*ResolutionY());
		vy[i] = Iresy_/2. + (double)(v-2+i)*Iresy_;
	}
#endif
        // Define the coefficients of the polinomium q[j]
	double q[4];
	double vz[6], vzaux[6];

	dx *= Iresx_;
	for(j = 0; j < 6; j++)
	{
                // Set the z values
		for(i = 0; i < 6; i++)
			vz[i] = mono_(v-2+j,u-2+i);

//F1		if ( Def_polynom(vx,vz,q) == false )
		if ( Def_polynom(Iresx_,vz,q) == false )
		{
			MagLog::debug() << "Error at Akima474::InterpolateBicubicAt" << "\n";
			return mono_.missing();
		}

                // Calculate intermediate z values vzaux
		if (q[0] >= mono_.missing())
			vzaux[j] = mono_.missing();
		else
			vzaux[j] = q[0] + q[1]*dx + q[2]*dx*dx + q[3]*dx*dx*dx;
			
	}

	// Interpolate at opposite direction
//F1	if ( Def_polynom(vy,vzaux,q) == false )
	if ( Def_polynom(Iresy_,vzaux,q) == false )
	{
		 MagLog::debug() << "Error at Akima474::InterpolateBicubicAt" << "\n";
		 return mono_.missing();
	}
	dy *= Iresy_;

	double  zvalue;
	if ( q[0] >= mono_.missing() )
		zvalue = InterpolateAt(lin, col);
	else
		zvalue = q[0] + q[1]*dy + q[2]*dy*dy + q[3]*dy*dy*dy;

	return zvalue;
}


//F1bool Akima474::Def_polynom(double* x, double* y, double* p) const
bool Akima474::Def_polynom(double x, double* y, double* p) const
{
	short	i;
	double	m[6],  	// tangent vector
		auxd1,	// auxiliar variable
		d2,d3; 	// slope at points 2 e 3

//F1	if( x[3] == x[2] ) return false; // points with the same x value

	// Check the existence of dummy values among the y elements
	if( y[2] >= mono_.missing() || y[3] >= mono_.missing() )
	{
		p[0]=p[1]=p[2]=p[3]=mono_.missing();
		return true;
	}
	else if( y[0] >= mono_.missing() || y[1] >= mono_.missing() || y[4] >= mono_.missing() || y[5] >= mono_.missing() )
	{
		p[0] = y[2];
//F		p[1] = (y[3]-y[2])/(x[3]-x[2]);
		p[1] = (y[3]-y[2])/x;
		p[2] = 0.; p[3] = 0.; 

		return true;
	}
	
	// Calculates the slope d2 (in the point 2) and d3 (in the point 3)
	for (i = 0; i < 5; i++)
	{
//F1		if( x[i+1] == x[i] ) m[i] = (double)1.0e30;
//F1		else                 m[i] = (y[i+1]-y[i]) / (x[i+1]-x[i]);
		m[i] = (y[i+1]-y[i]) / x;
	}

	if(((m[0]==m[1])&&(m[2]==m[3])) || ((m[1]==m[2])&&(m[3]==m[4])))
	{
		d2 = .5 * (m[1]+m[2]);
		d3 = .5 * (m[2]+m[3]);
	}
	else
	{
		d2 = (fabs(m[3]-m[2])*m[1] + fabs(m[1]-m[0])*m[2]) /
		     (fabs(m[3]-m[2]) + fabs(m[1]-m[0]));

		d3 = (fabs(m[4]-m[3])*m[2] + fabs(m[2]-m[1])*m[3]) /
		     (fabs(m[4]-m[3]) + fabs(m[2]-m[1]));
	}
	
	//Define the polinomium coefficients	
//F1	auxd1 = x[3] - x[2];
	auxd1 = x;
	p[0]  = y[2];
	p[1]  = d2;
	p[2]  = (3.*m[2] - 2.*d2 - d3) / auxd1;
	p[3]  = (d2 + d3 - 2.*m[2]) / (auxd1*auxd1);

	return true;
}

// METHOD TO CALCULATE A Z VALUE USING BILINEAR INTERPOLATION

double Akima474::InterpolateAt (double lin, double col) const
{
	double	dx, dy, dx1, dy1, p11, p12, p21, p22;
	int	u, v;

	if (col < 0. || col >= (double)(mono_.columns()-1) || lin < 0. || lin >= (double)(mono_.rows()-1))
		return mono_.missing();

        // Compute weights
	u   = (int)col;
	v   = (int)lin;
	dx  = col - (double)u;
	dy  = lin - (double)v;
	dx1 = 1. - dx;
	dy1 = 1. - dy;

	// Retrieve points
	p11 = mono_(v,u);
	p12 = mono_(v+1,u);
	p21 = mono_(v,u+1);
	p22 = mono_(v+1,u+1);

	// Check for dummy values
	if (p11 >= mono_.missing())
	{
		if (p12 >= mono_.missing())
			return mono_.missing();
		else
		{
			if (p21 >= mono_.missing())
				return mono_.missing();
			else
			{
				if (p22 >= mono_.missing())
					return mono_.missing();
				else
					p11 = (p12 + p21 + p22) / 3.;
			}
		}
	}
	else
	{
		if (p12 >= mono_.missing())
		{
			if (p21 >= mono_.missing())
				return mono_.missing();
			else
			{
				if (p22 >= mono_.missing())
					return mono_.missing();
				else
					p12 = (p11 + p21 + p22) / 3.;
			}
		}
		else
		{
			if (p21 >= mono_.missing())
			{
				if (p22 >= mono_.missing())
					return mono_.missing();
				else
					p21 = (p11 + p12 + p22) / 3.;
			}
			else
				if (p22 >= mono_.missing())
					p22 = (p11 + p12 + p21) / 3.;
		}
	}

	// Interpolation
	p11 = p11*dy1 + p12*dy;
	p22 = p21*dy1 + p22*dy;

	return(p11 * dx1 + p22 * dx);
}
#endif

// TEST, REMOVE LATER
#if 0
#define NXI 19
#define NYI 23

void Akima474::test_build_data()
{

float     XIMN=-0.5;
float	  XIMX= 8.5;
float     YIMN=-0.5;
float 	  YIMX=10.5;

double    XI[NXI],YI[NYI];
double    ZI[NYI][NXI];      

      float     ANXIM1,ANYIM1,DXI,DYI;
      int    ISEC,IXD,IXI,IXIMN,IXIMX,IYD,IYI,IYIR,MD,NXD1,NYD1;

      //     ..Local Arrays ..
      double     DZI[NYI][NXI];

      // Initializations

     double ZIE[NYI][NXI] = { -.847,-.533,
                      -.274,-.117,-.031,.000,.000,.000,.000,.000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
                      .401,.250,.119,.043,.011,.000,.000,.000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
                      .000,.000,-.665,-.376,-.143,-.033,-.007,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
		      .000,.000,.000,.000,
		      .000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,2.449,
                      1.368,.537,.149,.025,.000,.000,.000,.000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
                      5.083,3.200,1.642,.700,.187,.000,.000,.000,.000,
                      .000,.000,.000,.000,.000,.000,.000,.000,.000,
                      .000,6.588,5.234,3.878,2.542,1.188,.253,.026,
                      .026,.007,.000,.000,.000,.000,.000,.000,.000,
                      .000,.000,.000,8.017,7.400,6.400,4.800,2.963,
                      1.400,.457,.100,.027,.000,.000,.000,.000,.000,
		      .000,.000,.000,.000,.000,
		      11.055,
                      9.670,8.083,6.305,4.786,3.421,2.043,1.112,.565,
                      .131,-.019,.000,.000,.000,.000,.000,.000,.000,
                      .000,14.492,12.000,9.746,8.000,6.594,5.300,4.081,
                      2.900,1.697,.600,.059,.000,.000,.000,.000,.000,
                      .000,.000,.000,15.999,14.376,12.657,10.774,8.620,
                      6.659,5.291,4.392,3.926,3.005,1.223,.139,.051,
                      .025,.009,.000,.000,.000,-.005,15.525,16.800,
                      16.749,14.400,10.956,8.100,6.735,6.900,7.298,
                      6.200,3.010,.600,.248,.100,.024,.000,.006,.000,
                      -.025,15.876,19.280,20.563,17.856,13.242,10.219,
                      10.577,11.999,10.170,7.053,5.198,3.543,1.831,
		      .350,-.130,.168,.408,.168,-.224,
		      17.700,
                      21.800,23.531,20.500,15.087,12.800,15.817,17.600,
                      11.477,5.800,6.988,7.600,4.410,.800,-.392,.600,
                      1.261,.600,-.417,17.913,22.788,24.944,21.881,
                      16.302,14.382,18.557,20.807,11.916,4.561,7.327,
                      8.518,5.133,1.284,-.013,1.201,1.998,1.200,-.065,
                      16.383,22.400,25.330,22.500,16.796,14.600,19.172,
                      22.500,13.159,4.700,6.689,7.200,4.392,1.800,
                      1.150,2.100,2.734,2.100,1.025,18.109,26.756,
                      31.311,28.143,21.004,18.237,24.236,28.979,17.970,
                      7.469,10.467,11.985,9.022,6.833,6.901,8.292,
                      9.186,8.524,7.101,24.667,37.200,44.007,40.000,
                      30.508,27.000,34.974,41.300,27.136,14.100,20.473,
                      24.500,20.557,17.300,17.639,20.200,21.826,20.800,
		      18.458,
		      33.414,
                      48.009,56.017,51.561,40.817,36.922,45.856,52.860,
                      37.376,23.200,30.839,36.192,31.969,28.037,28.437,
                      31.604,33.579,32.332,29.561,44.842,58.200,65.537,
                      61.500,51.657,47.900,55.899,62.300,47.891,34.600,
                      41.239,45.500,41.479,38.200,38.591,41.200,42.823,
                      41.700,39.192,58.284,68.917,74.644,71.333,63.413,
                      60.125,66.293,71.400,59.129,47.725,52.451,54.592,
		      50.842,48.483,48.639,50.142,51.089,50.200,48.268 };

	int i,j;

//*     ..
//* Calculation
//* Opens the output file and writes the input data.
//
      NXD1 = mono_.columns();
      NYD1 = mono_.rows();
      printf("\nPRG3P  Original Data  ZD(YD,XD)\n\n       ");
      for (IXD = 0; IXD < NXD1; IXD++) printf("%7.1f",mono_.regular_column(IXD));
      for (IYD = NYD1-1; IYD >= 0; IYD--)
      {
	      printf("\n%7.1f",mono_.regular_row(IYD));
	      for (IXD = 0; IXD < NXD1; IXD++) printf("%7.1f",mono_(IYD,IXD));
      }

//* Program check for the RGBI3P subroutine
//* - Performs interpolation and calculates the differences.
      DXI = XIMX - XIMN;
      ANXIM1 = NXI - 1;
      printf("\n\nXI %d\n",NXI);
      for (IXI = 0; IXI < NXI; IXI++)
      {
	      XI[IXI] = XIMN + DXI*float(IXI)/ANXIM1;
	      printf("%5.1f",XI[IXI]);
      }

      DYI = YIMX - YIMN;
      ANYIM1 = NYI - 1;
      printf("\n\nYI %d\n",NYI);
      for (IYI = 0; IYI < NYI; IYI++)
      {
	      YI[IYI] = YIMN + DYI*float(IYI)/ANYIM1;
	      printf("%5.1f",YI[IYI]);
      }

      printf("\n\n PROCESSING\n");

      for (IYI = 0; IYI < NYI; IYI++)
      {
	      for (IXI = 0; IXI < NXI; IXI++)
	      {
		      InterpolateBicubicAt(XI[IXI],YI[IYI],ZI[IYI][IXI]);
		      DZI[IYI][IXI] = ZI[IYI][IXI] - ZIE[IYI][IXI];
	      }
      }

//* - Writes the calculated results.
      printf("\n\n\nTPRG3P  Program Check for RGBI3P   Calculated ZI Values\n");
      for (ISEC = 1; ISEC < 3; ISEC++)
      {
	      if (ISEC == 1)
	      {
		  IXIMN = 0;
		  IXIMX = 11;
	      }
	      else
	      {
		  IXIMN = 8;
		  IXIMX = NXI;
	      }

	      printf("\n\n                ZI(XI,YI)\n\n     ");
	      for (IXI = IXIMN; IXI < IXIMX; IXI++)
		  printf("%7.1f",XI[IXI]);

	      for (IYI = NYI-1; IYI >= 0; IYI--)
	      {
		      printf("\n%7.1f",YI[IYI]);
		      for (IXI = IXIMN; IXI < IXIMX; IXI++)
			      printf("%7.2f",ZI[IYI][IXI]);
	      }
      }

//* - Writes the differences.
      printf("\n\n\nTPRG3P  Program Check for RGBI3P   Differences\n");
      for (ISEC = 1; ISEC < 3; ISEC++)
      {
	      if (ISEC == 1)
	      {
		  IXIMN = 0;
		  IXIMX = 11;
	      }
	      else
	      {
		  IXIMN = 8;
		  IXIMX = NXI;
	      }

	      printf("\n\n           DZI(YI,XI)\n\n       ");
	      for (IXI = IXIMN; IXI < IXIMX; IXI++)
		  printf("%7.1f",XI[IXI]);

	      for (IYI = NYI-1; IYI >= 0; IYI--)
	      {
		      printf("\n%7.1f",YI[IYI]);
		      for (IXI = IXIMN; IXI < IXIMX; IXI++)
			      printf("%7.2f",DZI[IYI][IXI]);
	      }
      }

      printf("\n\nEND TEST\n");
}

// to test high/low

void Akima474::test_build_data_hl()
{
	Matrix matrix3(11, 9);
#define NXD 9
#define NYD 11
	double     XD[NXD] = { 0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0 };
	double     YD[NYD] = { 0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0 };
	double     ZD[NYD][NXD] = { 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,
				    0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,
				    0.0,0.0,-3.0,0.0,0.0,0.0,0.0,0.0,0.0,
				    3.2,0.7,0.0,0.0,0.0,0.0,0.0,0.0,0.0,
				    7.4,4.8,1.4,0.1,0.0,0.0,0.0,0.0,0.0,
				    12.0,8.0,5.3,2.9,0.6,0.0,0.0,0.0,0.0,
				    16.8,14.4,8.1,6.9,6.2,0.6,0.1,0.0,0.0,
				    21.8,20.5,12.8,17.6,5.8,7.6,0.8,0.6,0.6,
				    22.4,22.5,14.6,22.5,4.7,7.2,1.8,2.1,2.1,
				    37.2,40.0,27.0,41.3,14.1,24.5,17.3,20.2,20.8,
				    58.2,61.5,47.9,62.3,34.6,45.5,38.2,41.2,41.7 };

	int IXD,IYD;

            for (int r = 0; r < NYD; r++)
                for (int c = 0; c < NXD; c++) 
                    matrix3.push_back(ZD[r][c]);

            for (int r = 0; r < NYD; r++) matrix3.rowsAxis().push_back(YD[r]);
            for (int c = 0; c < NXD; c++) matrix3.columnsAxis().push_back(XD[c]);
            matrix3.setMapsAxis();

	   
	    MatrixHandler mm1(matrix3);

      int dim=5;
      MinMaxFilter fil(mm1,dim,dim);
      fil.test_build_data();
}
#endif
