/*************************************************************************
        Unset spline, i.e. initialize it with random garbage
        *************************************************************************/
        private static void unsetspline2d(ref spline2d.spline2dinterpolant c)
        {
            double[] x = new double[0];
            double[] y = new double[0];
            double[,] f = new double[0,0];

            x = new double[2];
            y = new double[2];
            f = new double[2, 2];
            x[0] = -1;
            x[1] = +1;
            y[0] = -1;
            y[1] = +1;
            f[0,0] = 0;
            f[0,1] = 0;
            f[1,0] = 0;
            f[1,1] = 0;
            spline2d.spline2dbuildbilinear(x, y, f, 2, 2, ref c);
        }
        /*************************************************************************
        LinTrans test
        *************************************************************************/
        private static bool testlintrans(ref spline2d.spline2dinterpolant c,
            double ax,
            double bx,
            double ay,
            double by)
        {
            bool result = new bool();
            double err = 0;
            double a1 = 0;
            double a2 = 0;
            double b1 = 0;
            double b2 = 0;
            double tx = 0;
            double ty = 0;
            double vx = 0;
            double vy = 0;
            double v1 = 0;
            double v2 = 0;
            int pass = 0;
            int passcount = 0;
            int xjob = 0;
            int yjob = 0;
            spline2d.spline2dinterpolant c2 = new spline2d.spline2dinterpolant();

            passcount = 5;
            err = 0;
            for(xjob=0; xjob<=1; xjob++)
            {
                for(yjob=0; yjob<=1; yjob++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        
                        //
                        // Prepare
                        //
                        do
                        {
                            a1 = 2*AP.Math.RandomReal()-1;
                        }
                        while( (double)(a1)==(double)(0) );
                        a1 = a1*xjob;
                        b1 = 2*AP.Math.RandomReal()-1;
                        do
                        {
                            a2 = 2*AP.Math.RandomReal()-1;
                        }
                        while( (double)(a2)==(double)(0) );
                        a2 = a2*yjob;
                        b2 = 2*AP.Math.RandomReal()-1;
                        
                        //
                        // Test XY
                        //
                        spline2d.spline2dcopy(ref c, ref c2);
                        spline2d.spline2dlintransxy(ref c2, a1, b1, a2, b2);
                        tx = ax+AP.Math.RandomReal()*(bx-ax);
                        ty = ay+AP.Math.RandomReal()*(by-ay);
                        if( xjob==0 )
                        {
                            tx = b1;
                            vx = ax+AP.Math.RandomReal()*(bx-ax);
                        }
                        else
                        {
                            vx = (tx-b1)/a1;
                        }
                        if( yjob==0 )
                        {
                            ty = b2;
                            vy = ay+AP.Math.RandomReal()*(by-ay);
                        }
                        else
                        {
                            vy = (ty-b2)/a2;
                        }
                        v1 = spline2d.spline2dcalc(ref c, tx, ty);
                        v2 = spline2d.spline2dcalc(ref c2, vx, vy);
                        err = Math.Max(err, Math.Abs(v1-v2));
                        
                        //
                        // Test F
                        //
                        spline2d.spline2dcopy(ref c, ref c2);
                        spline2d.spline2dlintransf(ref c2, a1, b1);
                        tx = ax+AP.Math.RandomReal()*(bx-ax);
                        ty = ay+AP.Math.RandomReal()*(by-ay);
                        v1 = spline2d.spline2dcalc(ref c, tx, ty);
                        v2 = spline2d.spline2dcalc(ref c2, tx, ty);
                        err = Math.Max(err, Math.Abs(a1*v1+b1-v2));
                    }
                }
            }
            result = (double)(err)<(double)(10000*AP.Math.MachineEpsilon);
            return result;
        }
 /*************************************************************************
 Numerical differentiation.
 *************************************************************************/
 private static void twodnumder(ref spline2d.spline2dinterpolant c,
     double x,
     double y,
     double h,
     ref double f,
     ref double fx,
     ref double fy,
     ref double fxy)
 {
     f = spline2d.spline2dcalc(ref c, x, y);
     fx = (spline2d.spline2dcalc(ref c, x+h, y)-spline2d.spline2dcalc(ref c, x-h, y))/(2*h);
     fy = (spline2d.spline2dcalc(ref c, x, y+h)-spline2d.spline2dcalc(ref c, x, y-h))/(2*h);
     fxy = (spline2d.spline2dcalc(ref c, x+h, y+h)-spline2d.spline2dcalc(ref c, x-h, y+h)-spline2d.spline2dcalc(ref c, x+h, y-h)+spline2d.spline2dcalc(ref c, x-h, y-h))/AP.Math.Sqr(2*h);
 }
        /*************************************************************************
        Unpack test
        *************************************************************************/
        private static bool testunpack(ref spline2d.spline2dinterpolant c,
            ref double[] lx,
            ref double[] ly)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;
            int n = 0;
            int m = 0;
            int ci = 0;
            int cj = 0;
            int p = 0;
            double err = 0;
            double tx = 0;
            double ty = 0;
            double v1 = 0;
            double v2 = 0;
            int pass = 0;
            int passcount = 0;
            double[,] tbl = new double[0,0];

            passcount = 20;
            err = 0;
            spline2d.spline2dunpack(ref c, ref m, ref n, ref tbl);
            for(i=0; i<=m-2; i++)
            {
                for(j=0; j<=n-2; j++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        p = (n-1)*i+j;
                        tx = (0.001+0.999*AP.Math.RandomReal())*(tbl[p,1]-tbl[p,0]);
                        ty = (0.001+0.999*AP.Math.RandomReal())*(tbl[p,3]-tbl[p,2]);
                        
                        //
                        // Interpolation properties
                        //
                        v1 = 0;
                        for(ci=0; ci<=3; ci++)
                        {
                            for(cj=0; cj<=3; cj++)
                            {
                                v1 = v1+tbl[p,4+ci*4+cj]*Math.Pow(tx, ci)*Math.Pow(ty, cj);
                            }
                        }
                        v2 = spline2d.spline2dcalc(ref c, tbl[p,0]+tx, tbl[p,2]+ty);
                        err = Math.Max(err, Math.Abs(v1-v2));
                        
                        //
                        // Grid correctness
                        //
                        err = Math.Max(err, Math.Abs(lx[2*j]-tbl[p,0]));
                        err = Math.Max(err, Math.Abs(lx[2*(j+1)]-tbl[p,1]));
                        err = Math.Max(err, Math.Abs(ly[2*i]-tbl[p,2]));
                        err = Math.Max(err, Math.Abs(ly[2*(i+1)]-tbl[p,3]));
                    }
                }
            }
            result = (double)(err)<(double)(10000*AP.Math.MachineEpsilon);
            return result;
        }
        /*************************************************************************
        Lipschitz constants for spline inself, first and second derivatives.
        *************************************************************************/
        private static void lconst(ref spline2d.spline2dinterpolant c,
            ref double[] lx,
            ref double[] ly,
            int m,
            int n,
            double lstep,
            ref double lc,
            ref double lcx,
            ref double lcy,
            ref double lcxy)
        {
            int i = 0;
            int j = 0;
            double f1 = 0;
            double f2 = 0;
            double f3 = 0;
            double f4 = 0;
            double fx1 = 0;
            double fx2 = 0;
            double fx3 = 0;
            double fx4 = 0;
            double fy1 = 0;
            double fy2 = 0;
            double fy3 = 0;
            double fy4 = 0;
            double fxy1 = 0;
            double fxy2 = 0;
            double fxy3 = 0;
            double fxy4 = 0;
            double s2lstep = 0;

            lc = 0;
            lcx = 0;
            lcy = 0;
            lcxy = 0;
            s2lstep = Math.Sqrt(2)*lstep;
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    //
                    // Calculate
                    //
                    twodnumder(ref c, lx[j]-lstep/2, ly[i]-lstep/2, lstep/4, ref f1, ref fx1, ref fy1, ref fxy1);
                    twodnumder(ref c, lx[j]+lstep/2, ly[i]-lstep/2, lstep/4, ref f2, ref fx2, ref fy2, ref fxy2);
                    twodnumder(ref c, lx[j]+lstep/2, ly[i]+lstep/2, lstep/4, ref f3, ref fx3, ref fy3, ref fxy3);
                    twodnumder(ref c, lx[j]-lstep/2, ly[i]+lstep/2, lstep/4, ref f4, ref fx4, ref fy4, ref fxy4);
                    
                    //
                    // Lipschitz constant for the function itself
                    //
                    lc = Math.Max(lc, Math.Abs((f1-f2)/lstep));
                    lc = Math.Max(lc, Math.Abs((f2-f3)/lstep));
                    lc = Math.Max(lc, Math.Abs((f3-f4)/lstep));
                    lc = Math.Max(lc, Math.Abs((f4-f1)/lstep));
                    lc = Math.Max(lc, Math.Abs((f1-f3)/s2lstep));
                    lc = Math.Max(lc, Math.Abs((f2-f4)/s2lstep));
                    
                    //
                    // Lipschitz constant for the first derivative
                    //
                    lcx = Math.Max(lcx, Math.Abs((fx1-fx2)/lstep));
                    lcx = Math.Max(lcx, Math.Abs((fx2-fx3)/lstep));
                    lcx = Math.Max(lcx, Math.Abs((fx3-fx4)/lstep));
                    lcx = Math.Max(lcx, Math.Abs((fx4-fx1)/lstep));
                    lcx = Math.Max(lcx, Math.Abs((fx1-fx3)/s2lstep));
                    lcx = Math.Max(lcx, Math.Abs((fx2-fx4)/s2lstep));
                    
                    //
                    // Lipschitz constant for the first derivative
                    //
                    lcy = Math.Max(lcy, Math.Abs((fy1-fy2)/lstep));
                    lcy = Math.Max(lcy, Math.Abs((fy2-fy3)/lstep));
                    lcy = Math.Max(lcy, Math.Abs((fy3-fy4)/lstep));
                    lcy = Math.Max(lcy, Math.Abs((fy4-fy1)/lstep));
                    lcy = Math.Max(lcy, Math.Abs((fy1-fy3)/s2lstep));
                    lcy = Math.Max(lcy, Math.Abs((fy2-fy4)/s2lstep));
                    
                    //
                    // Lipschitz constant for the cross-derivative
                    //
                    lcxy = Math.Max(lcxy, Math.Abs((fxy1-fxy2)/lstep));
                    lcxy = Math.Max(lcxy, Math.Abs((fxy2-fxy3)/lstep));
                    lcxy = Math.Max(lcxy, Math.Abs((fxy3-fxy4)/lstep));
                    lcxy = Math.Max(lcxy, Math.Abs((fxy4-fxy1)/lstep));
                    lcxy = Math.Max(lcxy, Math.Abs((fxy1-fxy3)/s2lstep));
                    lcxy = Math.Max(lcxy, Math.Abs((fxy2-fxy4)/s2lstep));
                }
            }
        }