#pragma warning disable 162
#pragma warning disable 219
using System;

public partial class alglib
{
    public class testhqrndunit
    {
        public static bool testhqrnd(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            int samplesize = 0;
            double sigmathreshold = 0;
            int passcount = 0;
            int n = 0;
            int i = 0;
            int pass = 0;
            int s1 = 0;
            int s2 = 0;
            int i1 = 0;
            int i2 = 0;
            double r1 = 0;
            double r2 = 0;
            double[] x = new double[0];
            double mean = 0;
            double means = 0;
            double stddev = 0;
            double stddevs = 0;
            double lambdav = 0;
            bool seederrors = new bool();
            bool urerrors = new bool();
            double ursigmaerr = 0;
            bool uierrors = new bool();
            double uisigmaerr = 0;
            bool normerrors = new bool();
            double normsigmaerr = 0;
            bool experrors = new bool();
            double expsigmaerr = 0;
            hqrnd.hqrndstate state = new hqrnd.hqrndstate();

            waserrors = false;
            sigmathreshold = 7;
            samplesize = 100000;
            passcount = 50;
            seederrors = false;
            urerrors = false;
            uierrors = false;
            normerrors = false;
            experrors = false;
            x = new double[samplesize-1+1];
            
            //
            // Test seed errors
            //
            for(pass=1; pass<=passcount; pass++)
            {
                s1 = 1+math.randominteger(32000);
                s2 = 1+math.randominteger(32000);
                unsetstate(state);
                hqrnd.hqrndseed(s1, s2, state);
                i1 = hqrnd.hqrnduniformi(state, 100);
                unsetstate(state);
                hqrnd.hqrndseed(s1, s2, state);
                i2 = hqrnd.hqrnduniformi(state, 100);
                seederrors = seederrors | i1!=i2;
                unsetstate(state);
                hqrnd.hqrndseed(s1, s2, state);
                r1 = hqrnd.hqrnduniformr(state);
                unsetstate(state);
                hqrnd.hqrndseed(s1, s2, state);
                r2 = hqrnd.hqrnduniformr(state);
                seederrors = seederrors | (double)(r1)!=(double)(r2);
            }
            
            //
            // Test HQRNDRandomize() and real uniform generator
            //
            unsetstate(state);
            hqrnd.hqrndrandomize(state);
            ursigmaerr = 0;
            for(i=0; i<=samplesize-1; i++)
            {
                x[i] = hqrnd.hqrnduniformr(state);
            }
            for(i=0; i<=samplesize-1; i++)
            {
                urerrors = (urerrors | (double)(x[i])<=(double)(0)) | (double)(x[i])>=(double)(1);
            }
            calculatemv(x, samplesize, ref mean, ref means, ref stddev, ref stddevs);
            if( (double)(means)!=(double)(0) )
            {
                ursigmaerr = Math.Max(ursigmaerr, Math.Abs((mean-0.5)/means));
            }
            else
            {
                urerrors = true;
            }
            if( (double)(stddevs)!=(double)(0) )
            {
                ursigmaerr = Math.Max(ursigmaerr, Math.Abs((stddev-Math.Sqrt((double)1/(double)12))/stddevs));
            }
            else
            {
                urerrors = true;
            }
            urerrors = urerrors | (double)(ursigmaerr)>(double)(sigmathreshold);
            
            //
            // Test HQRNDRandomize() and integer uniform
            //
            unsetstate(state);
            hqrnd.hqrndrandomize(state);
            uisigmaerr = 0;
            for(n=2; n<=10; n++)
            {
                for(i=0; i<=samplesize-1; i++)
                {
                    x[i] = hqrnd.hqrnduniformi(state, n);
                }
                for(i=0; i<=samplesize-1; i++)
                {
                    uierrors = (uierrors | (double)(x[i])<(double)(0)) | (double)(x[i])>=(double)(n);
                }
                calculatemv(x, samplesize, ref mean, ref means, ref stddev, ref stddevs);
                if( (double)(means)!=(double)(0) )
                {
                    uisigmaerr = Math.Max(uisigmaerr, Math.Abs((mean-0.5*(n-1))/means));
                }
                else
                {
                    uierrors = true;
                }
                if( (double)(stddevs)!=(double)(0) )
                {
                    uisigmaerr = Math.Max(uisigmaerr, Math.Abs((stddev-Math.Sqrt((math.sqr(n)-1)/12))/stddevs));
                }
                else
                {
                    uierrors = true;
                }
            }
            uierrors = uierrors | (double)(uisigmaerr)>(double)(sigmathreshold);
            
            //
            // Special 'close-to-limit' test on uniformity of integers
            // (straightforward implementation like 'RND mod N' will return
            //  non-uniform numbers for N=2/3*LIMIT)
            //
            unsetstate(state);
            hqrnd.hqrndrandomize(state);
            uisigmaerr = 0;
            n = 1431655708;
            for(i=0; i<=samplesize-1; i++)
            {
                x[i] = hqrnd.hqrnduniformi(state, n);
            }
            for(i=0; i<=samplesize-1; i++)
            {
                uierrors = (uierrors | (double)(x[i])<(double)(0)) | (double)(x[i])>=(double)(n);
            }
            calculatemv(x, samplesize, ref mean, ref means, ref stddev, ref stddevs);
            if( (double)(means)!=(double)(0) )
            {
                uisigmaerr = Math.Max(uisigmaerr, Math.Abs((mean-0.5*(n-1))/means));
            }
            else
            {
                uierrors = true;
            }
            if( (double)(stddevs)!=(double)(0) )
            {
                uisigmaerr = Math.Max(uisigmaerr, Math.Abs((stddev-Math.Sqrt((math.sqr(n)-1)/12))/stddevs));
            }
            else
            {
                uierrors = true;
            }
            uierrors = uierrors | (double)(uisigmaerr)>(double)(sigmathreshold);
            
            //
            // Test normal
            //
            unsetstate(state);
            hqrnd.hqrndrandomize(state);
            normsigmaerr = 0;
            i = 0;
            while( i<samplesize )
            {
                hqrnd.hqrndnormal2(state, ref r1, ref r2);
                x[i] = r1;
                if( i+1<samplesize )
                {
                    x[i+1] = r2;
                }
                i = i+2;
            }
            calculatemv(x, samplesize, ref mean, ref means, ref stddev, ref stddevs);
            if( (double)(means)!=(double)(0) )
            {
                normsigmaerr = Math.Max(normsigmaerr, Math.Abs((mean-0)/means));
            }
            else
            {
                normerrors = true;
            }
            if( (double)(stddevs)!=(double)(0) )
            {
                normsigmaerr = Math.Max(normsigmaerr, Math.Abs((stddev-1)/stddevs));
            }
            else
            {
                normerrors = true;
            }
            normerrors = normerrors | (double)(normsigmaerr)>(double)(sigmathreshold);
            
            //
            // Test exponential
            //
            unsetstate(state);
            hqrnd.hqrndrandomize(state);
            expsigmaerr = 0;
            lambdav = 2+5*math.randomreal();
            for(i=0; i<=samplesize-1; i++)
            {
                x[i] = hqrnd.hqrndexponential(state, lambdav);
            }
            for(i=0; i<=samplesize-1; i++)
            {
                uierrors = uierrors | (double)(x[i])<(double)(0);
            }
            calculatemv(x, samplesize, ref mean, ref means, ref stddev, ref stddevs);
            if( (double)(means)!=(double)(0) )
            {
                expsigmaerr = Math.Max(expsigmaerr, Math.Abs((mean-1.0/lambdav)/means));
            }
            else
            {
                experrors = true;
            }
            if( (double)(stddevs)!=(double)(0) )
            {
                expsigmaerr = Math.Max(expsigmaerr, Math.Abs((stddev-1.0/lambdav)/stddevs));
            }
            else
            {
                experrors = true;
            }
            experrors = experrors | (double)(expsigmaerr)>(double)(sigmathreshold);
            
            //
            // Final report
            //
            waserrors = (((seederrors | urerrors) | uierrors) | normerrors) | experrors;
            if( !silent )
            {
                System.Console.Write("RNG TEST");
                System.Console.WriteLine();
                System.Console.Write("SEED TEST:                               ");
                if( !seederrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("UNIFORM CONTINUOUS:                      ");
                if( !urerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("UNIFORM INTEGER:                         ");
                if( !uierrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("NORMAL:                                  ");
                if( !normerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("EXPONENTIAL:                             ");
                if( !experrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        private static void calculatemv(double[] x,
            int n,
            ref double mean,
            ref double means,
            ref double stddev,
            ref double stddevs)
        {
            int i = 0;
            double v1 = 0;
            double v2 = 0;
            double variance = 0;

            mean = 0;
            means = 0;
            stddev = 0;
            stddevs = 0;

            mean = 0;
            means = 1;
            stddev = 0;
            stddevs = 1;
            variance = 0;
            if( n<=1 )
            {
                return;
            }
            
            //
            // Mean
            //
            for(i=0; i<=n-1; i++)
            {
                mean = mean+x[i];
            }
            mean = mean/n;
            
            //
            // Variance (using corrected two-pass algorithm)
            //
            if( n!=1 )
            {
                v1 = 0;
                for(i=0; i<=n-1; i++)
                {
                    v1 = v1+math.sqr(x[i]-mean);
                }
                v2 = 0;
                for(i=0; i<=n-1; i++)
                {
                    v2 = v2+(x[i]-mean);
                }
                v2 = math.sqr(v2)/n;
                variance = (v1-v2)/(n-1);
                if( (double)(variance)<(double)(0) )
                {
                    variance = 0;
                }
                stddev = Math.Sqrt(variance);
            }
            
            //
            // Errors
            //
            means = stddev/Math.Sqrt(n);
            stddevs = stddev*Math.Sqrt(2)/Math.Sqrt(n-1);
        }


        /*************************************************************************
        Unsets HQRNDState structure
        *************************************************************************/
        private static void unsetstate(hqrnd.hqrndstate state)
        {
            state.s1 = 0;
            state.s2 = 0;
            state.v = 0;
            state.magicv = 0;
        }


    }
    public class testtsortunit
    {
        /*************************************************************************
        Testing tag sort
        *************************************************************************/
        public static bool testtsort(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            int n = 0;
            int i = 0;
            int pass = 0;
            int passcount = 0;
            int maxn = 0;
            double[] a = new double[0];
            double[] a0 = new double[0];
            double[] a1 = new double[0];
            double[] a2 = new double[0];
            double[] a3 = new double[0];
            double[] ar = new double[0];
            int[] ai = new int[0];
            int[] p1 = new int[0];
            int[] p2 = new int[0];
            double[] bufr1 = new double[0];
            double[] bufr2 = new double[0];
            int[] bufi1 = new int[0];

            waserrors = false;
            maxn = 100;
            passcount = 10;
            
            //
            // Test tagsort
            //
            for(n=1; n<=maxn; n++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // (probably) distinct sort:
                    // * first sort A0 using TagSort and test sort results
                    // * now we can use A0 as reference point and test other functions
                    //
                    unset1di(ref p1);
                    unset1di(ref p2);
                    a = new double[n];
                    a0 = new double[n];
                    a1 = new double[n];
                    a2 = new double[n];
                    a3 = new double[n];
                    ar = new double[n];
                    ai = new int[n];
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = 2*math.randomreal()-1;
                        a0[i] = a[i];
                        a1[i] = a[i];
                        a2[i] = a[i];
                        a3[i] = a[i];
                        ar[i] = i;
                        ai[i] = i;
                    }
                    tsort.tagsort(ref a0, n, ref p1, ref p2);
                    testsortresults(a0, p1, p2, a, n, ref waserrors);
                    tsort.tagsortfasti(ref a1, ref ai, ref bufr1, ref bufi1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = (waserrors | (double)(a1[i])!=(double)(a0[i])) | ai[i]!=p1[i];
                    }
                    tsort.tagsortfastr(ref a2, ref ar, ref bufr1, ref bufr2, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = (waserrors | (double)(a2[i])!=(double)(a0[i])) | (double)(ar[i])!=(double)(p1[i]);
                    }
                    tsort.tagsortfast(ref a3, ref bufr1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors | (double)(a3[i])!=(double)(a0[i]);
                    }
                    
                    //
                    // non-distinct sort
                    //
                    unset1di(ref p1);
                    unset1di(ref p2);
                    a = new double[n];
                    a0 = new double[n];
                    a1 = new double[n];
                    a2 = new double[n];
                    a3 = new double[n];
                    ar = new double[n];
                    ai = new int[n];
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = i/2;
                        a0[i] = a[i];
                        a1[i] = a[i];
                        a2[i] = a[i];
                        a3[i] = a[i];
                        ar[i] = i;
                        ai[i] = i;
                    }
                    tsort.tagsort(ref a0, n, ref p1, ref p2);
                    testsortresults(a0, p1, p2, a, n, ref waserrors);
                    tsort.tagsortfasti(ref a1, ref ai, ref bufr1, ref bufi1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = (waserrors | (double)(a1[i])!=(double)(a0[i])) | ai[i]!=p1[i];
                    }
                    tsort.tagsortfastr(ref a2, ref ar, ref bufr1, ref bufr2, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = (waserrors | (double)(a2[i])!=(double)(a0[i])) | (double)(ar[i])!=(double)(p1[i]);
                    }
                    tsort.tagsortfast(ref a3, ref bufr1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors | (double)(a3[i])!=(double)(a0[i]);
                    }
                    
                    //
                    // 'All same' sort
                    //
                    unset1di(ref p1);
                    unset1di(ref p2);
                    a = new double[n];
                    a0 = new double[n];
                    a1 = new double[n];
                    a2 = new double[n];
                    a3 = new double[n];
                    ar = new double[n];
                    ai = new int[n];
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = 0;
                        a0[i] = a[i];
                        a1[i] = a[i];
                        a2[i] = a[i];
                        a3[i] = a[i];
                        ar[i] = i;
                        ai[i] = i;
                    }
                    tsort.tagsort(ref a0, n, ref p1, ref p2);
                    testsortresults(a0, p1, p2, a, n, ref waserrors);
                    tsort.tagsortfasti(ref a1, ref ai, ref bufr1, ref bufi1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = (waserrors | (double)(a1[i])!=(double)(a0[i])) | ai[i]!=p1[i];
                    }
                    tsort.tagsortfastr(ref a2, ref ar, ref bufr1, ref bufr2, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = (waserrors | (double)(a2[i])!=(double)(a0[i])) | (double)(ar[i])!=(double)(p1[i]);
                    }
                    tsort.tagsortfast(ref a3, ref bufr1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors | (double)(a3[i])!=(double)(a0[i]);
                    }
                    
                    //
                    // 0-1 sort
                    //
                    unset1di(ref p1);
                    unset1di(ref p2);
                    a = new double[n];
                    a0 = new double[n];
                    a1 = new double[n];
                    a2 = new double[n];
                    a3 = new double[n];
                    ar = new double[n];
                    ai = new int[n];
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = math.randominteger(2);
                        a0[i] = a[i];
                        a1[i] = a[i];
                        a2[i] = a[i];
                        a3[i] = a[i];
                        ar[i] = i;
                        ai[i] = i;
                    }
                    tsort.tagsort(ref a0, n, ref p1, ref p2);
                    testsortresults(a0, p1, p2, a, n, ref waserrors);
                    tsort.tagsortfasti(ref a1, ref ai, ref bufr1, ref bufi1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = (waserrors | (double)(a1[i])!=(double)(a0[i])) | ai[i]!=p1[i];
                    }
                    tsort.tagsortfastr(ref a2, ref ar, ref bufr1, ref bufr2, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = (waserrors | (double)(a2[i])!=(double)(a0[i])) | (double)(ar[i])!=(double)(p1[i]);
                    }
                    tsort.tagsortfast(ref a3, ref bufr1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors | (double)(a3[i])!=(double)(a0[i]);
                    }
                }
            }
            
            //
            // report
            //
            if( !silent )
            {
                System.Console.Write("TESTING TAGSORT");
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Unsets 2D array.
        *************************************************************************/
        private static void unset2d(ref complex[,] a)
        {
            a = new complex[0+1, 0+1];
            a[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 1D array.
        *************************************************************************/
        private static void unset1d(ref double[] a)
        {
            a = new double[0+1];
            a[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 1D array.
        *************************************************************************/
        private static void unset1di(ref int[] a)
        {
            a = new int[0+1];
            a[0] = math.randominteger(3)-1;
        }


        private static void testsortresults(double[] asorted,
            int[] p1,
            int[] p2,
            double[] aoriginal,
            int n,
            ref bool waserrors)
        {
            int i = 0;
            double[] a2 = new double[0];
            double t = 0;
            int[] f = new int[0];

            a2 = new double[n-1+1];
            f = new int[n-1+1];
            
            //
            // is set ordered?
            //
            for(i=0; i<=n-2; i++)
            {
                waserrors = waserrors | (double)(asorted[i])>(double)(asorted[i+1]);
            }
            
            //
            // P1 correctness
            //
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors | (double)(asorted[i])!=(double)(aoriginal[p1[i]]);
            }
            for(i=0; i<=n-1; i++)
            {
                f[i] = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                f[p1[i]] = f[p1[i]]+1;
            }
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors | f[i]!=1;
            }
            
            //
            // P2 correctness
            //
            for(i=0; i<=n-1; i++)
            {
                a2[i] = aoriginal[i];
            }
            for(i=0; i<=n-1; i++)
            {
                if( p2[i]!=i )
                {
                    t = a2[i];
                    a2[i] = a2[p2[i]];
                    a2[p2[i]] = t;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors | (double)(asorted[i])!=(double)(a2[i]);
            }
        }


    }
    public class testnearestneighborunit
    {
        /*************************************************************************
        Testing Nearest Neighbor Search
        *************************************************************************/
        public static bool testnearestneighbor(bool silent)
        {
            bool result = new bool();
            double[,] xy = new double[0,0];
            int i = 0;
            int j = 0;
            double v = 0;
            int normtype = 0;
            int nx = 0;
            int ny = 0;
            int n = 0;
            int smalln = 0;
            int largen = 0;
            int passcount = 0;
            int pass = 0;
            bool waserrors = new bool();
            bool kdterrors = new bool();

            kdterrors = false;
            passcount = 2;
            smalln = 256;
            largen = 2048;
            ny = 3;
            
            //
            //
            //
            testkdtreeserialization(ref kdterrors);
            for(pass=1; pass<=passcount; pass++)
            {
                for(normtype=0; normtype<=2; normtype++)
                {
                    for(nx=1; nx<=3; nx++)
                    {
                        
                        //
                        // Test in hypercube
                        //
                        xy = new double[largen, nx+ny];
                        for(i=0; i<=largen-1; i++)
                        {
                            for(j=0; j<=nx+ny-1; j++)
                            {
                                xy[i,j] = 10*math.randomreal()-5;
                            }
                        }
                        for(n=1; n<=10; n++)
                        {
                            testkdtuniform(xy, n, nx, math.randominteger(ny+1), normtype, ref kdterrors);
                        }
                        testkdtuniform(xy, largen, nx, math.randominteger(ny+1), normtype, ref kdterrors);
                        
                        //
                        // Test clustered (2*N points, pairs of equal points)
                        //
                        xy = new double[2*smalln, nx+ny];
                        for(i=0; i<=smalln-1; i++)
                        {
                            for(j=0; j<=nx+ny-1; j++)
                            {
                                xy[2*i+0,j] = 10*math.randomreal()-5;
                                xy[2*i+1,j] = xy[2*i+0,j];
                            }
                        }
                        testkdtuniform(xy, 2*smalln, nx, math.randominteger(ny+1), normtype, ref kdterrors);
                        
                        //
                        // Test degenerate case: all points are same except for one
                        //
                        xy = new double[smalln, nx+ny];
                        v = math.randomreal();
                        for(i=0; i<=smalln-2; i++)
                        {
                            for(j=0; j<=nx+ny-1; j++)
                            {
                                xy[i,j] = v;
                            }
                        }
                        for(j=0; j<=nx+ny-1; j++)
                        {
                            xy[smalln-1,j] = 10*math.randomreal()-5;
                        }
                        testkdtuniform(xy, smalln, nx, math.randominteger(ny+1), normtype, ref kdterrors);
                    }
                }
            }
            
            //
            // report
            //
            waserrors = kdterrors;
            if( !silent )
            {
                System.Console.Write("TESTING NEAREST NEIGHBOR SEARCH");
                System.Console.WriteLine();
                System.Console.Write("* KD TREES:                              ");
                if( !kdterrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Unsets 2D array.
        *************************************************************************/
        private static void unset2d(ref complex[,] a)
        {
            a = new complex[0+1, 0+1];
            a[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 1D array.
        *************************************************************************/
        private static void unset1d(ref double[] a)
        {
            a = new double[0+1];
            a[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Compare results from different queries:
        * X     just X-values
        * XY    X-values and Y-values
        * XT    X-values and tag values
        *************************************************************************/
        private static bool kdtresultsdifferent(double[,] refxy,
            int ntotal,
            double[,] qx,
            double[,] qxy,
            int[] qt,
            int n,
            int nx,
            int ny)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = false;
            for(i=0; i<=n-1; i++)
            {
                if( qt[i]<0 | qt[i]>=ntotal )
                {
                    result = true;
                    return result;
                }
                for(j=0; j<=nx-1; j++)
                {
                    result = result | (double)(qx[i,j])!=(double)(refxy[qt[i],j]);
                    result = result | (double)(qxy[i,j])!=(double)(refxy[qt[i],j]);
                }
                for(j=0; j<=ny-1; j++)
                {
                    result = result | (double)(qxy[i,nx+j])!=(double)(refxy[qt[i],nx+j]);
                }
            }
            return result;
        }


        /*************************************************************************
        Returns norm
        *************************************************************************/
        private static double vnorm(double[] x,
            int n,
            int normtype)
        {
            double result = 0;
            int i = 0;

            result = math.randomreal();
            if( normtype==0 )
            {
                result = 0;
                for(i=0; i<=n-1; i++)
                {
                    result = Math.Max(result, Math.Abs(x[i]));
                }
                return result;
            }
            if( normtype==1 )
            {
                result = 0;
                for(i=0; i<=n-1; i++)
                {
                    result = result+Math.Abs(x[i]);
                }
                return result;
            }
            if( normtype==2 )
            {
                result = 0;
                for(i=0; i<=n-1; i++)
                {
                    result = result+math.sqr(x[i]);
                }
                result = Math.Sqrt(result);
                return result;
            }
            return result;
        }


        /*************************************************************************
        Testing Nearest Neighbor Search on uniformly distributed hypercube

        NormType: 0, 1, 2
        D: space dimension
        N: points count
        *************************************************************************/
        private static void testkdtuniform(double[,] xy,
            int n,
            int nx,
            int ny,
            int normtype,
            ref bool kdterrors)
        {
            double errtol = 0;
            int[] tags = new int[0];
            double[] ptx = new double[0];
            double[] tmpx = new double[0];
            bool[] tmpb = new bool[0];
            nearestneighbor.kdtree treex = new nearestneighbor.kdtree();
            nearestneighbor.kdtree treexy = new nearestneighbor.kdtree();
            nearestneighbor.kdtree treext = new nearestneighbor.kdtree();
            double[,] qx = new double[0,0];
            double[,] qxy = new double[0,0];
            int[] qtags = new int[0];
            double[] qr = new double[0];
            int kx = 0;
            int kxy = 0;
            int kt = 0;
            double eps = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int task = 0;
            bool isequal = new bool();
            double r = 0;
            int q = 0;
            int qcount = 0;
            int i_ = 0;

            qcount = 10;
            
            //
            // Tol - roundoff error tolerance (for '>=' comparisons)
            //
            errtol = 100000*math.machineepsilon;
            
            //
            // fill tags
            //
            tags = new int[n];
            for(i=0; i<=n-1; i++)
            {
                tags[i] = i;
            }
            
            //
            // build trees
            //
            nearestneighbor.kdtreebuild(xy, n, nx, 0, normtype, treex);
            nearestneighbor.kdtreebuild(xy, n, nx, ny, normtype, treexy);
            nearestneighbor.kdtreebuildtagged(xy, tags, n, nx, 0, normtype, treext);
            
            //
            // allocate arrays
            //
            tmpx = new double[nx];
            tmpb = new bool[n];
            qx = new double[n, nx];
            qxy = new double[n, nx+ny];
            qtags = new int[n];
            qr = new double[n];
            ptx = new double[nx];
            
            //
            // test general K-NN queries (with self-matches):
            // * compare results from different trees (must be equal) and
            //   check that correct (value,tag) pairs are returned
            // * test results from XT tree - let R be radius of query result.
            //   then all points not in result must be not closer than R.
            //
            for(q=1; q<=qcount; q++)
            {
                
                //
                // Select K: 1..N
                //
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    k = 1+math.randominteger(n);
                }
                else
                {
                    k = 1;
                }
                
                //
                // Select point (either one of the points, or random)
                //
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    i = math.randominteger(n);
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        ptx[i_] = xy[i,i_];
                    }
                }
                else
                {
                    for(i=0; i<=nx-1; i++)
                    {
                        ptx[i] = 2*math.randomreal()-1;
                    }
                }
                
                //
                // Test:
                // * consistency of results from different queries
                // * points in query are IN the R-sphere (or at the boundary),
                //   and points not in query are outside of the R-sphere (or at the boundary)
                // * distances are correct and are ordered
                //
                kx = nearestneighbor.kdtreequeryknn(treex, ptx, k, true);
                kxy = nearestneighbor.kdtreequeryknn(treexy, ptx, k, true);
                kt = nearestneighbor.kdtreequeryknn(treext, ptx, k, true);
                if( (kx!=k | kxy!=k) | kt!=k )
                {
                    kdterrors = true;
                    return;
                }
                nearestneighbor.kdtreequeryresultsxi(treex, ref qx);
                nearestneighbor.kdtreequeryresultsxyi(treexy, ref qxy);
                nearestneighbor.kdtreequeryresultstagsi(treext, ref qtags);
                nearestneighbor.kdtreequeryresultsdistancesi(treext, ref qr);
                kdterrors = kdterrors | kdtresultsdifferent(xy, n, qx, qxy, qtags, k, nx, ny);
                nearestneighbor.kdtreequeryresultsx(treex, ref qx);
                nearestneighbor.kdtreequeryresultsxy(treexy, ref qxy);
                nearestneighbor.kdtreequeryresultstags(treext, ref qtags);
                nearestneighbor.kdtreequeryresultsdistances(treext, ref qr);
                kdterrors = kdterrors | kdtresultsdifferent(xy, n, qx, qxy, qtags, k, nx, ny);
                for(i=0; i<=n-1; i++)
                {
                    tmpb[i] = true;
                }
                r = 0;
                for(i=0; i<=k-1; i++)
                {
                    tmpb[qtags[i]] = false;
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = ptx[i_];
                    }
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = tmpx[i_] - qx[i,i_];
                    }
                    r = Math.Max(r, vnorm(tmpx, nx, normtype));
                }
                for(i=0; i<=n-1; i++)
                {
                    if( tmpb[i] )
                    {
                        for(i_=0; i_<=nx-1;i_++)
                        {
                            tmpx[i_] = ptx[i_];
                        }
                        for(i_=0; i_<=nx-1;i_++)
                        {
                            tmpx[i_] = tmpx[i_] - xy[i,i_];
                        }
                        kdterrors = kdterrors | (double)(vnorm(tmpx, nx, normtype))<(double)(r*(1-errtol));
                    }
                }
                for(i=0; i<=k-2; i++)
                {
                    kdterrors = kdterrors | (double)(qr[i])>(double)(qr[i+1]);
                }
                for(i=0; i<=k-1; i++)
                {
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = ptx[i_];
                    }
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = tmpx[i_] - xy[qtags[i],i_];
                    }
                    kdterrors = kdterrors | (double)(Math.Abs(vnorm(tmpx, nx, normtype)-qr[i]))>(double)(errtol);
                }
                
                //
                // Test reallocation properties: buffered functions must automatically
                // resize array which is too small, but leave unchanged array which is
                // too large.
                //
                if( n>=2 )
                {
                    
                    //
                    // First step: array is too small, two elements are required
                    //
                    k = 2;
                    kx = nearestneighbor.kdtreequeryknn(treex, ptx, k, true);
                    kxy = nearestneighbor.kdtreequeryknn(treexy, ptx, k, true);
                    kt = nearestneighbor.kdtreequeryknn(treext, ptx, k, true);
                    if( (kx!=k | kxy!=k) | kt!=k )
                    {
                        kdterrors = true;
                        return;
                    }
                    qx = new double[1, 1];
                    qxy = new double[1, 1];
                    qtags = new int[1];
                    qr = new double[1];
                    nearestneighbor.kdtreequeryresultsx(treex, ref qx);
                    nearestneighbor.kdtreequeryresultsxy(treexy, ref qxy);
                    nearestneighbor.kdtreequeryresultstags(treext, ref qtags);
                    nearestneighbor.kdtreequeryresultsdistances(treext, ref qr);
                    kdterrors = kdterrors | kdtresultsdifferent(xy, n, qx, qxy, qtags, k, nx, ny);
                    
                    //
                    // Second step: array is one row larger than needed, so only first
                    // row is overwritten. Test it.
                    //
                    k = 1;
                    kx = nearestneighbor.kdtreequeryknn(treex, ptx, k, true);
                    kxy = nearestneighbor.kdtreequeryknn(treexy, ptx, k, true);
                    kt = nearestneighbor.kdtreequeryknn(treext, ptx, k, true);
                    if( (kx!=k | kxy!=k) | kt!=k )
                    {
                        kdterrors = true;
                        return;
                    }
                    for(i=0; i<=nx-1; i++)
                    {
                        qx[1,i] = Double.NaN;
                    }
                    for(i=0; i<=nx+ny-1; i++)
                    {
                        qxy[1,i] = Double.NaN;
                    }
                    qtags[1] = 999;
                    qr[1] = Double.NaN;
                    nearestneighbor.kdtreequeryresultsx(treex, ref qx);
                    nearestneighbor.kdtreequeryresultsxy(treexy, ref qxy);
                    nearestneighbor.kdtreequeryresultstags(treext, ref qtags);
                    nearestneighbor.kdtreequeryresultsdistances(treext, ref qr);
                    kdterrors = kdterrors | kdtresultsdifferent(xy, n, qx, qxy, qtags, k, nx, ny);
                    for(i=0; i<=nx-1; i++)
                    {
                        kdterrors = kdterrors | !Double.IsNaN(qx[1,i]);
                    }
                    for(i=0; i<=nx+ny-1; i++)
                    {
                        kdterrors = kdterrors | !Double.IsNaN(qxy[1,i]);
                    }
                    kdterrors = kdterrors | !(qtags[1]==999);
                    kdterrors = kdterrors | !Double.IsNaN(qr[1]);
                }
                
                //
                // Test reallocation properties: 'interactive' functions must allocate
                // new array on each call.
                //
                if( n>=2 )
                {
                    
                    //
                    // On input array is either too small or too large
                    //
                    for(k=1; k<=2; k++)
                    {
                        ap.assert(k==1 | k==2, "KNN: internal error (unexpected K)!");
                        kx = nearestneighbor.kdtreequeryknn(treex, ptx, k, true);
                        kxy = nearestneighbor.kdtreequeryknn(treexy, ptx, k, true);
                        kt = nearestneighbor.kdtreequeryknn(treext, ptx, k, true);
                        if( (kx!=k | kxy!=k) | kt!=k )
                        {
                            kdterrors = true;
                            return;
                        }
                        qx = new double[3-k, 3-k];
                        qxy = new double[3-k, 3-k];
                        qtags = new int[3-k];
                        qr = new double[3-k];
                        nearestneighbor.kdtreequeryresultsxi(treex, ref qx);
                        nearestneighbor.kdtreequeryresultsxyi(treexy, ref qxy);
                        nearestneighbor.kdtreequeryresultstagsi(treext, ref qtags);
                        nearestneighbor.kdtreequeryresultsdistancesi(treext, ref qr);
                        kdterrors = kdterrors | kdtresultsdifferent(xy, n, qx, qxy, qtags, k, nx, ny);
                        kdterrors = (kdterrors | ap.rows(qx)!=k) | ap.cols(qx)!=nx;
                        kdterrors = (kdterrors | ap.rows(qxy)!=k) | ap.cols(qxy)!=nx+ny;
                        kdterrors = kdterrors | ap.len(qtags)!=k;
                        kdterrors = kdterrors | ap.len(qr)!=k;
                    }
                }
            }
            
            //
            // test general approximate K-NN queries (with self-matches):
            // * compare results from different trees (must be equal) and
            //   check that correct (value,tag) pairs are returned
            // * test results from XT tree - let R be radius of query result.
            //   then all points not in result must be not closer than R/(1+Eps).
            //
            for(q=1; q<=qcount; q++)
            {
                
                //
                // Select K: 1..N
                //
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    k = 1+math.randominteger(n);
                }
                else
                {
                    k = 1;
                }
                
                //
                // Select Eps
                //
                eps = 0.5+math.randomreal();
                
                //
                // Select point (either one of the points, or random)
                //
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    i = math.randominteger(n);
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        ptx[i_] = xy[i,i_];
                    }
                }
                else
                {
                    for(i=0; i<=nx-1; i++)
                    {
                        ptx[i] = 2*math.randomreal()-1;
                    }
                }
                
                //
                // Test:
                // * consistency of results from different queries
                // * points in query are IN the R-sphere (or at the boundary),
                //   and points not in query are outside of the R-sphere (or at the boundary)
                // * distances are correct and are ordered
                //
                kx = nearestneighbor.kdtreequeryaknn(treex, ptx, k, true, eps);
                kxy = nearestneighbor.kdtreequeryaknn(treexy, ptx, k, true, eps);
                kt = nearestneighbor.kdtreequeryaknn(treext, ptx, k, true, eps);
                if( (kx!=k | kxy!=k) | kt!=k )
                {
                    kdterrors = true;
                    return;
                }
                nearestneighbor.kdtreequeryresultsxi(treex, ref qx);
                nearestneighbor.kdtreequeryresultsxyi(treexy, ref qxy);
                nearestneighbor.kdtreequeryresultstagsi(treext, ref qtags);
                nearestneighbor.kdtreequeryresultsdistancesi(treext, ref qr);
                kdterrors = kdterrors | kdtresultsdifferent(xy, n, qx, qxy, qtags, k, nx, ny);
                nearestneighbor.kdtreequeryresultsx(treex, ref qx);
                nearestneighbor.kdtreequeryresultsxy(treexy, ref qxy);
                nearestneighbor.kdtreequeryresultstags(treext, ref qtags);
                nearestneighbor.kdtreequeryresultsdistances(treext, ref qr);
                kdterrors = kdterrors | kdtresultsdifferent(xy, n, qx, qxy, qtags, k, nx, ny);
                for(i=0; i<=n-1; i++)
                {
                    tmpb[i] = true;
                }
                r = 0;
                for(i=0; i<=k-1; i++)
                {
                    tmpb[qtags[i]] = false;
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = ptx[i_];
                    }
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = tmpx[i_] - qx[i,i_];
                    }
                    r = Math.Max(r, vnorm(tmpx, nx, normtype));
                }
                for(i=0; i<=n-1; i++)
                {
                    if( tmpb[i] )
                    {
                        for(i_=0; i_<=nx-1;i_++)
                        {
                            tmpx[i_] = ptx[i_];
                        }
                        for(i_=0; i_<=nx-1;i_++)
                        {
                            tmpx[i_] = tmpx[i_] - xy[i,i_];
                        }
                        kdterrors = kdterrors | (double)(vnorm(tmpx, nx, normtype))<(double)(r*(1-errtol)/(1+eps));
                    }
                }
                for(i=0; i<=k-2; i++)
                {
                    kdterrors = kdterrors | (double)(qr[i])>(double)(qr[i+1]);
                }
                for(i=0; i<=k-1; i++)
                {
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = ptx[i_];
                    }
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = tmpx[i_] - xy[qtags[i],i_];
                    }
                    kdterrors = kdterrors | (double)(Math.Abs(vnorm(tmpx, nx, normtype)-qr[i]))>(double)(errtol);
                }
            }
            
            //
            // test general R-NN queries  (with self-matches):
            // * compare results from different trees (must be equal) and
            //   check that correct (value,tag) pairs are returned
            // * test results from XT tree - let R be radius of query result.
            //   then all points not in result must be not closer than R.
            //
            for(q=1; q<=qcount; q++)
            {
                
                //
                // Select R
                //
                if( (double)(math.randomreal())>(double)(0.3) )
                {
                    r = Math.Max(math.randomreal(), math.machineepsilon);
                }
                else
                {
                    r = math.machineepsilon;
                }
                
                //
                // Select point (either one of the points, or random)
                //
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    i = math.randominteger(n);
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        ptx[i_] = xy[i,i_];
                    }
                }
                else
                {
                    for(i=0; i<=nx-1; i++)
                    {
                        ptx[i] = 2*math.randomreal()-1;
                    }
                }
                
                //
                // Test:
                // * consistency of results from different queries
                // * points in query are IN the R-sphere (or at the boundary),
                //   and points not in query are outside of the R-sphere (or at the boundary)
                // * distances are correct and are ordered
                //
                kx = nearestneighbor.kdtreequeryrnn(treex, ptx, r, true);
                kxy = nearestneighbor.kdtreequeryrnn(treexy, ptx, r, true);
                kt = nearestneighbor.kdtreequeryrnn(treext, ptx, r, true);
                if( kxy!=kx | kt!=kx )
                {
                    kdterrors = true;
                    return;
                }
                nearestneighbor.kdtreequeryresultsxi(treex, ref qx);
                nearestneighbor.kdtreequeryresultsxyi(treexy, ref qxy);
                nearestneighbor.kdtreequeryresultstagsi(treext, ref qtags);
                nearestneighbor.kdtreequeryresultsdistancesi(treext, ref qr);
                kdterrors = kdterrors | kdtresultsdifferent(xy, n, qx, qxy, qtags, kx, nx, ny);
                nearestneighbor.kdtreequeryresultsx(treex, ref qx);
                nearestneighbor.kdtreequeryresultsxy(treexy, ref qxy);
                nearestneighbor.kdtreequeryresultstags(treext, ref qtags);
                nearestneighbor.kdtreequeryresultsdistances(treext, ref qr);
                kdterrors = kdterrors | kdtresultsdifferent(xy, n, qx, qxy, qtags, kx, nx, ny);
                for(i=0; i<=n-1; i++)
                {
                    tmpb[i] = true;
                }
                for(i=0; i<=kx-1; i++)
                {
                    tmpb[qtags[i]] = false;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = ptx[i_];
                    }
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        tmpx[i_] = tmpx[i_] - xy[i,i_];
                    }
                    if( tmpb[i] )
                    {
                        kdterrors = kdterrors | (double)(vnorm(tmpx, nx, normtype))<(double)(r*(1-errtol));
                    }
                    else
                    {
                        kdterrors = kdterrors | (double)(vnorm(tmpx, nx, normtype))>(double)(r*(1+errtol));
                    }
                }
                for(i=0; i<=kx-2; i++)
                {
                    kdterrors = kdterrors | (double)(qr[i])>(double)(qr[i+1]);
                }
            }
            
            //
            // Test self-matching:
            // * self-match - nearest neighbor of each point in XY is the point itself
            // * no self-match - nearest neighbor is NOT the point itself
            //
            if( n>1 )
            {
                
                //
                // test for N=1 have non-general form, but it is not really needed
                //
                for(task=0; task<=1; task++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=0; i_<=nx-1;i_++)
                        {
                            ptx[i_] = xy[i,i_];
                        }
                        kx = nearestneighbor.kdtreequeryknn(treex, ptx, 1, task==0);
                        nearestneighbor.kdtreequeryresultsxi(treex, ref qx);
                        if( kx!=1 )
                        {
                            kdterrors = true;
                            return;
                        }
                        isequal = true;
                        for(j=0; j<=nx-1; j++)
                        {
                            isequal = isequal & (double)(qx[0,j])==(double)(ptx[j]);
                        }
                        if( task==0 )
                        {
                            kdterrors = kdterrors | !isequal;
                        }
                        else
                        {
                            kdterrors = kdterrors | isequal;
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Testing serialization of KD trees

        This function sets Err to True on errors, but leaves it unchanged on success
        *************************************************************************/
        private static void testkdtreeserialization(ref bool err)
        {
            int n = 0;
            int nx = 0;
            int ny = 0;
            int normtype = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int q = 0;
            double[,] xy = new double[0,0];
            double[] x = new double[0];
            int[] tags = new int[0];
            int[] qsizes = new int[0];
            double threshold = 0;
            nearestneighbor.kdtree tree0 = new nearestneighbor.kdtree();
            nearestneighbor.kdtree tree1 = new nearestneighbor.kdtree();
            int k0 = 0;
            int k1 = 0;
            double[,] xy0 = new double[0,0];
            double[,] xy1 = new double[0,0];
            int[] tags0 = new int[0];
            int[] tags1 = new int[0];
            int i_ = 0;

            threshold = 100*math.machineepsilon;
            
            //
            // different N, NX, NY, NormType
            //
            n = 1;
            while( n<=51 )
            {
                
                //
                // prepare array with query sizes
                //
                qsizes = new int[4];
                qsizes[0] = 1;
                qsizes[1] = Math.Min(2, n);
                qsizes[2] = Math.Min(4, n);
                qsizes[3] = n;
                
                //
                // different NX/NY/NormType
                //
                for(nx=1; nx<=2; nx++)
                {
                    for(ny=0; ny<=2; ny++)
                    {
                        for(normtype=0; normtype<=2; normtype++)
                        {
                            
                            //
                            // Prepare data
                            //
                            xy = new double[n, nx+ny];
                            tags = new int[n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=nx+ny-1; j++)
                                {
                                    xy[i,j] = math.randomreal();
                                }
                                tags[i] = math.randominteger(100);
                            }
                            
                            //
                            // Build tree, pass it through serializer
                            //
                            nearestneighbor.kdtreebuildtagged(xy, tags, n, nx, ny, normtype, tree0);
                            {
                                //
                                // This code passes data structure through serializers
                                // (serializes it to string and loads back)
                                //
                                serializer _local_serializer;
                                string _local_str;
                                
                                _local_serializer = new serializer();
                                _local_serializer.alloc_start();
                                nearestneighbor.kdtreealloc(_local_serializer, tree0);
                                _local_serializer.sstart_str();
                                nearestneighbor.kdtreeserialize(_local_serializer, tree0);
                                _local_serializer.stop();
                                _local_str = _local_serializer.get_string();
                                
                                _local_serializer = new serializer();
                                _local_serializer.ustart_str(_local_str);
                                nearestneighbor.kdtreeunserialize(_local_serializer, tree1);
                                _local_serializer.stop();
                            }
                            
                            //
                            // For each point of XY we make queries with different sizes
                            //
                            x = new double[nx];
                            for(k=0; k<=n-1; k++)
                            {
                                for(q=0; q<=ap.len(qsizes)-1; q++)
                                {
                                    for(i_=0; i_<=nx-1;i_++)
                                    {
                                        x[i_] = xy[k,i_];
                                    }
                                    k0 = nearestneighbor.kdtreequeryknn(tree0, x, qsizes[q], true);
                                    k1 = nearestneighbor.kdtreequeryknn(tree1, x, qsizes[q], true);
                                    if( k0!=k1 )
                                    {
                                        err = true;
                                        return;
                                    }
                                    nearestneighbor.kdtreequeryresultsxy(tree0, ref xy0);
                                    nearestneighbor.kdtreequeryresultsxy(tree1, ref xy1);
                                    for(i=0; i<=k0-1; i++)
                                    {
                                        for(j=0; j<=nx+ny-1; j++)
                                        {
                                            if( (double)(Math.Abs(xy0[i,j]-xy1[i,j]))>(double)(threshold) )
                                            {
                                                err = true;
                                                return;
                                            }
                                        }
                                    }
                                    nearestneighbor.kdtreequeryresultstags(tree0, ref tags0);
                                    nearestneighbor.kdtreequeryresultstags(tree1, ref tags1);
                                    for(i=0; i<=k0-1; i++)
                                    {
                                        if( tags0[i]!=tags1[i] )
                                        {
                                            err = true;
                                            return;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                
                //
                // Next N
                //
                n = n+25;
            }
        }


    }
    public class testablasunit
    {
        public static bool testablas(bool silent)
        {
            bool result = new bool();
            double threshold = 0;
            bool trsmerrors = new bool();
            bool syrkerrors = new bool();
            bool gemmerrors = new bool();
            bool transerrors = new bool();
            bool rank1errors = new bool();
            bool mverrors = new bool();
            bool copyerrors = new bool();
            bool waserrors = new bool();
            double[,] ra = new double[0,0];

            trsmerrors = false;
            syrkerrors = false;
            gemmerrors = false;
            transerrors = false;
            rank1errors = false;
            mverrors = false;
            copyerrors = false;
            waserrors = false;
            threshold = 10000*math.machineepsilon;
            trsmerrors = trsmerrors | testtrsm(1, 3*ablas.ablasblocksize(ra)+1);
            syrkerrors = syrkerrors | testsyrk(1, 3*ablas.ablasblocksize(ra)+1);
            gemmerrors = gemmerrors | testgemm(1, 3*ablas.ablasblocksize(ra)+1);
            transerrors = transerrors | testtrans(1, 3*ablas.ablasblocksize(ra)+1);
            rank1errors = rank1errors | testrank1(1, 3*ablas.ablasblocksize(ra)+1);
            mverrors = mverrors | testmv(1, 3*ablas.ablasblocksize(ra)+1);
            copyerrors = copyerrors | testcopy(1, 3*ablas.ablasblocksize(ra)+1);
            
            //
            // report
            //
            waserrors = (((((trsmerrors | syrkerrors) | gemmerrors) | transerrors) | rank1errors) | mverrors) | copyerrors;
            if( !silent )
            {
                System.Console.Write("TESTING ABLAS");
                System.Console.WriteLine();
                System.Console.Write("* TRSM:                                  ");
                if( trsmerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SYRK:                                  ");
                if( syrkerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* GEMM:                                  ");
                if( gemmerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* TRANS:                                 ");
                if( transerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* RANK1:                                 ");
                if( rank1errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* MV:                                    ");
                if( mverrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* COPY:                                  ");
                if( copyerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        private static void naivematrixmatrixmultiply(double[,] a,
            int ai1,
            int ai2,
            int aj1,
            int aj2,
            bool transa,
            double[,] b,
            int bi1,
            int bi2,
            int bj1,
            int bj2,
            bool transb,
            double alpha,
            ref double[,] c,
            int ci1,
            int ci2,
            int cj1,
            int cj2,
            double beta)
        {
            int arows = 0;
            int acols = 0;
            int brows = 0;
            int bcols = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int l = 0;
            int r = 0;
            double v = 0;
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            int i_ = 0;
            int i1_ = 0;

            
            //
            // Setup
            //
            if( !transa )
            {
                arows = ai2-ai1+1;
                acols = aj2-aj1+1;
            }
            else
            {
                arows = aj2-aj1+1;
                acols = ai2-ai1+1;
            }
            if( !transb )
            {
                brows = bi2-bi1+1;
                bcols = bj2-bj1+1;
            }
            else
            {
                brows = bj2-bj1+1;
                bcols = bi2-bi1+1;
            }
            ap.assert(acols==brows, "NaiveMatrixMatrixMultiply: incorrect matrix sizes!");
            if( ((arows<=0 | acols<=0) | brows<=0) | bcols<=0 )
            {
                return;
            }
            l = arows;
            r = bcols;
            k = acols;
            x1 = new double[k+1];
            x2 = new double[k+1];
            for(i=1; i<=l; i++)
            {
                for(j=1; j<=r; j++)
                {
                    if( !transa )
                    {
                        if( !transb )
                        {
                            i1_ = (aj1)-(bi1);
                            v = 0.0;
                            for(i_=bi1; i_<=bi2;i_++)
                            {
                                v += b[i_,bj1+j-1]*a[ai1+i-1,i_+i1_];
                            }
                        }
                        else
                        {
                            i1_ = (aj1)-(bj1);
                            v = 0.0;
                            for(i_=bj1; i_<=bj2;i_++)
                            {
                                v += b[bi1+j-1,i_]*a[ai1+i-1,i_+i1_];
                            }
                        }
                    }
                    else
                    {
                        if( !transb )
                        {
                            i1_ = (ai1)-(bi1);
                            v = 0.0;
                            for(i_=bi1; i_<=bi2;i_++)
                            {
                                v += b[i_,bj1+j-1]*a[i_+i1_,aj1+i-1];
                            }
                        }
                        else
                        {
                            i1_ = (ai1)-(bj1);
                            v = 0.0;
                            for(i_=bj1; i_<=bj2;i_++)
                            {
                                v += b[bi1+j-1,i_]*a[i_+i1_,aj1+i-1];
                            }
                        }
                    }
                    if( (double)(beta)==(double)(0) )
                    {
                        c[ci1+i-1,cj1+j-1] = alpha*v;
                    }
                    else
                    {
                        c[ci1+i-1,cj1+j-1] = beta*c[ci1+i-1,cj1+j-1]+alpha*v;
                    }
                }
            }
        }


        /*************************************************************************
        ?Matrix????TRSM tests

        Returns False for passed test, True - for failed
        *************************************************************************/
        private static bool testtrsm(int minn,
            int maxn)
        {
            bool result = new bool();
            int n = 0;
            int m = 0;
            int mx = 0;
            int i = 0;
            int j = 0;
            int optype = 0;
            int uppertype = 0;
            int unittype = 0;
            int xoffsi = 0;
            int xoffsj = 0;
            int aoffsitype = 0;
            int aoffsjtype = 0;
            int aoffsi = 0;
            int aoffsj = 0;
            double[,] refra = new double[0,0];
            double[,] refrxl = new double[0,0];
            double[,] refrxr = new double[0,0];
            complex[,] refca = new complex[0,0];
            complex[,] refcxl = new complex[0,0];
            complex[,] refcxr = new complex[0,0];
            double[,] ra = new double[0,0];
            complex[,] ca = new complex[0,0];
            double[,] rxr1 = new double[0,0];
            double[,] rxl1 = new double[0,0];
            complex[,] cxr1 = new complex[0,0];
            complex[,] cxl1 = new complex[0,0];
            double[,] rxr2 = new double[0,0];
            double[,] rxl2 = new double[0,0];
            complex[,] cxr2 = new complex[0,0];
            complex[,] cxl2 = new complex[0,0];
            double threshold = 0;

            threshold = math.sqr(maxn)*100*math.machineepsilon;
            result = false;
            for(mx=minn; mx<=maxn; mx++)
            {
                
                //
                // Select random M/N in [1,MX] such that max(M,N)=MX
                //
                m = 1+math.randominteger(mx);
                n = 1+math.randominteger(mx);
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    m = mx;
                }
                else
                {
                    n = mx;
                }
                
                //
                // Initialize RefRA/RefCA by random matrices whose upper
                // and lower triangle submatrices are non-degenerate
                // well-conditioned matrices.
                //
                // Matrix size is 2Mx2M (four copies of same MxM matrix
                // to test different offsets)
                //
                refra = new double[2*m, 2*m];
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        refra[i,j] = 0.2*math.randomreal()-0.1;
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    refra[i,i] = (2*math.randominteger(1)-1)*(2*m+math.randomreal());
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        refra[i+m,j] = refra[i,j];
                        refra[i,j+m] = refra[i,j];
                        refra[i+m,j+m] = refra[i,j];
                    }
                }
                refca = new complex[2*m, 2*m];
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        refca[i,j].x = 0.2*math.randomreal()-0.1;
                        refca[i,j].y = 0.2*math.randomreal()-0.1;
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    refca[i,i].x = (2*math.randominteger(2)-1)*(2*m+math.randomreal());
                    refca[i,i].y = (2*math.randominteger(2)-1)*(2*m+math.randomreal());
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        refca[i+m,j] = refca[i,j];
                        refca[i,j+m] = refca[i,j];
                        refca[i+m,j+m] = refca[i,j];
                    }
                }
                
                //
                // Generate random XL/XR.
                //
                // XR is NxM matrix (matrix for 'Right' subroutines)
                // XL is MxN matrix (matrix for 'Left' subroutines)
                //
                refrxr = new double[n, m];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        refrxr[i,j] = 2*math.randomreal()-1;
                    }
                }
                refrxl = new double[m, n];
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        refrxl[i,j] = 2*math.randomreal()-1;
                    }
                }
                refcxr = new complex[n, m];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        refcxr[i,j].x = 2*math.randomreal()-1;
                        refcxr[i,j].y = 2*math.randomreal()-1;
                    }
                }
                refcxl = new complex[m, n];
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        refcxl[i,j].x = 2*math.randomreal()-1;
                        refcxl[i,j].y = 2*math.randomreal()-1;
                    }
                }
                
                //
                // test different types of operations, offsets, and so on...
                //
                // to avoid unnecessary slowdown we don't test ALL possible
                // combinations of operation types. We just generate one random
                // set of parameters and test it.
                //
                ra = new double[2*m, 2*m];
                rxr1 = new double[n, m];
                rxr2 = new double[n, m];
                rxl1 = new double[m, n];
                rxl2 = new double[m, n];
                ca = new complex[2*m, 2*m];
                cxr1 = new complex[n, m];
                cxr2 = new complex[n, m];
                cxl1 = new complex[m, n];
                cxl2 = new complex[m, n];
                optype = math.randominteger(3);
                uppertype = math.randominteger(2);
                unittype = math.randominteger(2);
                xoffsi = math.randominteger(2);
                xoffsj = math.randominteger(2);
                aoffsitype = math.randominteger(2);
                aoffsjtype = math.randominteger(2);
                aoffsi = m*aoffsitype;
                aoffsj = m*aoffsjtype;
                
                //
                // copy A, XR, XL (fill unused parts with random garbage)
                //
                for(i=0; i<=2*m-1; i++)
                {
                    for(j=0; j<=2*m-1; j++)
                    {
                        if( ((i>=aoffsi & i<aoffsi+m) & j>=aoffsj) & j<aoffsj+m )
                        {
                            ca[i,j] = refca[i,j];
                            ra[i,j] = refra[i,j];
                        }
                        else
                        {
                            ca[i,j] = math.randomreal();
                            ra[i,j] = math.randomreal();
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        if( i>=xoffsi & j>=xoffsj )
                        {
                            cxr1[i,j] = refcxr[i,j];
                            cxr2[i,j] = refcxr[i,j];
                            rxr1[i,j] = refrxr[i,j];
                            rxr2[i,j] = refrxr[i,j];
                        }
                        else
                        {
                            cxr1[i,j] = math.randomreal();
                            cxr2[i,j] = cxr1[i,j];
                            rxr1[i,j] = math.randomreal();
                            rxr2[i,j] = rxr1[i,j];
                        }
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i>=xoffsi & j>=xoffsj )
                        {
                            cxl1[i,j] = refcxl[i,j];
                            cxl2[i,j] = refcxl[i,j];
                            rxl1[i,j] = refrxl[i,j];
                            rxl2[i,j] = refrxl[i,j];
                        }
                        else
                        {
                            cxl1[i,j] = math.randomreal();
                            cxl2[i,j] = cxl1[i,j];
                            rxl1[i,j] = math.randomreal();
                            rxl2[i,j] = rxl1[i,j];
                        }
                    }
                }
                
                //
                // Test CXR
                //
                ablas.cmatrixrighttrsm(n-xoffsi, m-xoffsj, ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, ref cxr1, xoffsi, xoffsj);
                refcmatrixrighttrsm(n-xoffsi, m-xoffsj, ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, ref cxr2, xoffsi, xoffsj);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        result = result | (double)(math.abscomplex(cxr1[i,j]-cxr2[i,j]))>(double)(threshold);
                    }
                }
                
                //
                // Test CXL
                //
                ablas.cmatrixlefttrsm(m-xoffsi, n-xoffsj, ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, ref cxl1, xoffsi, xoffsj);
                refcmatrixlefttrsm(m-xoffsi, n-xoffsj, ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, ref cxl2, xoffsi, xoffsj);
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        result = result | (double)(math.abscomplex(cxl1[i,j]-cxl2[i,j]))>(double)(threshold);
                    }
                }
                if( optype<2 )
                {
                    
                    //
                    // Test RXR
                    //
                    ablas.rmatrixrighttrsm(n-xoffsi, m-xoffsj, ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, ref rxr1, xoffsi, xoffsj);
                    refrmatrixrighttrsm(n-xoffsi, m-xoffsj, ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, ref rxr2, xoffsi, xoffsj);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            result = result | (double)(Math.Abs(rxr1[i,j]-rxr2[i,j]))>(double)(threshold);
                        }
                    }
                    
                    //
                    // Test RXL
                    //
                    ablas.rmatrixlefttrsm(m-xoffsi, n-xoffsj, ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, ref rxl1, xoffsi, xoffsj);
                    refrmatrixlefttrsm(m-xoffsi, n-xoffsj, ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, ref rxl2, xoffsi, xoffsj);
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            result = result | (double)(Math.Abs(rxl1[i,j]-rxl2[i,j]))>(double)(threshold);
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        SYRK tests

        Returns False for passed test, True - for failed
        *************************************************************************/
        private static bool testsyrk(int minn,
            int maxn)
        {
            bool result = new bool();
            int n = 0;
            int k = 0;
            int mx = 0;
            int i = 0;
            int j = 0;
            int uppertype = 0;
            int xoffsi = 0;
            int xoffsj = 0;
            int aoffsitype = 0;
            int aoffsjtype = 0;
            int aoffsi = 0;
            int aoffsj = 0;
            int alphatype = 0;
            int betatype = 0;
            double[,] refra = new double[0,0];
            double[,] refrc = new double[0,0];
            complex[,] refca = new complex[0,0];
            complex[,] refcc = new complex[0,0];
            double alpha = 0;
            double beta = 0;
            double[,] ra1 = new double[0,0];
            double[,] ra2 = new double[0,0];
            complex[,] ca1 = new complex[0,0];
            complex[,] ca2 = new complex[0,0];
            double[,] rc = new double[0,0];
            double[,] rct = new double[0,0];
            complex[,] cc = new complex[0,0];
            complex[,] cct = new complex[0,0];
            double threshold = 0;

            threshold = maxn*100*math.machineepsilon;
            result = false;
            for(mx=minn; mx<=maxn; mx++)
            {
                
                //
                // Select random M/N in [1,MX] such that max(M,N)=MX
                //
                k = 1+math.randominteger(mx);
                n = 1+math.randominteger(mx);
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    k = mx;
                }
                else
                {
                    n = mx;
                }
                
                //
                // Initialize RefRA/RefCA by random Hermitian matrices,
                // RefRC/RefCC by random matrices
                //
                // RA/CA size is 2Nx2N (four copies of same NxN matrix
                // to test different offsets)
                //
                refra = new double[2*n, 2*n];
                refca = new complex[2*n, 2*n];
                for(i=0; i<=n-1; i++)
                {
                    refra[i,i] = 2*math.randomreal()-1;
                    refca[i,i] = 2*math.randomreal()-1;
                    for(j=i+1; j<=n-1; j++)
                    {
                        refra[i,j] = 2*math.randomreal()-1;
                        refca[i,j].x = 2*math.randomreal()-1;
                        refca[i,j].y = 2*math.randomreal()-1;
                        refra[j,i] = refra[i,j];
                        refca[j,i] = math.conj(refca[i,j]);
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        refra[i+n,j] = refra[i,j];
                        refra[i,j+n] = refra[i,j];
                        refra[i+n,j+n] = refra[i,j];
                        refca[i+n,j] = refca[i,j];
                        refca[i,j+n] = refca[i,j];
                        refca[i+n,j+n] = refca[i,j];
                    }
                }
                refrc = new double[n, k];
                refcc = new complex[n, k];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=k-1; j++)
                    {
                        refrc[i,j] = 2*math.randomreal()-1;
                        refcc[i,j].x = 2*math.randomreal()-1;
                        refcc[i,j].y = 2*math.randomreal()-1;
                    }
                }
                
                //
                // test different types of operations, offsets, and so on...
                //
                // to avoid unnecessary slowdown we don't test ALL possible
                // combinations of operation types. We just generate one random
                // set of parameters and test it.
                //
                ra1 = new double[2*n, 2*n];
                ra2 = new double[2*n, 2*n];
                ca1 = new complex[2*n, 2*n];
                ca2 = new complex[2*n, 2*n];
                rc = new double[n, k];
                rct = new double[k, n];
                cc = new complex[n, k];
                cct = new complex[k, n];
                uppertype = math.randominteger(2);
                xoffsi = math.randominteger(2);
                xoffsj = math.randominteger(2);
                aoffsitype = math.randominteger(2);
                aoffsjtype = math.randominteger(2);
                alphatype = math.randominteger(2);
                betatype = math.randominteger(2);
                aoffsi = n*aoffsitype;
                aoffsj = n*aoffsjtype;
                alpha = alphatype*(2*math.randomreal()-1);
                beta = betatype*(2*math.randomreal()-1);
                
                //
                // copy A, C (fill unused parts with random garbage)
                //
                for(i=0; i<=2*n-1; i++)
                {
                    for(j=0; j<=2*n-1; j++)
                    {
                        if( ((i>=aoffsi & i<aoffsi+n) & j>=aoffsj) & j<aoffsj+n )
                        {
                            ca1[i,j] = refca[i,j];
                            ca2[i,j] = refca[i,j];
                            ra1[i,j] = refra[i,j];
                            ra2[i,j] = refra[i,j];
                        }
                        else
                        {
                            ca1[i,j] = math.randomreal();
                            ca2[i,j] = ca1[i,j];
                            ra1[i,j] = math.randomreal();
                            ra2[i,j] = ra1[i,j];
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=k-1; j++)
                    {
                        if( i>=xoffsi & j>=xoffsj )
                        {
                            rc[i,j] = refrc[i,j];
                            rct[j,i] = refrc[i,j];
                            cc[i,j] = refcc[i,j];
                            cct[j,i] = refcc[i,j];
                        }
                        else
                        {
                            rc[i,j] = math.randomreal();
                            rct[j,i] = rc[i,j];
                            cc[i,j] = math.randomreal();
                            cct[j,i] = cct[j,i];
                        }
                    }
                }
                
                //
                // Test complex
                // Only one of transform types is selected and tested
                //
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    ablas.cmatrixsyrk(n-xoffsi, k-xoffsj, alpha, cc, xoffsi, xoffsj, 0, beta, ref ca1, aoffsi, aoffsj, uppertype==0);
                    refcmatrixsyrk(n-xoffsi, k-xoffsj, alpha, cc, xoffsi, xoffsj, 0, beta, ref ca2, aoffsi, aoffsj, uppertype==0);
                }
                else
                {
                    ablas.cmatrixsyrk(n-xoffsi, k-xoffsj, alpha, cct, xoffsj, xoffsi, 2, beta, ref ca1, aoffsi, aoffsj, uppertype==0);
                    refcmatrixsyrk(n-xoffsi, k-xoffsj, alpha, cct, xoffsj, xoffsi, 2, beta, ref ca2, aoffsi, aoffsj, uppertype==0);
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        result = result | (double)(math.abscomplex(ca1[i,j]-ca2[i,j]))>(double)(threshold);
                    }
                }
                
                //
                // Test real
                // Only one of transform types is selected and tested
                //
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    ablas.rmatrixsyrk(n-xoffsi, k-xoffsj, alpha, rc, xoffsi, xoffsj, 0, beta, ref ra1, aoffsi, aoffsj, uppertype==0);
                    refrmatrixsyrk(n-xoffsi, k-xoffsj, alpha, rc, xoffsi, xoffsj, 0, beta, ref ra2, aoffsi, aoffsj, uppertype==0);
                }
                else
                {
                    ablas.rmatrixsyrk(n-xoffsi, k-xoffsj, alpha, rct, xoffsj, xoffsi, 1, beta, ref ra1, aoffsi, aoffsj, uppertype==0);
                    refrmatrixsyrk(n-xoffsi, k-xoffsj, alpha, rct, xoffsj, xoffsi, 1, beta, ref ra2, aoffsi, aoffsj, uppertype==0);
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        result = result | (double)(Math.Abs(ra1[i,j]-ra2[i,j]))>(double)(threshold);
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        GEMM tests

        Returns False for passed test, True - for failed
        *************************************************************************/
        private static bool testgemm(int minn,
            int maxn)
        {
            bool result = new bool();
            int m = 0;
            int n = 0;
            int k = 0;
            int mx = 0;
            int i = 0;
            int j = 0;
            int aoffsi = 0;
            int aoffsj = 0;
            int aoptype = 0;
            int aoptyper = 0;
            int boffsi = 0;
            int boffsj = 0;
            int boptype = 0;
            int boptyper = 0;
            int coffsi = 0;
            int coffsj = 0;
            double[,] refra = new double[0,0];
            double[,] refrb = new double[0,0];
            double[,] refrc = new double[0,0];
            complex[,] refca = new complex[0,0];
            complex[,] refcb = new complex[0,0];
            complex[,] refcc = new complex[0,0];
            double alphar = 0;
            double betar = 0;
            complex alphac = 0;
            complex betac = 0;
            double[,] rc1 = new double[0,0];
            double[,] rc2 = new double[0,0];
            complex[,] cc1 = new complex[0,0];
            complex[,] cc2 = new complex[0,0];
            double threshold = 0;

            threshold = maxn*100*math.machineepsilon;
            result = false;
            for(mx=minn; mx<=maxn; mx++)
            {
                
                //
                // Select random M/N/K in [1,MX] such that max(M,N,K)=MX
                //
                m = 1+math.randominteger(mx);
                n = 1+math.randominteger(mx);
                k = 1+math.randominteger(mx);
                i = math.randominteger(3);
                if( i==0 )
                {
                    m = mx;
                }
                if( i==1 )
                {
                    n = mx;
                }
                if( i==2 )
                {
                    k = mx;
                }
                
                //
                // Initialize A/B/C by random matrices with size (MaxN+1)*(MaxN+1)
                //
                refra = new double[maxn+1, maxn+1];
                refrb = new double[maxn+1, maxn+1];
                refrc = new double[maxn+1, maxn+1];
                refca = new complex[maxn+1, maxn+1];
                refcb = new complex[maxn+1, maxn+1];
                refcc = new complex[maxn+1, maxn+1];
                for(i=0; i<=maxn; i++)
                {
                    for(j=0; j<=maxn; j++)
                    {
                        refra[i,j] = 2*math.randomreal()-1;
                        refrb[i,j] = 2*math.randomreal()-1;
                        refrc[i,j] = 2*math.randomreal()-1;
                        refca[i,j].x = 2*math.randomreal()-1;
                        refca[i,j].y = 2*math.randomreal()-1;
                        refcb[i,j].x = 2*math.randomreal()-1;
                        refcb[i,j].y = 2*math.randomreal()-1;
                        refcc[i,j].x = 2*math.randomreal()-1;
                        refcc[i,j].y = 2*math.randomreal()-1;
                    }
                }
                
                //
                // test different types of operations, offsets, and so on...
                //
                // to avoid unnecessary slowdown we don't test ALL possible
                // combinations of operation types. We just generate one random
                // set of parameters and test it.
                //
                rc1 = new double[maxn+1, maxn+1];
                rc2 = new double[maxn+1, maxn+1];
                cc1 = new complex[maxn+1, maxn+1];
                cc2 = new complex[maxn+1, maxn+1];
                aoffsi = math.randominteger(2);
                aoffsj = math.randominteger(2);
                aoptype = math.randominteger(3);
                aoptyper = math.randominteger(2);
                boffsi = math.randominteger(2);
                boffsj = math.randominteger(2);
                boptype = math.randominteger(3);
                boptyper = math.randominteger(2);
                coffsi = math.randominteger(2);
                coffsj = math.randominteger(2);
                alphar = math.randominteger(2)*(2*math.randomreal()-1);
                betar = math.randominteger(2)*(2*math.randomreal()-1);
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    alphac.x = 2*math.randomreal()-1;
                    alphac.y = 2*math.randomreal()-1;
                }
                else
                {
                    alphac = 0;
                }
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    betac.x = 2*math.randomreal()-1;
                    betac.y = 2*math.randomreal()-1;
                }
                else
                {
                    betac = 0;
                }
                
                //
                // copy C
                //
                for(i=0; i<=maxn; i++)
                {
                    for(j=0; j<=maxn; j++)
                    {
                        rc1[i,j] = refrc[i,j];
                        rc2[i,j] = refrc[i,j];
                        cc1[i,j] = refcc[i,j];
                        cc2[i,j] = refcc[i,j];
                    }
                }
                
                //
                // Test complex
                //
                ablas.cmatrixgemm(m, n, k, alphac, refca, aoffsi, aoffsj, aoptype, refcb, boffsi, boffsj, boptype, betac, ref cc1, coffsi, coffsj);
                refcmatrixgemm(m, n, k, alphac, refca, aoffsi, aoffsj, aoptype, refcb, boffsi, boffsj, boptype, betac, ref cc2, coffsi, coffsj);
                for(i=0; i<=maxn; i++)
                {
                    for(j=0; j<=maxn; j++)
                    {
                        result = result | (double)(math.abscomplex(cc1[i,j]-cc2[i,j]))>(double)(threshold);
                    }
                }
                
                //
                // Test real
                //
                ablas.rmatrixgemm(m, n, k, alphar, refra, aoffsi, aoffsj, aoptyper, refrb, boffsi, boffsj, boptyper, betar, ref rc1, coffsi, coffsj);
                refrmatrixgemm(m, n, k, alphar, refra, aoffsi, aoffsj, aoptyper, refrb, boffsi, boffsj, boptyper, betar, ref rc2, coffsi, coffsj);
                for(i=0; i<=maxn; i++)
                {
                    for(j=0; j<=maxn; j++)
                    {
                        result = result | (double)(Math.Abs(rc1[i,j]-rc2[i,j]))>(double)(threshold);
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        transpose tests

        Returns False for passed test, True - for failed
        *************************************************************************/
        private static bool testtrans(int minn,
            int maxn)
        {
            bool result = new bool();
            int m = 0;
            int n = 0;
            int mx = 0;
            int i = 0;
            int j = 0;
            int aoffsi = 0;
            int aoffsj = 0;
            int boffsi = 0;
            int boffsj = 0;
            double v1 = 0;
            double v2 = 0;
            double threshold = 0;
            double[,] refra = new double[0,0];
            double[,] refrb = new double[0,0];
            complex[,] refca = new complex[0,0];
            complex[,] refcb = new complex[0,0];

            result = false;
            threshold = 1000*math.machineepsilon;
            for(mx=minn; mx<=maxn; mx++)
            {
                
                //
                // Select random M/N in [1,MX] such that max(M,N)=MX
                // Generate random V1 and V2 which are used to fill
                // RefRB/RefCB with control values.
                //
                m = 1+math.randominteger(mx);
                n = 1+math.randominteger(mx);
                if( math.randominteger(2)==0 )
                {
                    m = mx;
                }
                else
                {
                    n = mx;
                }
                v1 = math.randomreal();
                v2 = math.randomreal();
                
                //
                // Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
                // Fill B with control values
                //
                refra = new double[maxn+1, maxn+1];
                refrb = new double[maxn+1, maxn+1];
                refca = new complex[maxn+1, maxn+1];
                refcb = new complex[maxn+1, maxn+1];
                for(i=0; i<=maxn; i++)
                {
                    for(j=0; j<=maxn; j++)
                    {
                        refra[i,j] = 2*math.randomreal()-1;
                        refca[i,j].x = 2*math.randomreal()-1;
                        refca[i,j].y = 2*math.randomreal()-1;
                        refrb[i,j] = i*v1+j*v2;
                        refcb[i,j] = i*v1+j*v2;
                    }
                }
                
                //
                // test different offsets (zero or one)
                //
                // to avoid unnecessary slowdown we don't test ALL possible
                // combinations of operation types. We just generate one random
                // set of parameters and test it.
                //
                aoffsi = math.randominteger(2);
                aoffsj = math.randominteger(2);
                boffsi = math.randominteger(2);
                boffsj = math.randominteger(2);
                ablas.rmatrixtranspose(m, n, refra, aoffsi, aoffsj, ref refrb, boffsi, boffsj);
                for(i=0; i<=maxn; i++)
                {
                    for(j=0; j<=maxn; j++)
                    {
                        if( ((i<boffsi | i>=boffsi+n) | j<boffsj) | j>=boffsj+m )
                        {
                            result = result | (double)(Math.Abs(refrb[i,j]-(v1*i+v2*j)))>(double)(threshold);
                        }
                        else
                        {
                            result = result | (double)(Math.Abs(refrb[i,j]-refra[aoffsi+j-boffsj,aoffsj+i-boffsi]))>(double)(threshold);
                        }
                    }
                }
                ablas.cmatrixtranspose(m, n, refca, aoffsi, aoffsj, ref refcb, boffsi, boffsj);
                for(i=0; i<=maxn; i++)
                {
                    for(j=0; j<=maxn; j++)
                    {
                        if( ((i<boffsi | i>=boffsi+n) | j<boffsj) | j>=boffsj+m )
                        {
                            result = result | (double)(math.abscomplex(refcb[i,j]-(v1*i+v2*j)))>(double)(threshold);
                        }
                        else
                        {
                            result = result | (double)(math.abscomplex(refcb[i,j]-refca[aoffsi+j-boffsj,aoffsj+i-boffsi]))>(double)(threshold);
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        rank-1tests

        Returns False for passed test, True - for failed
        *************************************************************************/
        private static bool testrank1(int minn,
            int maxn)
        {
            bool result = new bool();
            int m = 0;
            int n = 0;
            int mx = 0;
            int i = 0;
            int j = 0;
            int aoffsi = 0;
            int aoffsj = 0;
            int uoffs = 0;
            int voffs = 0;
            double threshold = 0;
            double[,] refra = new double[0,0];
            double[,] refrb = new double[0,0];
            complex[,] refca = new complex[0,0];
            complex[,] refcb = new complex[0,0];
            double[] ru = new double[0];
            double[] rv = new double[0];
            complex[] cu = new complex[0];
            complex[] cv = new complex[0];

            result = false;
            threshold = 1000*math.machineepsilon;
            for(mx=minn; mx<=maxn; mx++)
            {
                
                //
                // Select random M/N in [1,MX] such that max(M,N)=MX
                //
                m = 1+math.randominteger(mx);
                n = 1+math.randominteger(mx);
                if( math.randominteger(2)==0 )
                {
                    m = mx;
                }
                else
                {
                    n = mx;
                }
                
                //
                // Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
                // Fill B with control values
                //
                refra = new double[maxn+maxn, maxn+maxn];
                refrb = new double[maxn+maxn, maxn+maxn];
                refca = new complex[maxn+maxn, maxn+maxn];
                refcb = new complex[maxn+maxn, maxn+maxn];
                for(i=0; i<=2*maxn-1; i++)
                {
                    for(j=0; j<=2*maxn-1; j++)
                    {
                        refra[i,j] = 2*math.randomreal()-1;
                        refca[i,j].x = 2*math.randomreal()-1;
                        refca[i,j].y = 2*math.randomreal()-1;
                        refrb[i,j] = refra[i,j];
                        refcb[i,j] = refca[i,j];
                    }
                }
                ru = new double[2*m];
                cu = new complex[2*m];
                for(i=0; i<=2*m-1; i++)
                {
                    ru[i] = 2*math.randomreal()-1;
                    cu[i].x = 2*math.randomreal()-1;
                    cu[i].y = 2*math.randomreal()-1;
                }
                rv = new double[2*n];
                cv = new complex[2*n];
                for(i=0; i<=2*n-1; i++)
                {
                    rv[i] = 2*math.randomreal()-1;
                    cv[i].x = 2*math.randomreal()-1;
                    cv[i].y = 2*math.randomreal()-1;
                }
                
                //
                // test different offsets (zero or one)
                //
                // to avoid unnecessary slowdown we don't test ALL possible
                // combinations of operation types. We just generate one random
                // set of parameters and test it.
                //
                aoffsi = math.randominteger(maxn);
                aoffsj = math.randominteger(maxn);
                uoffs = math.randominteger(m);
                voffs = math.randominteger(n);
                ablas.cmatrixrank1(m, n, ref refca, aoffsi, aoffsj, ref cu, uoffs, ref cv, voffs);
                for(i=0; i<=2*maxn-1; i++)
                {
                    for(j=0; j<=2*maxn-1; j++)
                    {
                        if( ((i<aoffsi | i>=aoffsi+m) | j<aoffsj) | j>=aoffsj+n )
                        {
                            result = result | (double)(math.abscomplex(refca[i,j]-refcb[i,j]))>(double)(threshold);
                        }
                        else
                        {
                            result = result | (double)(math.abscomplex(refca[i,j]-(refcb[i,j]+cu[i-aoffsi+uoffs]*cv[j-aoffsj+voffs])))>(double)(threshold);
                        }
                    }
                }
                ablas.rmatrixrank1(m, n, ref refra, aoffsi, aoffsj, ref ru, uoffs, ref rv, voffs);
                for(i=0; i<=2*maxn-1; i++)
                {
                    for(j=0; j<=2*maxn-1; j++)
                    {
                        if( ((i<aoffsi | i>=aoffsi+m) | j<aoffsj) | j>=aoffsj+n )
                        {
                            result = result | (double)(Math.Abs(refra[i,j]-refrb[i,j]))>(double)(threshold);
                        }
                        else
                        {
                            result = result | (double)(Math.Abs(refra[i,j]-(refrb[i,j]+ru[i-aoffsi+uoffs]*rv[j-aoffsj+voffs])))>(double)(threshold);
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        MV tests

        Returns False for passed test, True - for failed
        *************************************************************************/
        private static bool testmv(int minn,
            int maxn)
        {
            bool result = new bool();
            int m = 0;
            int n = 0;
            int mx = 0;
            int i = 0;
            int j = 0;
            int aoffsi = 0;
            int aoffsj = 0;
            int xoffs = 0;
            int yoffs = 0;
            int opca = 0;
            int opra = 0;
            double threshold = 0;
            double rv1 = 0;
            double rv2 = 0;
            complex cv1 = 0;
            complex cv2 = 0;
            double[,] refra = new double[0,0];
            complex[,] refca = new complex[0,0];
            double[] rx = new double[0];
            double[] ry = new double[0];
            complex[] cx = new complex[0];
            complex[] cy = new complex[0];
            int i_ = 0;
            int i1_ = 0;

            result = false;
            threshold = 1000*math.machineepsilon;
            for(mx=minn; mx<=maxn; mx++)
            {
                
                //
                // Select random M/N in [1,MX] such that max(M,N)=MX
                //
                m = 1+math.randominteger(mx);
                n = 1+math.randominteger(mx);
                if( math.randominteger(2)==0 )
                {
                    m = mx;
                }
                else
                {
                    n = mx;
                }
                
                //
                // Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
                // Initialize X by random vector with size (MaxN+MaxN)
                // Fill Y by control values
                //
                refra = new double[maxn+maxn, maxn+maxn];
                refca = new complex[maxn+maxn, maxn+maxn];
                for(i=0; i<=2*maxn-1; i++)
                {
                    for(j=0; j<=2*maxn-1; j++)
                    {
                        refra[i,j] = 2*math.randomreal()-1;
                        refca[i,j].x = 2*math.randomreal()-1;
                        refca[i,j].y = 2*math.randomreal()-1;
                    }
                }
                rx = new double[2*maxn];
                cx = new complex[2*maxn];
                ry = new double[2*maxn];
                cy = new complex[2*maxn];
                for(i=0; i<=2*maxn-1; i++)
                {
                    rx[i] = 2*math.randomreal()-1;
                    cx[i].x = 2*math.randomreal()-1;
                    cx[i].y = 2*math.randomreal()-1;
                    ry[i] = i;
                    cy[i] = i;
                }
                
                //
                // test different offsets (zero or one)
                //
                // to avoid unnecessary slowdown we don't test ALL possible
                // combinations of operation types. We just generate one random
                // set of parameters and test it.
                //
                aoffsi = math.randominteger(maxn);
                aoffsj = math.randominteger(maxn);
                xoffs = math.randominteger(maxn);
                yoffs = math.randominteger(maxn);
                opca = math.randominteger(3);
                opra = math.randominteger(2);
                ablas.cmatrixmv(m, n, refca, aoffsi, aoffsj, opca, cx, xoffs, ref cy, yoffs);
                for(i=0; i<=2*maxn-1; i++)
                {
                    if( i<yoffs | i>=yoffs+m )
                    {
                        result = result | cy[i]!=i;
                    }
                    else
                    {
                        cv1 = cy[i];
                        cv2 = 0.0;
                        if( opca==0 )
                        {
                            i1_ = (xoffs)-(aoffsj);
                            cv2 = 0.0;
                            for(i_=aoffsj; i_<=aoffsj+n-1;i_++)
                            {
                                cv2 += refca[aoffsi+i-yoffs,i_]*cx[i_+i1_];
                            }
                        }
                        if( opca==1 )
                        {
                            i1_ = (xoffs)-(aoffsi);
                            cv2 = 0.0;
                            for(i_=aoffsi; i_<=aoffsi+n-1;i_++)
                            {
                                cv2 += refca[i_,aoffsj+i-yoffs]*cx[i_+i1_];
                            }
                        }
                        if( opca==2 )
                        {
                            i1_ = (xoffs)-(aoffsi);
                            cv2 = 0.0;
                            for(i_=aoffsi; i_<=aoffsi+n-1;i_++)
                            {
                                cv2 += math.conj(refca[i_,aoffsj+i-yoffs])*cx[i_+i1_];
                            }
                        }
                        result = result | (double)(math.abscomplex(cv1-cv2))>(double)(threshold);
                    }
                }
                ablas.rmatrixmv(m, n, refra, aoffsi, aoffsj, opra, rx, xoffs, ref ry, yoffs);
                for(i=0; i<=2*maxn-1; i++)
                {
                    if( i<yoffs | i>=yoffs+m )
                    {
                        result = result | (double)(ry[i])!=(double)(i);
                    }
                    else
                    {
                        rv1 = ry[i];
                        rv2 = 0;
                        if( opra==0 )
                        {
                            i1_ = (xoffs)-(aoffsj);
                            rv2 = 0.0;
                            for(i_=aoffsj; i_<=aoffsj+n-1;i_++)
                            {
                                rv2 += refra[aoffsi+i-yoffs,i_]*rx[i_+i1_];
                            }
                        }
                        if( opra==1 )
                        {
                            i1_ = (xoffs)-(aoffsi);
                            rv2 = 0.0;
                            for(i_=aoffsi; i_<=aoffsi+n-1;i_++)
                            {
                                rv2 += refra[i_,aoffsj+i-yoffs]*rx[i_+i1_];
                            }
                        }
                        result = result | (double)(Math.Abs(rv1-rv2))>(double)(threshold);
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        COPY tests

        Returns False for passed test, True - for failed
        *************************************************************************/
        private static bool testcopy(int minn,
            int maxn)
        {
            bool result = new bool();
            int m = 0;
            int n = 0;
            int mx = 0;
            int i = 0;
            int j = 0;
            int aoffsi = 0;
            int aoffsj = 0;
            int boffsi = 0;
            int boffsj = 0;
            double threshold = 0;
            double[,] ra = new double[0,0];
            double[,] rb = new double[0,0];
            complex[,] ca = new complex[0,0];
            complex[,] cb = new complex[0,0];

            result = false;
            threshold = 1000*math.machineepsilon;
            for(mx=minn; mx<=maxn; mx++)
            {
                
                //
                // Select random M/N in [1,MX] such that max(M,N)=MX
                //
                m = 1+math.randominteger(mx);
                n = 1+math.randominteger(mx);
                if( math.randominteger(2)==0 )
                {
                    m = mx;
                }
                else
                {
                    n = mx;
                }
                
                //
                // Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
                // Initialize X by random vector with size (MaxN+MaxN)
                // Fill Y by control values
                //
                ra = new double[maxn+maxn, maxn+maxn];
                ca = new complex[maxn+maxn, maxn+maxn];
                rb = new double[maxn+maxn, maxn+maxn];
                cb = new complex[maxn+maxn, maxn+maxn];
                for(i=0; i<=2*maxn-1; i++)
                {
                    for(j=0; j<=2*maxn-1; j++)
                    {
                        ra[i,j] = 2*math.randomreal()-1;
                        ca[i,j].x = 2*math.randomreal()-1;
                        ca[i,j].y = 2*math.randomreal()-1;
                        rb[i,j] = 1+2*i+3*j;
                        cb[i,j] = 1+2*i+3*j;
                    }
                }
                
                //
                // test different offsets (zero or one)
                //
                // to avoid unnecessary slowdown we don't test ALL possible
                // combinations of operation types. We just generate one random
                // set of parameters and test it.
                //
                aoffsi = math.randominteger(maxn);
                aoffsj = math.randominteger(maxn);
                boffsi = math.randominteger(maxn);
                boffsj = math.randominteger(maxn);
                ablas.cmatrixcopy(m, n, ca, aoffsi, aoffsj, ref cb, boffsi, boffsj);
                for(i=0; i<=2*maxn-1; i++)
                {
                    for(j=0; j<=2*maxn-1; j++)
                    {
                        if( ((i<boffsi | i>=boffsi+m) | j<boffsj) | j>=boffsj+n )
                        {
                            result = result | cb[i,j]!=1+2*i+3*j;
                        }
                        else
                        {
                            result = result | (double)(math.abscomplex(ca[aoffsi+i-boffsi,aoffsj+j-boffsj]-cb[i,j]))>(double)(threshold);
                        }
                    }
                }
                ablas.rmatrixcopy(m, n, ra, aoffsi, aoffsj, ref rb, boffsi, boffsj);
                for(i=0; i<=2*maxn-1; i++)
                {
                    for(j=0; j<=2*maxn-1; j++)
                    {
                        if( ((i<boffsi | i>=boffsi+m) | j<boffsj) | j>=boffsj+n )
                        {
                            result = result | (double)(rb[i,j])!=(double)(1+2*i+3*j);
                        }
                        else
                        {
                            result = result | (double)(Math.Abs(ra[aoffsi+i-boffsi,aoffsj+j-boffsj]-rb[i,j]))>(double)(threshold);
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Reference implementation

          -- ALGLIB routine --
             15.12.2009
             Bochkanov Sergey
        *************************************************************************/
        private static void refcmatrixrighttrsm(int m,
            int n,
            complex[,] a,
            int i1,
            int j1,
            bool isupper,
            bool isunit,
            int optype,
            ref complex[,] x,
            int i2,
            int j2)
        {
            complex[,] a1 = new complex[0,0];
            complex[,] a2 = new complex[0,0];
            complex[] tx = new complex[0];
            int i = 0;
            int j = 0;
            complex vc = 0;
            bool rupper = new bool();
            int i_ = 0;
            int i1_ = 0;

            if( n*m==0 )
            {
                return;
            }
            a1 = new complex[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a1[i,j] = 0;
                }
            }
            if( isupper )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=i; j<=n-1; j++)
                    {
                        a1[i,j] = a[i1+i,j1+j];
                    }
                }
            }
            else
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=i; j++)
                    {
                        a1[i,j] = a[i1+i,j1+j];
                    }
                }
            }
            rupper = isupper;
            if( isunit )
            {
                for(i=0; i<=n-1; i++)
                {
                    a1[i,i] = 1;
                }
            }
            a2 = new complex[n, n];
            if( optype==0 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a2[i,j] = a1[i,j];
                    }
                }
            }
            if( optype==1 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a2[i,j] = a1[j,i];
                    }
                }
                rupper = !rupper;
            }
            if( optype==2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a2[i,j] = math.conj(a1[j,i]);
                    }
                }
                rupper = !rupper;
            }
            internalcmatrixtrinverse(ref a2, n, rupper, false);
            tx = new complex[n];
            for(i=0; i<=m-1; i++)
            {
                i1_ = (j2) - (0);
                for(i_=0; i_<=n-1;i_++)
                {
                    tx[i_] = x[i2+i,i_+i1_];
                }
                for(j=0; j<=n-1; j++)
                {
                    vc = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        vc += tx[i_]*a2[i_,j];
                    }
                    x[i2+i,j2+j] = vc;
                }
            }
        }


        /*************************************************************************
        Reference implementation

          -- ALGLIB routine --
             15.12.2009
             Bochkanov Sergey
        *************************************************************************/
        private static void refcmatrixlefttrsm(int m,
            int n,
            complex[,] a,
            int i1,
            int j1,
            bool isupper,
            bool isunit,
            int optype,
            ref complex[,] x,
            int i2,
            int j2)
        {
            complex[,] a1 = new complex[0,0];
            complex[,] a2 = new complex[0,0];
            complex[] tx = new complex[0];
            int i = 0;
            int j = 0;
            complex vc = 0;
            bool rupper = new bool();
            int i_ = 0;
            int i1_ = 0;

            if( n*m==0 )
            {
                return;
            }
            a1 = new complex[m, m];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    a1[i,j] = 0;
                }
            }
            if( isupper )
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=i; j<=m-1; j++)
                    {
                        a1[i,j] = a[i1+i,j1+j];
                    }
                }
            }
            else
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=i; j++)
                    {
                        a1[i,j] = a[i1+i,j1+j];
                    }
                }
            }
            rupper = isupper;
            if( isunit )
            {
                for(i=0; i<=m-1; i++)
                {
                    a1[i,i] = 1;
                }
            }
            a2 = new complex[m, m];
            if( optype==0 )
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        a2[i,j] = a1[i,j];
                    }
                }
            }
            if( optype==1 )
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        a2[i,j] = a1[j,i];
                    }
                }
                rupper = !rupper;
            }
            if( optype==2 )
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        a2[i,j] = math.conj(a1[j,i]);
                    }
                }
                rupper = !rupper;
            }
            internalcmatrixtrinverse(ref a2, m, rupper, false);
            tx = new complex[m];
            for(j=0; j<=n-1; j++)
            {
                i1_ = (i2) - (0);
                for(i_=0; i_<=m-1;i_++)
                {
                    tx[i_] = x[i_+i1_,j2+j];
                }
                for(i=0; i<=m-1; i++)
                {
                    vc = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        vc += a2[i,i_]*tx[i_];
                    }
                    x[i2+i,j2+j] = vc;
                }
            }
        }


        /*************************************************************************
        Reference implementation

          -- ALGLIB routine --
             15.12.2009
             Bochkanov Sergey
        *************************************************************************/
        private static void refrmatrixrighttrsm(int m,
            int n,
            double[,] a,
            int i1,
            int j1,
            bool isupper,
            bool isunit,
            int optype,
            ref double[,] x,
            int i2,
            int j2)
        {
            double[,] a1 = new double[0,0];
            double[,] a2 = new double[0,0];
            double[] tx = new double[0];
            int i = 0;
            int j = 0;
            double vr = 0;
            bool rupper = new bool();
            int i_ = 0;
            int i1_ = 0;

            if( n*m==0 )
            {
                return;
            }
            a1 = new double[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a1[i,j] = 0;
                }
            }
            if( isupper )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=i; j<=n-1; j++)
                    {
                        a1[i,j] = a[i1+i,j1+j];
                    }
                }
            }
            else
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=i; j++)
                    {
                        a1[i,j] = a[i1+i,j1+j];
                    }
                }
            }
            rupper = isupper;
            if( isunit )
            {
                for(i=0; i<=n-1; i++)
                {
                    a1[i,i] = 1;
                }
            }
            a2 = new double[n, n];
            if( optype==0 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a2[i,j] = a1[i,j];
                    }
                }
            }
            if( optype==1 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a2[i,j] = a1[j,i];
                    }
                }
                rupper = !rupper;
            }
            internalrmatrixtrinverse(ref a2, n, rupper, false);
            tx = new double[n];
            for(i=0; i<=m-1; i++)
            {
                i1_ = (j2) - (0);
                for(i_=0; i_<=n-1;i_++)
                {
                    tx[i_] = x[i2+i,i_+i1_];
                }
                for(j=0; j<=n-1; j++)
                {
                    vr = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        vr += tx[i_]*a2[i_,j];
                    }
                    x[i2+i,j2+j] = vr;
                }
            }
        }


        /*************************************************************************
        Reference implementation

          -- ALGLIB routine --
             15.12.2009
             Bochkanov Sergey
        *************************************************************************/
        private static void refrmatrixlefttrsm(int m,
            int n,
            double[,] a,
            int i1,
            int j1,
            bool isupper,
            bool isunit,
            int optype,
            ref double[,] x,
            int i2,
            int j2)
        {
            double[,] a1 = new double[0,0];
            double[,] a2 = new double[0,0];
            double[] tx = new double[0];
            int i = 0;
            int j = 0;
            double vr = 0;
            bool rupper = new bool();
            int i_ = 0;
            int i1_ = 0;

            if( n*m==0 )
            {
                return;
            }
            a1 = new double[m, m];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    a1[i,j] = 0;
                }
            }
            if( isupper )
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=i; j<=m-1; j++)
                    {
                        a1[i,j] = a[i1+i,j1+j];
                    }
                }
            }
            else
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=i; j++)
                    {
                        a1[i,j] = a[i1+i,j1+j];
                    }
                }
            }
            rupper = isupper;
            if( isunit )
            {
                for(i=0; i<=m-1; i++)
                {
                    a1[i,i] = 1;
                }
            }
            a2 = new double[m, m];
            if( optype==0 )
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        a2[i,j] = a1[i,j];
                    }
                }
            }
            if( optype==1 )
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        a2[i,j] = a1[j,i];
                    }
                }
                rupper = !rupper;
            }
            internalrmatrixtrinverse(ref a2, m, rupper, false);
            tx = new double[m];
            for(j=0; j<=n-1; j++)
            {
                i1_ = (i2) - (0);
                for(i_=0; i_<=m-1;i_++)
                {
                    tx[i_] = x[i_+i1_,j2+j];
                }
                for(i=0; i<=m-1; i++)
                {
                    vr = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        vr += a2[i,i_]*tx[i_];
                    }
                    x[i2+i,j2+j] = vr;
                }
            }
        }


        /*************************************************************************
        Internal subroutine.
        Triangular matrix inversion

          -- LAPACK routine (version 3.0) --
             Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
             Courant Institute, Argonne National Lab, and Rice University
             February 29, 1992
        *************************************************************************/
        private static bool internalcmatrixtrinverse(ref complex[,] a,
            int n,
            bool isupper,
            bool isunittriangular)
        {
            bool result = new bool();
            bool nounit = new bool();
            int i = 0;
            int j = 0;
            complex v = 0;
            complex ajj = 0;
            complex[] t = new complex[0];
            int i_ = 0;

            result = true;
            t = new complex[n-1+1];
            
            //
            // Test the input parameters.
            //
            nounit = !isunittriangular;
            if( isupper )
            {
                
                //
                // Compute inverse of upper triangular matrix.
                //
                for(j=0; j<=n-1; j++)
                {
                    if( nounit )
                    {
                        if( a[j,j]==0 )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    
                    //
                    // Compute elements 1:j-1 of j-th column.
                    //
                    if( j>0 )
                    {
                        for(i_=0; i_<=j-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=0; i<=j-1; i++)
                        {
                            if( i+1<j )
                            {
                                v = 0.0;
                                for(i_=i+1; i_<=j-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=0; i_<=j-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            else
            {
                
                //
                // Compute inverse of lower triangular matrix.
                //
                for(j=n-1; j>=0; j--)
                {
                    if( nounit )
                    {
                        if( a[j,j]==0 )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    if( j+1<n )
                    {
                        
                        //
                        // Compute elements j+1:n of j-th column.
                        //
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=j+1; i<=n-1; i++)
                        {
                            if( i>j+1 )
                            {
                                v = 0.0;
                                for(i_=j+1; i_<=i-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Internal subroutine.
        Triangular matrix inversion

          -- LAPACK routine (version 3.0) --
             Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
             Courant Institute, Argonne National Lab, and Rice University
             February 29, 1992
        *************************************************************************/
        private static bool internalrmatrixtrinverse(ref double[,] a,
            int n,
            bool isupper,
            bool isunittriangular)
        {
            bool result = new bool();
            bool nounit = new bool();
            int i = 0;
            int j = 0;
            double v = 0;
            double ajj = 0;
            double[] t = new double[0];
            int i_ = 0;

            result = true;
            t = new double[n-1+1];
            
            //
            // Test the input parameters.
            //
            nounit = !isunittriangular;
            if( isupper )
            {
                
                //
                // Compute inverse of upper triangular matrix.
                //
                for(j=0; j<=n-1; j++)
                {
                    if( nounit )
                    {
                        if( (double)(a[j,j])==(double)(0) )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    
                    //
                    // Compute elements 1:j-1 of j-th column.
                    //
                    if( j>0 )
                    {
                        for(i_=0; i_<=j-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=0; i<=j-1; i++)
                        {
                            if( i<j-1 )
                            {
                                v = 0.0;
                                for(i_=i+1; i_<=j-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=0; i_<=j-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            else
            {
                
                //
                // Compute inverse of lower triangular matrix.
                //
                for(j=n-1; j>=0; j--)
                {
                    if( nounit )
                    {
                        if( (double)(a[j,j])==(double)(0) )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    if( j<n-1 )
                    {
                        
                        //
                        // Compute elements j+1:n of j-th column.
                        //
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=j+1; i<=n-1; i++)
                        {
                            if( i>j+1 )
                            {
                                v = 0.0;
                                for(i_=j+1; i_<=i-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Reference SYRK subroutine.

          -- ALGLIB routine --
             16.12.2009
             Bochkanov Sergey
        *************************************************************************/
        private static void refcmatrixsyrk(int n,
            int k,
            double alpha,
            complex[,] a,
            int ia,
            int ja,
            int optypea,
            double beta,
            ref complex[,] c,
            int ic,
            int jc,
            bool isupper)
        {
            complex[,] ae = new complex[0,0];
            int i = 0;
            int j = 0;
            complex vc = 0;
            int i_ = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (isupper & j>=i) | (!isupper & j<=i) )
                    {
                        if( (double)(beta)==(double)(0) )
                        {
                            c[i+ic,j+jc] = 0;
                        }
                        else
                        {
                            c[i+ic,j+jc] = c[i+ic,j+jc]*beta;
                        }
                    }
                }
            }
            if( (double)(alpha)==(double)(0) )
            {
                return;
            }
            if( n*k>0 )
            {
                ae = new complex[n, k];
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=k-1; j++)
                {
                    if( optypea==0 )
                    {
                        ae[i,j] = a[ia+i,ja+j];
                    }
                    if( optypea==2 )
                    {
                        ae[i,j] = math.conj(a[ia+j,ja+i]);
                    }
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    vc = 0;
                    if( k>0 )
                    {
                        vc = 0.0;
                        for(i_=0; i_<=k-1;i_++)
                        {
                            vc += ae[i,i_]*math.conj(ae[j,i_]);
                        }
                    }
                    vc = alpha*vc;
                    if( isupper & j>=i )
                    {
                        c[ic+i,jc+j] = vc+c[ic+i,jc+j];
                    }
                    if( !isupper & j<=i )
                    {
                        c[ic+i,jc+j] = vc+c[ic+i,jc+j];
                    }
                }
            }
        }


        /*************************************************************************
        Reference SYRK subroutine.

          -- ALGLIB routine --
             16.12.2009
             Bochkanov Sergey
        *************************************************************************/
        private static void refrmatrixsyrk(int n,
            int k,
            double alpha,
            double[,] a,
            int ia,
            int ja,
            int optypea,
            double beta,
            ref double[,] c,
            int ic,
            int jc,
            bool isupper)
        {
            double[,] ae = new double[0,0];
            int i = 0;
            int j = 0;
            double vr = 0;
            int i_ = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (isupper & j>=i) | (!isupper & j<=i) )
                    {
                        if( (double)(beta)==(double)(0) )
                        {
                            c[i+ic,j+jc] = 0;
                        }
                        else
                        {
                            c[i+ic,j+jc] = c[i+ic,j+jc]*beta;
                        }
                    }
                }
            }
            if( (double)(alpha)==(double)(0) )
            {
                return;
            }
            if( n*k>0 )
            {
                ae = new double[n, k];
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=k-1; j++)
                {
                    if( optypea==0 )
                    {
                        ae[i,j] = a[ia+i,ja+j];
                    }
                    if( optypea==1 )
                    {
                        ae[i,j] = a[ia+j,ja+i];
                    }
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    vr = 0;
                    if( k>0 )
                    {
                        vr = 0.0;
                        for(i_=0; i_<=k-1;i_++)
                        {
                            vr += ae[i,i_]*ae[j,i_];
                        }
                    }
                    vr = alpha*vr;
                    if( isupper & j>=i )
                    {
                        c[ic+i,jc+j] = vr+c[ic+i,jc+j];
                    }
                    if( !isupper & j<=i )
                    {
                        c[ic+i,jc+j] = vr+c[ic+i,jc+j];
                    }
                }
            }
        }


        /*************************************************************************
        Reference GEMM,
        ALGLIB subroutine
        *************************************************************************/
        private static void refcmatrixgemm(int m,
            int n,
            int k,
            complex alpha,
            complex[,] a,
            int ia,
            int ja,
            int optypea,
            complex[,] b,
            int ib,
            int jb,
            int optypeb,
            complex beta,
            ref complex[,] c,
            int ic,
            int jc)
        {
            complex[,] ae = new complex[0,0];
            complex[,] be = new complex[0,0];
            int i = 0;
            int j = 0;
            complex vc = 0;
            int i_ = 0;

            ae = new complex[m, k];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=k-1; j++)
                {
                    if( optypea==0 )
                    {
                        ae[i,j] = a[ia+i,ja+j];
                    }
                    if( optypea==1 )
                    {
                        ae[i,j] = a[ia+j,ja+i];
                    }
                    if( optypea==2 )
                    {
                        ae[i,j] = math.conj(a[ia+j,ja+i]);
                    }
                }
            }
            be = new complex[k, n];
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( optypeb==0 )
                    {
                        be[i,j] = b[ib+i,jb+j];
                    }
                    if( optypeb==1 )
                    {
                        be[i,j] = b[ib+j,jb+i];
                    }
                    if( optypeb==2 )
                    {
                        be[i,j] = math.conj(b[ib+j,jb+i]);
                    }
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    vc = 0.0;
                    for(i_=0; i_<=k-1;i_++)
                    {
                        vc += ae[i,i_]*be[i_,j];
                    }
                    vc = alpha*vc;
                    if( beta!=0 )
                    {
                        vc = vc+beta*c[ic+i,jc+j];
                    }
                    c[ic+i,jc+j] = vc;
                }
            }
        }


        /*************************************************************************
        Reference GEMM,
        ALGLIB subroutine
        *************************************************************************/
        private static void refrmatrixgemm(int m,
            int n,
            int k,
            double alpha,
            double[,] a,
            int ia,
            int ja,
            int optypea,
            double[,] b,
            int ib,
            int jb,
            int optypeb,
            double beta,
            ref double[,] c,
            int ic,
            int jc)
        {
            double[,] ae = new double[0,0];
            double[,] be = new double[0,0];
            int i = 0;
            int j = 0;
            double vc = 0;
            int i_ = 0;

            ae = new double[m, k];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=k-1; j++)
                {
                    if( optypea==0 )
                    {
                        ae[i,j] = a[ia+i,ja+j];
                    }
                    if( optypea==1 )
                    {
                        ae[i,j] = a[ia+j,ja+i];
                    }
                }
            }
            be = new double[k, n];
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( optypeb==0 )
                    {
                        be[i,j] = b[ib+i,jb+j];
                    }
                    if( optypeb==1 )
                    {
                        be[i,j] = b[ib+j,jb+i];
                    }
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    vc = 0.0;
                    for(i_=0; i_<=k-1;i_++)
                    {
                        vc += ae[i,i_]*be[i_,j];
                    }
                    vc = alpha*vc;
                    if( (double)(beta)!=(double)(0) )
                    {
                        vc = vc+beta*c[ic+i,jc+j];
                    }
                    c[ic+i,jc+j] = vc;
                }
            }
        }


    }
    public class testbasestatunit
    {
        public static bool testbasestat(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool s1errors = new bool();
            bool covcorrerrors = new bool();
            double threshold = 0;
            int i = 0;
            int j = 0;
            int n = 0;
            int kx = 0;
            int ky = 0;
            int ctype = 0;
            int cidxx = 0;
            int cidxy = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[,] mx = new double[0,0];
            double[,] my = new double[0,0];
            double[,] cc = new double[0,0];
            double[,] cp = new double[0,0];
            double[,] cs = new double[0,0];
            double mean = 0;
            double variance = 0;
            double skewness = 0;
            double kurtosis = 0;
            double adev = 0;
            double median = 0;
            double pv = 0;
            double v = 0;
            int i_ = 0;

            
            //
            // Primary settings
            //
            waserrors = false;
            s1errors = false;
            covcorrerrors = false;
            threshold = 1000*math.machineepsilon;
            
            //
            // * prepare X and Y - two test samples
            // * test 1-sample coefficients
            //
            n = 10;
            x = new double[n];
            for(i=0; i<=n-1; i++)
            {
                x[i] = math.sqr(i);
            }
            basestat.samplemoments(x, n, ref mean, ref variance, ref skewness, ref kurtosis);
            s1errors = s1errors | (double)(Math.Abs(mean-28.5))>(double)(0.001);
            s1errors = s1errors | (double)(Math.Abs(variance-801.1667))>(double)(0.001);
            s1errors = s1errors | (double)(Math.Abs(skewness-0.5751))>(double)(0.001);
            s1errors = s1errors | (double)(Math.Abs(kurtosis+1.2666))>(double)(0.001);
            basestat.sampleadev(x, n, ref adev);
            s1errors = s1errors | (double)(Math.Abs(adev-23.2000))>(double)(0.001);
            basestat.samplemedian(x, n, ref median);
            s1errors = s1errors | (double)(Math.Abs(median-0.5*(16+25)))>(double)(0.001);
            for(i=0; i<=n-1; i++)
            {
                basestat.samplepercentile(x, n, (double)i/(double)(n-1), ref pv);
                s1errors = s1errors | (double)(Math.Abs(pv-x[i]))>(double)(0.001);
            }
            basestat.samplepercentile(x, n, 0.5, ref pv);
            s1errors = s1errors | (double)(Math.Abs(pv-0.5*(16+25)))>(double)(0.001);
            
            //
            // test covariance/correlation:
            // * 2-sample coefficients
            //
            // We generate random matrices MX and MY
            //
            n = 10;
            x = new double[n];
            y = new double[n];
            for(i=0; i<=n-1; i++)
            {
                x[i] = math.sqr(i);
                y[i] = i;
            }
            covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.pearsoncorr2(x, y, n)-0.9627))>(double)(0.0001);
            covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.spearmancorr2(x, y, n)-1.0000))>(double)(0.0001);
            covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.cov2(x, y, n)-82.5000))>(double)(0.0001);
            for(i=0; i<=n-1; i++)
            {
                x[i] = math.sqr(i-0.5*n);
                y[i] = i;
            }
            covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.pearsoncorr2(x, y, n)+0.3676))>(double)(0.0001);
            covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.spearmancorr2(x, y, n)+0.2761))>(double)(0.0001);
            covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.cov2(x, y, n)+9.1667))>(double)(0.0001);
            
            //
            // test covariance/correlation:
            // * matrix covariance/correlation
            // * matrix cross-covariance/cross-correlation
            //
            // We generate random matrices MX and MY which contain KX (KY)
            // columns, all except one are random, one of them is constant.
            // We test that function (a) do not crash on constant column,
            // and (b) return variances/correlations that are exactly zero
            // for this column.
            //
            // CType control variable controls type of constant: 0 - no constant
            // column, 1 - zero column, 2 - nonzero column with value whose
            // binary representation contains many non-zero bits. Using such
            // type of constant column we are able to ensure than even in the
            // presense of roundoff error functions correctly detect constant
            // columns.
            //
            for(n=0; n<=10; n++)
            {
                if( n>0 )
                {
                    x = new double[n];
                    y = new double[n];
                }
                for(ctype=0; ctype<=2; ctype++)
                {
                    for(kx=1; kx<=10; kx++)
                    {
                        for(ky=1; ky<=10; ky++)
                        {
                            
                            //
                            // Fill matrices, add constant column (when CType=1 or =2)
                            //
                            cidxx = -1;
                            cidxy = -1;
                            if( n>0 )
                            {
                                mx = new double[n, kx];
                                my = new double[n, ky];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=kx-1; j++)
                                    {
                                        mx[i,j] = 2*math.randomreal()-1;
                                    }
                                    for(j=0; j<=ky-1; j++)
                                    {
                                        my[i,j] = 2*math.randomreal()-1;
                                    }
                                }
                                if( ctype==1 )
                                {
                                    cidxx = math.randominteger(kx);
                                    cidxy = math.randominteger(ky);
                                    for(i=0; i<=n-1; i++)
                                    {
                                        mx[i,cidxx] = 0.0;
                                        my[i,cidxy] = 0.0;
                                    }
                                }
                                if( ctype==2 )
                                {
                                    cidxx = math.randominteger(kx);
                                    cidxy = math.randominteger(ky);
                                    v = Math.Sqrt((double)(math.randominteger(kx)+1)/(double)kx);
                                    for(i=0; i<=n-1; i++)
                                    {
                                        mx[i,cidxx] = v;
                                        my[i,cidxy] = v;
                                    }
                                }
                            }
                            
                            //
                            // test covariance/correlation matrix using
                            // 2-sample functions as reference point.
                            //
                            // We also test that coefficients for constant variables
                            // are exactly zero.
                            //
                            basestat.covm(mx, n, kx, ref cc);
                            basestat.pearsoncorrm(mx, n, kx, ref cp);
                            basestat.spearmancorrm(mx, n, kx, ref cs);
                            for(i=0; i<=kx-1; i++)
                            {
                                for(j=0; j<=kx-1; j++)
                                {
                                    if( n>0 )
                                    {
                                        for(i_=0; i_<=n-1;i_++)
                                        {
                                            x[i_] = mx[i_,i];
                                        }
                                        for(i_=0; i_<=n-1;i_++)
                                        {
                                            y[i_] = mx[i_,j];
                                        }
                                    }
                                    covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.cov2(x, y, n)-cc[i,j]))>(double)(threshold);
                                    covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.pearsoncorr2(x, y, n)-cp[i,j]))>(double)(threshold);
                                    covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.spearmancorr2(x, y, n)-cs[i,j]))>(double)(threshold);
                                }
                            }
                            if( ctype!=0 & n>0 )
                            {
                                for(i=0; i<=kx-1; i++)
                                {
                                    covcorrerrors = covcorrerrors | (double)(cc[i,cidxx])!=(double)(0);
                                    covcorrerrors = covcorrerrors | (double)(cc[cidxx,i])!=(double)(0);
                                    covcorrerrors = covcorrerrors | (double)(cp[i,cidxx])!=(double)(0);
                                    covcorrerrors = covcorrerrors | (double)(cp[cidxx,i])!=(double)(0);
                                    covcorrerrors = covcorrerrors | (double)(cs[i,cidxx])!=(double)(0);
                                    covcorrerrors = covcorrerrors | (double)(cs[cidxx,i])!=(double)(0);
                                }
                            }
                            
                            //
                            // test cross-covariance/cross-correlation matrix using
                            // 2-sample functions as reference point.
                            //
                            // We also test that coefficients for constant variables
                            // are exactly zero.
                            //
                            basestat.covm2(mx, my, n, kx, ky, ref cc);
                            basestat.pearsoncorrm2(mx, my, n, kx, ky, ref cp);
                            basestat.spearmancorrm2(mx, my, n, kx, ky, ref cs);
                            for(i=0; i<=kx-1; i++)
                            {
                                for(j=0; j<=ky-1; j++)
                                {
                                    if( n>0 )
                                    {
                                        for(i_=0; i_<=n-1;i_++)
                                        {
                                            x[i_] = mx[i_,i];
                                        }
                                        for(i_=0; i_<=n-1;i_++)
                                        {
                                            y[i_] = my[i_,j];
                                        }
                                    }
                                    covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.cov2(x, y, n)-cc[i,j]))>(double)(threshold);
                                    covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.pearsoncorr2(x, y, n)-cp[i,j]))>(double)(threshold);
                                    covcorrerrors = covcorrerrors | (double)(Math.Abs(basestat.spearmancorr2(x, y, n)-cs[i,j]))>(double)(threshold);
                                }
                            }
                            if( ctype!=0 & n>0 )
                            {
                                for(i=0; i<=kx-1; i++)
                                {
                                    covcorrerrors = covcorrerrors | (double)(cc[i,cidxy])!=(double)(0);
                                    covcorrerrors = covcorrerrors | (double)(cp[i,cidxy])!=(double)(0);
                                    covcorrerrors = covcorrerrors | (double)(cs[i,cidxy])!=(double)(0);
                                }
                                for(j=0; j<=ky-1; j++)
                                {
                                    covcorrerrors = covcorrerrors | (double)(cc[cidxx,j])!=(double)(0);
                                    covcorrerrors = covcorrerrors | (double)(cp[cidxx,j])!=(double)(0);
                                    covcorrerrors = covcorrerrors | (double)(cs[cidxx,j])!=(double)(0);
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // Final report
            //
            waserrors = s1errors | covcorrerrors;
            if( !silent )
            {
                System.Console.Write("DESC.STAT TEST");
                System.Console.WriteLine();
                System.Console.Write("TOTAL RESULTS:                           ");
                if( !waserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* 1-SAMPLE FUNCTIONALITY:                ");
                if( !s1errors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* CORRELATION/COVARIATION:               ");
                if( !covcorrerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


    }
    public class testbdssunit
    {
        /*************************************************************************
        Testing BDSS operations
        *************************************************************************/
        public static bool testbdss(bool silent)
        {
            bool result = new bool();
            int n = 0;
            int i = 0;
            int j = 0;
            int pass = 0;
            int passcount = 0;
            int maxn = 0;
            int maxnq = 0;
            double[] a = new double[0];
            double[] a0 = new double[0];
            double[] at = new double[0];
            double[,] p = new double[0,0];
            double[] thresholds = new double[0];
            int ni = 0;
            int[] c = new int[0];
            int[] p1 = new int[0];
            int[] p2 = new int[0];
            int[] ties = new int[0];
            int[] pt1 = new int[0];
            int[] pt2 = new int[0];
            int tiecount = 0;
            int c1 = 0;
            int c0 = 0;
            int nc = 0;
            double[] tmp = new double[0];
            double[] sortrbuf = new double[0];
            double[] sortrbuf2 = new double[0];
            int[] sortibuf = new int[0];
            double pal = 0;
            double pbl = 0;
            double par = 0;
            double pbr = 0;
            double cve = 0;
            double cvr = 0;
            int info = 0;
            double threshold = 0;
            int[] tiebuf = new int[0];
            int[] cntbuf = new int[0];
            double rms = 0;
            double cvrms = 0;
            bool waserrors = new bool();
            bool tieserrors = new bool();
            bool split2errors = new bool();
            bool optimalsplitkerrors = new bool();
            bool splitkerrors = new bool();

            waserrors = false;
            tieserrors = false;
            split2errors = false;
            splitkerrors = false;
            optimalsplitkerrors = false;
            maxn = 100;
            maxnq = 49;
            passcount = 10;
            
            //
            // Test ties
            //
            for(n=1; n<=maxn; n++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // untied data, test DSTie
                    //
                    unset1di(ref p1);
                    unset1di(ref p2);
                    unset1di(ref pt1);
                    unset1di(ref pt2);
                    a = new double[n-1+1];
                    a0 = new double[n-1+1];
                    at = new double[n-1+1];
                    tmp = new double[n-1+1];
                    a[0] = 2*math.randomreal()-1;
                    tmp[0] = math.randomreal();
                    for(i=1; i<=n-1; i++)
                    {
                        
                        //
                        // A is randomly permuted
                        //
                        a[i] = a[i-1]+0.1*math.randomreal()+0.1;
                        tmp[i] = math.randomreal();
                    }
                    tsort.tagsortfastr(ref tmp, ref a, ref sortrbuf, ref sortrbuf2, n);
                    for(i=0; i<=n-1; i++)
                    {
                        a0[i] = a[i];
                        at[i] = a[i];
                    }
                    bdss.dstie(ref a0, n, ref ties, ref tiecount, ref p1, ref p2);
                    tsort.tagsort(ref at, n, ref pt1, ref pt2);
                    for(i=0; i<=n-1; i++)
                    {
                        tieserrors = tieserrors | p1[i]!=pt1[i];
                        tieserrors = tieserrors | p2[i]!=pt2[i];
                    }
                    tieserrors = tieserrors | tiecount!=n;
                    if( tiecount==n )
                    {
                        for(i=0; i<=n; i++)
                        {
                            tieserrors = tieserrors | ties[i]!=i;
                        }
                    }
                    
                    //
                    // tied data, test DSTie
                    //
                    unset1di(ref p1);
                    unset1di(ref p2);
                    unset1di(ref pt1);
                    unset1di(ref pt2);
                    a = new double[n-1+1];
                    a0 = new double[n-1+1];
                    at = new double[n-1+1];
                    c1 = 0;
                    c0 = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = math.randominteger(2);
                        if( (double)(a[i])==(double)(0) )
                        {
                            c0 = c0+1;
                        }
                        else
                        {
                            c1 = c1+1;
                        }
                        a0[i] = a[i];
                        at[i] = a[i];
                    }
                    bdss.dstie(ref a0, n, ref ties, ref tiecount, ref p1, ref p2);
                    tsort.tagsort(ref at, n, ref pt1, ref pt2);
                    for(i=0; i<=n-1; i++)
                    {
                        tieserrors = tieserrors | p1[i]!=pt1[i];
                        tieserrors = tieserrors | p2[i]!=pt2[i];
                    }
                    if( c0==0 | c1==0 )
                    {
                        tieserrors = tieserrors | tiecount!=1;
                        if( tiecount==1 )
                        {
                            tieserrors = tieserrors | ties[0]!=0;
                            tieserrors = tieserrors | ties[1]!=n;
                        }
                    }
                    else
                    {
                        tieserrors = tieserrors | tiecount!=2;
                        if( tiecount==2 )
                        {
                            tieserrors = tieserrors | ties[0]!=0;
                            tieserrors = tieserrors | ties[1]!=c0;
                            tieserrors = tieserrors | ties[2]!=n;
                        }
                    }
                }
            }
            
            //
            // split-2
            //
            
            //
            // General tests for different N's
            //
            for(n=1; n<=maxn; n++)
            {
                a = new double[n-1+1];
                c = new int[n-1+1];
                
                //
                // one-tie test
                //
                if( n%2==0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = n;
                        c[i] = i%2;
                    }
                    bdss.dsoptimalsplit2(a, c, n, ref info, ref threshold, ref pal, ref pbl, ref par, ref pbr, ref cve);
                    if( info!=-3 )
                    {
                        split2errors = true;
                        continue;
                    }
                }
                
                //
                // two-tie test
                //
                
                //
                // test #1
                //
                if( n>1 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = i/((n+1)/2);
                        c[i] = i/((n+1)/2);
                    }
                    bdss.dsoptimalsplit2(a, c, n, ref info, ref threshold, ref pal, ref pbl, ref par, ref pbr, ref cve);
                    if( info!=1 )
                    {
                        split2errors = true;
                        continue;
                    }
                    split2errors = split2errors | (double)(Math.Abs(threshold-0.5))>(double)(100*math.machineepsilon);
                    split2errors = split2errors | (double)(Math.Abs(pal-1))>(double)(100*math.machineepsilon);
                    split2errors = split2errors | (double)(Math.Abs(pbl-0))>(double)(100*math.machineepsilon);
                    split2errors = split2errors | (double)(Math.Abs(par-0))>(double)(100*math.machineepsilon);
                    split2errors = split2errors | (double)(Math.Abs(pbr-1))>(double)(100*math.machineepsilon);
                }
            }
            
            //
            // Special "CREDIT"-test (transparency coefficient)
            //
            n = 110;
            a = new double[n-1+1];
            c = new int[n-1+1];
            a[0] = 0.000;
            c[0] = 0;
            a[1] = 0.000;
            c[1] = 0;
            a[2] = 0.000;
            c[2] = 0;
            a[3] = 0.000;
            c[3] = 0;
            a[4] = 0.000;
            c[4] = 0;
            a[5] = 0.000;
            c[5] = 0;
            a[6] = 0.000;
            c[6] = 0;
            a[7] = 0.000;
            c[7] = 1;
            a[8] = 0.000;
            c[8] = 0;
            a[9] = 0.000;
            c[9] = 1;
            a[10] = 0.000;
            c[10] = 0;
            a[11] = 0.000;
            c[11] = 0;
            a[12] = 0.000;
            c[12] = 0;
            a[13] = 0.000;
            c[13] = 0;
            a[14] = 0.000;
            c[14] = 0;
            a[15] = 0.000;
            c[15] = 0;
            a[16] = 0.000;
            c[16] = 0;
            a[17] = 0.000;
            c[17] = 0;
            a[18] = 0.000;
            c[18] = 0;
            a[19] = 0.000;
            c[19] = 0;
            a[20] = 0.000;
            c[20] = 0;
            a[21] = 0.000;
            c[21] = 0;
            a[22] = 0.000;
            c[22] = 1;
            a[23] = 0.000;
            c[23] = 0;
            a[24] = 0.000;
            c[24] = 0;
            a[25] = 0.000;
            c[25] = 0;
            a[26] = 0.000;
            c[26] = 0;
            a[27] = 0.000;
            c[27] = 1;
            a[28] = 0.000;
            c[28] = 0;
            a[29] = 0.000;
            c[29] = 1;
            a[30] = 0.000;
            c[30] = 0;
            a[31] = 0.000;
            c[31] = 1;
            a[32] = 0.000;
            c[32] = 0;
            a[33] = 0.000;
            c[33] = 1;
            a[34] = 0.000;
            c[34] = 0;
            a[35] = 0.030;
            c[35] = 0;
            a[36] = 0.030;
            c[36] = 0;
            a[37] = 0.050;
            c[37] = 0;
            a[38] = 0.070;
            c[38] = 1;
            a[39] = 0.110;
            c[39] = 0;
            a[40] = 0.110;
            c[40] = 1;
            a[41] = 0.120;
            c[41] = 0;
            a[42] = 0.130;
            c[42] = 0;
            a[43] = 0.140;
            c[43] = 0;
            a[44] = 0.140;
            c[44] = 0;
            a[45] = 0.140;
            c[45] = 0;
            a[46] = 0.150;
            c[46] = 0;
            a[47] = 0.150;
            c[47] = 0;
            a[48] = 0.170;
            c[48] = 0;
            a[49] = 0.190;
            c[49] = 1;
            a[50] = 0.200;
            c[50] = 0;
            a[51] = 0.200;
            c[51] = 0;
            a[52] = 0.250;
            c[52] = 0;
            a[53] = 0.250;
            c[53] = 0;
            a[54] = 0.260;
            c[54] = 0;
            a[55] = 0.270;
            c[55] = 0;
            a[56] = 0.280;
            c[56] = 0;
            a[57] = 0.310;
            c[57] = 0;
            a[58] = 0.310;
            c[58] = 0;
            a[59] = 0.330;
            c[59] = 0;
            a[60] = 0.330;
            c[60] = 0;
            a[61] = 0.340;
            c[61] = 0;
            a[62] = 0.340;
            c[62] = 0;
            a[63] = 0.370;
            c[63] = 0;
            a[64] = 0.380;
            c[64] = 1;
            a[65] = 0.380;
            c[65] = 0;
            a[66] = 0.410;
            c[66] = 0;
            a[67] = 0.460;
            c[67] = 0;
            a[68] = 0.520;
            c[68] = 0;
            a[69] = 0.530;
            c[69] = 0;
            a[70] = 0.540;
            c[70] = 0;
            a[71] = 0.560;
            c[71] = 0;
            a[72] = 0.560;
            c[72] = 0;
            a[73] = 0.570;
            c[73] = 0;
            a[74] = 0.600;
            c[74] = 0;
            a[75] = 0.600;
            c[75] = 0;
            a[76] = 0.620;
            c[76] = 0;
            a[77] = 0.650;
            c[77] = 0;
            a[78] = 0.660;
            c[78] = 0;
            a[79] = 0.680;
            c[79] = 0;
            a[80] = 0.700;
            c[80] = 0;
            a[81] = 0.750;
            c[81] = 0;
            a[82] = 0.770;
            c[82] = 0;
            a[83] = 0.770;
            c[83] = 0;
            a[84] = 0.770;
            c[84] = 0;
            a[85] = 0.790;
            c[85] = 0;
            a[86] = 0.810;
            c[86] = 0;
            a[87] = 0.840;
            c[87] = 0;
            a[88] = 0.860;
            c[88] = 0;
            a[89] = 0.870;
            c[89] = 0;
            a[90] = 0.890;
            c[90] = 0;
            a[91] = 0.900;
            c[91] = 1;
            a[92] = 0.900;
            c[92] = 0;
            a[93] = 0.910;
            c[93] = 0;
            a[94] = 0.940;
            c[94] = 0;
            a[95] = 0.950;
            c[95] = 0;
            a[96] = 0.952;
            c[96] = 0;
            a[97] = 0.970;
            c[97] = 0;
            a[98] = 0.970;
            c[98] = 0;
            a[99] = 0.980;
            c[99] = 0;
            a[100] = 1.000;
            c[100] = 0;
            a[101] = 1.000;
            c[101] = 0;
            a[102] = 1.000;
            c[102] = 0;
            a[103] = 1.000;
            c[103] = 0;
            a[104] = 1.000;
            c[104] = 0;
            a[105] = 1.020;
            c[105] = 0;
            a[106] = 1.090;
            c[106] = 0;
            a[107] = 1.130;
            c[107] = 0;
            a[108] = 1.840;
            c[108] = 0;
            a[109] = 2.470;
            c[109] = 0;
            bdss.dsoptimalsplit2(a, c, n, ref info, ref threshold, ref pal, ref pbl, ref par, ref pbr, ref cve);
            if( info!=1 )
            {
                split2errors = true;
            }
            else
            {
                split2errors = split2errors | (double)(Math.Abs(threshold-0.195))>(double)(100*math.machineepsilon);
                split2errors = split2errors | (double)(Math.Abs(pal-0.80))>(double)(0.02);
                split2errors = split2errors | (double)(Math.Abs(pbl-0.20))>(double)(0.02);
                split2errors = split2errors | (double)(Math.Abs(par-0.97))>(double)(0.02);
                split2errors = split2errors | (double)(Math.Abs(pbr-0.03))>(double)(0.02);
            }
            
            //
            // split-2 fast
            //
            
            //
            // General tests for different N's
            //
            for(n=1; n<=maxn; n++)
            {
                a = new double[n-1+1];
                c = new int[n-1+1];
                tiebuf = new int[n+1];
                cntbuf = new int[3+1];
                
                //
                // one-tie test
                //
                if( n%2==0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = n;
                        c[i] = i%2;
                    }
                    bdss.dsoptimalsplit2fast(ref a, ref c, ref tiebuf, ref cntbuf, ref sortrbuf, ref sortibuf, n, 2, 0.00, ref info, ref threshold, ref rms, ref cvrms);
                    if( info!=-3 )
                    {
                        split2errors = true;
                        continue;
                    }
                }
                
                //
                // two-tie test
                //
                
                //
                // test #1
                //
                if( n>1 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = i/((n+1)/2);
                        c[i] = i/((n+1)/2);
                    }
                    bdss.dsoptimalsplit2fast(ref a, ref c, ref tiebuf, ref cntbuf, ref sortrbuf, ref sortibuf, n, 2, 0.00, ref info, ref threshold, ref rms, ref cvrms);
                    if( info!=1 )
                    {
                        split2errors = true;
                        continue;
                    }
                    split2errors = split2errors | (double)(Math.Abs(threshold-0.5))>(double)(100*math.machineepsilon);
                    split2errors = split2errors | (double)(Math.Abs(rms-0))>(double)(100*math.machineepsilon);
                    if( n==2 )
                    {
                        split2errors = split2errors | (double)(Math.Abs(cvrms-0.5))>(double)(100*math.machineepsilon);
                    }
                    else
                    {
                        if( n==3 )
                        {
                            split2errors = split2errors | (double)(Math.Abs(cvrms-Math.Sqrt((2*0+2*0+2*0.25)/6)))>(double)(100*math.machineepsilon);
                        }
                        else
                        {
                            split2errors = split2errors | (double)(Math.Abs(cvrms))>(double)(100*math.machineepsilon);
                        }
                    }
                }
            }
            
            //
            // special tests
            //
            n = 10;
            a = new double[n-1+1];
            c = new int[n-1+1];
            tiebuf = new int[n+1];
            cntbuf = new int[2*3-1+1];
            for(i=0; i<=n-1; i++)
            {
                a[i] = i;
                if( i<=n-3 )
                {
                    c[i] = 0;
                }
                else
                {
                    c[i] = i-(n-3);
                }
            }
            bdss.dsoptimalsplit2fast(ref a, ref c, ref tiebuf, ref cntbuf, ref sortrbuf, ref sortibuf, n, 3, 0.00, ref info, ref threshold, ref rms, ref cvrms);
            if( info!=1 )
            {
                split2errors = true;
            }
            else
            {
                split2errors = split2errors | (double)(Math.Abs(threshold-(n-2.5)))>(double)(100*math.machineepsilon);
                split2errors = split2errors | (double)(Math.Abs(rms-Math.Sqrt((0.25+0.25+0.25+0.25)/(3*n))))>(double)(100*math.machineepsilon);
                split2errors = split2errors | (double)(Math.Abs(cvrms-Math.Sqrt((double)(1+1+1+1)/(double)(3*n))))>(double)(100*math.machineepsilon);
            }
            
            //
            // Optimal split-K
            //
            
            //
            // General tests for different N's
            //
            for(n=1; n<=maxnq; n++)
            {
                a = new double[n-1+1];
                c = new int[n-1+1];
                
                //
                // one-tie test
                //
                if( n%2==0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = n;
                        c[i] = i%2;
                    }
                    bdss.dsoptimalsplitk(a, c, n, 2, 2+math.randominteger(5), ref info, ref thresholds, ref ni, ref cve);
                    if( info!=-3 )
                    {
                        optimalsplitkerrors = true;
                        continue;
                    }
                }
                
                //
                // two-tie test
                //
                
                //
                // test #1
                //
                if( n>1 )
                {
                    c0 = 0;
                    c1 = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = i/((n+1)/2);
                        c[i] = i/((n+1)/2);
                        if( c[i]==0 )
                        {
                            c0 = c0+1;
                        }
                        if( c[i]==1 )
                        {
                            c1 = c1+1;
                        }
                    }
                    bdss.dsoptimalsplitk(a, c, n, 2, 2+math.randominteger(5), ref info, ref thresholds, ref ni, ref cve);
                    if( info!=1 )
                    {
                        optimalsplitkerrors = true;
                        continue;
                    }
                    optimalsplitkerrors = optimalsplitkerrors | ni!=2;
                    optimalsplitkerrors = optimalsplitkerrors | (double)(Math.Abs(thresholds[0]-0.5))>(double)(100*math.machineepsilon);
                    optimalsplitkerrors = optimalsplitkerrors | (double)(Math.Abs(cve-(-(c0*Math.Log((double)c0/(double)(c0+1)))-c1*Math.Log((double)c1/(double)(c1+1)))))>(double)(100*math.machineepsilon);
                }
                
                //
                // test #2
                //
                if( n>2 )
                {
                    c0 = 1+math.randominteger(n-1);
                    c1 = n-c0;
                    for(i=0; i<=n-1; i++)
                    {
                        if( i<c0 )
                        {
                            a[i] = 0;
                            c[i] = 0;
                        }
                        else
                        {
                            a[i] = 1;
                            c[i] = 1;
                        }
                    }
                    bdss.dsoptimalsplitk(a, c, n, 2, 2+math.randominteger(5), ref info, ref thresholds, ref ni, ref cve);
                    if( info!=1 )
                    {
                        optimalsplitkerrors = true;
                        continue;
                    }
                    optimalsplitkerrors = optimalsplitkerrors | ni!=2;
                    optimalsplitkerrors = optimalsplitkerrors | (double)(Math.Abs(thresholds[0]-0.5))>(double)(100*math.machineepsilon);
                    optimalsplitkerrors = optimalsplitkerrors | (double)(Math.Abs(cve-(-(c0*Math.Log((double)c0/(double)(c0+1)))-c1*Math.Log((double)c1/(double)(c1+1)))))>(double)(100*math.machineepsilon);
                }
                
                //
                // multi-tie test
                //
                if( n>=16 )
                {
                    
                    //
                    // Multi-tie test.
                    //
                    // First NC-1 ties have C0 entries, remaining NC-th tie
                    // have C1 entries.
                    //
                    nc = (int)Math.Round(Math.Sqrt(n));
                    c0 = n/nc;
                    c1 = n-c0*(nc-1);
                    for(i=0; i<=nc-2; i++)
                    {
                        for(j=c0*i; j<=c0*(i+1)-1; j++)
                        {
                            a[j] = j;
                            c[j] = i;
                        }
                    }
                    for(j=c0*(nc-1); j<=n-1; j++)
                    {
                        a[j] = j;
                        c[j] = nc-1;
                    }
                    bdss.dsoptimalsplitk(a, c, n, nc, nc+math.randominteger(nc), ref info, ref thresholds, ref ni, ref cve);
                    if( info!=1 )
                    {
                        optimalsplitkerrors = true;
                        continue;
                    }
                    optimalsplitkerrors = optimalsplitkerrors | ni!=nc;
                    if( ni==nc )
                    {
                        for(i=0; i<=nc-2; i++)
                        {
                            optimalsplitkerrors = optimalsplitkerrors | (double)(Math.Abs(thresholds[i]-(c0*(i+1)-1+0.5)))>(double)(100*math.machineepsilon);
                        }
                        cvr = -((nc-1)*c0*Math.Log((double)c0/(double)(c0+nc-1))+c1*Math.Log((double)c1/(double)(c1+nc-1)));
                        optimalsplitkerrors = optimalsplitkerrors | (double)(Math.Abs(cve-cvr))>(double)(100*math.machineepsilon);
                    }
                }
            }
            
            //
            // Non-optimal split-K
            //
            
            //
            // General tests for different N's
            //
            for(n=1; n<=maxnq; n++)
            {
                a = new double[n-1+1];
                c = new int[n-1+1];
                
                //
                // one-tie test
                //
                if( n%2==0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = pass;
                        c[i] = i%2;
                    }
                    bdss.dssplitk(a, c, n, 2, 2+math.randominteger(5), ref info, ref thresholds, ref ni, ref cve);
                    if( info!=-3 )
                    {
                        splitkerrors = true;
                        continue;
                    }
                }
                
                //
                // two-tie test
                //
                
                //
                // test #1
                //
                if( n>1 )
                {
                    c0 = 0;
                    c1 = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = i/((n+1)/2);
                        c[i] = i/((n+1)/2);
                        if( c[i]==0 )
                        {
                            c0 = c0+1;
                        }
                        if( c[i]==1 )
                        {
                            c1 = c1+1;
                        }
                    }
                    bdss.dssplitk(a, c, n, 2, 2+math.randominteger(5), ref info, ref thresholds, ref ni, ref cve);
                    if( info!=1 )
                    {
                        splitkerrors = true;
                        continue;
                    }
                    splitkerrors = splitkerrors | ni!=2;
                    if( ni==2 )
                    {
                        splitkerrors = splitkerrors | (double)(Math.Abs(thresholds[0]-0.5))>(double)(100*math.machineepsilon);
                        splitkerrors = splitkerrors | (double)(Math.Abs(cve-(-(c0*Math.Log((double)c0/(double)(c0+1)))-c1*Math.Log((double)c1/(double)(c1+1)))))>(double)(100*math.machineepsilon);
                    }
                }
                
                //
                // test #2
                //
                if( n>2 )
                {
                    c0 = 1+math.randominteger(n-1);
                    c1 = n-c0;
                    for(i=0; i<=n-1; i++)
                    {
                        if( i<c0 )
                        {
                            a[i] = 0;
                            c[i] = 0;
                        }
                        else
                        {
                            a[i] = 1;
                            c[i] = 1;
                        }
                    }
                    bdss.dssplitk(a, c, n, 2, 2+math.randominteger(5), ref info, ref thresholds, ref ni, ref cve);
                    if( info!=1 )
                    {
                        splitkerrors = true;
                        continue;
                    }
                    splitkerrors = splitkerrors | ni!=2;
                    if( ni==2 )
                    {
                        splitkerrors = splitkerrors | (double)(Math.Abs(thresholds[0]-0.5))>(double)(100*math.machineepsilon);
                        splitkerrors = splitkerrors | (double)(Math.Abs(cve-(-(c0*Math.Log((double)c0/(double)(c0+1)))-c1*Math.Log((double)c1/(double)(c1+1)))))>(double)(100*math.machineepsilon);
                    }
                }
                
                //
                // multi-tie test
                //
                for(c0=4; c0<=n; c0++)
                {
                    if( (n%c0==0 & n/c0<=c0) & n/c0>1 )
                    {
                        nc = n/c0;
                        for(i=0; i<=nc-1; i++)
                        {
                            for(j=c0*i; j<=c0*(i+1)-1; j++)
                            {
                                a[j] = j;
                                c[j] = i;
                            }
                        }
                        bdss.dssplitk(a, c, n, nc, nc+math.randominteger(nc), ref info, ref thresholds, ref ni, ref cve);
                        if( info!=1 )
                        {
                            splitkerrors = true;
                            continue;
                        }
                        splitkerrors = splitkerrors | ni!=nc;
                        if( ni==nc )
                        {
                            for(i=0; i<=nc-2; i++)
                            {
                                splitkerrors = splitkerrors | (double)(Math.Abs(thresholds[i]-(c0*(i+1)-1+0.5)))>(double)(100*math.machineepsilon);
                            }
                            cvr = -(nc*c0*Math.Log((double)c0/(double)(c0+nc-1)));
                            splitkerrors = splitkerrors | (double)(Math.Abs(cve-cvr))>(double)(100*math.machineepsilon);
                        }
                    }
                }
            }
            
            //
            // report
            //
            waserrors = ((tieserrors | split2errors) | optimalsplitkerrors) | splitkerrors;
            if( !silent )
            {
                System.Console.Write("TESTING BASIC DATASET SUBROUTINES");
                System.Console.WriteLine();
                System.Console.Write("TIES:                               ");
                if( !tieserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("SPLIT-2:                            ");
                if( !split2errors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("OPTIMAL SPLIT-K:                    ");
                if( !optimalsplitkerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("SPLIT-K:                            ");
                if( !splitkerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Unsets 2D array.
        *************************************************************************/
        private static void unset2d(ref complex[,] a)
        {
            a = new complex[0+1, 0+1];
            a[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 1D array.
        *************************************************************************/
        private static void unset1d(ref double[] a)
        {
            a = new double[0+1];
            a[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 1D array.
        *************************************************************************/
        private static void unset1di(ref int[] a)
        {
            a = new int[0+1];
            a[0] = math.randominteger(3)-1;
        }


        private static void testsortresults(double[] asorted,
            int[] p1,
            int[] p2,
            double[] aoriginal,
            int n,
            ref bool waserrors)
        {
            int i = 0;
            double[] a2 = new double[0];
            double t = 0;
            int[] f = new int[0];

            a2 = new double[n-1+1];
            f = new int[n-1+1];
            
            //
            // is set ordered?
            //
            for(i=0; i<=n-2; i++)
            {
                waserrors = waserrors | (double)(asorted[i])>(double)(asorted[i+1]);
            }
            
            //
            // P1 correctness
            //
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors | (double)(asorted[i])!=(double)(aoriginal[p1[i]]);
            }
            for(i=0; i<=n-1; i++)
            {
                f[i] = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                f[p1[i]] = f[p1[i]]+1;
            }
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors | f[i]!=1;
            }
            
            //
            // P2 correctness
            //
            for(i=0; i<=n-1; i++)
            {
                a2[i] = aoriginal[i];
            }
            for(i=0; i<=n-1; i++)
            {
                if( p2[i]!=i )
                {
                    t = a2[i];
                    a2[i] = a2[p2[i]];
                    a2[p2[i]] = t;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors | (double)(asorted[i])!=(double)(a2[i]);
            }
        }


    }
    public class testdforestunit
    {
        public static bool testdforest(bool silent)
        {
            bool result = new bool();
            int ncmax = 0;
            int nvmax = 0;
            int passcount = 0;
            int nvars = 0;
            int nclasses = 0;
            bool waserrors = new bool();
            bool basicerrors = new bool();
            bool procerrors = new bool();

            
            //
            // Primary settings
            //
            nvmax = 4;
            ncmax = 3;
            passcount = 10;
            basicerrors = false;
            procerrors = false;
            waserrors = false;
            
            //
            // Tests
            //
            testprocessing(ref procerrors);
            for(nvars=1; nvars<=nvmax; nvars++)
            {
                for(nclasses=1; nclasses<=ncmax; nclasses++)
                {
                    basictest1(nvars, nclasses, passcount, ref basicerrors);
                }
            }
            basictest2(ref basicerrors);
            basictest3(ref basicerrors);
            basictest4(ref basicerrors);
            basictest5(ref basicerrors);
            
            //
            // Final report
            //
            waserrors = basicerrors | procerrors;
            if( !silent )
            {
                System.Console.Write("RANDOM FOREST TEST");
                System.Console.WriteLine();
                System.Console.Write("TOTAL RESULTS:                           ");
                if( !waserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* PROCESSING FUNCTIONS:                  ");
                if( !procerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* BASIC TESTS:                           ");
                if( !basicerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Processing functions test
        *************************************************************************/
        private static void testprocessing(ref bool err)
        {
            int nvars = 0;
            int nclasses = 0;
            int nsample = 0;
            int ntrees = 0;
            int nfeatures = 0;
            int flags = 0;
            dforest.decisionforest df1 = new dforest.decisionforest();
            dforest.decisionforest df2 = new dforest.decisionforest();
            int npoints = 0;
            double[,] xy = new double[0,0];
            int pass = 0;
            int passcount = 0;
            int i = 0;
            int j = 0;
            bool allsame = new bool();
            int info = 0;
            dforest.dfreport rep = new dforest.dfreport();
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[] y1 = new double[0];
            double[] y2 = new double[0];
            double v = 0;

            passcount = 100;
            
            //
            // Main cycle
            //
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // initialize parameters
                //
                nvars = 1+math.randominteger(5);
                nclasses = 1+math.randominteger(3);
                ntrees = 1+math.randominteger(4);
                nfeatures = 1+math.randominteger(nvars);
                flags = 0;
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    flags = flags+2;
                }
                
                //
                // Initialize arrays and data
                //
                npoints = 10+math.randominteger(50);
                nsample = Math.Max(10, math.randominteger(npoints));
                x1 = new double[nvars-1+1];
                x2 = new double[nvars-1+1];
                y1 = new double[nclasses-1+1];
                y2 = new double[nclasses-1+1];
                xy = new double[npoints-1+1, nvars+1];
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=0; j<=nvars-1; j++)
                    {
                        if( j%2==0 )
                        {
                            xy[i,j] = 2*math.randomreal()-1;
                        }
                        else
                        {
                            xy[i,j] = math.randominteger(2);
                        }
                    }
                    if( nclasses==1 )
                    {
                        xy[i,nvars] = 2*math.randomreal()-1;
                    }
                    else
                    {
                        xy[i,nvars] = math.randominteger(nclasses);
                    }
                }
                
                //
                // create forest
                //
                dforest.dfbuildinternal(xy, npoints, nvars, nclasses, ntrees, nsample, nfeatures, flags, ref info, df1, rep);
                if( info<=0 )
                {
                    err = true;
                    return;
                }
                
                //
                // Same inputs leads to same outputs
                //
                for(i=0; i<=nvars-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                for(i=0; i<=nclasses-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = 2*math.randomreal()-1;
                }
                dforest.dfprocess(df1, x1, ref y1);
                dforest.dfprocess(df1, x2, ref y2);
                allsame = true;
                for(i=0; i<=nclasses-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | !allsame;
                
                //
                // Same inputs on original forest leads to same outputs
                // on copy created using DFCopy
                //
                unsetdf(df2);
                dforest.dfcopy(df1, df2);
                for(i=0; i<=nvars-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                for(i=0; i<=nclasses-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = 2*math.randomreal()-1;
                }
                dforest.dfprocess(df1, x1, ref y1);
                dforest.dfprocess(df2, x2, ref y2);
                allsame = true;
                for(i=0; i<=nclasses-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | !allsame;
                
                //
                // Same inputs on original forest leads to same outputs
                // on copy created using DFSerialize
                //
                unsetdf(df2);
                {
                    //
                    // This code passes data structure through serializers
                    // (serializes it to string and loads back)
                    //
                    serializer _local_serializer;
                    string _local_str;
                    
                    _local_serializer = new serializer();
                    _local_serializer.alloc_start();
                    dforest.dfalloc(_local_serializer, df1);
                    _local_serializer.sstart_str();
                    dforest.dfserialize(_local_serializer, df1);
                    _local_serializer.stop();
                    _local_str = _local_serializer.get_string();
                    
                    _local_serializer = new serializer();
                    _local_serializer.ustart_str(_local_str);
                    dforest.dfunserialize(_local_serializer, df2);
                    _local_serializer.stop();
                }
                for(i=0; i<=nvars-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                for(i=0; i<=nclasses-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = 2*math.randomreal()-1;
                }
                dforest.dfprocess(df1, x1, ref y1);
                dforest.dfprocess(df2, x2, ref y2);
                allsame = true;
                for(i=0; i<=nclasses-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | !allsame;
                
                //
                // Normalization properties
                //
                if( nclasses>1 )
                {
                    for(i=0; i<=nvars-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                    }
                    dforest.dfprocess(df1, x1, ref y1);
                    v = 0;
                    for(i=0; i<=nclasses-1; i++)
                    {
                        v = v+y1[i];
                        err = err | (double)(y1[i])<(double)(0);
                    }
                    err = err | (double)(Math.Abs(v-1))>(double)(1000*math.machineepsilon);
                }
            }
        }


        /*************************************************************************
        Basic test:  one-tree forest built using full sample must remember all the
        training cases
        *************************************************************************/
        private static void basictest1(int nvars,
            int nclasses,
            int passcount,
            ref bool err)
        {
            int pass = 0;
            double[,] xy = new double[0,0];
            int npoints = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double s = 0;
            int info = 0;
            dforest.decisionforest df = new dforest.decisionforest();
            double[] x = new double[0];
            double[] y = new double[0];
            dforest.dfreport rep = new dforest.dfreport();
            bool hassame = new bool();
            int i_ = 0;

            if( nclasses==1 )
            {
                
                //
                // only classification tasks
                //
                return;
            }
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // select number of points
                //
                if( pass<=3 & passcount>3 )
                {
                    npoints = pass;
                }
                else
                {
                    npoints = 100+math.randominteger(100);
                }
                
                //
                // Prepare task
                //
                xy = new double[npoints-1+1, nvars+1];
                x = new double[nvars-1+1];
                y = new double[nclasses-1+1];
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=0; j<=nvars-1; j++)
                    {
                        xy[i,j] = 2*math.randomreal()-1;
                    }
                    xy[i,nvars] = math.randominteger(nclasses);
                }
                
                //
                // Test
                //
                dforest.dfbuildinternal(xy, npoints, nvars, nclasses, 1, npoints, 1, 1, ref info, df, rep);
                if( info<=0 )
                {
                    err = true;
                    return;
                }
                for(i=0; i<=npoints-1; i++)
                {
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        x[i_] = xy[i,i_];
                    }
                    dforest.dfprocess(df, x, ref y);
                    s = 0;
                    for(j=0; j<=nclasses-1; j++)
                    {
                        if( (double)(y[j])<(double)(0) )
                        {
                            err = true;
                            return;
                        }
                        s = s+y[j];
                    }
                    if( (double)(Math.Abs(s-1))>(double)(1000*math.machineepsilon) )
                    {
                        err = true;
                        return;
                    }
                    if( (double)(Math.Abs(y[(int)Math.Round(xy[i,nvars])]-1))>(double)(1000*math.machineepsilon) )
                    {
                        
                        //
                        // not an error if there exists such K,J that XY[K,J]=XY[I,J]
                        // (may be we just can't distinguish two tied values).
                        //
                        // definitely error otherwise.
                        //
                        hassame = false;
                        for(k=0; k<=npoints-1; k++)
                        {
                            if( k!=i )
                            {
                                for(j=0; j<=nvars-1; j++)
                                {
                                    if( (double)(xy[k,j])==(double)(xy[i,j]) )
                                    {
                                        hassame = true;
                                    }
                                }
                            }
                        }
                        if( !hassame )
                        {
                            err = true;
                            return;
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Basic test:  tests generalization ability on a simple noisy classification
        task:
        * 0<x<1 - P(class=0)=1
        * 1<x<2 - P(class=0)=2-x
        * 2<x<3 - P(class=0)=0
        *************************************************************************/
        private static void basictest2(ref bool err)
        {
            int pass = 0;
            int passcount = 0;
            double[,] xy = new double[0,0];
            int npoints = 0;
            int ntrees = 0;
            int i = 0;
            int j = 0;
            double s = 0;
            int info = 0;
            dforest.decisionforest df = new dforest.decisionforest();
            double[] x = new double[0];
            double[] y = new double[0];
            dforest.dfreport rep = new dforest.dfreport();

            passcount = 1;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // select npoints and ntrees
                //
                npoints = 3000;
                ntrees = 50;
                
                //
                // Prepare task
                //
                xy = new double[npoints-1+1, 1+1];
                x = new double[0+1];
                y = new double[1+1];
                for(i=0; i<=npoints-1; i++)
                {
                    xy[i,0] = 3*math.randomreal();
                    if( (double)(xy[i,0])<=(double)(1) )
                    {
                        xy[i,1] = 0;
                    }
                    else
                    {
                        if( (double)(xy[i,0])<=(double)(2) )
                        {
                            if( (double)(math.randomreal())<(double)(xy[i,0]-1) )
                            {
                                xy[i,1] = 1;
                            }
                            else
                            {
                                xy[i,1] = 0;
                            }
                        }
                        else
                        {
                            xy[i,1] = 1;
                        }
                    }
                }
                
                //
                // Test
                //
                dforest.dfbuildinternal(xy, npoints, 1, 2, ntrees, (int)Math.Round(0.05*npoints), 1, 0, ref info, df, rep);
                if( info<=0 )
                {
                    err = true;
                    return;
                }
                x[0] = 0.0;
                while( (double)(x[0])<=(double)(3.0) )
                {
                    dforest.dfprocess(df, x, ref y);
                    
                    //
                    // Test for basic properties
                    //
                    s = 0;
                    for(j=0; j<=1; j++)
                    {
                        if( (double)(y[j])<(double)(0) )
                        {
                            err = true;
                            return;
                        }
                        s = s+y[j];
                    }
                    if( (double)(Math.Abs(s-1))>(double)(1000*math.machineepsilon) )
                    {
                        err = true;
                        return;
                    }
                    
                    //
                    // test for good correlation with results
                    //
                    if( (double)(x[0])<(double)(1) )
                    {
                        err = err | (double)(y[0])<(double)(0.8);
                    }
                    if( (double)(x[0])>=(double)(1) & (double)(x[0])<=(double)(2) )
                    {
                        err = err | (double)(Math.Abs(y[1]-(x[0]-1)))>(double)(0.5);
                    }
                    if( (double)(x[0])>(double)(2) )
                    {
                        err = err | (double)(y[1])<(double)(0.8);
                    }
                    x[0] = x[0]+0.01;
                }
            }
        }


        /*************************************************************************
        Basic test:  tests  generalization ability on a simple classification task
        (no noise):
        * |x|<1, |y|<1
        * x^2+y^2<=0.25 - P(class=0)=1
        * x^2+y^2>0.25  - P(class=0)=0
        *************************************************************************/
        private static void basictest3(ref bool err)
        {
            int pass = 0;
            int passcount = 0;
            double[,] xy = new double[0,0];
            int npoints = 0;
            int ntrees = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double s = 0;
            int info = 0;
            dforest.decisionforest df = new dforest.decisionforest();
            double[] x = new double[0];
            double[] y = new double[0];
            dforest.dfreport rep = new dforest.dfreport();
            int testgridsize = 0;
            double r = 0;

            passcount = 1;
            testgridsize = 50;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // select npoints and ntrees
                //
                npoints = 2000;
                ntrees = 100;
                
                //
                // Prepare task
                //
                xy = new double[npoints-1+1, 2+1];
                x = new double[1+1];
                y = new double[1+1];
                for(i=0; i<=npoints-1; i++)
                {
                    xy[i,0] = 2*math.randomreal()-1;
                    xy[i,1] = 2*math.randomreal()-1;
                    if( (double)(math.sqr(xy[i,0])+math.sqr(xy[i,1]))<=(double)(0.25) )
                    {
                        xy[i,2] = 0;
                    }
                    else
                    {
                        xy[i,2] = 1;
                    }
                }
                
                //
                // Test
                //
                dforest.dfbuildinternal(xy, npoints, 2, 2, ntrees, (int)Math.Round(0.1*npoints), 1, 0, ref info, df, rep);
                if( info<=0 )
                {
                    err = true;
                    return;
                }
                for(i=-(testgridsize/2); i<=testgridsize/2; i++)
                {
                    for(j=-(testgridsize/2); j<=testgridsize/2; j++)
                    {
                        x[0] = (double)i/(double)(testgridsize/2);
                        x[1] = (double)j/(double)(testgridsize/2);
                        dforest.dfprocess(df, x, ref y);
                        
                        //
                        // Test for basic properties
                        //
                        s = 0;
                        for(k=0; k<=1; k++)
                        {
                            if( (double)(y[k])<(double)(0) )
                            {
                                err = true;
                                return;
                            }
                            s = s+y[k];
                        }
                        if( (double)(Math.Abs(s-1))>(double)(1000*math.machineepsilon) )
                        {
                            err = true;
                            return;
                        }
                        
                        //
                        // test for good correlation with results
                        //
                        r = Math.Sqrt(math.sqr(x[0])+math.sqr(x[1]));
                        if( (double)(r)<(double)(0.5*0.5) )
                        {
                            err = err | (double)(y[0])<(double)(0.6);
                        }
                        if( (double)(r)>(double)(0.5*1.5) )
                        {
                            err = err | (double)(y[1])<(double)(0.6);
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Basic test: simple regression task without noise:
        * |x|<1, |y|<1
        * F(x,y) = x^2+y
        *************************************************************************/
        private static void basictest4(ref bool err)
        {
            int pass = 0;
            int passcount = 0;
            double[,] xy = new double[0,0];
            int npoints = 0;
            int ntrees = 0;
            int ns = 0;
            int strongc = 0;
            int i = 0;
            int j = 0;
            int info = 0;
            dforest.decisionforest df = new dforest.decisionforest();
            dforest.decisionforest df2 = new dforest.decisionforest();
            double[] x = new double[0];
            double[] y = new double[0];
            dforest.dfreport rep = new dforest.dfreport();
            dforest.dfreport rep2 = new dforest.dfreport();
            int testgridsize = 0;
            double maxerr = 0;
            double maxerr2 = 0;
            double avgerr = 0;
            double avgerr2 = 0;
            int cnt = 0;
            double ey = 0;

            passcount = 1;
            testgridsize = 50;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // select npoints and ntrees
                //
                npoints = 5000;
                ntrees = 100;
                ns = (int)Math.Round(0.1*npoints);
                strongc = 1;
                
                //
                // Prepare task
                //
                xy = new double[npoints-1+1, 2+1];
                x = new double[1+1];
                y = new double[0+1];
                for(i=0; i<=npoints-1; i++)
                {
                    xy[i,0] = 2*math.randomreal()-1;
                    xy[i,1] = 2*math.randomreal()-1;
                    xy[i,2] = math.sqr(xy[i,0])+xy[i,1];
                }
                
                //
                // Test
                //
                dforest.dfbuildinternal(xy, npoints, 2, 1, ntrees, ns, 1, 0, ref info, df, rep);
                if( info<=0 )
                {
                    err = true;
                    return;
                }
                dforest.dfbuildinternal(xy, npoints, 2, 1, ntrees, ns, 1, strongc, ref info, df2, rep2);
                if( info<=0 )
                {
                    err = true;
                    return;
                }
                maxerr = 0;
                maxerr2 = 0;
                avgerr = 0;
                avgerr2 = 0;
                cnt = 0;
                for(i=(int)Math.Round(-(0.7*testgridsize/2)); i<=(int)Math.Round(0.7*testgridsize/2); i++)
                {
                    for(j=(int)Math.Round(-(0.7*testgridsize/2)); j<=(int)Math.Round(0.7*testgridsize/2); j++)
                    {
                        x[0] = (double)i/(double)(testgridsize/2);
                        x[1] = (double)j/(double)(testgridsize/2);
                        ey = math.sqr(x[0])+x[1];
                        dforest.dfprocess(df, x, ref y);
                        maxerr = Math.Max(maxerr, Math.Abs(y[0]-ey));
                        avgerr = avgerr+Math.Abs(y[0]-ey);
                        dforest.dfprocess(df2, x, ref y);
                        maxerr2 = Math.Max(maxerr2, Math.Abs(y[0]-ey));
                        avgerr2 = avgerr2+Math.Abs(y[0]-ey);
                        cnt = cnt+1;
                    }
                }
                avgerr = avgerr/cnt;
                avgerr2 = avgerr2/cnt;
                err = err | (double)(maxerr)>(double)(0.2);
                err = err | (double)(maxerr2)>(double)(0.2);
                err = err | (double)(avgerr)>(double)(0.1);
                err = err | (double)(avgerr2)>(double)(0.1);
            }
        }


        /*************************************************************************
        Basic test: extended variable selection leads to better results.

        Next task CAN be solved without EVS but it is very unlikely. With EVS
        it can be easily and exactly solved.

        Task matrix:
            1 0 0 0 ... 0   0
            0 1 0 0 ... 0   1
            0 0 1 0 ... 0   2
            0 0 0 1 ... 0   3
            0 0 0 0 ... 1   N-1
        *************************************************************************/
        private static void basictest5(ref bool err)
        {
            double[,] xy = new double[0,0];
            int nvars = 0;
            int npoints = 0;
            int nfeatures = 0;
            int nsample = 0;
            int ntrees = 0;
            int evs = 0;
            int i = 0;
            int j = 0;
            bool eflag = new bool();
            int info = 0;
            dforest.decisionforest df = new dforest.decisionforest();
            double[] x = new double[0];
            double[] y = new double[0];
            dforest.dfreport rep = new dforest.dfreport();
            int i_ = 0;

            
            //
            // select npoints and ntrees
            //
            npoints = 50;
            nvars = npoints;
            ntrees = 1;
            nsample = npoints;
            evs = 2;
            nfeatures = 1;
            
            //
            // Prepare task
            //
            xy = new double[npoints-1+1, nvars+1];
            x = new double[nvars-1+1];
            y = new double[0+1];
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=nvars-1; j++)
                {
                    xy[i,j] = 0;
                }
                xy[i,i] = 1;
                xy[i,nvars] = i;
            }
            
            //
            // Without EVS
            //
            dforest.dfbuildinternal(xy, npoints, nvars, 1, ntrees, nsample, nfeatures, 0, ref info, df, rep);
            if( info<=0 )
            {
                err = true;
                return;
            }
            eflag = false;
            for(i=0; i<=npoints-1; i++)
            {
                for(i_=0; i_<=nvars-1;i_++)
                {
                    x[i_] = xy[i,i_];
                }
                dforest.dfprocess(df, x, ref y);
                if( (double)(Math.Abs(y[0]-xy[i,nvars]))>(double)(1000*math.machineepsilon) )
                {
                    eflag = true;
                }
            }
            if( !eflag )
            {
                err = true;
                return;
            }
            
            //
            // With EVS
            //
            dforest.dfbuildinternal(xy, npoints, nvars, 1, ntrees, nsample, nfeatures, evs, ref info, df, rep);
            if( info<=0 )
            {
                err = true;
                return;
            }
            eflag = false;
            for(i=0; i<=npoints-1; i++)
            {
                for(i_=0; i_<=nvars-1;i_++)
                {
                    x[i_] = xy[i,i_];
                }
                dforest.dfprocess(df, x, ref y);
                if( (double)(Math.Abs(y[0]-xy[i,nvars]))>(double)(1000*math.machineepsilon) )
                {
                    eflag = true;
                }
            }
            if( eflag )
            {
                err = true;
                return;
            }
        }


        /*************************************************************************
        Random normal number
        *************************************************************************/
        private static double rnormal()
        {
            double result = 0;
            double u = 0;
            double v = 0;
            double s = 0;
            double x1 = 0;
            double x2 = 0;

            while( true )
            {
                u = 2*math.randomreal()-1;
                v = 2*math.randomreal()-1;
                s = math.sqr(u)+math.sqr(v);
                if( (double)(s)>(double)(0) & (double)(s)<(double)(1) )
                {
                    s = Math.Sqrt(-(2*Math.Log(s)/s));
                    x1 = u*s;
                    x2 = v*s;
                    break;
                }
            }
            result = x1;
            return result;
        }


        /*************************************************************************
        Random point from sphere
        *************************************************************************/
        private static void rsphere(ref double[,] xy,
            int n,
            int i)
        {
            int j = 0;
            double v = 0;
            int i_ = 0;

            for(j=0; j<=n-1; j++)
            {
                xy[i,j] = rnormal();
            }
            v = 0.0;
            for(i_=0; i_<=n-1;i_++)
            {
                v += xy[i,i_]*xy[i,i_];
            }
            v = math.randomreal()/Math.Sqrt(v);
            for(i_=0; i_<=n-1;i_++)
            {
                xy[i,i_] = v*xy[i,i_];
            }
        }


        /*************************************************************************
        Unsets DF
        *************************************************************************/
        private static void unsetdf(dforest.decisionforest df)
        {
            double[,] xy = new double[0,0];
            int info = 0;
            dforest.dfreport rep = new dforest.dfreport();

            xy = new double[0+1, 1+1];
            xy[0,0] = 0;
            xy[0,1] = 0;
            dforest.dfbuildinternal(xy, 1, 1, 1, 1, 1, 1, 0, ref info, df, rep);
        }


    }
    public class testblasunit
    {
        public static bool testblas(bool silent)
        {
            bool result = new bool();
            int pass = 0;
            int passcount = 0;
            int n = 0;
            int i = 0;
            int i1 = 0;
            int i2 = 0;
            int j = 0;
            int j1 = 0;
            int j2 = 0;
            int l = 0;
            int k = 0;
            int r = 0;
            int i3 = 0;
            int j3 = 0;
            int col1 = 0;
            int col2 = 0;
            int row1 = 0;
            int row2 = 0;
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[,] a = new double[0,0];
            double[,] b = new double[0,0];
            double[,] c1 = new double[0,0];
            double[,] c2 = new double[0,0];
            double err = 0;
            double e1 = 0;
            double e2 = 0;
            double e3 = 0;
            double v = 0;
            double scl1 = 0;
            double scl2 = 0;
            double scl3 = 0;
            bool was1 = new bool();
            bool was2 = new bool();
            bool trans1 = new bool();
            bool trans2 = new bool();
            double threshold = 0;
            bool n2errors = new bool();
            bool hsnerrors = new bool();
            bool amaxerrors = new bool();
            bool mverrors = new bool();
            bool iterrors = new bool();
            bool cterrors = new bool();
            bool mmerrors = new bool();
            bool waserrors = new bool();
            int i_ = 0;

            n2errors = false;
            amaxerrors = false;
            hsnerrors = false;
            mverrors = false;
            iterrors = false;
            cterrors = false;
            mmerrors = false;
            waserrors = false;
            threshold = 10000*math.machineepsilon;
            
            //
            // Test Norm2
            //
            passcount = 1000;
            e1 = 0;
            e2 = 0;
            e3 = 0;
            scl2 = 0.5*math.maxrealnumber;
            scl3 = 2*math.minrealnumber;
            for(pass=1; pass<=passcount; pass++)
            {
                n = 1+math.randominteger(1000);
                i1 = math.randominteger(10);
                i2 = n+i1-1;
                x1 = new double[i2+1];
                x2 = new double[i2+1];
                for(i=i1; i<=i2; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                }
                v = 0;
                for(i=i1; i<=i2; i++)
                {
                    v = v+math.sqr(x1[i]);
                }
                v = Math.Sqrt(v);
                e1 = Math.Max(e1, Math.Abs(v-blas.vectornorm2(x1, i1, i2)));
                for(i=i1; i<=i2; i++)
                {
                    x2[i] = scl2*x1[i];
                }
                e2 = Math.Max(e2, Math.Abs(v*scl2-blas.vectornorm2(x2, i1, i2)));
                for(i=i1; i<=i2; i++)
                {
                    x2[i] = scl3*x1[i];
                }
                e3 = Math.Max(e3, Math.Abs(v*scl3-blas.vectornorm2(x2, i1, i2)));
            }
            e2 = e2/scl2;
            e3 = e3/scl3;
            n2errors = ((double)(e1)>=(double)(threshold) | (double)(e2)>=(double)(threshold)) | (double)(e3)>=(double)(threshold);
            
            //
            // Testing VectorAbsMax, Column/Row AbsMax
            //
            x1 = new double[5+1];
            x1[1] = 2.0;
            x1[2] = 0.2;
            x1[3] = -1.3;
            x1[4] = 0.7;
            x1[5] = -3.0;
            amaxerrors = (blas.vectoridxabsmax(x1, 1, 5)!=5 | blas.vectoridxabsmax(x1, 1, 4)!=1) | blas.vectoridxabsmax(x1, 2, 4)!=3;
            n = 30;
            x1 = new double[n+1];
            a = new double[n+1, n+1];
            for(i=1; i<=n; i++)
            {
                for(j=1; j<=n; j++)
                {
                    a[i,j] = 2*math.randomreal()-1;
                }
            }
            was1 = false;
            was2 = false;
            for(pass=1; pass<=1000; pass++)
            {
                j = 1+math.randominteger(n);
                i1 = 1+math.randominteger(n);
                i2 = i1+math.randominteger(n+1-i1);
                for(i_=i1; i_<=i2;i_++)
                {
                    x1[i_] = a[i_,j];
                }
                if( blas.vectoridxabsmax(x1, i1, i2)!=blas.columnidxabsmax(a, i1, i2, j) )
                {
                    was1 = true;
                }
                i = 1+math.randominteger(n);
                j1 = 1+math.randominteger(n);
                j2 = j1+math.randominteger(n+1-j1);
                for(i_=j1; i_<=j2;i_++)
                {
                    x1[i_] = a[i,i_];
                }
                if( blas.vectoridxabsmax(x1, j1, j2)!=blas.rowidxabsmax(a, j1, j2, i) )
                {
                    was2 = true;
                }
            }
            amaxerrors = (amaxerrors | was1) | was2;
            
            //
            // Testing upper Hessenberg 1-norm
            //
            a = new double[3+1, 3+1];
            x1 = new double[3+1];
            a[1,1] = 2;
            a[1,2] = 3;
            a[1,3] = 1;
            a[2,1] = 4;
            a[2,2] = -5;
            a[2,3] = 8;
            a[3,1] = 99;
            a[3,2] = 3;
            a[3,3] = 1;
            hsnerrors = (double)(Math.Abs(blas.upperhessenberg1norm(a, 1, 3, 1, 3, ref x1)-11))>(double)(threshold);
            
            //
            // Testing MatrixVectorMultiply
            //
            a = new double[3+1, 5+1];
            x1 = new double[3+1];
            x2 = new double[2+1];
            a[2,3] = 2;
            a[2,4] = -1;
            a[2,5] = -1;
            a[3,3] = 1;
            a[3,4] = -2;
            a[3,5] = 2;
            x1[1] = 1;
            x1[2] = 2;
            x1[3] = 1;
            x2[1] = -1;
            x2[2] = -1;
            blas.matrixvectormultiply(a, 2, 3, 3, 5, false, x1, 1, 3, 1.0, ref x2, 1, 2, 1.0);
            blas.matrixvectormultiply(a, 2, 3, 3, 5, true, x2, 1, 2, 1.0, ref x1, 1, 3, 1.0);
            e1 = Math.Abs(x1[1]+5)+Math.Abs(x1[2]-8)+Math.Abs(x1[3]+1)+Math.Abs(x2[1]+2)+Math.Abs(x2[2]+2);
            x1[1] = 1;
            x1[2] = 2;
            x1[3] = 1;
            x2[1] = -1;
            x2[2] = -1;
            blas.matrixvectormultiply(a, 2, 3, 3, 5, false, x1, 1, 3, 1.0, ref x2, 1, 2, 0.0);
            blas.matrixvectormultiply(a, 2, 3, 3, 5, true, x2, 1, 2, 1.0, ref x1, 1, 3, 0.0);
            e2 = Math.Abs(x1[1]+3)+Math.Abs(x1[2]-3)+Math.Abs(x1[3]+1)+Math.Abs(x2[1]+1)+Math.Abs(x2[2]+1);
            mverrors = (double)(e1+e2)>=(double)(threshold);
            
            //
            // testing inplace transpose
            //
            n = 10;
            a = new double[n+1, n+1];
            b = new double[n+1, n+1];
            x1 = new double[n-1+1];
            for(i=1; i<=n; i++)
            {
                for(j=1; j<=n; j++)
                {
                    a[i,j] = math.randomreal();
                }
            }
            passcount = 10000;
            was1 = false;
            for(pass=1; pass<=passcount; pass++)
            {
                i1 = 1+math.randominteger(n);
                i2 = i1+math.randominteger(n-i1+1);
                j1 = 1+math.randominteger(n-(i2-i1));
                j2 = j1+(i2-i1);
                blas.copymatrix(a, i1, i2, j1, j2, ref b, i1, i2, j1, j2);
                blas.inplacetranspose(ref b, i1, i2, j1, j2, ref x1);
                for(i=i1; i<=i2; i++)
                {
                    for(j=j1; j<=j2; j++)
                    {
                        if( (double)(a[i,j])!=(double)(b[i1+(j-j1),j1+(i-i1)]) )
                        {
                            was1 = true;
                        }
                    }
                }
            }
            iterrors = was1;
            
            //
            // testing copy and transpose
            //
            n = 10;
            a = new double[n+1, n+1];
            b = new double[n+1, n+1];
            for(i=1; i<=n; i++)
            {
                for(j=1; j<=n; j++)
                {
                    a[i,j] = math.randomreal();
                }
            }
            passcount = 10000;
            was1 = false;
            for(pass=1; pass<=passcount; pass++)
            {
                i1 = 1+math.randominteger(n);
                i2 = i1+math.randominteger(n-i1+1);
                j1 = 1+math.randominteger(n);
                j2 = j1+math.randominteger(n-j1+1);
                blas.copyandtranspose(a, i1, i2, j1, j2, ref b, j1, j2, i1, i2);
                for(i=i1; i<=i2; i++)
                {
                    for(j=j1; j<=j2; j++)
                    {
                        if( (double)(a[i,j])!=(double)(b[j,i]) )
                        {
                            was1 = true;
                        }
                    }
                }
            }
            cterrors = was1;
            
            //
            // Testing MatrixMatrixMultiply
            //
            n = 10;
            a = new double[2*n+1, 2*n+1];
            b = new double[2*n+1, 2*n+1];
            c1 = new double[2*n+1, 2*n+1];
            c2 = new double[2*n+1, 2*n+1];
            x1 = new double[n+1];
            x2 = new double[n+1];
            for(i=1; i<=2*n; i++)
            {
                for(j=1; j<=2*n; j++)
                {
                    a[i,j] = math.randomreal();
                    b[i,j] = math.randomreal();
                }
            }
            passcount = 1000;
            was1 = false;
            for(pass=1; pass<=passcount; pass++)
            {
                for(i=1; i<=2*n; i++)
                {
                    for(j=1; j<=2*n; j++)
                    {
                        c1[i,j] = 2.1*i+3.1*j;
                        c2[i,j] = c1[i,j];
                    }
                }
                l = 1+math.randominteger(n);
                k = 1+math.randominteger(n);
                r = 1+math.randominteger(n);
                i1 = 1+math.randominteger(n);
                j1 = 1+math.randominteger(n);
                i2 = 1+math.randominteger(n);
                j2 = 1+math.randominteger(n);
                i3 = 1+math.randominteger(n);
                j3 = 1+math.randominteger(n);
                trans1 = (double)(math.randomreal())>(double)(0.5);
                trans2 = (double)(math.randomreal())>(double)(0.5);
                if( trans1 )
                {
                    col1 = l;
                    row1 = k;
                }
                else
                {
                    col1 = k;
                    row1 = l;
                }
                if( trans2 )
                {
                    col2 = k;
                    row2 = r;
                }
                else
                {
                    col2 = r;
                    row2 = k;
                }
                scl1 = math.randomreal();
                scl2 = math.randomreal();
                blas.matrixmatrixmultiply(a, i1, i1+row1-1, j1, j1+col1-1, trans1, b, i2, i2+row2-1, j2, j2+col2-1, trans2, scl1, ref c1, i3, i3+l-1, j3, j3+r-1, scl2, ref x1);
                naivematrixmatrixmultiply(a, i1, i1+row1-1, j1, j1+col1-1, trans1, b, i2, i2+row2-1, j2, j2+col2-1, trans2, scl1, ref c2, i3, i3+l-1, j3, j3+r-1, scl2);
                err = 0;
                for(i=1; i<=l; i++)
                {
                    for(j=1; j<=r; j++)
                    {
                        err = Math.Max(err, Math.Abs(c1[i3+i-1,j3+j-1]-c2[i3+i-1,j3+j-1]));
                    }
                }
                if( (double)(err)>(double)(threshold) )
                {
                    was1 = true;
                    break;
                }
            }
            mmerrors = was1;
            
            //
            // report
            //
            waserrors = (((((n2errors | amaxerrors) | hsnerrors) | mverrors) | iterrors) | cterrors) | mmerrors;
            if( !silent )
            {
                System.Console.Write("TESTING BLAS");
                System.Console.WriteLine();
                System.Console.Write("VectorNorm2:                             ");
                if( n2errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("AbsMax (vector/row/column):              ");
                if( amaxerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("UpperHessenberg1Norm:                    ");
                if( hsnerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("MatrixVectorMultiply:                    ");
                if( mverrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("InplaceTranspose:                        ");
                if( iterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("CopyAndTranspose:                        ");
                if( cterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("MatrixMatrixMultiply:                    ");
                if( mmerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        private static void naivematrixmatrixmultiply(double[,] a,
            int ai1,
            int ai2,
            int aj1,
            int aj2,
            bool transa,
            double[,] b,
            int bi1,
            int bi2,
            int bj1,
            int bj2,
            bool transb,
            double alpha,
            ref double[,] c,
            int ci1,
            int ci2,
            int cj1,
            int cj2,
            double beta)
        {
            int arows = 0;
            int acols = 0;
            int brows = 0;
            int bcols = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int l = 0;
            int r = 0;
            double v = 0;
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            int i_ = 0;
            int i1_ = 0;

            
            //
            // Setup
            //
            if( !transa )
            {
                arows = ai2-ai1+1;
                acols = aj2-aj1+1;
            }
            else
            {
                arows = aj2-aj1+1;
                acols = ai2-ai1+1;
            }
            if( !transb )
            {
                brows = bi2-bi1+1;
                bcols = bj2-bj1+1;
            }
            else
            {
                brows = bj2-bj1+1;
                bcols = bi2-bi1+1;
            }
            ap.assert(acols==brows, "NaiveMatrixMatrixMultiply: incorrect matrix sizes!");
            if( ((arows<=0 | acols<=0) | brows<=0) | bcols<=0 )
            {
                return;
            }
            l = arows;
            r = bcols;
            k = acols;
            x1 = new double[k+1];
            x2 = new double[k+1];
            for(i=1; i<=l; i++)
            {
                for(j=1; j<=r; j++)
                {
                    if( !transa )
                    {
                        if( !transb )
                        {
                            i1_ = (aj1)-(bi1);
                            v = 0.0;
                            for(i_=bi1; i_<=bi2;i_++)
                            {
                                v += b[i_,bj1+j-1]*a[ai1+i-1,i_+i1_];
                            }
                        }
                        else
                        {
                            i1_ = (aj1)-(bj1);
                            v = 0.0;
                            for(i_=bj1; i_<=bj2;i_++)
                            {
                                v += b[bi1+j-1,i_]*a[ai1+i-1,i_+i1_];
                            }
                        }
                    }
                    else
                    {
                        if( !transb )
                        {
                            i1_ = (ai1)-(bi1);
                            v = 0.0;
                            for(i_=bi1; i_<=bi2;i_++)
                            {
                                v += b[i_,bj1+j-1]*a[i_+i1_,aj1+i-1];
                            }
                        }
                        else
                        {
                            i1_ = (ai1)-(bj1);
                            v = 0.0;
                            for(i_=bj1; i_<=bj2;i_++)
                            {
                                v += b[bi1+j-1,i_]*a[i_+i1_,aj1+i-1];
                            }
                        }
                    }
                    if( (double)(beta)==(double)(0) )
                    {
                        c[ci1+i-1,cj1+j-1] = alpha*v;
                    }
                    else
                    {
                        c[ci1+i-1,cj1+j-1] = beta*c[ci1+i-1,cj1+j-1]+alpha*v;
                    }
                }
            }
        }


    }
    public class testkmeansunit
    {
        public static bool testkmeans(bool silent)
        {
            bool result = new bool();
            int nf = 0;
            int maxnf = 0;
            int nc = 0;
            int maxnc = 0;
            int passcount = 0;
            bool waserrors = new bool();
            bool converrors = new bool();
            bool simpleerrors = new bool();
            bool complexerrors = new bool();
            bool othererrors = new bool();
            bool restartserrors = new bool();

            
            //
            // Primary settings
            //
            maxnf = 5;
            maxnc = 5;
            passcount = 10;
            waserrors = false;
            converrors = false;
            othererrors = false;
            simpleerrors = false;
            complexerrors = false;
            restartserrors = false;
            
            //
            //
            //
            for(nf=1; nf<=maxnf; nf++)
            {
                for(nc=1; nc<=maxnc; nc++)
                {
                    simpletest1(nf, nc, passcount, ref converrors, ref othererrors, ref simpleerrors);
                }
            }
            restartstest(ref converrors, ref restartserrors);
            
            //
            // Final report
            //
            waserrors = (((converrors | othererrors) | simpleerrors) | complexerrors) | restartserrors;
            if( !silent )
            {
                System.Console.Write("K-MEANS TEST");
                System.Console.WriteLine();
                System.Console.Write("TOTAL RESULTS:                           ");
                if( !waserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* CONVERGENCE:                           ");
                if( !converrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SIMPLE TASKS:                          ");
                if( !simpleerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* COMPLEX TASKS:                         ");
                if( !complexerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* OTHER PROPERTIES:                      ");
                if( !othererrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* RESTARTS PROPERTIES:                   ");
                if( !restartserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Simple test 1: ellipsoid in NF-dimensional space.
        compare k-means centers with random centers
        *************************************************************************/
        private static void simpletest1(int nvars,
            int nc,
            int passcount,
            ref bool converrors,
            ref bool othererrors,
            ref bool simpleerrors)
        {
            int npoints = 0;
            int majoraxis = 0;
            double[,] xy = new double[0,0];
            double[] tmp = new double[0];
            double v = 0;
            int i = 0;
            int j = 0;
            int info = 0;
            double[,] c = new double[0,0];
            int[] xyc = new int[0];
            int pass = 0;
            int restarts = 0;
            double ekmeans = 0;
            double erandom = 0;
            double dclosest = 0;
            int cclosest = 0;
            int i_ = 0;

            npoints = nc*100;
            restarts = 5;
            passcount = 10;
            tmp = new double[nvars-1+1];
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Fill
                //
                xy = new double[npoints-1+1, nvars-1+1];
                majoraxis = math.randominteger(nvars);
                for(i=0; i<=npoints-1; i++)
                {
                    rsphere(ref xy, nvars, i);
                    xy[i,majoraxis] = nc*xy[i,majoraxis];
                }
                
                //
                // Test
                //
                kmeans.kmeansgenerate(xy, npoints, nvars, nc, restarts, ref info, ref c, ref xyc);
                if( info<0 )
                {
                    converrors = true;
                    return;
                }
                
                //
                // Test that XYC is correct mapping to cluster centers
                //
                for(i=0; i<=npoints-1; i++)
                {
                    cclosest = -1;
                    dclosest = math.maxrealnumber;
                    for(j=0; j<=nc-1; j++)
                    {
                        for(i_=0; i_<=nvars-1;i_++)
                        {
                            tmp[i_] = xy[i,i_];
                        }
                        for(i_=0; i_<=nvars-1;i_++)
                        {
                            tmp[i_] = tmp[i_] - c[i_,j];
                        }
                        v = 0.0;
                        for(i_=0; i_<=nvars-1;i_++)
                        {
                            v += tmp[i_]*tmp[i_];
                        }
                        if( (double)(v)<(double)(dclosest) )
                        {
                            cclosest = j;
                            dclosest = v;
                        }
                    }
                    if( cclosest!=xyc[i] )
                    {
                        othererrors = true;
                        return;
                    }
                }
                
                //
                // Use first NC rows of XY as random centers
                // (XY is totally random, so it is as good as any other choice).
                //
                // Compare potential functions.
                //
                ekmeans = 0;
                for(i=0; i<=npoints-1; i++)
                {
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        tmp[i_] = xy[i,i_];
                    }
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        tmp[i_] = tmp[i_] - c[i_,xyc[i]];
                    }
                    v = 0.0;
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        v += tmp[i_]*tmp[i_];
                    }
                    ekmeans = ekmeans+v;
                }
                erandom = 0;
                for(i=0; i<=npoints-1; i++)
                {
                    dclosest = math.maxrealnumber;
                    v = 0;
                    for(j=0; j<=nc-1; j++)
                    {
                        for(i_=0; i_<=nvars-1;i_++)
                        {
                            tmp[i_] = xy[i,i_];
                        }
                        for(i_=0; i_<=nvars-1;i_++)
                        {
                            tmp[i_] = tmp[i_] - xy[j,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=nvars-1;i_++)
                        {
                            v += tmp[i_]*tmp[i_];
                        }
                        if( (double)(v)<(double)(dclosest) )
                        {
                            dclosest = v;
                        }
                    }
                    erandom = erandom+v;
                }
                if( (double)(erandom)<(double)(ekmeans) )
                {
                    simpleerrors = true;
                    return;
                }
            }
        }


        /*************************************************************************
        This non-deterministic test checks that Restarts>1 significantly  improves
        quality of results.

        Subroutine generates random task 3 unit balls in 2D, each with 20  points,
        separated by 5 units wide gaps, and solves it  with  Restarts=1  and  with
        Restarts=5. Potential functions are compared,  outcome  of  the  trial  is
        either 0 or 1 (depending on what is better).

        Sequence of 1000 such tasks is  solved.  If  Restarts>1  actually  improve
        quality of solution, sum of outcome will be non-binomial.  If  it  doesn't
        matter, it will be binomially distributed.

        P.S. This test was added after report from Gianluca  Borello  who  noticed
        error in the handling of multiple restarts.
        *************************************************************************/
        private static void restartstest(ref bool converrors,
            ref bool restartserrors)
        {
            int npoints = 0;
            int nvars = 0;
            int nclusters = 0;
            int clustersize = 0;
            int restarts = 0;
            int passcount = 0;
            double sigmathreshold = 0;
            double p = 0;
            double s = 0;
            double[,] xy = new double[0,0];
            double[,] ca = new double[0,0];
            double[,] cb = new double[0,0];
            int[] xyca = new int[0];
            int[] xycb = new int[0];
            double[] tmp = new double[0];
            int i = 0;
            int j = 0;
            int info = 0;
            int pass = 0;
            double ea = 0;
            double eb = 0;
            double v = 0;
            int i_ = 0;

            restarts = 5;
            passcount = 1000;
            clustersize = 20;
            nclusters = 3;
            nvars = 2;
            npoints = nclusters*clustersize;
            sigmathreshold = 5;
            xy = new double[npoints, nvars];
            tmp = new double[nvars];
            p = 0;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Fill
                //
                for(i=0; i<=npoints-1; i++)
                {
                    rsphere(ref xy, nvars, i);
                    for(j=0; j<=nvars-1; j++)
                    {
                        xy[i,j] = xy[i,j]+(double)i/(double)clustersize*5;
                    }
                }
                
                //
                // Test: Restarts=1
                //
                kmeans.kmeansgenerate(xy, npoints, nvars, nclusters, 1, ref info, ref ca, ref xyca);
                if( info<0 )
                {
                    converrors = true;
                    return;
                }
                ea = 0;
                for(i=0; i<=npoints-1; i++)
                {
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        tmp[i_] = xy[i,i_];
                    }
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        tmp[i_] = tmp[i_] - ca[i_,xyca[i]];
                    }
                    v = 0.0;
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        v += tmp[i_]*tmp[i_];
                    }
                    ea = ea+v;
                }
                
                //
                // Test: Restarts>1
                //
                kmeans.kmeansgenerate(xy, npoints, nvars, nclusters, restarts, ref info, ref cb, ref xycb);
                if( info<0 )
                {
                    converrors = true;
                    return;
                }
                eb = 0;
                for(i=0; i<=npoints-1; i++)
                {
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        tmp[i_] = xy[i,i_];
                    }
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        tmp[i_] = tmp[i_] - cb[i_,xycb[i]];
                    }
                    v = 0.0;
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        v += tmp[i_]*tmp[i_];
                    }
                    eb = eb+v;
                }
                
                //
                // Calculate statistic.
                //
                if( (double)(ea)<(double)(eb) )
                {
                    p = p+1;
                }
                if( (double)(ea)==(double)(eb) )
                {
                    p = p+0.5;
                }
            }
            
            //
            // If Restarts doesn't influence quality of centers found, P must be
            // binomially distributed random value with mean 0.5*PassCount and
            // standard deviation Sqrt(PassCount/4).
            //
            // If Restarts do influence quality of solution, P must be significantly
            // lower than 0.5*PassCount.
            //
            s = (p-0.5*passcount)/Math.Sqrt((double)passcount/(double)4);
            restartserrors = restartserrors | (double)(s)>(double)(-sigmathreshold);
        }


        /*************************************************************************
        Random normal number
        *************************************************************************/
        private static double rnormal()
        {
            double result = 0;
            double u = 0;
            double v = 0;
            double s = 0;
            double x1 = 0;
            double x2 = 0;

            while( true )
            {
                u = 2*math.randomreal()-1;
                v = 2*math.randomreal()-1;
                s = math.sqr(u)+math.sqr(v);
                if( (double)(s)>(double)(0) & (double)(s)<(double)(1) )
                {
                    s = Math.Sqrt(-(2*Math.Log(s)/s));
                    x1 = u*s;
                    x2 = v*s;
                    break;
                }
            }
            result = x1;
            return result;
        }


        /*************************************************************************
        Random point from sphere
        *************************************************************************/
        private static void rsphere(ref double[,] xy,
            int n,
            int i)
        {
            int j = 0;
            double v = 0;
            int i_ = 0;

            for(j=0; j<=n-1; j++)
            {
                xy[i,j] = rnormal();
            }
            v = 0.0;
            for(i_=0; i_<=n-1;i_++)
            {
                v += xy[i,i_]*xy[i,i_];
            }
            v = math.randomreal()/Math.Sqrt(v);
            for(i_=0; i_<=n-1;i_++)
            {
                xy[i,i_] = v*xy[i,i_];
            }
        }


    }
    public class testhblasunit
    {
        public static bool testhblas(bool silent)
        {
            bool result = new bool();
            complex[,] a = new complex[0,0];
            complex[,] ua = new complex[0,0];
            complex[,] la = new complex[0,0];
            complex[] x = new complex[0];
            complex[] y1 = new complex[0];
            complex[] y2 = new complex[0];
            complex[] y3 = new complex[0];
            int n = 0;
            int maxn = 0;
            int i = 0;
            int j = 0;
            int i1 = 0;
            int i2 = 0;
            bool waserrors = new bool();
            double mverr = 0;
            double threshold = 0;
            complex alpha = 0;
            complex v = 0;
            int i_ = 0;
            int i1_ = 0;

            mverr = 0;
            waserrors = false;
            maxn = 10;
            threshold = 1000*math.machineepsilon;
            
            //
            // Test MV
            //
            for(n=2; n<=maxn; n++)
            {
                a = new complex[n+1, n+1];
                ua = new complex[n+1, n+1];
                la = new complex[n+1, n+1];
                x = new complex[n+1];
                y1 = new complex[n+1];
                y2 = new complex[n+1];
                y3 = new complex[n+1];
                
                //
                // fill A, UA, LA
                //
                for(i=1; i<=n; i++)
                {
                    a[i,i].x = 2*math.randomreal()-1;
                    a[i,i].y = 0;
                    for(j=i+1; j<=n; j++)
                    {
                        a[i,j].x = 2*math.randomreal()-1;
                        a[i,j].y = 2*math.randomreal()-1;
                        a[j,i] = math.conj(a[i,j]);
                    }
                }
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        ua[i,j] = 0;
                    }
                }
                for(i=1; i<=n; i++)
                {
                    for(j=i; j<=n; j++)
                    {
                        ua[i,j] = a[i,j];
                    }
                }
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        la[i,j] = 0;
                    }
                }
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=i; j++)
                    {
                        la[i,j] = a[i,j];
                    }
                }
                
                //
                // test on different I1, I2
                //
                for(i1=1; i1<=n; i1++)
                {
                    for(i2=i1; i2<=n; i2++)
                    {
                        
                        //
                        // Fill X, choose Alpha
                        //
                        for(i=1; i<=i2-i1+1; i++)
                        {
                            x[i].x = 2*math.randomreal()-1;
                            x[i].y = 2*math.randomreal()-1;
                        }
                        alpha.x = 2*math.randomreal()-1;
                        alpha.y = 2*math.randomreal()-1;
                        
                        //
                        // calculate A*x, UA*x, LA*x
                        //
                        for(i=i1; i<=i2; i++)
                        {
                            i1_ = (1)-(i1);
                            v = 0.0;
                            for(i_=i1; i_<=i2;i_++)
                            {
                                v += a[i,i_]*x[i_+i1_];
                            }
                            y1[i-i1+1] = alpha*v;
                        }
                        hblas.hermitianmatrixvectormultiply(ua, true, i1, i2, x, alpha, ref y2);
                        hblas.hermitianmatrixvectormultiply(la, false, i1, i2, x, alpha, ref y3);
                        
                        //
                        // Calculate error
                        //
                        for(i_=1; i_<=i2-i1+1;i_++)
                        {
                            y2[i_] = y2[i_] - y1[i_];
                        }
                        v = 0.0;
                        for(i_=1; i_<=i2-i1+1;i_++)
                        {
                            v += y2[i_]*math.conj(y2[i_]);
                        }
                        mverr = Math.Max(mverr, Math.Sqrt(math.abscomplex(v)));
                        for(i_=1; i_<=i2-i1+1;i_++)
                        {
                            y3[i_] = y3[i_] - y1[i_];
                        }
                        v = 0.0;
                        for(i_=1; i_<=i2-i1+1;i_++)
                        {
                            v += y3[i_]*math.conj(y3[i_]);
                        }
                        mverr = Math.Max(mverr, Math.Sqrt(math.abscomplex(v)));
                    }
                }
            }
            
            //
            // report
            //
            waserrors = (double)(mverr)>(double)(threshold);
            if( !silent )
            {
                System.Console.Write("TESTING HERMITIAN BLAS");
                System.Console.WriteLine();
                System.Console.Write("MV error:                                ");
                System.Console.Write("{0,5:E3}",mverr);
                System.Console.WriteLine();
                System.Console.Write("Threshold:                               ");
                System.Console.Write("{0,5:E3}",threshold);
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


    }
    public class testreflectionsunit
    {
        public static bool testreflections(bool silent)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;
            int n = 0;
            int m = 0;
            int maxmn = 0;
            double[] x = new double[0];
            double[] v = new double[0];
            double[] work = new double[0];
            double[,] h = new double[0,0];
            double[,] a = new double[0,0];
            double[,] b = new double[0,0];
            double[,] c = new double[0,0];
            double tmp = 0;
            double beta = 0;
            double tau = 0;
            double err = 0;
            double mer = 0;
            double mel = 0;
            double meg = 0;
            int pass = 0;
            int passcount = 0;
            double threshold = 0;
            int tasktype = 0;
            double xscale = 0;
            int i_ = 0;

            passcount = 10;
            threshold = 100*math.machineepsilon;
            mer = 0;
            mel = 0;
            meg = 0;
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=10; n++)
                {
                    for(m=1; m<=10; m++)
                    {
                        
                        //
                        // Task
                        //
                        n = 1+math.randominteger(10);
                        m = 1+math.randominteger(10);
                        maxmn = Math.Max(m, n);
                        
                        //
                        // Initialize
                        //
                        x = new double[maxmn+1];
                        v = new double[maxmn+1];
                        work = new double[maxmn+1];
                        h = new double[maxmn+1, maxmn+1];
                        a = new double[maxmn+1, maxmn+1];
                        b = new double[maxmn+1, maxmn+1];
                        c = new double[maxmn+1, maxmn+1];
                        
                        //
                        // GenerateReflection, three tasks are possible:
                        // * random X
                        // * zero X
                        // * non-zero X[1], all other are zeros
                        // * random X, near underflow scale
                        // * random X, near overflow scale
                        //
                        for(tasktype=0; tasktype<=4; tasktype++)
                        {
                            xscale = 1;
                            if( tasktype==0 )
                            {
                                for(i=1; i<=n; i++)
                                {
                                    x[i] = 2*math.randomreal()-1;
                                }
                            }
                            if( tasktype==1 )
                            {
                                for(i=1; i<=n; i++)
                                {
                                    x[i] = 0;
                                }
                            }
                            if( tasktype==2 )
                            {
                                x[1] = 2*math.randomreal()-1;
                                for(i=2; i<=n; i++)
                                {
                                    x[i] = 0;
                                }
                            }
                            if( tasktype==3 )
                            {
                                for(i=1; i<=n; i++)
                                {
                                    x[i] = (math.randominteger(21)-10)*math.minrealnumber;
                                }
                                xscale = 10*math.minrealnumber;
                            }
                            if( tasktype==4 )
                            {
                                for(i=1; i<=n; i++)
                                {
                                    x[i] = (2*math.randomreal()-1)*math.maxrealnumber;
                                }
                                xscale = math.maxrealnumber;
                            }
                            for(i_=1; i_<=n;i_++)
                            {
                                v[i_] = x[i_];
                            }
                            reflections.generatereflection(ref v, n, ref tau);
                            beta = v[1];
                            v[1] = 1;
                            for(i=1; i<=n; i++)
                            {
                                for(j=1; j<=n; j++)
                                {
                                    if( i==j )
                                    {
                                        h[i,j] = 1-tau*v[i]*v[j];
                                    }
                                    else
                                    {
                                        h[i,j] = -(tau*v[i]*v[j]);
                                    }
                                }
                            }
                            err = 0;
                            for(i=1; i<=n; i++)
                            {
                                tmp = 0.0;
                                for(i_=1; i_<=n;i_++)
                                {
                                    tmp += h[i,i_]*x[i_];
                                }
                                if( i==1 )
                                {
                                    err = Math.Max(err, Math.Abs(tmp-beta));
                                }
                                else
                                {
                                    err = Math.Max(err, Math.Abs(tmp));
                                }
                            }
                            meg = Math.Max(meg, err/xscale);
                        }
                        
                        //
                        // ApplyReflectionFromTheLeft
                        //
                        for(i=1; i<=m; i++)
                        {
                            x[i] = 2*math.randomreal()-1;
                            v[i] = x[i];
                        }
                        for(i=1; i<=m; i++)
                        {
                            for(j=1; j<=n; j++)
                            {
                                a[i,j] = 2*math.randomreal()-1;
                                b[i,j] = a[i,j];
                            }
                        }
                        reflections.generatereflection(ref v, m, ref tau);
                        beta = v[1];
                        v[1] = 1;
                        reflections.applyreflectionfromtheleft(ref b, tau, v, 1, m, 1, n, ref work);
                        for(i=1; i<=m; i++)
                        {
                            for(j=1; j<=m; j++)
                            {
                                if( i==j )
                                {
                                    h[i,j] = 1-tau*v[i]*v[j];
                                }
                                else
                                {
                                    h[i,j] = -(tau*v[i]*v[j]);
                                }
                            }
                        }
                        for(i=1; i<=m; i++)
                        {
                            for(j=1; j<=n; j++)
                            {
                                tmp = 0.0;
                                for(i_=1; i_<=m;i_++)
                                {
                                    tmp += h[i,i_]*a[i_,j];
                                }
                                c[i,j] = tmp;
                            }
                        }
                        err = 0;
                        for(i=1; i<=m; i++)
                        {
                            for(j=1; j<=n; j++)
                            {
                                err = Math.Max(err, Math.Abs(b[i,j]-c[i,j]));
                            }
                        }
                        mel = Math.Max(mel, err);
                        
                        //
                        // ApplyReflectionFromTheRight
                        //
                        for(i=1; i<=n; i++)
                        {
                            x[i] = 2*math.randomreal()-1;
                            v[i] = x[i];
                        }
                        for(i=1; i<=m; i++)
                        {
                            for(j=1; j<=n; j++)
                            {
                                a[i,j] = 2*math.randomreal()-1;
                                b[i,j] = a[i,j];
                            }
                        }
                        reflections.generatereflection(ref v, n, ref tau);
                        beta = v[1];
                        v[1] = 1;
                        reflections.applyreflectionfromtheright(ref b, tau, v, 1, m, 1, n, ref work);
                        for(i=1; i<=n; i++)
                        {
                            for(j=1; j<=n; j++)
                            {
                                if( i==j )
                                {
                                    h[i,j] = 1-tau*v[i]*v[j];
                                }
                                else
                                {
                                    h[i,j] = -(tau*v[i]*v[j]);
                                }
                            }
                        }
                        for(i=1; i<=m; i++)
                        {
                            for(j=1; j<=n; j++)
                            {
                                tmp = 0.0;
                                for(i_=1; i_<=n;i_++)
                                {
                                    tmp += a[i,i_]*h[i_,j];
                                }
                                c[i,j] = tmp;
                            }
                        }
                        err = 0;
                        for(i=1; i<=m; i++)
                        {
                            for(j=1; j<=n; j++)
                            {
                                err = Math.Max(err, Math.Abs(b[i,j]-c[i,j]));
                            }
                        }
                        mer = Math.Max(mer, err);
                    }
                }
            }
            
            //
            // Overflow crash test
            //
            x = new double[10+1];
            v = new double[10+1];
            for(i=1; i<=10; i++)
            {
                v[i] = math.maxrealnumber*0.01*(2*math.randomreal()-1);
            }
            reflections.generatereflection(ref v, 10, ref tau);
            result = ((double)(meg)<=(double)(threshold) & (double)(mel)<=(double)(threshold)) & (double)(mer)<=(double)(threshold);
            if( !silent )
            {
                System.Console.Write("TESTING REFLECTIONS");
                System.Console.WriteLine();
                System.Console.Write("Pass count is ");
                System.Console.Write("{0,0:d}",passcount);
                System.Console.WriteLine();
                System.Console.Write("Generate     absolute error is       ");
                System.Console.Write("{0,5:E3}",meg);
                System.Console.WriteLine();
                System.Console.Write("Apply(Left)  absolute error is       ");
                System.Console.Write("{0,5:E3}",mel);
                System.Console.WriteLine();
                System.Console.Write("Apply(Right) absolute error is       ");
                System.Console.Write("{0,5:E3}",mer);
                System.Console.WriteLine();
                System.Console.Write("Overflow crash test passed");
                System.Console.WriteLine();
                if( result )
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
            }
            return result;
        }


    }
    public class testcreflectionsunit
    {
        public static bool testcreflections(bool silent)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;
            int n = 0;
            int m = 0;
            int maxmn = 0;
            complex[] x = new complex[0];
            complex[] v = new complex[0];
            complex[] work = new complex[0];
            complex[,] h = new complex[0,0];
            complex[,] a = new complex[0,0];
            complex[,] b = new complex[0,0];
            complex[,] c = new complex[0,0];
            complex tmp = 0;
            complex beta = 0;
            complex tau = 0;
            double err = 0;
            double mer = 0;
            double mel = 0;
            double meg = 0;
            int pass = 0;
            int passcount = 0;
            bool waserrors = new bool();
            double threshold = 0;
            int i_ = 0;

            threshold = 1000*math.machineepsilon;
            passcount = 1000;
            mer = 0;
            mel = 0;
            meg = 0;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Task
                //
                n = 1+math.randominteger(10);
                m = 1+math.randominteger(10);
                maxmn = Math.Max(m, n);
                
                //
                // Initialize
                //
                x = new complex[maxmn+1];
                v = new complex[maxmn+1];
                work = new complex[maxmn+1];
                h = new complex[maxmn+1, maxmn+1];
                a = new complex[maxmn+1, maxmn+1];
                b = new complex[maxmn+1, maxmn+1];
                c = new complex[maxmn+1, maxmn+1];
                
                //
                // GenerateReflection
                //
                for(i=1; i<=n; i++)
                {
                    x[i].x = 2*math.randomreal()-1;
                    x[i].y = 2*math.randomreal()-1;
                    v[i] = x[i];
                }
                creflections.complexgeneratereflection(ref v, n, ref tau);
                beta = v[1];
                v[1] = 1;
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        if( i==j )
                        {
                            h[i,j] = 1-tau*v[i]*math.conj(v[j]);
                        }
                        else
                        {
                            h[i,j] = -(tau*v[i]*math.conj(v[j]));
                        }
                    }
                }
                err = 0;
                for(i=1; i<=n; i++)
                {
                    tmp = 0.0;
                    for(i_=1; i_<=n;i_++)
                    {
                        tmp += math.conj(h[i_,i])*x[i_];
                    }
                    if( i==1 )
                    {
                        err = Math.Max(err, math.abscomplex(tmp-beta));
                    }
                    else
                    {
                        err = Math.Max(err, math.abscomplex(tmp));
                    }
                }
                err = Math.Max(err, Math.Abs(beta.y));
                meg = Math.Max(meg, err);
                
                //
                // ApplyReflectionFromTheLeft
                //
                for(i=1; i<=m; i++)
                {
                    x[i].x = 2*math.randomreal()-1;
                    x[i].y = 2*math.randomreal()-1;
                    v[i] = x[i];
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        a[i,j].x = 2*math.randomreal()-1;
                        a[i,j].y = 2*math.randomreal()-1;
                        b[i,j] = a[i,j];
                    }
                }
                creflections.complexgeneratereflection(ref v, m, ref tau);
                beta = v[1];
                v[1] = 1;
                creflections.complexapplyreflectionfromtheleft(ref b, tau, v, 1, m, 1, n, ref work);
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=m; j++)
                    {
                        if( i==j )
                        {
                            h[i,j] = 1-tau*v[i]*math.conj(v[j]);
                        }
                        else
                        {
                            h[i,j] = -(tau*v[i]*math.conj(v[j]));
                        }
                    }
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        tmp = 0.0;
                        for(i_=1; i_<=m;i_++)
                        {
                            tmp += h[i,i_]*a[i_,j];
                        }
                        c[i,j] = tmp;
                    }
                }
                err = 0;
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        err = Math.Max(err, math.abscomplex(b[i,j]-c[i,j]));
                    }
                }
                mel = Math.Max(mel, err);
                
                //
                // ApplyReflectionFromTheRight
                //
                for(i=1; i<=n; i++)
                {
                    x[i] = 2*math.randomreal()-1;
                    v[i] = x[i];
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        a[i,j] = 2*math.randomreal()-1;
                        b[i,j] = a[i,j];
                    }
                }
                creflections.complexgeneratereflection(ref v, n, ref tau);
                beta = v[1];
                v[1] = 1;
                creflections.complexapplyreflectionfromtheright(ref b, tau, ref v, 1, m, 1, n, ref work);
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        if( i==j )
                        {
                            h[i,j] = 1-tau*v[i]*math.conj(v[j]);
                        }
                        else
                        {
                            h[i,j] = -(tau*v[i]*math.conj(v[j]));
                        }
                    }
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        tmp = 0.0;
                        for(i_=1; i_<=n;i_++)
                        {
                            tmp += a[i,i_]*h[i_,j];
                        }
                        c[i,j] = tmp;
                    }
                }
                err = 0;
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        err = Math.Max(err, math.abscomplex(b[i,j]-c[i,j]));
                    }
                }
                mer = Math.Max(mer, err);
            }
            
            //
            // Overflow crash test
            //
            x = new complex[10+1];
            v = new complex[10+1];
            for(i=1; i<=10; i++)
            {
                v[i] = math.maxrealnumber*0.01*(2*math.randomreal()-1);
            }
            creflections.complexgeneratereflection(ref v, 10, ref tau);
            
            //
            // report
            //
            waserrors = ((double)(meg)>(double)(threshold) | (double)(mel)>(double)(threshold)) | (double)(mer)>(double)(threshold);
            if( !silent )
            {
                System.Console.Write("TESTING COMPLEX REFLECTIONS");
                System.Console.WriteLine();
                System.Console.Write("Generate error:                          ");
                System.Console.Write("{0,5:E3}",meg);
                System.Console.WriteLine();
                System.Console.Write("Apply(L) error:                          ");
                System.Console.Write("{0,5:E3}",mel);
                System.Console.WriteLine();
                System.Console.Write("Apply(R) error:                          ");
                System.Console.Write("{0,5:E3}",mer);
                System.Console.WriteLine();
                System.Console.Write("Threshold:                               ");
                System.Console.Write("{0,5:E3}",threshold);
                System.Console.WriteLine();
                System.Console.Write("Overflow crash test:                     PASSED");
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


    }
    public class testsblasunit
    {
        public static bool testsblas(bool silent)
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            double[,] ua = new double[0,0];
            double[,] la = new double[0,0];
            double[] x = new double[0];
            double[] y1 = new double[0];
            double[] y2 = new double[0];
            double[] y3 = new double[0];
            int n = 0;
            int maxn = 0;
            int i = 0;
            int j = 0;
            int i1 = 0;
            int i2 = 0;
            bool waserrors = new bool();
            double mverr = 0;
            double threshold = 0;
            double alpha = 0;
            double v = 0;
            int i_ = 0;
            int i1_ = 0;

            mverr = 0;
            waserrors = false;
            maxn = 10;
            threshold = 1000*math.machineepsilon;
            
            //
            // Test MV
            //
            for(n=2; n<=maxn; n++)
            {
                a = new double[n+1, n+1];
                ua = new double[n+1, n+1];
                la = new double[n+1, n+1];
                x = new double[n+1];
                y1 = new double[n+1];
                y2 = new double[n+1];
                y3 = new double[n+1];
                
                //
                // fill A, UA, LA
                //
                for(i=1; i<=n; i++)
                {
                    a[i,i] = 2*math.randomreal()-1;
                    for(j=i+1; j<=n; j++)
                    {
                        a[i,j] = 2*math.randomreal()-1;
                        a[j,i] = a[i,j];
                    }
                }
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        ua[i,j] = 0;
                    }
                }
                for(i=1; i<=n; i++)
                {
                    for(j=i; j<=n; j++)
                    {
                        ua[i,j] = a[i,j];
                    }
                }
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        la[i,j] = 0;
                    }
                }
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=i; j++)
                    {
                        la[i,j] = a[i,j];
                    }
                }
                
                //
                // test on different I1, I2
                //
                for(i1=1; i1<=n; i1++)
                {
                    for(i2=i1; i2<=n; i2++)
                    {
                        
                        //
                        // Fill X, choose Alpha
                        //
                        for(i=1; i<=i2-i1+1; i++)
                        {
                            x[i] = 2*math.randomreal()-1;
                        }
                        alpha = 2*math.randomreal()-1;
                        
                        //
                        // calculate A*x, UA*x, LA*x
                        //
                        for(i=i1; i<=i2; i++)
                        {
                            i1_ = (1)-(i1);
                            v = 0.0;
                            for(i_=i1; i_<=i2;i_++)
                            {
                                v += a[i,i_]*x[i_+i1_];
                            }
                            y1[i-i1+1] = alpha*v;
                        }
                        sblas.symmetricmatrixvectormultiply(ua, true, i1, i2, x, alpha, ref y2);
                        sblas.symmetricmatrixvectormultiply(la, false, i1, i2, x, alpha, ref y3);
                        
                        //
                        // Calculate error
                        //
                        for(i_=1; i_<=i2-i1+1;i_++)
                        {
                            y2[i_] = y2[i_] - y1[i_];
                        }
                        v = 0.0;
                        for(i_=1; i_<=i2-i1+1;i_++)
                        {
                            v += y2[i_]*y2[i_];
                        }
                        mverr = Math.Max(mverr, Math.Sqrt(v));
                        for(i_=1; i_<=i2-i1+1;i_++)
                        {
                            y3[i_] = y3[i_] - y1[i_];
                        }
                        v = 0.0;
                        for(i_=1; i_<=i2-i1+1;i_++)
                        {
                            v += y3[i_]*y3[i_];
                        }
                        mverr = Math.Max(mverr, Math.Sqrt(v));
                    }
                }
            }
            
            //
            // report
            //
            waserrors = (double)(mverr)>(double)(threshold);
            if( !silent )
            {
                System.Console.Write("TESTING SYMMETRIC BLAS");
                System.Console.WriteLine();
                System.Console.Write("MV error:                                ");
                System.Console.Write("{0,5:E3}",mverr);
                System.Console.WriteLine();
                System.Console.Write("Threshold:                               ");
                System.Console.Write("{0,5:E3}",threshold);
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


    }
    public class testortfacunit
    {
        /*************************************************************************
        Main unittest subroutine
        *************************************************************************/
        public static bool testortfac(bool silent)
        {
            bool result = new bool();
            int maxmn = 0;
            double threshold = 0;
            int passcount = 0;
            int mx = 0;
            double[,] ra = new double[0,0];
            complex[,] ca = new complex[0,0];
            int m = 0;
            int n = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            bool rqrerrors = new bool();
            bool rlqerrors = new bool();
            bool cqrerrors = new bool();
            bool clqerrors = new bool();
            bool rbderrors = new bool();
            bool rhesserrors = new bool();
            bool rtderrors = new bool();
            bool ctderrors = new bool();
            bool waserrors = new bool();

            waserrors = false;
            rqrerrors = false;
            rlqerrors = false;
            cqrerrors = false;
            clqerrors = false;
            rbderrors = false;
            rhesserrors = false;
            rtderrors = false;
            ctderrors = false;
            maxmn = 3*ablas.ablasblocksize(ra)+1;
            passcount = 1;
            threshold = 5*1000*math.machineepsilon;
            
            //
            // Different problems
            //
            for(mx=1; mx<=maxmn; mx++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // Rectangular factorizations: QR, LQ, bidiagonal
                    // Matrix types: zero, dense, sparse
                    //
                    n = 1+math.randominteger(mx);
                    m = 1+math.randominteger(mx);
                    if( (double)(math.randomreal())>(double)(0.5) )
                    {
                        n = mx;
                    }
                    else
                    {
                        m = mx;
                    }
                    ra = new double[m, n];
                    ca = new complex[m, n];
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ra[i,j] = 0;
                            ca[i,j] = 0;
                        }
                    }
                    testrqrproblem(ra, m, n, threshold, ref rqrerrors);
                    testrlqproblem(ra, m, n, threshold, ref rlqerrors);
                    testcqrproblem(ca, m, n, threshold, ref cqrerrors);
                    testclqproblem(ca, m, n, threshold, ref clqerrors);
                    testrbdproblem(ra, m, n, threshold, ref rbderrors);
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ra[i,j] = 2*math.randomreal()-1;
                            ca[i,j].x = 2*math.randomreal()-1;
                            ca[i,j].y = 2*math.randomreal()-1;
                        }
                    }
                    testrqrproblem(ra, m, n, threshold, ref rqrerrors);
                    testrlqproblem(ra, m, n, threshold, ref rlqerrors);
                    testcqrproblem(ca, m, n, threshold, ref cqrerrors);
                    testclqproblem(ca, m, n, threshold, ref clqerrors);
                    testrbdproblem(ra, m, n, threshold, ref rbderrors);
                    rmatrixfillsparsea(ref ra, m, n, 0.95);
                    cmatrixfillsparsea(ref ca, m, n, 0.95);
                    testrqrproblem(ra, m, n, threshold, ref rqrerrors);
                    testrlqproblem(ra, m, n, threshold, ref rlqerrors);
                    testcqrproblem(ca, m, n, threshold, ref cqrerrors);
                    testclqproblem(ca, m, n, threshold, ref clqerrors);
                    testrbdproblem(ra, m, n, threshold, ref rbderrors);
                    
                    //
                    // Square factorizations: Hessenberg, tridiagonal
                    // Matrix types: zero, dense, sparse
                    //
                    ra = new double[mx, mx];
                    ca = new complex[mx, mx];
                    for(i=0; i<=mx-1; i++)
                    {
                        for(j=0; j<=mx-1; j++)
                        {
                            ra[i,j] = 0;
                            ca[i,j] = 0;
                        }
                    }
                    testrhessproblem(ra, mx, threshold, ref rhesserrors);
                    for(i=0; i<=mx-1; i++)
                    {
                        for(j=0; j<=mx-1; j++)
                        {
                            ra[i,j] = 2*math.randomreal()-1;
                            ca[i,j].x = 2*math.randomreal()-1;
                            ca[i,j].y = 2*math.randomreal()-1;
                        }
                    }
                    testrhessproblem(ra, mx, threshold, ref rhesserrors);
                    rmatrixfillsparsea(ref ra, mx, mx, 0.95);
                    cmatrixfillsparsea(ref ca, mx, mx, 0.95);
                    testrhessproblem(ra, mx, threshold, ref rhesserrors);
                    
                    //
                    // Symetric factorizations: tridiagonal
                    // Matrix types: zero, dense, sparse
                    //
                    ra = new double[mx, mx];
                    ca = new complex[mx, mx];
                    for(i=0; i<=mx-1; i++)
                    {
                        for(j=0; j<=mx-1; j++)
                        {
                            ra[i,j] = 0;
                            ca[i,j] = 0;
                        }
                    }
                    testrtdproblem(ra, mx, threshold, ref rtderrors);
                    testctdproblem(ca, mx, threshold, ref ctderrors);
                    for(i=0; i<=mx-1; i++)
                    {
                        for(j=i; j<=mx-1; j++)
                        {
                            ra[i,j] = 2*math.randomreal()-1;
                            ca[i,j].x = 2*math.randomreal()-1;
                            ca[i,j].y = 2*math.randomreal()-1;
                            ra[j,i] = ra[i,j];
                            ca[j,i] = math.conj(ca[i,j]);
                        }
                    }
                    for(i=0; i<=mx-1; i++)
                    {
                        ca[i,i] = 2*math.randomreal()-1;
                    }
                    testrtdproblem(ra, mx, threshold, ref rtderrors);
                    testctdproblem(ca, mx, threshold, ref ctderrors);
                    rmatrixfillsparsea(ref ra, mx, mx, 0.95);
                    cmatrixfillsparsea(ref ca, mx, mx, 0.95);
                    for(i=0; i<=mx-1; i++)
                    {
                        for(j=i; j<=mx-1; j++)
                        {
                            ra[j,i] = ra[i,j];
                            ca[j,i] = math.conj(ca[i,j]);
                        }
                    }
                    for(i=0; i<=mx-1; i++)
                    {
                        ca[i,i] = 2*math.randomreal()-1;
                    }
                    testrtdproblem(ra, mx, threshold, ref rtderrors);
                    testctdproblem(ca, mx, threshold, ref ctderrors);
                }
            }
            
            //
            // report
            //
            waserrors = ((((((rqrerrors | rlqerrors) | cqrerrors) | clqerrors) | rbderrors) | rhesserrors) | rtderrors) | ctderrors;
            if( !silent )
            {
                System.Console.Write("TESTING ORTFAC UNIT");
                System.Console.WriteLine();
                System.Console.Write("RQR ERRORS:                              ");
                if( !rqrerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("RLQ ERRORS:                              ");
                if( !rlqerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("CQR ERRORS:                              ");
                if( !cqrerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("CLQ ERRORS:                              ");
                if( !clqerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("RBD ERRORS:                              ");
                if( !rbderrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("RHESS ERRORS:                            ");
                if( !rhesserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("RTD ERRORS:                              ");
                if( !rtderrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("CTD ERRORS:                              ");
                if( !ctderrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Diff
        *************************************************************************/
        private static double rmatrixdiff(double[,] a,
            double[,] b,
            int m,
            int n)
        {
            double result = 0;
            int i = 0;
            int j = 0;

            result = 0;
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    result = Math.Max(result, Math.Abs(b[i,j]-a[i,j]));
                }
            }
            return result;
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void rmatrixmakeacopy(double[,] a,
            int m,
            int n,
            ref double[,] b)
        {
            int i = 0;
            int j = 0;

            b = new double[0,0];

            b = new double[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void cmatrixmakeacopy(complex[,] a,
            int m,
            int n,
            ref complex[,] b)
        {
            int i = 0;
            int j = 0;

            b = new complex[0,0];

            b = new complex[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        Sparse fill
        *************************************************************************/
        private static void rmatrixfillsparsea(ref double[,] a,
            int m,
            int n,
            double sparcity)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (double)(math.randomreal())>=(double)(sparcity) )
                    {
                        a[i,j] = 2*math.randomreal()-1;
                    }
                    else
                    {
                        a[i,j] = 0;
                    }
                }
            }
        }


        /*************************************************************************
        Sparse fill
        *************************************************************************/
        private static void cmatrixfillsparsea(ref complex[,] a,
            int m,
            int n,
            double sparcity)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (double)(math.randomreal())>=(double)(sparcity) )
                    {
                        a[i,j].x = 2*math.randomreal()-1;
                        a[i,j].y = 2*math.randomreal()-1;
                    }
                    else
                    {
                        a[i,j] = 0;
                    }
                }
            }
        }


        /*************************************************************************
        Matrix multiplication
        *************************************************************************/
        private static void internalmatrixmatrixmultiply(double[,] a,
            int ai1,
            int ai2,
            int aj1,
            int aj2,
            bool transa,
            double[,] b,
            int bi1,
            int bi2,
            int bj1,
            int bj2,
            bool transb,
            ref double[,] c,
            int ci1,
            int ci2,
            int cj1,
            int cj2)
        {
            int arows = 0;
            int acols = 0;
            int brows = 0;
            int bcols = 0;
            int crows = 0;
            int ccols = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int l = 0;
            int r = 0;
            double v = 0;
            double[] work = new double[0];
            double beta = 0;
            double alpha = 0;
            int i_ = 0;
            int i1_ = 0;

            
            //
            // Pre-setup
            //
            k = Math.Max(ai2-ai1+1, aj2-aj1+1);
            k = Math.Max(k, bi2-bi1+1);
            k = Math.Max(k, bj2-bj1+1);
            work = new double[k+1];
            beta = 0;
            alpha = 1;
            
            //
            // Setup
            //
            if( !transa )
            {
                arows = ai2-ai1+1;
                acols = aj2-aj1+1;
            }
            else
            {
                arows = aj2-aj1+1;
                acols = ai2-ai1+1;
            }
            if( !transb )
            {
                brows = bi2-bi1+1;
                bcols = bj2-bj1+1;
            }
            else
            {
                brows = bj2-bj1+1;
                bcols = bi2-bi1+1;
            }
            ap.assert(acols==brows, "MatrixMatrixMultiply: incorrect matrix sizes!");
            if( ((arows<=0 | acols<=0) | brows<=0) | bcols<=0 )
            {
                return;
            }
            crows = arows;
            ccols = bcols;
            
            //
            // Test WORK
            //
            i = Math.Max(arows, acols);
            i = Math.Max(brows, i);
            i = Math.Max(i, bcols);
            work[1] = 0;
            work[i] = 0;
            
            //
            // Prepare C
            //
            if( (double)(beta)==(double)(0) )
            {
                for(i=ci1; i<=ci2; i++)
                {
                    for(j=cj1; j<=cj2; j++)
                    {
                        c[i,j] = 0;
                    }
                }
            }
            else
            {
                for(i=ci1; i<=ci2; i++)
                {
                    for(i_=cj1; i_<=cj2;i_++)
                    {
                        c[i,i_] = beta*c[i,i_];
                    }
                }
            }
            
            //
            // A*B
            //
            if( !transa & !transb )
            {
                for(l=ai1; l<=ai2; l++)
                {
                    for(r=bi1; r<=bi2; r++)
                    {
                        v = alpha*a[l,aj1+r-bi1];
                        k = ci1+l-ai1;
                        i1_ = (bj1) - (cj1);
                        for(i_=cj1; i_<=cj2;i_++)
                        {
                            c[k,i_] = c[k,i_] + v*b[r,i_+i1_];
                        }
                    }
                }
                return;
            }
            
            //
            // A*B'
            //
            if( !transa & transb )
            {
                if( arows*acols<brows*bcols )
                {
                    for(r=bi1; r<=bi2; r++)
                    {
                        for(l=ai1; l<=ai2; l++)
                        {
                            i1_ = (bj1)-(aj1);
                            v = 0.0;
                            for(i_=aj1; i_<=aj2;i_++)
                            {
                                v += a[l,i_]*b[r,i_+i1_];
                            }
                            c[ci1+l-ai1,cj1+r-bi1] = c[ci1+l-ai1,cj1+r-bi1]+alpha*v;
                        }
                    }
                    return;
                }
                else
                {
                    for(l=ai1; l<=ai2; l++)
                    {
                        for(r=bi1; r<=bi2; r++)
                        {
                            i1_ = (bj1)-(aj1);
                            v = 0.0;
                            for(i_=aj1; i_<=aj2;i_++)
                            {
                                v += a[l,i_]*b[r,i_+i1_];
                            }
                            c[ci1+l-ai1,cj1+r-bi1] = c[ci1+l-ai1,cj1+r-bi1]+alpha*v;
                        }
                    }
                    return;
                }
            }
            
            //
            // A'*B
            //
            if( transa & !transb )
            {
                for(l=aj1; l<=aj2; l++)
                {
                    for(r=bi1; r<=bi2; r++)
                    {
                        v = alpha*a[ai1+r-bi1,l];
                        k = ci1+l-aj1;
                        i1_ = (bj1) - (cj1);
                        for(i_=cj1; i_<=cj2;i_++)
                        {
                            c[k,i_] = c[k,i_] + v*b[r,i_+i1_];
                        }
                    }
                }
                return;
            }
            
            //
            // A'*B'
            //
            if( transa & transb )
            {
                if( arows*acols<brows*bcols )
                {
                    for(r=bi1; r<=bi2; r++)
                    {
                        for(i=1; i<=crows; i++)
                        {
                            work[i] = 0.0;
                        }
                        for(l=ai1; l<=ai2; l++)
                        {
                            v = alpha*b[r,bj1+l-ai1];
                            k = cj1+r-bi1;
                            i1_ = (aj1) - (1);
                            for(i_=1; i_<=crows;i_++)
                            {
                                work[i_] = work[i_] + v*a[l,i_+i1_];
                            }
                        }
                        i1_ = (1) - (ci1);
                        for(i_=ci1; i_<=ci2;i_++)
                        {
                            c[i_,k] = c[i_,k] + work[i_+i1_];
                        }
                    }
                    return;
                }
                else
                {
                    for(l=aj1; l<=aj2; l++)
                    {
                        k = ai2-ai1+1;
                        i1_ = (ai1) - (1);
                        for(i_=1; i_<=k;i_++)
                        {
                            work[i_] = a[i_+i1_,l];
                        }
                        for(r=bi1; r<=bi2; r++)
                        {
                            i1_ = (bj1)-(1);
                            v = 0.0;
                            for(i_=1; i_<=k;i_++)
                            {
                                v += work[i_]*b[r,i_+i1_];
                            }
                            c[ci1+l-aj1,cj1+r-bi1] = c[ci1+l-aj1,cj1+r-bi1]+alpha*v;
                        }
                    }
                    return;
                }
            }
        }


        /*************************************************************************
        Problem testing
        *************************************************************************/
        private static void testrqrproblem(double[,] a,
            int m,
            int n,
            double threshold,
            ref bool qrerrors)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            double[,] b = new double[0,0];
            double[] taub = new double[0];
            double[,] q = new double[0,0];
            double[,] r = new double[0,0];
            double[,] q2 = new double[0,0];
            double v = 0;
            int i_ = 0;

            
            //
            // Test decompose-and-unpack error
            //
            rmatrixmakeacopy(a, m, n, ref b);
            ortfac.rmatrixqr(ref b, m, n, ref taub);
            ortfac.rmatrixqrunpackq(b, m, n, taub, m, ref q);
            ortfac.rmatrixqrunpackr(b, m, n, ref r);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        v += q[i,i_]*r[i_,j];
                    }
                    qrerrors = qrerrors | (double)(Math.Abs(v-a[i,j]))>(double)(threshold);
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=Math.Min(i, n-1)-1; j++)
                {
                    qrerrors = qrerrors | (double)(r[i,j])!=(double)(0);
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        v += q[i,i_]*q[j,i_];
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    qrerrors = qrerrors | (double)(Math.Abs(v))>=(double)(threshold);
                }
            }
            
            //
            // Test for other errors
            //
            k = 1+math.randominteger(m);
            ortfac.rmatrixqrunpackq(b, m, n, taub, k, ref q2);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=k-1; j++)
                {
                    qrerrors = qrerrors | (double)(Math.Abs(q2[i,j]-q[i,j]))>(double)(10*math.machineepsilon);
                }
            }
        }


        /*************************************************************************
        Problem testing
        *************************************************************************/
        private static void testcqrproblem(complex[,] a,
            int m,
            int n,
            double threshold,
            ref bool qrerrors)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            complex[,] b = new complex[0,0];
            complex[] taub = new complex[0];
            complex[,] q = new complex[0,0];
            complex[,] r = new complex[0,0];
            complex[,] q2 = new complex[0,0];
            complex v = 0;
            int i_ = 0;

            
            //
            // Test decompose-and-unpack error
            //
            cmatrixmakeacopy(a, m, n, ref b);
            ortfac.cmatrixqr(ref b, m, n, ref taub);
            ortfac.cmatrixqrunpackq(b, m, n, taub, m, ref q);
            ortfac.cmatrixqrunpackr(b, m, n, ref r);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        v += q[i,i_]*r[i_,j];
                    }
                    qrerrors = qrerrors | (double)(math.abscomplex(v-a[i,j]))>(double)(threshold);
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=Math.Min(i, n-1)-1; j++)
                {
                    qrerrors = qrerrors | r[i,j]!=0;
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        v += q[i,i_]*math.conj(q[j,i_]);
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    qrerrors = qrerrors | (double)(math.abscomplex(v))>=(double)(threshold);
                }
            }
            
            //
            // Test for other errors
            //
            k = 1+math.randominteger(m);
            ortfac.cmatrixqrunpackq(b, m, n, taub, k, ref q2);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=k-1; j++)
                {
                    qrerrors = qrerrors | (double)(math.abscomplex(q2[i,j]-q[i,j]))>(double)(10*math.machineepsilon);
                }
            }
        }


        /*************************************************************************
        Problem testing
        *************************************************************************/
        private static void testrlqproblem(double[,] a,
            int m,
            int n,
            double threshold,
            ref bool lqerrors)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            double[,] b = new double[0,0];
            double[] taub = new double[0];
            double[,] q = new double[0,0];
            double[,] l = new double[0,0];
            double[,] q2 = new double[0,0];
            double v = 0;
            int i_ = 0;

            
            //
            // Test decompose-and-unpack error
            //
            rmatrixmakeacopy(a, m, n, ref b);
            ortfac.rmatrixlq(ref b, m, n, ref taub);
            ortfac.rmatrixlqunpackq(b, m, n, taub, n, ref q);
            ortfac.rmatrixlqunpackl(b, m, n, ref l);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += l[i,i_]*q[i_,j];
                    }
                    lqerrors = lqerrors | (double)(Math.Abs(v-a[i,j]))>=(double)(threshold);
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=Math.Min(i, n-1)+1; j<=n-1; j++)
                {
                    lqerrors = lqerrors | (double)(l[i,j])!=(double)(0);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += q[i,i_]*q[j,i_];
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    lqerrors = lqerrors | (double)(Math.Abs(v))>=(double)(threshold);
                }
            }
            
            //
            // Test for other errors
            //
            k = 1+math.randominteger(n);
            ortfac.rmatrixlqunpackq(b, m, n, taub, k, ref q2);
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    lqerrors = lqerrors | (double)(Math.Abs(q2[i,j]-q[i,j]))>(double)(10*math.machineepsilon);
                }
            }
        }


        /*************************************************************************
        Problem testing
        *************************************************************************/
        private static void testclqproblem(complex[,] a,
            int m,
            int n,
            double threshold,
            ref bool lqerrors)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            complex[,] b = new complex[0,0];
            complex[] taub = new complex[0];
            complex[,] q = new complex[0,0];
            complex[,] l = new complex[0,0];
            complex[,] q2 = new complex[0,0];
            complex v = 0;
            int i_ = 0;

            
            //
            // Test decompose-and-unpack error
            //
            cmatrixmakeacopy(a, m, n, ref b);
            ortfac.cmatrixlq(ref b, m, n, ref taub);
            ortfac.cmatrixlqunpackq(b, m, n, taub, n, ref q);
            ortfac.cmatrixlqunpackl(b, m, n, ref l);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += l[i,i_]*q[i_,j];
                    }
                    lqerrors = lqerrors | (double)(math.abscomplex(v-a[i,j]))>=(double)(threshold);
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=Math.Min(i, n-1)+1; j<=n-1; j++)
                {
                    lqerrors = lqerrors | l[i,j]!=0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += q[i,i_]*math.conj(q[j,i_]);
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    lqerrors = lqerrors | (double)(math.abscomplex(v))>=(double)(threshold);
                }
            }
            
            //
            // Test for other errors
            //
            k = 1+math.randominteger(n);
            ortfac.cmatrixlqunpackq(b, m, n, taub, k, ref q2);
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    lqerrors = lqerrors | (double)(math.abscomplex(q2[i,j]-q[i,j]))>(double)(10*math.machineepsilon);
                }
            }
        }


        /*************************************************************************
        Problem testing
        *************************************************************************/
        private static void testrbdproblem(double[,] a,
            int m,
            int n,
            double threshold,
            ref bool bderrors)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            double[,] t = new double[0,0];
            double[,] pt = new double[0,0];
            double[,] q = new double[0,0];
            double[,] r = new double[0,0];
            double[,] bd = new double[0,0];
            double[,] x = new double[0,0];
            double[,] r1 = new double[0,0];
            double[,] r2 = new double[0,0];
            double[] taup = new double[0];
            double[] tauq = new double[0];
            double[] d = new double[0];
            double[] e = new double[0];
            bool up = new bool();
            double v = 0;
            int mtsize = 0;
            int i_ = 0;

            
            //
            // Bidiagonal decomposition error
            //
            rmatrixmakeacopy(a, m, n, ref t);
            ortfac.rmatrixbd(ref t, m, n, ref tauq, ref taup);
            ortfac.rmatrixbdunpackq(t, m, n, tauq, m, ref q);
            ortfac.rmatrixbdunpackpt(t, m, n, taup, n, ref pt);
            ortfac.rmatrixbdunpackdiagonals(t, m, n, ref up, ref d, ref e);
            bd = new double[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    bd[i,j] = 0;
                }
            }
            for(i=0; i<=Math.Min(m, n)-1; i++)
            {
                bd[i,i] = d[i];
            }
            if( up )
            {
                for(i=0; i<=Math.Min(m, n)-2; i++)
                {
                    bd[i,i+1] = e[i];
                }
            }
            else
            {
                for(i=0; i<=Math.Min(m, n)-2; i++)
                {
                    bd[i+1,i] = e[i];
                }
            }
            r = new double[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        v += q[i,i_]*bd[i_,j];
                    }
                    r[i,j] = v;
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += r[i,i_]*pt[i_,j];
                    }
                    bderrors = bderrors | (double)(Math.Abs(v-a[i,j]))>(double)(threshold);
                }
            }
            
            //
            // Orthogonality test for Q/PT
            //
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        v += q[i_,i]*q[i_,j];
                    }
                    if( i==j )
                    {
                        bderrors = bderrors | (double)(Math.Abs(v-1))>(double)(threshold);
                    }
                    else
                    {
                        bderrors = bderrors | (double)(Math.Abs(v))>(double)(threshold);
                    }
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += pt[i,i_]*pt[j,i_];
                    }
                    if( i==j )
                    {
                        bderrors = bderrors | (double)(Math.Abs(v-1))>(double)(threshold);
                    }
                    else
                    {
                        bderrors = bderrors | (double)(Math.Abs(v))>(double)(threshold);
                    }
                }
            }
            
            //
            // Partial unpacking test
            //
            k = 1+math.randominteger(m);
            ortfac.rmatrixbdunpackq(t, m, n, tauq, k, ref r);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=k-1; j++)
                {
                    bderrors = bderrors | (double)(Math.Abs(r[i,j]-q[i,j]))>(double)(10*math.machineepsilon);
                }
            }
            k = 1+math.randominteger(n);
            ortfac.rmatrixbdunpackpt(t, m, n, taup, k, ref r);
            for(i=0; i<=k-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    bderrors = bderrors | (double)(r[i,j]-pt[i,j])!=(double)(0);
                }
            }
            
            //
            // Multiplication test
            //
            x = new double[Math.Max(m, n)-1+1, Math.Max(m, n)-1+1];
            r = new double[Math.Max(m, n)-1+1, Math.Max(m, n)-1+1];
            r1 = new double[Math.Max(m, n)-1+1, Math.Max(m, n)-1+1];
            r2 = new double[Math.Max(m, n)-1+1, Math.Max(m, n)-1+1];
            for(i=0; i<=Math.Max(m, n)-1; i++)
            {
                for(j=0; j<=Math.Max(m, n)-1; j++)
                {
                    x[i,j] = 2*math.randomreal()-1;
                }
            }
            mtsize = 1+math.randominteger(Math.Max(m, n));
            rmatrixmakeacopy(x, mtsize, m, ref r);
            internalmatrixmatrixmultiply(r, 0, mtsize-1, 0, m-1, false, q, 0, m-1, 0, m-1, false, ref r1, 0, mtsize-1, 0, m-1);
            rmatrixmakeacopy(x, mtsize, m, ref r2);
            ortfac.rmatrixbdmultiplybyq(t, m, n, tauq, ref r2, mtsize, m, true, false);
            bderrors = bderrors | (double)(rmatrixdiff(r1, r2, mtsize, m))>(double)(threshold);
            rmatrixmakeacopy(x, mtsize, m, ref r);
            internalmatrixmatrixmultiply(r, 0, mtsize-1, 0, m-1, false, q, 0, m-1, 0, m-1, true, ref r1, 0, mtsize-1, 0, m-1);
            rmatrixmakeacopy(x, mtsize, m, ref r2);
            ortfac.rmatrixbdmultiplybyq(t, m, n, tauq, ref r2, mtsize, m, true, true);
            bderrors = bderrors | (double)(rmatrixdiff(r1, r2, mtsize, m))>(double)(threshold);
            rmatrixmakeacopy(x, m, mtsize, ref r);
            internalmatrixmatrixmultiply(q, 0, m-1, 0, m-1, false, r, 0, m-1, 0, mtsize-1, false, ref r1, 0, m-1, 0, mtsize-1);
            rmatrixmakeacopy(x, m, mtsize, ref r2);
            ortfac.rmatrixbdmultiplybyq(t, m, n, tauq, ref r2, m, mtsize, false, false);
            bderrors = bderrors | (double)(rmatrixdiff(r1, r2, m, mtsize))>(double)(threshold);
            rmatrixmakeacopy(x, m, mtsize, ref r);
            internalmatrixmatrixmultiply(q, 0, m-1, 0, m-1, true, r, 0, m-1, 0, mtsize-1, false, ref r1, 0, m-1, 0, mtsize-1);
            rmatrixmakeacopy(x, m, mtsize, ref r2);
            ortfac.rmatrixbdmultiplybyq(t, m, n, tauq, ref r2, m, mtsize, false, true);
            bderrors = bderrors | (double)(rmatrixdiff(r1, r2, m, mtsize))>(double)(threshold);
            rmatrixmakeacopy(x, mtsize, n, ref r);
            internalmatrixmatrixmultiply(r, 0, mtsize-1, 0, n-1, false, pt, 0, n-1, 0, n-1, true, ref r1, 0, mtsize-1, 0, n-1);
            rmatrixmakeacopy(x, mtsize, n, ref r2);
            ortfac.rmatrixbdmultiplybyp(t, m, n, taup, ref r2, mtsize, n, true, false);
            bderrors = bderrors | (double)(rmatrixdiff(r1, r2, mtsize, n))>(double)(threshold);
            rmatrixmakeacopy(x, mtsize, n, ref r);
            internalmatrixmatrixmultiply(r, 0, mtsize-1, 0, n-1, false, pt, 0, n-1, 0, n-1, false, ref r1, 0, mtsize-1, 0, n-1);
            rmatrixmakeacopy(x, mtsize, n, ref r2);
            ortfac.rmatrixbdmultiplybyp(t, m, n, taup, ref r2, mtsize, n, true, true);
            bderrors = bderrors | (double)(rmatrixdiff(r1, r2, mtsize, n))>(double)(threshold);
            rmatrixmakeacopy(x, n, mtsize, ref r);
            internalmatrixmatrixmultiply(pt, 0, n-1, 0, n-1, true, r, 0, n-1, 0, mtsize-1, false, ref r1, 0, n-1, 0, mtsize-1);
            rmatrixmakeacopy(x, n, mtsize, ref r2);
            ortfac.rmatrixbdmultiplybyp(t, m, n, taup, ref r2, n, mtsize, false, false);
            bderrors = bderrors | (double)(rmatrixdiff(r1, r2, n, mtsize))>(double)(threshold);
            rmatrixmakeacopy(x, n, mtsize, ref r);
            internalmatrixmatrixmultiply(pt, 0, n-1, 0, n-1, false, r, 0, n-1, 0, mtsize-1, false, ref r1, 0, n-1, 0, mtsize-1);
            rmatrixmakeacopy(x, n, mtsize, ref r2);
            ortfac.rmatrixbdmultiplybyp(t, m, n, taup, ref r2, n, mtsize, false, true);
            bderrors = bderrors | (double)(rmatrixdiff(r1, r2, n, mtsize))>(double)(threshold);
        }


        /*************************************************************************
        Problem testing
        *************************************************************************/
        private static void testrhessproblem(double[,] a,
            int n,
            double threshold,
            ref bool hesserrors)
        {
            double[,] b = new double[0,0];
            double[,] h = new double[0,0];
            double[,] q = new double[0,0];
            double[,] t1 = new double[0,0];
            double[,] t2 = new double[0,0];
            double[] tau = new double[0];
            int i = 0;
            int j = 0;
            double v = 0;
            int i_ = 0;

            rmatrixmakeacopy(a, n, n, ref b);
            
            //
            // Decomposition
            //
            ortfac.rmatrixhessenberg(ref b, n, ref tau);
            ortfac.rmatrixhessenbergunpackq(b, n, tau, ref q);
            ortfac.rmatrixhessenbergunpackh(b, n, ref h);
            
            //
            // Matrix properties
            //
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += q[i_,i]*q[i_,j];
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    hesserrors = hesserrors | (double)(Math.Abs(v))>(double)(threshold);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=i-2; j++)
                {
                    hesserrors = hesserrors | (double)(h[i,j])!=(double)(0);
                }
            }
            
            //
            // Decomposition error
            //
            t1 = new double[n, n];
            t2 = new double[n, n];
            internalmatrixmatrixmultiply(q, 0, n-1, 0, n-1, false, h, 0, n-1, 0, n-1, false, ref t1, 0, n-1, 0, n-1);
            internalmatrixmatrixmultiply(t1, 0, n-1, 0, n-1, false, q, 0, n-1, 0, n-1, true, ref t2, 0, n-1, 0, n-1);
            hesserrors = hesserrors | (double)(rmatrixdiff(t2, a, n, n))>(double)(threshold);
        }


        /*************************************************************************
        Tridiagonal tester
        *************************************************************************/
        private static void testrtdproblem(double[,] a,
            int n,
            double threshold,
            ref bool tderrors)
        {
            int i = 0;
            int j = 0;
            double[,] ua = new double[0,0];
            double[,] la = new double[0,0];
            double[,] t = new double[0,0];
            double[,] q = new double[0,0];
            double[,] t2 = new double[0,0];
            double[,] t3 = new double[0,0];
            double[] tau = new double[0];
            double[] d = new double[0];
            double[] e = new double[0];
            double v = 0;
            int i_ = 0;

            ua = new double[n-1+1, n-1+1];
            la = new double[n-1+1, n-1+1];
            t = new double[n-1+1, n-1+1];
            q = new double[n-1+1, n-1+1];
            t2 = new double[n-1+1, n-1+1];
            t3 = new double[n-1+1, n-1+1];
            
            //
            // fill
            //
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ua[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=i; j<=n-1; j++)
                {
                    ua[i,j] = a[i,j];
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    la[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=i; j++)
                {
                    la[i,j] = a[i,j];
                }
            }
            
            //
            // Test 2tridiagonal: upper
            //
            ortfac.smatrixtd(ref ua, n, true, ref tau, ref d, ref e);
            ortfac.smatrixtdunpackq(ua, n, true, tau, ref q);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    t[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                t[i,i] = d[i];
            }
            for(i=0; i<=n-2; i++)
            {
                t[i,i+1] = e[i];
                t[i+1,i] = e[i];
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += q[i_,i]*a[i_,j];
                    }
                    t2[i,j] = v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += t2[i,i_]*q[i_,j];
                    }
                    t3[i,j] = v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    tderrors = tderrors | (double)(Math.Abs(t3[i,j]-t[i,j]))>(double)(threshold);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += q[i,i_]*q[j,i_];
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    tderrors = tderrors | (double)(Math.Abs(v))>(double)(threshold);
                }
            }
            
            //
            // Test 2tridiagonal: lower
            //
            ortfac.smatrixtd(ref la, n, false, ref tau, ref d, ref e);
            ortfac.smatrixtdunpackq(la, n, false, tau, ref q);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    t[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                t[i,i] = d[i];
            }
            for(i=0; i<=n-2; i++)
            {
                t[i,i+1] = e[i];
                t[i+1,i] = e[i];
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += q[i_,i]*a[i_,j];
                    }
                    t2[i,j] = v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += t2[i,i_]*q[i_,j];
                    }
                    t3[i,j] = v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    tderrors = tderrors | (double)(Math.Abs(t3[i,j]-t[i,j]))>(double)(threshold);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += q[i,i_]*q[j,i_];
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    tderrors = tderrors | (double)(Math.Abs(v))>(double)(threshold);
                }
            }
        }


        /*************************************************************************
        Hermitian problem tester
        *************************************************************************/
        private static void testctdproblem(complex[,] a,
            int n,
            double threshold,
            ref bool tderrors)
        {
            int i = 0;
            int j = 0;
            complex[,] ua = new complex[0,0];
            complex[,] la = new complex[0,0];
            complex[,] t = new complex[0,0];
            complex[,] q = new complex[0,0];
            complex[,] t2 = new complex[0,0];
            complex[,] t3 = new complex[0,0];
            complex[] tau = new complex[0];
            double[] d = new double[0];
            double[] e = new double[0];
            complex v = 0;
            int i_ = 0;

            ua = new complex[n-1+1, n-1+1];
            la = new complex[n-1+1, n-1+1];
            t = new complex[n-1+1, n-1+1];
            q = new complex[n-1+1, n-1+1];
            t2 = new complex[n-1+1, n-1+1];
            t3 = new complex[n-1+1, n-1+1];
            
            //
            // fill
            //
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ua[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=i; j<=n-1; j++)
                {
                    ua[i,j] = a[i,j];
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    la[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=i; j++)
                {
                    la[i,j] = a[i,j];
                }
            }
            
            //
            // Test 2tridiagonal: upper
            //
            ortfac.hmatrixtd(ref ua, n, true, ref tau, ref d, ref e);
            ortfac.hmatrixtdunpackq(ua, n, true, tau, ref q);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    t[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                t[i,i] = d[i];
            }
            for(i=0; i<=n-2; i++)
            {
                t[i,i+1] = e[i];
                t[i+1,i] = e[i];
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += math.conj(q[i_,i])*a[i_,j];
                    }
                    t2[i,j] = v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += t2[i,i_]*q[i_,j];
                    }
                    t3[i,j] = v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    tderrors = tderrors | (double)(math.abscomplex(t3[i,j]-t[i,j]))>(double)(threshold);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += q[i,i_]*math.conj(q[j,i_]);
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    tderrors = tderrors | (double)(math.abscomplex(v))>(double)(threshold);
                }
            }
            
            //
            // Test 2tridiagonal: lower
            //
            ortfac.hmatrixtd(ref la, n, false, ref tau, ref d, ref e);
            ortfac.hmatrixtdunpackq(la, n, false, tau, ref q);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    t[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                t[i,i] = d[i];
            }
            for(i=0; i<=n-2; i++)
            {
                t[i,i+1] = e[i];
                t[i+1,i] = e[i];
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += math.conj(q[i_,i])*a[i_,j];
                    }
                    t2[i,j] = v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += t2[i,i_]*q[i_,j];
                    }
                    t3[i,j] = v;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    tderrors = tderrors | (double)(math.abscomplex(t3[i,j]-t[i,j]))>(double)(threshold);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += q[i,i_]*math.conj(q[j,i_]);
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    tderrors = tderrors | (double)(math.abscomplex(v))>(double)(threshold);
                }
            }
        }


    }
    public class testevdunit
    {
        /*************************************************************************
        Testing symmetric EVD subroutine
        *************************************************************************/
        public static bool testevd(bool silent)
        {
            bool result = new bool();
            double[,] ra = new double[0,0];
            int n = 0;
            int j = 0;
            int failc = 0;
            int runs = 0;
            double failthreshold = 0;
            double threshold = 0;
            double bithreshold = 0;
            bool waserrors = new bool();
            bool nserrors = new bool();
            bool serrors = new bool();
            bool herrors = new bool();
            bool tderrors = new bool();
            bool sbierrors = new bool();
            bool hbierrors = new bool();
            bool tdbierrors = new bool();
            bool wfailed = new bool();

            failthreshold = 0.005;
            threshold = 100000*math.machineepsilon;
            bithreshold = 1.0E-6;
            nserrors = false;
            serrors = false;
            herrors = false;
            tderrors = false;
            sbierrors = false;
            hbierrors = false;
            tdbierrors = false;
            failc = 0;
            runs = 0;
            
            //
            // Test problems
            //
            for(n=1; n<=ablas.ablasblocksize(ra); n++)
            {
                testevdset(n, threshold, bithreshold, ref failc, ref runs, ref nserrors, ref serrors, ref herrors, ref tderrors, ref sbierrors, ref hbierrors, ref tdbierrors);
            }
            for(j=2; j<=3; j++)
            {
                for(n=j*ablas.ablasblocksize(ra)-1; n<=j*ablas.ablasblocksize(ra)+1; n++)
                {
                    testevdset(n, threshold, bithreshold, ref failc, ref runs, ref nserrors, ref serrors, ref herrors, ref tderrors, ref sbierrors, ref hbierrors, ref tdbierrors);
                }
            }
            
            //
            // report
            //
            wfailed = (double)((double)failc/(double)runs)>(double)(failthreshold);
            waserrors = ((((((nserrors | serrors) | herrors) | tderrors) | sbierrors) | hbierrors) | tdbierrors) | wfailed;
            if( !silent )
            {
                System.Console.Write("TESTING EVD UNIT");
                System.Console.WriteLine();
                System.Console.Write("NS ERRORS:                               ");
                if( !nserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("S ERRORS:                                ");
                if( !serrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("H ERRORS:                                ");
                if( !herrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("TD ERRORS:                               ");
                if( !tderrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("SBI ERRORS:                              ");
                if( !sbierrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("HBI ERRORS:                              ");
                if( !hbierrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("TDBI ERRORS:                             ");
                if( !tdbierrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("FAILURE THRESHOLD:                       ");
                if( !wfailed )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Sparse fill
        *************************************************************************/
        private static void rmatrixfillsparsea(ref double[,] a,
            int m,
            int n,
            double sparcity)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (double)(math.randomreal())>=(double)(sparcity) )
                    {
                        a[i,j] = 2*math.randomreal()-1;
                    }
                    else
                    {
                        a[i,j] = 0;
                    }
                }
            }
        }


        /*************************************************************************
        Sparse fill
        *************************************************************************/
        private static void cmatrixfillsparsea(ref complex[,] a,
            int m,
            int n,
            double sparcity)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (double)(math.randomreal())>=(double)(sparcity) )
                    {
                        a[i,j].x = 2*math.randomreal()-1;
                        a[i,j].y = 2*math.randomreal()-1;
                    }
                    else
                    {
                        a[i,j] = 0;
                    }
                }
            }
        }


        /*************************************************************************
        Copies A to AL (lower half) and AU (upper half), filling unused parts by
        random garbage.
        *************************************************************************/
        private static void rmatrixsymmetricsplit(double[,] a,
            int n,
            ref double[,] al,
            ref double[,] au)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=i+1; j<=n-1; j++)
                {
                    al[i,j] = 2*math.randomreal()-1;
                    al[j,i] = a[i,j];
                    au[i,j] = a[i,j];
                    au[j,i] = 2*math.randomreal()-1;
                }
                al[i,i] = a[i,i];
                au[i,i] = a[i,i];
            }
        }


        /*************************************************************************
        Copies A to AL (lower half) and AU (upper half), filling unused parts by
        random garbage.
        *************************************************************************/
        private static void cmatrixhermitiansplit(complex[,] a,
            int n,
            ref complex[,] al,
            ref complex[,] au)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=i+1; j<=n-1; j++)
                {
                    al[i,j] = 2*math.randomreal()-1;
                    al[j,i] = math.conj(a[i,j]);
                    au[i,j] = a[i,j];
                    au[j,i] = 2*math.randomreal()-1;
                }
                al[i,i] = a[i,i];
                au[i,i] = a[i,i];
            }
        }


        /*************************************************************************
        Unsets 2D array.
        *************************************************************************/
        private static void unset2d(ref double[,] a)
        {
            a = new double[0+1, 0+1];
            a[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 2D array.
        *************************************************************************/
        private static void cunset2d(ref complex[,] a)
        {
            a = new complex[0+1, 0+1];
            a[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 1D array.
        *************************************************************************/
        private static void unset1d(ref double[] a)
        {
            a = new double[0+1];
            a[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 1D array.
        *************************************************************************/
        private static void cunset1d(ref complex[] a)
        {
            a = new complex[0+1];
            a[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Tests Z*Lambda*Z' against tridiag(D,E).
        Returns relative error.
        *************************************************************************/
        private static double tdtestproduct(double[] d,
            double[] e,
            int n,
            double[,] z,
            double[] lambdav)
        {
            double result = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            double mx = 0;

            result = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    //
                    // Calculate V = A[i,j], A = Z*Lambda*Z'
                    //
                    v = 0;
                    for(k=0; k<=n-1; k++)
                    {
                        v = v+z[i,k]*lambdav[k]*z[j,k];
                    }
                    
                    //
                    // Compare
                    //
                    if( Math.Abs(i-j)==0 )
                    {
                        result = Math.Max(result, Math.Abs(v-d[i]));
                    }
                    if( Math.Abs(i-j)==1 )
                    {
                        result = Math.Max(result, Math.Abs(v-e[Math.Min(i, j)]));
                    }
                    if( Math.Abs(i-j)>1 )
                    {
                        result = Math.Max(result, Math.Abs(v));
                    }
                }
            }
            mx = 0;
            for(i=0; i<=n-1; i++)
            {
                mx = Math.Max(mx, Math.Abs(d[i]));
            }
            for(i=0; i<=n-2; i++)
            {
                mx = Math.Max(mx, Math.Abs(e[i]));
            }
            if( (double)(mx)==(double)(0) )
            {
                mx = 1;
            }
            result = result/mx;
            return result;
        }


        /*************************************************************************
        Tests Z*Lambda*Z' against A
        Returns relative error.
        *************************************************************************/
        private static double testproduct(double[,] a,
            int n,
            double[,] z,
            double[] lambdav)
        {
            double result = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            double mx = 0;

            result = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    //
                    // Calculate V = A[i,j], A = Z*Lambda*Z'
                    //
                    v = 0;
                    for(k=0; k<=n-1; k++)
                    {
                        v = v+z[i,k]*lambdav[k]*z[j,k];
                    }
                    
                    //
                    // Compare
                    //
                    result = Math.Max(result, Math.Abs(v-a[i,j]));
                }
            }
            mx = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    mx = Math.Max(mx, Math.Abs(a[i,j]));
                }
            }
            if( (double)(mx)==(double)(0) )
            {
                mx = 1;
            }
            result = result/mx;
            return result;
        }


        /*************************************************************************
        Tests Z*Z' against diag(1...1)
        Returns absolute error.
        *************************************************************************/
        private static double testort(double[,] z,
            int n)
        {
            double result = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            int i_ = 0;

            result = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,i]*z[i_,j];
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    result = Math.Max(result, Math.Abs(v));
                }
            }
            return result;
        }


        /*************************************************************************
        Tests Z*Lambda*Z' against A
        Returns relative error.
        *************************************************************************/
        private static double testcproduct(complex[,] a,
            int n,
            complex[,] z,
            double[] lambdav)
        {
            double result = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            complex v = 0;
            double mx = 0;

            result = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    //
                    // Calculate V = A[i,j], A = Z*Lambda*Z'
                    //
                    v = 0;
                    for(k=0; k<=n-1; k++)
                    {
                        v = v+z[i,k]*lambdav[k]*math.conj(z[j,k]);
                    }
                    
                    //
                    // Compare
                    //
                    result = Math.Max(result, math.abscomplex(v-a[i,j]));
                }
            }
            mx = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    mx = Math.Max(mx, math.abscomplex(a[i,j]));
                }
            }
            if( (double)(mx)==(double)(0) )
            {
                mx = 1;
            }
            result = result/mx;
            return result;
        }


        /*************************************************************************
        Tests Z*Z' against diag(1...1)
        Returns absolute error.
        *************************************************************************/
        private static double testcort(complex[,] z,
            int n)
        {
            double result = 0;
            int i = 0;
            int j = 0;
            complex v = 0;
            int i_ = 0;

            result = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,i]*math.conj(z[i_,j]);
                    }
                    if( i==j )
                    {
                        v = v-1;
                    }
                    result = Math.Max(result, math.abscomplex(v));
                }
            }
            return result;
        }


        /*************************************************************************
        Tests SEVD problem
        *************************************************************************/
        private static void testsevdproblem(double[,] a,
            double[,] al,
            double[,] au,
            int n,
            double threshold,
            ref bool serrors,
            ref int failc,
            ref int runs)
        {
            double[] lambdav = new double[0];
            double[] lambdaref = new double[0];
            double[,] z = new double[0,0];
            int i = 0;

            
            //
            // Test simple EVD: values and full vectors, lower A
            //
            unset1d(ref lambdaref);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevd(al, n, 1, false, ref lambdaref, ref z) )
            {
                failc = failc+1;
                return;
            }
            serrors = serrors | (double)(testproduct(a, n, z, lambdaref))>(double)(threshold);
            serrors = serrors | (double)(testort(z, n))>(double)(threshold);
            for(i=0; i<=n-2; i++)
            {
                if( (double)(lambdaref[i+1])<(double)(lambdaref[i]) )
                {
                    serrors = true;
                    return;
                }
            }
            
            //
            // Test simple EVD: values and full vectors, upper A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevd(au, n, 1, true, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            serrors = serrors | (double)(testproduct(a, n, z, lambdav))>(double)(threshold);
            serrors = serrors | (double)(testort(z, n))>(double)(threshold);
            for(i=0; i<=n-2; i++)
            {
                if( (double)(lambdav[i+1])<(double)(lambdav[i]) )
                {
                    serrors = true;
                    return;
                }
            }
            
            //
            // Test simple EVD: values only, lower A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevd(al, n, 0, false, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[i]-lambdaref[i]))>(double)(threshold);
            }
            
            //
            // Test simple EVD: values only, upper A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevd(au, n, 0, true, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[i]-lambdaref[i]))>(double)(threshold);
            }
        }


        /*************************************************************************
        Tests SEVD problem
        *************************************************************************/
        private static void testhevdproblem(complex[,] a,
            complex[,] al,
            complex[,] au,
            int n,
            double threshold,
            ref bool herrors,
            ref int failc,
            ref int runs)
        {
            double[] lambdav = new double[0];
            double[] lambdaref = new double[0];
            complex[,] z = new complex[0,0];
            int i = 0;

            
            //
            // Test simple EVD: values and full vectors, lower A
            //
            unset1d(ref lambdaref);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevd(al, n, 1, false, ref lambdaref, ref z) )
            {
                failc = failc+1;
                return;
            }
            herrors = herrors | (double)(testcproduct(a, n, z, lambdaref))>(double)(threshold);
            herrors = herrors | (double)(testcort(z, n))>(double)(threshold);
            for(i=0; i<=n-2; i++)
            {
                if( (double)(lambdaref[i+1])<(double)(lambdaref[i]) )
                {
                    herrors = true;
                    return;
                }
            }
            
            //
            // Test simple EVD: values and full vectors, upper A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevd(au, n, 1, true, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            herrors = herrors | (double)(testcproduct(a, n, z, lambdav))>(double)(threshold);
            herrors = herrors | (double)(testcort(z, n))>(double)(threshold);
            for(i=0; i<=n-2; i++)
            {
                if( (double)(lambdav[i+1])<(double)(lambdav[i]) )
                {
                    herrors = true;
                    return;
                }
            }
            
            //
            // Test simple EVD: values only, lower A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevd(al, n, 0, false, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[i]-lambdaref[i]))>(double)(threshold);
            }
            
            //
            // Test simple EVD: values only, upper A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevd(au, n, 0, true, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[i]-lambdaref[i]))>(double)(threshold);
            }
        }


        /*************************************************************************
        Tests EVD problem

        DistVals    -   is True, when eigenvalues are distinct. Is False, when we
                        are solving sparse task with  lots  of  zero  eigenvalues.
                        In such cases some tests related to the  eigenvectors  are
                        not performed.
        *************************************************************************/
        private static void testsevdbiproblem(double[,] afull,
            double[,] al,
            double[,] au,
            int n,
            bool distvals,
            double threshold,
            ref bool serrors,
            ref int failc,
            ref int runs)
        {
            double[] lambdav = new double[0];
            double[] lambdaref = new double[0];
            double[,] z = new double[0,0];
            double[,] zref = new double[0,0];
            double[,] a1 = new double[0,0];
            double[,] a2 = new double[0,0];
            double[,] ar = new double[0,0];
            int i = 0;
            int j = 0;
            int k = 0;
            int m = 0;
            int i1 = 0;
            int i2 = 0;
            double v = 0;
            double a = 0;
            double b = 0;
            int i_ = 0;

            lambdaref = new double[n-1+1];
            zref = new double[n-1+1, n-1+1];
            a1 = new double[n-1+1, n-1+1];
            a2 = new double[n-1+1, n-1+1];
            
            //
            // Reference EVD
            //
            runs = runs+1;
            if( !evd.smatrixevd(afull, n, 1, true, ref lambdaref, ref zref) )
            {
                failc = failc+1;
                return;
            }
            
            //
            // Select random interval boundaries.
            // If there are non-distinct eigenvalues at the boundaries,
            // we move indexes further until values splits. It is done to
            // avoid situations where we can't get definite answer.
            //
            i1 = math.randominteger(n);
            i2 = i1+math.randominteger(n-i1);
            while( i1>0 )
            {
                if( (double)(Math.Abs(lambdaref[i1-1]-lambdaref[i1]))>(double)(10*threshold) )
                {
                    break;
                }
                i1 = i1-1;
            }
            while( i2<n-1 )
            {
                if( (double)(Math.Abs(lambdaref[i2+1]-lambdaref[i2]))>(double)(10*threshold) )
                {
                    break;
                }
                i2 = i2+1;
            }
            
            //
            // Select A, B
            //
            if( i1>0 )
            {
                a = 0.5*(lambdaref[i1]+lambdaref[i1-1]);
            }
            else
            {
                a = lambdaref[0]-1;
            }
            if( i2<n-1 )
            {
                b = 0.5*(lambdaref[i2]+lambdaref[i2+1]);
            }
            else
            {
                b = lambdaref[n-1]+1;
            }
            
            //
            // Test interval, no vectors, lower A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevdr(al, n, 0, false, a, b, ref m, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test interval, no vectors, upper A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevdr(au, n, 0, true, a, b, ref m, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test indexes, no vectors, lower A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevdi(al, n, 0, false, i1, i2, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test indexes, no vectors, upper A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevdi(au, n, 0, true, i1, i2, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test interval, vectors, lower A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevdr(al, n, 1, false, a, b, ref m, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                
                //
                // Distinct eigenvalues, test vectors
                //
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*zref[i_,i1+j];
                    }
                    if( (double)(v)<(double)(0) )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            z[i_,j] = -1*z[i_,j];
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        serrors = serrors | (double)(Math.Abs(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
            
            //
            // Test interval, vectors, upper A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevdr(au, n, 1, true, a, b, ref m, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                
                //
                // Distinct eigenvalues, test vectors
                //
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*zref[i_,i1+j];
                    }
                    if( (double)(v)<(double)(0) )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            z[i_,j] = -1*z[i_,j];
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        serrors = serrors | (double)(Math.Abs(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
            
            //
            // Test indexes, vectors, lower A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevdi(al, n, 1, false, i1, i2, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                
                //
                // Distinct eigenvalues, test vectors
                //
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*zref[i_,i1+j];
                    }
                    if( (double)(v)<(double)(0) )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            z[i_,j] = -1*z[i_,j];
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        serrors = serrors | (double)(Math.Abs(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
            
            //
            // Test indexes, vectors, upper A
            //
            unset1d(ref lambdav);
            unset2d(ref z);
            runs = runs+1;
            if( !evd.smatrixevdi(au, n, 1, true, i1, i2, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                
                //
                // Distinct eigenvalues, test vectors
                //
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*zref[i_,i1+j];
                    }
                    if( (double)(v)<(double)(0) )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            z[i_,j] = -1*z[i_,j];
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        serrors = serrors | (double)(Math.Abs(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
        }


        /*************************************************************************
        Tests EVD problem

        DistVals    -   is True, when eigenvalues are distinct. Is False, when we
                        are solving sparse task with  lots  of  zero  eigenvalues.
                        In such cases some tests related to the  eigenvectors  are
                        not performed.
        *************************************************************************/
        private static void testhevdbiproblem(complex[,] afull,
            complex[,] al,
            complex[,] au,
            int n,
            bool distvals,
            double threshold,
            ref bool herrors,
            ref int failc,
            ref int runs)
        {
            double[] lambdav = new double[0];
            double[] lambdaref = new double[0];
            complex[,] z = new complex[0,0];
            complex[,] zref = new complex[0,0];
            complex[,] a1 = new complex[0,0];
            complex[,] a2 = new complex[0,0];
            complex[,] ar = new complex[0,0];
            int i = 0;
            int j = 0;
            int k = 0;
            int m = 0;
            int i1 = 0;
            int i2 = 0;
            complex v = 0;
            double a = 0;
            double b = 0;
            int i_ = 0;

            lambdaref = new double[n-1+1];
            zref = new complex[n-1+1, n-1+1];
            a1 = new complex[n-1+1, n-1+1];
            a2 = new complex[n-1+1, n-1+1];
            
            //
            // Reference EVD
            //
            runs = runs+1;
            if( !evd.hmatrixevd(afull, n, 1, true, ref lambdaref, ref zref) )
            {
                failc = failc+1;
                return;
            }
            
            //
            // Select random interval boundaries.
            // If there are non-distinct eigenvalues at the boundaries,
            // we move indexes further until values splits. It is done to
            // avoid situations where we can't get definite answer.
            //
            i1 = math.randominteger(n);
            i2 = i1+math.randominteger(n-i1);
            while( i1>0 )
            {
                if( (double)(Math.Abs(lambdaref[i1-1]-lambdaref[i1]))>(double)(10*threshold) )
                {
                    break;
                }
                i1 = i1-1;
            }
            while( i2<n-1 )
            {
                if( (double)(Math.Abs(lambdaref[i2+1]-lambdaref[i2]))>(double)(10*threshold) )
                {
                    break;
                }
                i2 = i2+1;
            }
            
            //
            // Select A, B
            //
            if( i1>0 )
            {
                a = 0.5*(lambdaref[i1]+lambdaref[i1-1]);
            }
            else
            {
                a = lambdaref[0]-1;
            }
            if( i2<n-1 )
            {
                b = 0.5*(lambdaref[i2]+lambdaref[i2+1]);
            }
            else
            {
                b = lambdaref[n-1]+1;
            }
            
            //
            // Test interval, no vectors, lower A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevdr(al, n, 0, false, a, b, ref m, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test interval, no vectors, upper A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevdr(au, n, 0, true, a, b, ref m, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test indexes, no vectors, lower A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevdi(al, n, 0, false, i1, i2, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test indexes, no vectors, upper A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevdi(au, n, 0, true, i1, i2, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test interval, vectors, lower A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevdr(al, n, 1, false, a, b, ref m, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                
                //
                // Distinct eigenvalues, test vectors
                //
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*math.conj(zref[i_,i1+j]);
                    }
                    v = math.conj(v/math.abscomplex(v));
                    for(i_=0; i_<=n-1;i_++)
                    {
                        z[i_,j] = v*z[i_,j];
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        herrors = herrors | (double)(math.abscomplex(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
            
            //
            // Test interval, vectors, upper A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevdr(au, n, 1, true, a, b, ref m, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                
                //
                // Distinct eigenvalues, test vectors
                //
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*math.conj(zref[i_,i1+j]);
                    }
                    v = math.conj(v/math.abscomplex(v));
                    for(i_=0; i_<=n-1;i_++)
                    {
                        z[i_,j] = v*z[i_,j];
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        herrors = herrors | (double)(math.abscomplex(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
            
            //
            // Test indexes, vectors, lower A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevdi(al, n, 1, false, i1, i2, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                
                //
                // Distinct eigenvalues, test vectors
                //
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*math.conj(zref[i_,i1+j]);
                    }
                    v = math.conj(v/math.abscomplex(v));
                    for(i_=0; i_<=n-1;i_++)
                    {
                        z[i_,j] = v*z[i_,j];
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        herrors = herrors | (double)(math.abscomplex(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
            
            //
            // Test indexes, vectors, upper A
            //
            unset1d(ref lambdav);
            cunset2d(ref z);
            runs = runs+1;
            if( !evd.hmatrixevdi(au, n, 1, true, i1, i2, ref lambdav, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                herrors = herrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                
                //
                // Distinct eigenvalues, test vectors
                //
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*math.conj(zref[i_,i1+j]);
                    }
                    v = math.conj(v/math.abscomplex(v));
                    for(i_=0; i_<=n-1;i_++)
                    {
                        z[i_,j] = v*z[i_,j];
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        herrors = herrors | (double)(math.abscomplex(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
        }


        /*************************************************************************
        Tests EVD problem
        *************************************************************************/
        private static void testtdevdproblem(double[] d,
            double[] e,
            int n,
            double threshold,
            ref bool tderrors,
            ref int failc,
            ref int runs)
        {
            double[] lambdav = new double[0];
            double[] ee = new double[0];
            double[] lambda2 = new double[0];
            double[,] z = new double[0,0];
            double[,] zref = new double[0,0];
            double[,] a1 = new double[0,0];
            double[,] a2 = new double[0,0];
            bool wsucc = new bool();
            int i = 0;
            int j = 0;
            double v = 0;
            int i_ = 0;

            lambdav = new double[n-1+1];
            lambda2 = new double[n-1+1];
            zref = new double[n-1+1, n-1+1];
            a1 = new double[n-1+1, n-1+1];
            a2 = new double[n-1+1, n-1+1];
            if( n>1 )
            {
                ee = new double[n-2+1];
            }
            
            //
            // Test simple EVD: values and full vectors
            //
            for(i=0; i<=n-1; i++)
            {
                lambdav[i] = d[i];
            }
            for(i=0; i<=n-2; i++)
            {
                ee[i] = e[i];
            }
            runs = runs+1;
            wsucc = evd.smatrixtdevd(ref lambdav, ee, n, 2, ref z);
            if( !wsucc )
            {
                failc = failc+1;
                return;
            }
            tderrors = tderrors | (double)(tdtestproduct(d, e, n, z, lambdav))>(double)(threshold);
            tderrors = tderrors | (double)(testort(z, n))>(double)(threshold);
            for(i=0; i<=n-2; i++)
            {
                if( (double)(lambdav[i+1])<(double)(lambdav[i]) )
                {
                    tderrors = true;
                    return;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    zref[i,j] = z[i,j];
                }
            }
            
            //
            // Test values only variant
            //
            for(i=0; i<=n-1; i++)
            {
                lambda2[i] = d[i];
            }
            for(i=0; i<=n-2; i++)
            {
                ee[i] = e[i];
            }
            runs = runs+1;
            wsucc = evd.smatrixtdevd(ref lambda2, ee, n, 0, ref z);
            if( !wsucc )
            {
                failc = failc+1;
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                tderrors = tderrors | (double)(Math.Abs(lambda2[i]-lambdav[i]))>(double)(threshold);
            }
            
            //
            // Test multiplication variant
            //
            for(i=0; i<=n-1; i++)
            {
                lambda2[i] = d[i];
            }
            for(i=0; i<=n-2; i++)
            {
                ee[i] = e[i];
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a1[i,j] = 2*math.randomreal()-1;
                    a2[i,j] = a1[i,j];
                }
            }
            runs = runs+1;
            wsucc = evd.smatrixtdevd(ref lambda2, ee, n, 1, ref a1);
            if( !wsucc )
            {
                failc = failc+1;
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                tderrors = tderrors | (double)(Math.Abs(lambda2[i]-lambdav[i]))>(double)(threshold);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += a2[i,i_]*zref[i_,j];
                    }
                    
                    //
                    // next line is a bit complicated because
                    // depending on algorithm used we can get either
                    // z or -z as eigenvector. so we compare result
                    // with both A*ZRef and -A*ZRef
                    //
                    tderrors = tderrors | ((double)(Math.Abs(v-a1[i,j]))>(double)(threshold) & (double)(Math.Abs(v+a1[i,j]))>(double)(threshold));
                }
            }
            
            //
            // Test first row variant
            //
            for(i=0; i<=n-1; i++)
            {
                lambda2[i] = d[i];
            }
            for(i=0; i<=n-2; i++)
            {
                ee[i] = e[i];
            }
            runs = runs+1;
            wsucc = evd.smatrixtdevd(ref lambda2, ee, n, 3, ref z);
            if( !wsucc )
            {
                failc = failc+1;
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                tderrors = tderrors | (double)(Math.Abs(lambda2[i]-lambdav[i]))>(double)(threshold);
                
                //
                // next line is a bit complicated because
                // depending on algorithm used we can get either
                // z or -z as eigenvector. so we compare result
                // with both z and -z
                //
                tderrors = tderrors | ((double)(Math.Abs(z[0,i]-zref[0,i]))>(double)(threshold) & (double)(Math.Abs(z[0,i]+zref[0,i]))>(double)(threshold));
            }
        }


        /*************************************************************************
        Tests EVD problem

        DistVals    -   is True, when eigenvalues are distinct. Is False, when we
                        are solving sparse task with  lots  of  zero  eigenvalues.
                        In such cases some tests related to the  eigenvectors  are
                        not performed.
        *************************************************************************/
        private static void testtdevdbiproblem(double[] d,
            double[] e,
            int n,
            bool distvals,
            double threshold,
            ref bool serrors,
            ref int failc,
            ref int runs)
        {
            double[] lambdav = new double[0];
            double[] lambdaref = new double[0];
            double[,] z = new double[0,0];
            double[,] zref = new double[0,0];
            double[,] a1 = new double[0,0];
            double[,] a2 = new double[0,0];
            double[,] ar = new double[0,0];
            int i = 0;
            int j = 0;
            int k = 0;
            int m = 0;
            int i1 = 0;
            int i2 = 0;
            double v = 0;
            double a = 0;
            double b = 0;
            int i_ = 0;

            lambdaref = new double[n-1+1];
            zref = new double[n-1+1, n-1+1];
            a1 = new double[n-1+1, n-1+1];
            a2 = new double[n-1+1, n-1+1];
            
            //
            // Reference EVD
            //
            lambdaref = new double[n];
            for(i_=0; i_<=n-1;i_++)
            {
                lambdaref[i_] = d[i_];
            }
            runs = runs+1;
            if( !evd.smatrixtdevd(ref lambdaref, e, n, 2, ref zref) )
            {
                failc = failc+1;
                return;
            }
            
            //
            // Select random interval boundaries.
            // If there are non-distinct eigenvalues at the boundaries,
            // we move indexes further until values splits. It is done to
            // avoid situations where we can't get definite answer.
            //
            i1 = math.randominteger(n);
            i2 = i1+math.randominteger(n-i1);
            while( i1>0 )
            {
                if( (double)(Math.Abs(lambdaref[i1-1]-lambdaref[i1]))>(double)(10*threshold) )
                {
                    break;
                }
                i1 = i1-1;
            }
            while( i2<n-1 )
            {
                if( (double)(Math.Abs(lambdaref[i2+1]-lambdaref[i2]))>(double)(10*threshold) )
                {
                    break;
                }
                i2 = i2+1;
            }
            
            //
            // Test different combinations
            //
            
            //
            // Select A, B
            //
            if( i1>0 )
            {
                a = 0.5*(lambdaref[i1]+lambdaref[i1-1]);
            }
            else
            {
                a = lambdaref[0]-1;
            }
            if( i2<n-1 )
            {
                b = 0.5*(lambdaref[i2]+lambdaref[i2+1]);
            }
            else
            {
                b = lambdaref[n-1]+1;
            }
            
            //
            // Test interval, no vectors
            //
            lambdav = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                lambdav[i] = d[i];
            }
            runs = runs+1;
            if( !evd.smatrixtdevdr(ref lambdav, e, n, 0, a, b, ref m, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test indexes, no vectors
            //
            lambdav = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                lambdav[i] = d[i];
            }
            runs = runs+1;
            if( !evd.smatrixtdevdi(ref lambdav, e, n, 0, i1, i2, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            
            //
            // Test interval, transform vectors
            //
            lambdav = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                lambdav[i] = d[i];
            }
            a1 = new double[n-1+1, n-1+1];
            a2 = new double[n-1+1, n-1+1];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a1[i,j] = 2*math.randomreal()-1;
                    a2[i,j] = a1[i,j];
                }
            }
            runs = runs+1;
            if( !evd.smatrixtdevdr(ref lambdav, e, n, 1, a, b, ref m, ref a1) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                ar = new double[n-1+1, m-1+1];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a2[i,i_]*zref[i_,i1+j];
                        }
                        ar[i,j] = v;
                    }
                }
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += a1[i_,j]*ar[i_,j];
                    }
                    if( (double)(v)<(double)(0) )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            ar[i_,j] = -1*ar[i_,j];
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        serrors = serrors | (double)(Math.Abs(a1[i,j]-ar[i,j]))>(double)(threshold);
                    }
                }
            }
            
            //
            // Test indexes, transform vectors
            //
            lambdav = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                lambdav[i] = d[i];
            }
            a1 = new double[n-1+1, n-1+1];
            a2 = new double[n-1+1, n-1+1];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a1[i,j] = 2*math.randomreal()-1;
                    a2[i,j] = a1[i,j];
                }
            }
            runs = runs+1;
            if( !evd.smatrixtdevdi(ref lambdav, e, n, 1, i1, i2, ref a1) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                ar = new double[n-1+1, m-1+1];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a2[i,i_]*zref[i_,i1+j];
                        }
                        ar[i,j] = v;
                    }
                }
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += a1[i_,j]*ar[i_,j];
                    }
                    if( (double)(v)<(double)(0) )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            ar[i_,j] = -1*ar[i_,j];
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        serrors = serrors | (double)(Math.Abs(a1[i,j]-ar[i,j]))>(double)(threshold);
                    }
                }
            }
            
            //
            // Test interval, do not transform vectors
            //
            lambdav = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                lambdav[i] = d[i];
            }
            z = new double[0+1, 0+1];
            runs = runs+1;
            if( !evd.smatrixtdevdr(ref lambdav, e, n, 2, a, b, ref m, ref z) )
            {
                failc = failc+1;
                return;
            }
            if( m!=i2-i1+1 )
            {
                failc = failc+1;
                return;
            }
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*zref[i_,i1+j];
                    }
                    if( (double)(v)<(double)(0) )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            z[i_,j] = -1*z[i_,j];
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        serrors = serrors | (double)(Math.Abs(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
            
            //
            // Test indexes, do not transform vectors
            //
            lambdav = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                lambdav[i] = d[i];
            }
            z = new double[0+1, 0+1];
            runs = runs+1;
            if( !evd.smatrixtdevdi(ref lambdav, e, n, 2, i1, i2, ref z) )
            {
                failc = failc+1;
                return;
            }
            m = i2-i1+1;
            for(k=0; k<=m-1; k++)
            {
                serrors = serrors | (double)(Math.Abs(lambdav[k]-lambdaref[i1+k]))>(double)(threshold);
            }
            if( distvals )
            {
                for(j=0; j<=m-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += z[i_,j]*zref[i_,i1+j];
                    }
                    if( (double)(v)<(double)(0) )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            z[i_,j] = -1*z[i_,j];
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        serrors = serrors | (double)(Math.Abs(z[i,j]-zref[i,i1+j]))>(double)(threshold);
                    }
                }
            }
        }


        /*************************************************************************
        Non-symmetric problem
        *************************************************************************/
        private static void testnsevdproblem(double[,] a,
            int n,
            double threshold,
            ref bool nserrors,
            ref int failc,
            ref int runs)
        {
            double mx = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int vjob = 0;
            bool needl = new bool();
            bool needr = new bool();
            double[] wr0 = new double[0];
            double[] wi0 = new double[0];
            double[] wr1 = new double[0];
            double[] wi1 = new double[0];
            double[] wr0s = new double[0];
            double[] wi0s = new double[0];
            double[] wr1s = new double[0];
            double[] wi1s = new double[0];
            double[,] vl = new double[0,0];
            double[,] vr = new double[0,0];
            double[] vec1r = new double[0];
            double[] vec1i = new double[0];
            double[] vec2r = new double[0];
            double[] vec2i = new double[0];
            double[] vec3r = new double[0];
            double[] vec3i = new double[0];
            double curwr = 0;
            double curwi = 0;
            double vt = 0;
            double tmp = 0;
            int i_ = 0;

            vec1r = new double[n-1+1];
            vec2r = new double[n-1+1];
            vec3r = new double[n-1+1];
            vec1i = new double[n-1+1];
            vec2i = new double[n-1+1];
            vec3i = new double[n-1+1];
            wr0s = new double[n-1+1];
            wr1s = new double[n-1+1];
            wi0s = new double[n-1+1];
            wi1s = new double[n-1+1];
            mx = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (double)(Math.Abs(a[i,j]))>(double)(mx) )
                    {
                        mx = Math.Abs(a[i,j]);
                    }
                }
            }
            if( (double)(mx)==(double)(0) )
            {
                mx = 1;
            }
            
            //
            // Load values-only
            //
            runs = runs+1;
            if( !evd.rmatrixevd(a, n, 0, ref wr0, ref wi0, ref vl, ref vr) )
            {
                failc = failc+1;
                return;
            }
            
            //
            // Test different jobs
            //
            for(vjob=1; vjob<=3; vjob++)
            {
                needr = vjob==1 | vjob==3;
                needl = vjob==2 | vjob==3;
                runs = runs+1;
                if( !evd.rmatrixevd(a, n, vjob, ref wr1, ref wi1, ref vl, ref vr) )
                {
                    failc = failc+1;
                    return;
                }
                
                //
                // Test values:
                // 1. sort by real part
                // 2. test
                //
                for(i_=0; i_<=n-1;i_++)
                {
                    wr0s[i_] = wr0[i_];
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    wi0s[i_] = wi0[i_];
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-2-i; j++)
                    {
                        if( (double)(wr0s[j])>(double)(wr0s[j+1]) )
                        {
                            tmp = wr0s[j];
                            wr0s[j] = wr0s[j+1];
                            wr0s[j+1] = tmp;
                            tmp = wi0s[j];
                            wi0s[j] = wi0s[j+1];
                            wi0s[j+1] = tmp;
                        }
                    }
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    wr1s[i_] = wr1[i_];
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    wi1s[i_] = wi1[i_];
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-2-i; j++)
                    {
                        if( (double)(wr1s[j])>(double)(wr1s[j+1]) )
                        {
                            tmp = wr1s[j];
                            wr1s[j] = wr1s[j+1];
                            wr1s[j+1] = tmp;
                            tmp = wi1s[j];
                            wi1s[j] = wi1s[j+1];
                            wi1s[j+1] = tmp;
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    nserrors = nserrors | (double)(Math.Abs(wr0s[i]-wr1s[i]))>(double)(threshold);
                    nserrors = nserrors | (double)(Math.Abs(wi0s[i]-wi1s[i]))>(double)(threshold);
                }
                
                //
                // Test right vectors
                //
                if( needr )
                {
                    k = 0;
                    while( k<=n-1 )
                    {
                        if( (double)(wi1[k])==(double)(0) )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1r[i_] = vr[i_,k];
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                vec1i[i] = 0;
                            }
                            curwr = wr1[k];
                            curwi = 0;
                        }
                        if( (double)(wi1[k])>(double)(0) )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1r[i_] = vr[i_,k];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1i[i_] = vr[i_,k+1];
                            }
                            curwr = wr1[k];
                            curwi = wi1[k];
                        }
                        if( (double)(wi1[k])<(double)(0) )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1r[i_] = vr[i_,k-1];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1i[i_] = -vr[i_,k];
                            }
                            curwr = wr1[k];
                            curwi = wi1[k];
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += a[i,i_]*vec1r[i_];
                            }
                            vec2r[i] = vt;
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += a[i,i_]*vec1i[i_];
                            }
                            vec2i[i] = vt;
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vec3r[i_] = curwr*vec1r[i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vec3r[i_] = vec3r[i_] - curwi*vec1i[i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vec3i[i_] = curwi*vec1r[i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vec3i[i_] = vec3i[i_] + curwr*vec1i[i_];
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            nserrors = nserrors | (double)(Math.Abs(vec2r[i]-vec3r[i]))>(double)(threshold);
                            nserrors = nserrors | (double)(Math.Abs(vec2i[i]-vec3i[i]))>(double)(threshold);
                        }
                        k = k+1;
                    }
                }
                
                //
                // Test left vectors
                //
                if( needl )
                {
                    k = 0;
                    while( k<=n-1 )
                    {
                        if( (double)(wi1[k])==(double)(0) )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1r[i_] = vl[i_,k];
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                vec1i[i] = 0;
                            }
                            curwr = wr1[k];
                            curwi = 0;
                        }
                        if( (double)(wi1[k])>(double)(0) )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1r[i_] = vl[i_,k];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1i[i_] = vl[i_,k+1];
                            }
                            curwr = wr1[k];
                            curwi = wi1[k];
                        }
                        if( (double)(wi1[k])<(double)(0) )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1r[i_] = vl[i_,k-1];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vec1i[i_] = -vl[i_,k];
                            }
                            curwr = wr1[k];
                            curwi = wi1[k];
                        }
                        for(j=0; j<=n-1; j++)
                        {
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += vec1r[i_]*a[i_,j];
                            }
                            vec2r[j] = vt;
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += vec1i[i_]*a[i_,j];
                            }
                            vec2i[j] = -vt;
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vec3r[i_] = curwr*vec1r[i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vec3r[i_] = vec3r[i_] + curwi*vec1i[i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vec3i[i_] = curwi*vec1r[i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vec3i[i_] = vec3i[i_] - curwr*vec1i[i_];
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            nserrors = nserrors | (double)(Math.Abs(vec2r[i]-vec3r[i]))>(double)(threshold);
                            nserrors = nserrors | (double)(Math.Abs(vec2i[i]-vec3i[i]))>(double)(threshold);
                        }
                        k = k+1;
                    }
                }
            }
        }


        /*************************************************************************
        Testing EVD subroutines for one N

        NOTES:
        * BIThreshold is a threshold for bisection-and-inverse-iteration subroutines.
          special threshold is needed because these subroutines may have much more
          larger error than QR-based algorithms.
        *************************************************************************/
        private static void testevdset(int n,
            double threshold,
            double bithreshold,
            ref int failc,
            ref int runs,
            ref bool nserrors,
            ref bool serrors,
            ref bool herrors,
            ref bool tderrors,
            ref bool sbierrors,
            ref bool hbierrors,
            ref bool tdbierrors)
        {
            double[,] ra = new double[0,0];
            double[,] ral = new double[0,0];
            double[,] rau = new double[0,0];
            complex[,] ca = new complex[0,0];
            complex[,] cal = new complex[0,0];
            complex[,] cau = new complex[0,0];
            double[] d = new double[0];
            double[] e = new double[0];
            int i = 0;
            int j = 0;
            int mkind = 0;

            
            //
            // Test symmetric problems
            //
            ra = new double[n, n];
            ral = new double[n, n];
            rau = new double[n, n];
            ca = new complex[n, n];
            cal = new complex[n, n];
            cau = new complex[n, n];
            
            //
            // Zero matrices
            //
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ra[i,j] = 0;
                    ca[i,j] = 0;
                }
            }
            rmatrixsymmetricsplit(ra, n, ref ral, ref rau);
            cmatrixhermitiansplit(ca, n, ref cal, ref cau);
            testsevdproblem(ra, ral, rau, n, threshold, ref serrors, ref failc, ref runs);
            testhevdproblem(ca, cal, cau, n, threshold, ref herrors, ref failc, ref runs);
            testsevdbiproblem(ra, ral, rau, n, false, bithreshold, ref sbierrors, ref failc, ref runs);
            testhevdbiproblem(ca, cal, cau, n, false, bithreshold, ref hbierrors, ref failc, ref runs);
            
            //
            // Random matrix
            //
            for(i=0; i<=n-1; i++)
            {
                for(j=i+1; j<=n-1; j++)
                {
                    ra[i,j] = 2*math.randomreal()-1;
                    ca[i,j].x = 2*math.randomreal()-1;
                    ca[i,j].y = 2*math.randomreal()-1;
                    ra[j,i] = ra[i,j];
                    ca[j,i] = math.conj(ca[i,j]);
                }
                ra[i,i] = 2*math.randomreal()-1;
                ca[i,i] = 2*math.randomreal()-1;
            }
            rmatrixsymmetricsplit(ra, n, ref ral, ref rau);
            cmatrixhermitiansplit(ca, n, ref cal, ref cau);
            testsevdproblem(ra, ral, rau, n, threshold, ref serrors, ref failc, ref runs);
            testhevdproblem(ca, cal, cau, n, threshold, ref herrors, ref failc, ref runs);
            
            //
            // Random diagonally dominant matrix with distinct eigenvalues
            //
            for(i=0; i<=n-1; i++)
            {
                for(j=i+1; j<=n-1; j++)
                {
                    ra[i,j] = 0.1*(2*math.randomreal()-1)/n;
                    ca[i,j].x = 0.1*(2*math.randomreal()-1)/n;
                    ca[i,j].y = 0.1*(2*math.randomreal()-1)/n;
                    ra[j,i] = ra[i,j];
                    ca[j,i] = math.conj(ca[i,j]);
                }
                ra[i,i] = 0.1*(2*math.randomreal()-1)+i;
                ca[i,i] = 0.1*(2*math.randomreal()-1)+i;
            }
            rmatrixsymmetricsplit(ra, n, ref ral, ref rau);
            cmatrixhermitiansplit(ca, n, ref cal, ref cau);
            testsevdproblem(ra, ral, rau, n, threshold, ref serrors, ref failc, ref runs);
            testhevdproblem(ca, cal, cau, n, threshold, ref herrors, ref failc, ref runs);
            testsevdbiproblem(ra, ral, rau, n, true, bithreshold, ref sbierrors, ref failc, ref runs);
            testhevdbiproblem(ca, cal, cau, n, true, bithreshold, ref hbierrors, ref failc, ref runs);
            
            //
            // Sparse matrices
            //
            rmatrixfillsparsea(ref ra, n, n, 0.995);
            cmatrixfillsparsea(ref ca, n, n, 0.995);
            for(i=0; i<=n-1; i++)
            {
                for(j=i+1; j<=n-1; j++)
                {
                    ra[j,i] = ra[i,j];
                    ca[j,i] = math.conj(ca[i,j]);
                }
                ca[i,i].y = 0;
            }
            rmatrixsymmetricsplit(ra, n, ref ral, ref rau);
            cmatrixhermitiansplit(ca, n, ref cal, ref cau);
            testsevdproblem(ra, ral, rau, n, threshold, ref serrors, ref failc, ref runs);
            testhevdproblem(ca, cal, cau, n, threshold, ref herrors, ref failc, ref runs);
            testsevdbiproblem(ra, ral, rau, n, false, bithreshold, ref sbierrors, ref failc, ref runs);
            testhevdbiproblem(ca, cal, cau, n, false, bithreshold, ref hbierrors, ref failc, ref runs);
            
            //
            // testing tridiagonal problems
            //
            for(mkind=0; mkind<=7; mkind++)
            {
                d = new double[n];
                if( n>1 )
                {
                    e = new double[n-1];
                }
                if( mkind==0 )
                {
                    
                    //
                    // Zero matrix
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        d[i] = 0;
                    }
                    for(i=0; i<=n-2; i++)
                    {
                        e[i] = 0;
                    }
                }
                if( mkind==1 )
                {
                    
                    //
                    // Diagonal matrix
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        d[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=n-2; i++)
                    {
                        e[i] = 0;
                    }
                }
                if( mkind==2 )
                {
                    
                    //
                    // Off-diagonal matrix
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        d[i] = 0;
                    }
                    for(i=0; i<=n-2; i++)
                    {
                        e[i] = 2*math.randomreal()-1;
                    }
                }
                if( mkind==3 )
                {
                    
                    //
                    // Dense matrix with blocks
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        d[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=n-2; i++)
                    {
                        e[i] = 2*math.randomreal()-1;
                    }
                    j = 1;
                    i = 2;
                    while( j<=n-2 )
                    {
                        e[j] = 0;
                        j = j+i;
                        i = i+1;
                    }
                }
                if( mkind==4 )
                {
                    
                    //
                    // dense matrix
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        d[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=n-2; i++)
                    {
                        e[i] = 2*math.randomreal()-1;
                    }
                }
                if( mkind==5 )
                {
                    
                    //
                    // Diagonal matrix with distinct eigenvalues
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        d[i] = 0.1*(2*math.randomreal()-1)+i;
                    }
                    for(i=0; i<=n-2; i++)
                    {
                        e[i] = 0;
                    }
                }
                if( mkind==6 )
                {
                    
                    //
                    // Off-diagonal matrix with distinct eigenvalues
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        d[i] = 0;
                    }
                    for(i=0; i<=n-2; i++)
                    {
                        e[i] = 0.1*(2*math.randomreal()-1)+i+1;
                    }
                }
                if( mkind==7 )
                {
                    
                    //
                    // dense matrix with distinct eigenvalues
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        d[i] = 0.1*(2*math.randomreal()-1)+i+1;
                    }
                    for(i=0; i<=n-2; i++)
                    {
                        e[i] = 0.1*(2*math.randomreal()-1);
                    }
                }
                testtdevdproblem(d, e, n, threshold, ref tderrors, ref failc, ref runs);
                testtdevdbiproblem(d, e, n, (mkind==5 | mkind==6) | mkind==7, bithreshold, ref tdbierrors, ref failc, ref runs);
            }
            
            //
            // Test non-symmetric problems
            //
            
            //
            // Test non-symmetric problems: zero, random, sparse matrices.
            //
            ra = new double[n, n];
            ca = new complex[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ra[i,j] = 0;
                    ca[i,j] = 0;
                }
            }
            testnsevdproblem(ra, n, threshold, ref nserrors, ref failc, ref runs);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ra[i,j] = 2*math.randomreal()-1;
                    ca[i,j].x = 2*math.randomreal()-1;
                    ca[i,j].y = 2*math.randomreal()-1;
                }
            }
            testnsevdproblem(ra, n, threshold, ref nserrors, ref failc, ref runs);
            rmatrixfillsparsea(ref ra, n, n, 0.995);
            cmatrixfillsparsea(ref ca, n, n, 0.995);
            testnsevdproblem(ra, n, threshold, ref nserrors, ref failc, ref runs);
        }


    }
    public class testmatgenunit
    {
        public const int maxsvditerations = 60;


        public static bool testmatgen(bool silent)
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            double[,] b = new double[0,0];
            double[,] u = new double[0,0];
            double[,] v = new double[0,0];
            complex[,] ca = new complex[0,0];
            complex[,] cb = new complex[0,0];
            double[,] r1 = new double[0,0];
            double[,] r2 = new double[0,0];
            complex[,] c1 = new complex[0,0];
            complex[,] c2 = new complex[0,0];
            double[] w = new double[0];
            int n = 0;
            int maxn = 0;
            int i = 0;
            int j = 0;
            int pass = 0;
            int passcount = 0;
            bool waserrors = new bool();
            double cond = 0;
            double threshold = 0;
            double vt = 0;
            complex ct = 0;
            double minw = 0;
            double maxw = 0;
            bool serr = new bool();
            bool herr = new bool();
            bool spderr = new bool();
            bool hpderr = new bool();
            bool rerr = new bool();
            bool cerr = new bool();
            int i_ = 0;

            rerr = false;
            cerr = false;
            serr = false;
            herr = false;
            spderr = false;
            hpderr = false;
            waserrors = false;
            maxn = 20;
            passcount = 15;
            threshold = 1000*math.machineepsilon;
            
            //
            // Testing orthogonal
            //
            for(n=1; n<=maxn; n++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    r1 = new double[n-1+1, 2*n-1+1];
                    r2 = new double[2*n-1+1, n-1+1];
                    c1 = new complex[n-1+1, 2*n-1+1];
                    c2 = new complex[2*n-1+1, n-1+1];
                    
                    //
                    // Random orthogonal, real
                    //
                    unset2d(ref a);
                    unset2d(ref b);
                    matgen.rmatrixrndorthogonal(n, ref a);
                    matgen.rmatrixrndorthogonal(n, ref b);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            
                            //
                            // orthogonality test
                            //
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += a[i,i_]*a[j,i_];
                            }
                            if( i==j )
                            {
                                rerr = rerr | (double)(Math.Abs(vt-1))>(double)(threshold);
                            }
                            else
                            {
                                rerr = rerr | (double)(Math.Abs(vt))>(double)(threshold);
                            }
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += b[i,i_]*b[j,i_];
                            }
                            if( i==j )
                            {
                                rerr = rerr | (double)(Math.Abs(vt-1))>(double)(threshold);
                            }
                            else
                            {
                                rerr = rerr | (double)(Math.Abs(vt))>(double)(threshold);
                            }
                            
                            //
                            // test for difference in A and B
                            //
                            if( n>=2 )
                            {
                                rerr = rerr | (double)(a[i,j])==(double)(b[i,j]);
                            }
                        }
                    }
                    
                    //
                    // Random orthogonal, complex
                    //
                    unset2dc(ref ca);
                    unset2dc(ref cb);
                    matgen.cmatrixrndorthogonal(n, ref ca);
                    matgen.cmatrixrndorthogonal(n, ref cb);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            
                            //
                            // orthogonality test
                            //
                            ct = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                ct += ca[i,i_]*math.conj(ca[j,i_]);
                            }
                            if( i==j )
                            {
                                cerr = cerr | (double)(math.abscomplex(ct-1))>(double)(threshold);
                            }
                            else
                            {
                                cerr = cerr | (double)(math.abscomplex(ct))>(double)(threshold);
                            }
                            ct = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                ct += cb[i,i_]*math.conj(cb[j,i_]);
                            }
                            if( i==j )
                            {
                                cerr = cerr | (double)(math.abscomplex(ct-1))>(double)(threshold);
                            }
                            else
                            {
                                cerr = cerr | (double)(math.abscomplex(ct))>(double)(threshold);
                            }
                            
                            //
                            // test for difference in A and B
                            //
                            if( n>=2 )
                            {
                                cerr = cerr | ca[i,j]==cb[i,j];
                            }
                        }
                    }
                    
                    //
                    // From the right real tests:
                    // 1. E*Q is orthogonal
                    // 2. Q1<>Q2 (routine result is changing)
                    // 3. (E E)'*Q = (Q' Q')' (correct handling of non-square matrices)
                    //
                    unset2d(ref a);
                    unset2d(ref b);
                    a = new double[n-1+1, n-1+1];
                    b = new double[n-1+1, n-1+1];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0;
                            b[i,j] = 0;
                        }
                        a[i,i] = 1;
                        b[i,i] = 1;
                    }
                    matgen.rmatrixrndorthogonalfromtheright(ref a, n, n);
                    matgen.rmatrixrndorthogonalfromtheright(ref b, n, n);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            
                            //
                            // orthogonality test
                            //
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += a[i,i_]*a[j,i_];
                            }
                            if( i==j )
                            {
                                rerr = rerr | (double)(Math.Abs(vt-1))>(double)(threshold);
                            }
                            else
                            {
                                rerr = rerr | (double)(Math.Abs(vt))>(double)(threshold);
                            }
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += b[i,i_]*b[j,i_];
                            }
                            if( i==j )
                            {
                                rerr = rerr | (double)(Math.Abs(vt-1))>(double)(threshold);
                            }
                            else
                            {
                                rerr = rerr | (double)(Math.Abs(vt))>(double)(threshold);
                            }
                            
                            //
                            // test for difference in A and B
                            //
                            if( n>=2 )
                            {
                                rerr = rerr | (double)(a[i,j])==(double)(b[i,j]);
                            }
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            r2[i,j] = 2*math.randomreal()-1;
                            r2[i+n,j] = r2[i,j];
                        }
                    }
                    matgen.rmatrixrndorthogonalfromtheright(ref r2, 2*n, n);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            rerr = rerr | (double)(Math.Abs(r2[i+n,j]-r2[i,j]))>(double)(threshold);
                        }
                    }
                    
                    //
                    // From the left real tests:
                    // 1. Q*E is orthogonal
                    // 2. Q1<>Q2 (routine result is changing)
                    // 3. Q*(E E) = (Q Q) (correct handling of non-square matrices)
                    //
                    unset2d(ref a);
                    unset2d(ref b);
                    a = new double[n-1+1, n-1+1];
                    b = new double[n-1+1, n-1+1];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0;
                            b[i,j] = 0;
                        }
                        a[i,i] = 1;
                        b[i,i] = 1;
                    }
                    matgen.rmatrixrndorthogonalfromtheleft(ref a, n, n);
                    matgen.rmatrixrndorthogonalfromtheleft(ref b, n, n);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            
                            //
                            // orthogonality test
                            //
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += a[i,i_]*a[j,i_];
                            }
                            if( i==j )
                            {
                                rerr = rerr | (double)(Math.Abs(vt-1))>(double)(threshold);
                            }
                            else
                            {
                                rerr = rerr | (double)(Math.Abs(vt))>(double)(threshold);
                            }
                            vt = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                vt += b[i,i_]*b[j,i_];
                            }
                            if( i==j )
                            {
                                rerr = rerr | (double)(Math.Abs(vt-1))>(double)(threshold);
                            }
                            else
                            {
                                rerr = rerr | (double)(Math.Abs(vt))>(double)(threshold);
                            }
                            
                            //
                            // test for difference in A and B
                            //
                            if( n>=2 )
                            {
                                rerr = rerr | (double)(a[i,j])==(double)(b[i,j]);
                            }
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            r1[i,j] = 2*math.randomreal()-1;
                            r1[i,j+n] = r1[i,j];
                        }
                    }
                    matgen.rmatrixrndorthogonalfromtheleft(ref r1, n, 2*n);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            rerr = rerr | (double)(Math.Abs(r1[i,j]-r1[i,j+n]))>(double)(threshold);
                        }
                    }
                    
                    //
                    // From the right complex tests:
                    // 1. E*Q is orthogonal
                    // 2. Q1<>Q2 (routine result is changing)
                    // 3. (E E)'*Q = (Q' Q')' (correct handling of non-square matrices)
                    //
                    unset2dc(ref ca);
                    unset2dc(ref cb);
                    ca = new complex[n-1+1, n-1+1];
                    cb = new complex[n-1+1, n-1+1];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ca[i,j] = 0;
                            cb[i,j] = 0;
                        }
                        ca[i,i] = 1;
                        cb[i,i] = 1;
                    }
                    matgen.cmatrixrndorthogonalfromtheright(ref ca, n, n);
                    matgen.cmatrixrndorthogonalfromtheright(ref cb, n, n);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            
                            //
                            // orthogonality test
                            //
                            ct = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                ct += ca[i,i_]*math.conj(ca[j,i_]);
                            }
                            if( i==j )
                            {
                                cerr = cerr | (double)(math.abscomplex(ct-1))>(double)(threshold);
                            }
                            else
                            {
                                cerr = cerr | (double)(math.abscomplex(ct))>(double)(threshold);
                            }
                            ct = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                ct += cb[i,i_]*math.conj(cb[j,i_]);
                            }
                            if( i==j )
                            {
                                cerr = cerr | (double)(math.abscomplex(ct-1))>(double)(threshold);
                            }
                            else
                            {
                                cerr = cerr | (double)(math.abscomplex(ct))>(double)(threshold);
                            }
                            
                            //
                            // test for difference in A and B
                            //
                            cerr = cerr | ca[i,j]==cb[i,j];
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            c2[i,j] = 2*math.randomreal()-1;
                            c2[i+n,j] = c2[i,j];
                        }
                    }
                    matgen.cmatrixrndorthogonalfromtheright(ref c2, 2*n, n);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            cerr = cerr | (double)(math.abscomplex(c2[i+n,j]-c2[i,j]))>(double)(threshold);
                        }
                    }
                    
                    //
                    // From the left complex tests:
                    // 1. Q*E is orthogonal
                    // 2. Q1<>Q2 (routine result is changing)
                    // 3. Q*(E E) = (Q Q) (correct handling of non-square matrices)
                    //
                    unset2dc(ref ca);
                    unset2dc(ref cb);
                    ca = new complex[n-1+1, n-1+1];
                    cb = new complex[n-1+1, n-1+1];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ca[i,j] = 0;
                            cb[i,j] = 0;
                        }
                        ca[i,i] = 1;
                        cb[i,i] = 1;
                    }
                    matgen.cmatrixrndorthogonalfromtheleft(ref ca, n, n);
                    matgen.cmatrixrndorthogonalfromtheleft(ref cb, n, n);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            
                            //
                            // orthogonality test
                            //
                            ct = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                ct += ca[i,i_]*math.conj(ca[j,i_]);
                            }
                            if( i==j )
                            {
                                cerr = cerr | (double)(math.abscomplex(ct-1))>(double)(threshold);
                            }
                            else
                            {
                                cerr = cerr | (double)(math.abscomplex(ct))>(double)(threshold);
                            }
                            ct = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                ct += cb[i,i_]*math.conj(cb[j,i_]);
                            }
                            if( i==j )
                            {
                                cerr = cerr | (double)(math.abscomplex(ct-1))>(double)(threshold);
                            }
                            else
                            {
                                cerr = cerr | (double)(math.abscomplex(ct))>(double)(threshold);
                            }
                            
                            //
                            // test for difference in A and B
                            //
                            cerr = cerr | ca[i,j]==cb[i,j];
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            c1[i,j] = 2*math.randomreal()-1;
                            c1[i,j+n] = c1[i,j];
                        }
                    }
                    matgen.cmatrixrndorthogonalfromtheleft(ref c1, n, 2*n);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            cerr = cerr | (double)(math.abscomplex(c1[i,j]-c1[i,j+n]))>(double)(threshold);
                        }
                    }
                }
            }
            
            //
            // Testing GCond
            //
            for(n=2; n<=maxn; n++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // real test
                    //
                    unset2d(ref a);
                    cond = Math.Exp(Math.Log(1000)*math.randomreal());
                    matgen.rmatrixrndcond(n, cond, ref a);
                    b = new double[n+1, n+1];
                    for(i=1; i<=n; i++)
                    {
                        for(j=1; j<=n; j++)
                        {
                            b[i,j] = a[i-1,j-1];
                        }
                    }
                    if( obsoletesvddecomposition(ref b, n, n, ref w, ref v) )
                    {
                        maxw = w[1];
                        minw = w[1];
                        for(i=2; i<=n; i++)
                        {
                            if( (double)(w[i])>(double)(maxw) )
                            {
                                maxw = w[i];
                            }
                            if( (double)(w[i])<(double)(minw) )
                            {
                                minw = w[i];
                            }
                        }
                        vt = maxw/minw/cond;
                        if( (double)(Math.Abs(Math.Log(vt)))>(double)(Math.Log(1+threshold)) )
                        {
                            rerr = true;
                        }
                    }
                }
            }
            
            //
            // Symmetric/SPD
            // N = 2 .. 30
            //
            for(n=2; n<=maxn; n++)
            {
                
                //
                // SPD matrices
                //
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // Generate A
                    //
                    unset2d(ref a);
                    cond = Math.Exp(Math.Log(1000)*math.randomreal());
                    matgen.spdmatrixrndcond(n, cond, ref a);
                    
                    //
                    // test condition number
                    //
                    spderr = spderr | (double)(svdcond(a, n)/cond-1)>(double)(threshold);
                    
                    //
                    // test SPD
                    //
                    spderr = spderr | !isspd(a, n, true);
                    
                    //
                    // test that A is symmetic
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            spderr = spderr | (double)(Math.Abs(a[i,j]-a[j,i]))>(double)(threshold);
                        }
                    }
                    
                    //
                    // test for difference between A and B (subsequent matrix)
                    //
                    unset2d(ref b);
                    matgen.spdmatrixrndcond(n, cond, ref b);
                    if( n>=2 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                spderr = spderr | (double)(a[i,j])==(double)(b[i,j]);
                            }
                        }
                    }
                }
                
                //
                // HPD matrices
                //
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // Generate A
                    //
                    unset2dc(ref ca);
                    cond = Math.Exp(Math.Log(1000)*math.randomreal());
                    matgen.hpdmatrixrndcond(n, cond, ref ca);
                    
                    //
                    // test HPD
                    //
                    hpderr = hpderr | !ishpd(ca, n);
                    
                    //
                    // test that A is Hermitian
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            hpderr = hpderr | (double)(math.abscomplex(ca[i,j]-math.conj(ca[j,i])))>(double)(threshold);
                        }
                    }
                    
                    //
                    // test for difference between A and B (subsequent matrix)
                    //
                    unset2dc(ref cb);
                    matgen.hpdmatrixrndcond(n, cond, ref cb);
                    if( n>=2 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                hpderr = hpderr | ca[i,j]==cb[i,j];
                            }
                        }
                    }
                }
                
                //
                // Symmetric matrices
                //
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // test condition number
                    //
                    unset2d(ref a);
                    cond = Math.Exp(Math.Log(1000)*math.randomreal());
                    matgen.smatrixrndcond(n, cond, ref a);
                    serr = serr | (double)(svdcond(a, n)/cond-1)>(double)(threshold);
                    
                    //
                    // test for difference between A and B
                    //
                    unset2d(ref b);
                    matgen.smatrixrndcond(n, cond, ref b);
                    if( n>=2 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                serr = serr | (double)(a[i,j])==(double)(b[i,j]);
                            }
                        }
                    }
                }
                
                //
                // Hermitian matrices
                //
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // Generate A
                    //
                    unset2dc(ref ca);
                    cond = Math.Exp(Math.Log(1000)*math.randomreal());
                    matgen.hmatrixrndcond(n, cond, ref ca);
                    
                    //
                    // test that A is Hermitian
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            herr = herr | (double)(math.abscomplex(ca[i,j]-math.conj(ca[j,i])))>(double)(threshold);
                        }
                    }
                    
                    //
                    // test for difference between A and B (subsequent matrix)
                    //
                    unset2dc(ref cb);
                    matgen.hmatrixrndcond(n, cond, ref cb);
                    if( n>=2 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                herr = herr | ca[i,j]==cb[i,j];
                            }
                        }
                    }
                }
            }
            
            //
            // report
            //
            waserrors = ((((rerr | cerr) | serr) | spderr) | herr) | hpderr;
            if( !silent )
            {
                System.Console.Write("TESTING MATRIX GENERATOR");
                System.Console.WriteLine();
                System.Console.Write("REAL TEST:                               ");
                if( !rerr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("COMPLEX TEST:                            ");
                if( !cerr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("SYMMETRIC TEST:                          ");
                if( !serr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("HERMITIAN TEST:                          ");
                if( !herr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("SPD TEST:                                ");
                if( !spderr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("HPD TEST:                                ");
                if( !hpderr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Unsets 2D array.
        *************************************************************************/
        private static void unset2d(ref double[,] a)
        {
            a = new double[0+1, 0+1];
            a[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 2D array.
        *************************************************************************/
        private static void unset2dc(ref complex[,] a)
        {
            a = new complex[0+1, 0+1];
            a[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Test whether matrix is SPD
        *************************************************************************/
        private static bool isspd(double[,] a,
            int n,
            bool isupper)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;
            double ajj = 0;
            double v = 0;
            int i_ = 0;

            a = (double[,])a.Clone();

            
            //
            //     Test the input parameters.
            //
            ap.assert(n>=0, "Error in SMatrixCholesky: incorrect function arguments");
            
            //
            //     Quick return if possible
            //
            result = true;
            if( n<=0 )
            {
                return result;
            }
            if( isupper )
            {
                
                //
                // Compute the Cholesky factorization A = U'*U.
                //
                for(j=0; j<=n-1; j++)
                {
                    
                    //
                    // Compute U(J,J) and test for non-positive-definiteness.
                    //
                    v = 0.0;
                    for(i_=0; i_<=j-1;i_++)
                    {
                        v += a[i_,j]*a[i_,j];
                    }
                    ajj = a[j,j]-v;
                    if( (double)(ajj)<=(double)(0) )
                    {
                        result = false;
                        return result;
                    }
                    ajj = Math.Sqrt(ajj);
                    a[j,j] = ajj;
                    
                    //
                    // Compute elements J+1:N of row J.
                    //
                    if( j<n-1 )
                    {
                        for(i=j+1; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=j-1;i_++)
                            {
                                v += a[i_,i]*a[i_,j];
                            }
                            a[j,i] = a[j,i]-v;
                        }
                        v = 1/ajj;
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            a[j,i_] = v*a[j,i_];
                        }
                    }
                }
            }
            else
            {
                
                //
                // Compute the Cholesky factorization A = L*L'.
                //
                for(j=0; j<=n-1; j++)
                {
                    
                    //
                    // Compute L(J,J) and test for non-positive-definiteness.
                    //
                    v = 0.0;
                    for(i_=0; i_<=j-1;i_++)
                    {
                        v += a[j,i_]*a[j,i_];
                    }
                    ajj = a[j,j]-v;
                    if( (double)(ajj)<=(double)(0) )
                    {
                        result = false;
                        return result;
                    }
                    ajj = Math.Sqrt(ajj);
                    a[j,j] = ajj;
                    
                    //
                    // Compute elements J+1:N of column J.
                    //
                    if( j<n-1 )
                    {
                        for(i=j+1; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=j-1;i_++)
                            {
                                v += a[i,i_]*a[j,i_];
                            }
                            a[i,j] = a[i,j]-v;
                        }
                        v = 1/ajj;
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            a[i_,j] = v*a[i_,j];
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Tests whether A is HPD
        *************************************************************************/
        private static bool ishpd(complex[,] a,
            int n)
        {
            bool result = new bool();
            int j = 0;
            double ajj = 0;
            complex v = 0;
            double r = 0;
            complex[] t = new complex[0];
            complex[] t2 = new complex[0];
            complex[] t3 = new complex[0];
            int i = 0;
            complex[,] a1 = new complex[0,0];
            int i_ = 0;

            a = (complex[,])a.Clone();

            t = new complex[n-1+1];
            t2 = new complex[n-1+1];
            t3 = new complex[n-1+1];
            result = true;
            
            //
            // Compute the Cholesky factorization A = U'*U.
            //
            for(j=0; j<=n-1; j++)
            {
                
                //
                // Compute U(J,J) and test for non-positive-definiteness.
                //
                v = 0.0;
                for(i_=0; i_<=j-1;i_++)
                {
                    v += math.conj(a[i_,j])*a[i_,j];
                }
                ajj = (a[j,j]-v).x;
                if( (double)(ajj)<=(double)(0) )
                {
                    a[j,j] = ajj;
                    result = false;
                    return result;
                }
                ajj = Math.Sqrt(ajj);
                a[j,j] = ajj;
                
                //
                // Compute elements J+1:N-1 of row J.
                //
                if( j<n-1 )
                {
                    for(i_=0; i_<=j-1;i_++)
                    {
                        t2[i_] = math.conj(a[i_,j]);
                    }
                    for(i_=j+1; i_<=n-1;i_++)
                    {
                        t3[i_] = a[j,i_];
                    }
                    for(i=j+1; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=j-1;i_++)
                        {
                            v += a[i_,i]*t2[i_];
                        }
                        t3[i] = t3[i]-v;
                    }
                    for(i_=j+1; i_<=n-1;i_++)
                    {
                        a[j,i_] = t3[i_];
                    }
                    r = 1/ajj;
                    for(i_=j+1; i_<=n-1;i_++)
                    {
                        a[j,i_] = r*a[j,i_];
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        SVD condition number
        *************************************************************************/
        private static double svdcond(double[,] a,
            int n)
        {
            double result = 0;
            double[,] a1 = new double[0,0];
            double[,] v = new double[0,0];
            double[] w = new double[0];
            int i = 0;
            int j = 0;
            double minw = 0;
            double maxw = 0;

            a1 = new double[n+1, n+1];
            for(i=1; i<=n; i++)
            {
                for(j=1; j<=n; j++)
                {
                    a1[i,j] = a[i-1,j-1];
                }
            }
            if( !obsoletesvddecomposition(ref a1, n, n, ref w, ref v) )
            {
                result = 0;
                return result;
            }
            minw = w[1];
            maxw = w[1];
            for(i=2; i<=n; i++)
            {
                if( (double)(w[i])<(double)(minw) )
                {
                    minw = w[i];
                }
                if( (double)(w[i])>(double)(maxw) )
                {
                    maxw = w[i];
                }
            }
            result = maxw/minw;
            return result;
        }


        private static bool obsoletesvddecomposition(ref double[,] a,
            int m,
            int n,
            ref double[] w,
            ref double[,] v)
        {
            bool result = new bool();
            int nm = 0;
            int minmn = 0;
            int l = 0;
            int k = 0;
            int j = 0;
            int jj = 0;
            int its = 0;
            int i = 0;
            double z = 0;
            double y = 0;
            double x = 0;
            double vscale = 0;
            double s = 0;
            double h = 0;
            double g = 0;
            double f = 0;
            double c = 0;
            double anorm = 0;
            double[] rv1 = new double[0];
            bool flag = new bool();

            w = new double[0];
            v = new double[0,0];

            rv1 = new double[n+1];
            w = new double[n+1];
            v = new double[n+1, n+1];
            result = true;
            if( m<n )
            {
                minmn = m;
            }
            else
            {
                minmn = n;
            }
            g = 0.0;
            vscale = 0.0;
            anorm = 0.0;
            for(i=1; i<=n; i++)
            {
                l = i+1;
                rv1[i] = vscale*g;
                g = 0;
                s = 0;
                vscale = 0;
                if( i<=m )
                {
                    for(k=i; k<=m; k++)
                    {
                        vscale = vscale+Math.Abs(a[k,i]);
                    }
                    if( (double)(vscale)!=(double)(0.0) )
                    {
                        for(k=i; k<=m; k++)
                        {
                            a[k,i] = a[k,i]/vscale;
                            s = s+a[k,i]*a[k,i];
                        }
                        f = a[i,i];
                        g = -extsign(Math.Sqrt(s), f);
                        h = f*g-s;
                        a[i,i] = f-g;
                        if( i!=n )
                        {
                            for(j=l; j<=n; j++)
                            {
                                s = 0.0;
                                for(k=i; k<=m; k++)
                                {
                                    s = s+a[k,i]*a[k,j];
                                }
                                f = s/h;
                                for(k=i; k<=m; k++)
                                {
                                    a[k,j] = a[k,j]+f*a[k,i];
                                }
                            }
                        }
                        for(k=i; k<=m; k++)
                        {
                            a[k,i] = vscale*a[k,i];
                        }
                    }
                }
                w[i] = vscale*g;
                g = 0.0;
                s = 0.0;
                vscale = 0.0;
                if( i<=m & i!=n )
                {
                    for(k=l; k<=n; k++)
                    {
                        vscale = vscale+Math.Abs(a[i,k]);
                    }
                    if( (double)(vscale)!=(double)(0.0) )
                    {
                        for(k=l; k<=n; k++)
                        {
                            a[i,k] = a[i,k]/vscale;
                            s = s+a[i,k]*a[i,k];
                        }
                        f = a[i,l];
                        g = -extsign(Math.Sqrt(s), f);
                        h = f*g-s;
                        a[i,l] = f-g;
                        for(k=l; k<=n; k++)
                        {
                            rv1[k] = a[i,k]/h;
                        }
                        if( i!=m )
                        {
                            for(j=l; j<=m; j++)
                            {
                                s = 0.0;
                                for(k=l; k<=n; k++)
                                {
                                    s = s+a[j,k]*a[i,k];
                                }
                                for(k=l; k<=n; k++)
                                {
                                    a[j,k] = a[j,k]+s*rv1[k];
                                }
                            }
                        }
                        for(k=l; k<=n; k++)
                        {
                            a[i,k] = vscale*a[i,k];
                        }
                    }
                }
                anorm = mymax(anorm, Math.Abs(w[i])+Math.Abs(rv1[i]));
            }
            for(i=n; i>=1; i--)
            {
                if( i<n )
                {
                    if( (double)(g)!=(double)(0.0) )
                    {
                        for(j=l; j<=n; j++)
                        {
                            v[j,i] = a[i,j]/a[i,l]/g;
                        }
                        for(j=l; j<=n; j++)
                        {
                            s = 0.0;
                            for(k=l; k<=n; k++)
                            {
                                s = s+a[i,k]*v[k,j];
                            }
                            for(k=l; k<=n; k++)
                            {
                                v[k,j] = v[k,j]+s*v[k,i];
                            }
                        }
                    }
                    for(j=l; j<=n; j++)
                    {
                        v[i,j] = 0.0;
                        v[j,i] = 0.0;
                    }
                }
                v[i,i] = 1.0;
                g = rv1[i];
                l = i;
            }
            for(i=minmn; i>=1; i--)
            {
                l = i+1;
                g = w[i];
                if( i<n )
                {
                    for(j=l; j<=n; j++)
                    {
                        a[i,j] = 0.0;
                    }
                }
                if( (double)(g)!=(double)(0.0) )
                {
                    g = 1.0/g;
                    if( i!=n )
                    {
                        for(j=l; j<=n; j++)
                        {
                            s = 0.0;
                            for(k=l; k<=m; k++)
                            {
                                s = s+a[k,i]*a[k,j];
                            }
                            f = s/a[i,i]*g;
                            for(k=i; k<=m; k++)
                            {
                                a[k,j] = a[k,j]+f*a[k,i];
                            }
                        }
                    }
                    for(j=i; j<=m; j++)
                    {
                        a[j,i] = a[j,i]*g;
                    }
                }
                else
                {
                    for(j=i; j<=m; j++)
                    {
                        a[j,i] = 0.0;
                    }
                }
                a[i,i] = a[i,i]+1.0;
            }
            for(k=n; k>=1; k--)
            {
                for(its=1; its<=maxsvditerations; its++)
                {
                    flag = true;
                    for(l=k; l>=1; l--)
                    {
                        nm = l-1;
                        if( (double)(Math.Abs(rv1[l])+anorm)==(double)(anorm) )
                        {
                            flag = false;
                            break;
                        }
                        if( (double)(Math.Abs(w[nm])+anorm)==(double)(anorm) )
                        {
                            break;
                        }
                    }
                    if( flag )
                    {
                        c = 0.0;
                        s = 1.0;
                        for(i=l; i<=k; i++)
                        {
                            f = s*rv1[i];
                            if( (double)(Math.Abs(f)+anorm)!=(double)(anorm) )
                            {
                                g = w[i];
                                h = pythag(f, g);
                                w[i] = h;
                                h = 1.0/h;
                                c = g*h;
                                s = -(f*h);
                                for(j=1; j<=m; j++)
                                {
                                    y = a[j,nm];
                                    z = a[j,i];
                                    a[j,nm] = y*c+z*s;
                                    a[j,i] = -(y*s)+z*c;
                                }
                            }
                        }
                    }
                    z = w[k];
                    if( l==k )
                    {
                        if( (double)(z)<(double)(0.0) )
                        {
                            w[k] = -z;
                            for(j=1; j<=n; j++)
                            {
                                v[j,k] = -v[j,k];
                            }
                        }
                        break;
                    }
                    if( its==maxsvditerations )
                    {
                        result = false;
                        return result;
                    }
                    x = w[l];
                    nm = k-1;
                    y = w[nm];
                    g = rv1[nm];
                    h = rv1[k];
                    f = ((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
                    g = pythag(f, 1);
                    f = ((x-z)*(x+z)+h*(y/(f+extsign(g, f))-h))/x;
                    c = 1.0;
                    s = 1.0;
                    for(j=l; j<=nm; j++)
                    {
                        i = j+1;
                        g = rv1[i];
                        y = w[i];
                        h = s*g;
                        g = c*g;
                        z = pythag(f, h);
                        rv1[j] = z;
                        c = f/z;
                        s = h/z;
                        f = x*c+g*s;
                        g = -(x*s)+g*c;
                        h = y*s;
                        y = y*c;
                        for(jj=1; jj<=n; jj++)
                        {
                            x = v[jj,j];
                            z = v[jj,i];
                            v[jj,j] = x*c+z*s;
                            v[jj,i] = -(x*s)+z*c;
                        }
                        z = pythag(f, h);
                        w[j] = z;
                        if( (double)(z)!=(double)(0.0) )
                        {
                            z = 1.0/z;
                            c = f*z;
                            s = h*z;
                        }
                        f = c*g+s*y;
                        x = -(s*g)+c*y;
                        for(jj=1; jj<=m; jj++)
                        {
                            y = a[jj,j];
                            z = a[jj,i];
                            a[jj,j] = y*c+z*s;
                            a[jj,i] = -(y*s)+z*c;
                        }
                    }
                    rv1[l] = 0.0;
                    rv1[k] = f;
                    w[k] = x;
                }
            }
            return result;
        }


        private static double extsign(double a,
            double b)
        {
            double result = 0;

            if( (double)(b)>=(double)(0) )
            {
                result = Math.Abs(a);
            }
            else
            {
                result = -Math.Abs(a);
            }
            return result;
        }


        private static double mymax(double a,
            double b)
        {
            double result = 0;

            if( (double)(a)>(double)(b) )
            {
                result = a;
            }
            else
            {
                result = b;
            }
            return result;
        }


        private static double pythag(double a,
            double b)
        {
            double result = 0;

            if( (double)(Math.Abs(a))<(double)(Math.Abs(b)) )
            {
                result = Math.Abs(b)*Math.Sqrt(1+math.sqr(a/b));
            }
            else
            {
                result = Math.Abs(a)*Math.Sqrt(1+math.sqr(b/a));
            }
            return result;
        }


    }
    public class testtrfacunit
    {
        public static bool testtrfac(bool silent)
        {
            bool result = new bool();
            double[,] ra = new double[0,0];
            double[,] ral = new double[0,0];
            double[,] rau = new double[0,0];
            complex[,] ca = new complex[0,0];
            complex[,] cal = new complex[0,0];
            complex[,] cau = new complex[0,0];
            int m = 0;
            int n = 0;
            int mx = 0;
            int maxmn = 0;
            int i = 0;
            int j = 0;
            complex vc = 0;
            double vr = 0;
            bool waserrors = new bool();
            bool spderr = new bool();
            bool hpderr = new bool();
            bool rerr = new bool();
            bool cerr = new bool();
            bool properr = new bool();
            double threshold = 0;
            int i_ = 0;

            rerr = false;
            spderr = false;
            cerr = false;
            hpderr = false;
            properr = false;
            waserrors = false;
            maxmn = 4*ablas.ablasblocksize(ra)+1;
            threshold = 1000*math.machineepsilon*maxmn;
            
            //
            // test LU
            //
            for(mx=1; mx<=maxmn; mx++)
            {
                
                //
                // Initialize N/M, both are <=MX,
                // at least one of them is exactly equal to MX
                //
                n = 1+math.randominteger(mx);
                m = 1+math.randominteger(mx);
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    n = mx;
                }
                else
                {
                    m = mx;
                }
                
                //
                // First, test on zero matrix
                //
                ra = new double[m, n];
                ca = new complex[m, n];
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ra[i,j] = 0;
                        ca[i,j] = 0;
                    }
                }
                testcluproblem(ca, m, n, threshold, ref cerr, ref properr);
                testrluproblem(ra, m, n, threshold, ref rerr, ref properr);
                
                //
                // Second, random matrix with moderate condition number
                //
                ra = new double[m, n];
                ca = new complex[m, n];
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ra[i,j] = 0;
                        ca[i,j] = 0;
                    }
                }
                for(i=0; i<=Math.Min(m, n)-1; i++)
                {
                    ra[i,i] = 1+10*math.randomreal();
                    ca[i,i] = 1+10*math.randomreal();
                }
                matgen.cmatrixrndorthogonalfromtheleft(ref ca, m, n);
                matgen.cmatrixrndorthogonalfromtheright(ref ca, m, n);
                matgen.rmatrixrndorthogonalfromtheleft(ref ra, m, n);
                matgen.rmatrixrndorthogonalfromtheright(ref ra, m, n);
                testcluproblem(ca, m, n, threshold, ref cerr, ref properr);
                testrluproblem(ra, m, n, threshold, ref rerr, ref properr);
            }
            
            //
            // Test Cholesky
            //
            for(n=1; n<=maxmn; n++)
            {
                
                //
                // Load CA (HPD matrix with low condition number),
                //      CAL and CAU - its lower and upper triangles
                //
                matgen.hpdmatrixrndcond(n, 1+50*math.randomreal(), ref ca);
                cal = new complex[n, n];
                cau = new complex[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        cal[i,j] = i;
                        cau[i,j] = j;
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(i_=0; i_<=i;i_++)
                    {
                        cal[i,i_] = ca[i,i_];
                    }
                    for(i_=i; i_<=n-1;i_++)
                    {
                        cau[i,i_] = ca[i,i_];
                    }
                }
                
                //
                // Test HPDMatrixCholesky:
                // 1. it must leave upper (lower) part unchanged
                // 2. max(A-L*L^H) must be small
                //
                if( trfac.hpdmatrixcholesky(ref cal, n, false) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( j>i )
                            {
                                hpderr = hpderr | cal[i,j]!=i;
                            }
                            else
                            {
                                vc = 0.0;
                                for(i_=0; i_<=j;i_++)
                                {
                                    vc += cal[i,i_]*math.conj(cal[j,i_]);
                                }
                                hpderr = hpderr | (double)(math.abscomplex(ca[i,j]-vc))>(double)(threshold);
                            }
                        }
                    }
                }
                else
                {
                    hpderr = true;
                }
                if( trfac.hpdmatrixcholesky(ref cau, n, true) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( j<i )
                            {
                                hpderr = hpderr | cau[i,j]!=j;
                            }
                            else
                            {
                                vc = 0.0;
                                for(i_=0; i_<=i;i_++)
                                {
                                    vc += math.conj(cau[i_,i])*cau[i_,j];
                                }
                                hpderr = hpderr | (double)(math.abscomplex(ca[i,j]-vc))>(double)(threshold);
                            }
                        }
                    }
                }
                else
                {
                    hpderr = true;
                }
                
                //
                // Load RA (SPD matrix with low condition number),
                //      RAL and RAU - its lower and upper triangles
                //
                matgen.spdmatrixrndcond(n, 1+50*math.randomreal(), ref ra);
                ral = new double[n, n];
                rau = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ral[i,j] = i;
                        rau[i,j] = j;
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(i_=0; i_<=i;i_++)
                    {
                        ral[i,i_] = ra[i,i_];
                    }
                    for(i_=i; i_<=n-1;i_++)
                    {
                        rau[i,i_] = ra[i,i_];
                    }
                }
                
                //
                // Test SPDMatrixCholesky:
                // 1. it must leave upper (lower) part unchanged
                // 2. max(A-L*L^H) must be small
                //
                if( trfac.spdmatrixcholesky(ref ral, n, false) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( j>i )
                            {
                                spderr = spderr | (double)(ral[i,j])!=(double)(i);
                            }
                            else
                            {
                                vr = 0.0;
                                for(i_=0; i_<=j;i_++)
                                {
                                    vr += ral[i,i_]*ral[j,i_];
                                }
                                spderr = spderr | (double)(Math.Abs(ra[i,j]-vr))>(double)(threshold);
                            }
                        }
                    }
                }
                else
                {
                    spderr = true;
                }
                if( trfac.spdmatrixcholesky(ref rau, n, true) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( j<i )
                            {
                                spderr = spderr | (double)(rau[i,j])!=(double)(j);
                            }
                            else
                            {
                                vr = 0.0;
                                for(i_=0; i_<=i;i_++)
                                {
                                    vr += rau[i_,i]*rau[i_,j];
                                }
                                spderr = spderr | (double)(Math.Abs(ra[i,j]-vr))>(double)(threshold);
                            }
                        }
                    }
                }
                else
                {
                    spderr = true;
                }
            }
            
            //
            // report
            //
            waserrors = (((rerr | spderr) | cerr) | hpderr) | properr;
            if( !silent )
            {
                System.Console.Write("TESTING TRIANGULAR FACTORIZATIONS");
                System.Console.WriteLine();
                System.Console.Write("* REAL:                                  ");
                if( rerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SPD:                                   ");
                if( spderr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* COMPLEX:                               ");
                if( cerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* HPD:                                   ");
                if( hpderr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* OTHER PROPERTIES:                      ");
                if( properr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        private static void testcluproblem(complex[,] a,
            int m,
            int n,
            double threshold,
            ref bool err,
            ref bool properr)
        {
            complex[,] ca = new complex[0,0];
            complex[,] cl = new complex[0,0];
            complex[,] cu = new complex[0,0];
            complex[,] ca2 = new complex[0,0];
            complex[] ct = new complex[0];
            int i = 0;
            int j = 0;
            int minmn = 0;
            complex v = 0;
            int[] p = new int[0];
            int i_ = 0;

            minmn = Math.Min(m, n);
            
            //
            // PLU test
            //
            ca = new complex[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(i_=0; i_<=n-1;i_++)
                {
                    ca[i,i_] = a[i,i_];
                }
            }
            trfac.cmatrixplu(ref ca, m, n, ref p);
            for(i=0; i<=minmn-1; i++)
            {
                if( p[i]<i | p[i]>=m )
                {
                    properr = false;
                    return;
                }
            }
            cl = new complex[m, minmn];
            for(j=0; j<=minmn-1; j++)
            {
                for(i=0; i<=j-1; i++)
                {
                    cl[i,j] = 0.0;
                }
                cl[j,j] = 1.0;
                for(i=j+1; i<=m-1; i++)
                {
                    cl[i,j] = ca[i,j];
                }
            }
            cu = new complex[minmn, n];
            for(i=0; i<=minmn-1; i++)
            {
                for(j=0; j<=i-1; j++)
                {
                    cu[i,j] = 0.0;
                }
                for(j=i; j<=n-1; j++)
                {
                    cu[i,j] = ca[i,j];
                }
            }
            ca2 = new complex[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=minmn-1;i_++)
                    {
                        v += cl[i,i_]*cu[i_,j];
                    }
                    ca2[i,j] = v;
                }
            }
            ct = new complex[n];
            for(i=minmn-1; i>=0; i--)
            {
                if( i!=p[i] )
                {
                    for(i_=0; i_<=n-1;i_++)
                    {
                        ct[i_] = ca2[i,i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        ca2[i,i_] = ca2[p[i],i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        ca2[p[i],i_] = ct[i_];
                    }
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    err = err | (double)(math.abscomplex(a[i,j]-ca2[i,j]))>(double)(threshold);
                }
            }
            
            //
            // LUP test
            //
            ca = new complex[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(i_=0; i_<=n-1;i_++)
                {
                    ca[i,i_] = a[i,i_];
                }
            }
            trfac.cmatrixlup(ref ca, m, n, ref p);
            for(i=0; i<=minmn-1; i++)
            {
                if( p[i]<i | p[i]>=n )
                {
                    properr = false;
                    return;
                }
            }
            cl = new complex[m, minmn];
            for(j=0; j<=minmn-1; j++)
            {
                for(i=0; i<=j-1; i++)
                {
                    cl[i,j] = 0.0;
                }
                for(i=j; i<=m-1; i++)
                {
                    cl[i,j] = ca[i,j];
                }
            }
            cu = new complex[minmn, n];
            for(i=0; i<=minmn-1; i++)
            {
                for(j=0; j<=i-1; j++)
                {
                    cu[i,j] = 0.0;
                }
                cu[i,i] = 1.0;
                for(j=i+1; j<=n-1; j++)
                {
                    cu[i,j] = ca[i,j];
                }
            }
            ca2 = new complex[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=minmn-1;i_++)
                    {
                        v += cl[i,i_]*cu[i_,j];
                    }
                    ca2[i,j] = v;
                }
            }
            ct = new complex[m];
            for(i=minmn-1; i>=0; i--)
            {
                if( i!=p[i] )
                {
                    for(i_=0; i_<=m-1;i_++)
                    {
                        ct[i_] = ca2[i_,i];
                    }
                    for(i_=0; i_<=m-1;i_++)
                    {
                        ca2[i_,i] = ca2[i_,p[i]];
                    }
                    for(i_=0; i_<=m-1;i_++)
                    {
                        ca2[i_,p[i]] = ct[i_];
                    }
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    err = err | (double)(math.abscomplex(a[i,j]-ca2[i,j]))>(double)(threshold);
                }
            }
        }


        private static void testrluproblem(double[,] a,
            int m,
            int n,
            double threshold,
            ref bool err,
            ref bool properr)
        {
            double[,] ca = new double[0,0];
            double[,] cl = new double[0,0];
            double[,] cu = new double[0,0];
            double[,] ca2 = new double[0,0];
            double[] ct = new double[0];
            int i = 0;
            int j = 0;
            int minmn = 0;
            double v = 0;
            int[] p = new int[0];
            int i_ = 0;

            minmn = Math.Min(m, n);
            
            //
            // PLU test
            //
            ca = new double[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(i_=0; i_<=n-1;i_++)
                {
                    ca[i,i_] = a[i,i_];
                }
            }
            trfac.rmatrixplu(ref ca, m, n, ref p);
            for(i=0; i<=minmn-1; i++)
            {
                if( p[i]<i | p[i]>=m )
                {
                    properr = false;
                    return;
                }
            }
            cl = new double[m, minmn];
            for(j=0; j<=minmn-1; j++)
            {
                for(i=0; i<=j-1; i++)
                {
                    cl[i,j] = 0.0;
                }
                cl[j,j] = 1.0;
                for(i=j+1; i<=m-1; i++)
                {
                    cl[i,j] = ca[i,j];
                }
            }
            cu = new double[minmn, n];
            for(i=0; i<=minmn-1; i++)
            {
                for(j=0; j<=i-1; j++)
                {
                    cu[i,j] = 0.0;
                }
                for(j=i; j<=n-1; j++)
                {
                    cu[i,j] = ca[i,j];
                }
            }
            ca2 = new double[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=minmn-1;i_++)
                    {
                        v += cl[i,i_]*cu[i_,j];
                    }
                    ca2[i,j] = v;
                }
            }
            ct = new double[n];
            for(i=minmn-1; i>=0; i--)
            {
                if( i!=p[i] )
                {
                    for(i_=0; i_<=n-1;i_++)
                    {
                        ct[i_] = ca2[i,i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        ca2[i,i_] = ca2[p[i],i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        ca2[p[i],i_] = ct[i_];
                    }
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    err = err | (double)(Math.Abs(a[i,j]-ca2[i,j]))>(double)(threshold);
                }
            }
            
            //
            // LUP test
            //
            ca = new double[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(i_=0; i_<=n-1;i_++)
                {
                    ca[i,i_] = a[i,i_];
                }
            }
            trfac.rmatrixlup(ref ca, m, n, ref p);
            for(i=0; i<=minmn-1; i++)
            {
                if( p[i]<i | p[i]>=n )
                {
                    properr = false;
                    return;
                }
            }
            cl = new double[m, minmn];
            for(j=0; j<=minmn-1; j++)
            {
                for(i=0; i<=j-1; i++)
                {
                    cl[i,j] = 0.0;
                }
                for(i=j; i<=m-1; i++)
                {
                    cl[i,j] = ca[i,j];
                }
            }
            cu = new double[minmn, n];
            for(i=0; i<=minmn-1; i++)
            {
                for(j=0; j<=i-1; j++)
                {
                    cu[i,j] = 0.0;
                }
                cu[i,i] = 1.0;
                for(j=i+1; j<=n-1; j++)
                {
                    cu[i,j] = ca[i,j];
                }
            }
            ca2 = new double[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=minmn-1;i_++)
                    {
                        v += cl[i,i_]*cu[i_,j];
                    }
                    ca2[i,j] = v;
                }
            }
            ct = new double[m];
            for(i=minmn-1; i>=0; i--)
            {
                if( i!=p[i] )
                {
                    for(i_=0; i_<=m-1;i_++)
                    {
                        ct[i_] = ca2[i_,i];
                    }
                    for(i_=0; i_<=m-1;i_++)
                    {
                        ca2[i_,i] = ca2[i_,p[i]];
                    }
                    for(i_=0; i_<=m-1;i_++)
                    {
                        ca2[i_,p[i]] = ct[i_];
                    }
                }
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    err = err | (double)(Math.Abs(a[i,j]-ca2[i,j]))>(double)(threshold);
                }
            }
        }


    }
    public class testtrlinsolveunit
    {
        /*************************************************************************
        Main unittest subroutine
        *************************************************************************/
        public static bool testtrlinsolve(bool silent)
        {
            bool result = new bool();
            int maxmn = 0;
            int passcount = 0;
            double threshold = 0;
            double[,] aeffective = new double[0,0];
            double[,] aparam = new double[0,0];
            double[] xe = new double[0];
            double[] b = new double[0];
            int n = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int cnts = 0;
            int cntu = 0;
            int cntt = 0;
            int cntm = 0;
            bool waserrors = new bool();
            bool isupper = new bool();
            bool istrans = new bool();
            bool isunit = new bool();
            double v = 0;
            double s = 0;
            int i_ = 0;

            waserrors = false;
            maxmn = 15;
            passcount = 15;
            threshold = 1000*math.machineepsilon;
            
            //
            // Different problems
            //
            for(n=1; n<=maxmn; n++)
            {
                aeffective = new double[n-1+1, n-1+1];
                aparam = new double[n-1+1, n-1+1];
                xe = new double[n-1+1];
                b = new double[n-1+1];
                for(pass=1; pass<=passcount; pass++)
                {
                    for(cnts=0; cnts<=1; cnts++)
                    {
                        for(cntu=0; cntu<=1; cntu++)
                        {
                            for(cntt=0; cntt<=1; cntt++)
                            {
                                for(cntm=0; cntm<=2; cntm++)
                                {
                                    isupper = cnts==0;
                                    isunit = cntu==0;
                                    istrans = cntt==0;
                                    
                                    //
                                    // Skip meaningless combinations of parameters:
                                    // (matrix is singular) AND (matrix is unit diagonal)
                                    //
                                    if( cntm==2 & isunit )
                                    {
                                        continue;
                                    }
                                    
                                    //
                                    // Clear matrices
                                    //
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=0; j<=n-1; j++)
                                        {
                                            aeffective[i,j] = 0;
                                            aparam[i,j] = 0;
                                        }
                                    }
                                    
                                    //
                                    // Prepare matrices
                                    //
                                    if( isupper )
                                    {
                                        for(i=0; i<=n-1; i++)
                                        {
                                            for(j=i; j<=n-1; j++)
                                            {
                                                aeffective[i,j] = 0.9*(2*math.randomreal()-1);
                                                aparam[i,j] = aeffective[i,j];
                                            }
                                            aeffective[i,i] = (2*math.randominteger(2)-1)*(0.8+math.randomreal());
                                            aparam[i,i] = aeffective[i,i];
                                        }
                                    }
                                    else
                                    {
                                        for(i=0; i<=n-1; i++)
                                        {
                                            for(j=0; j<=i; j++)
                                            {
                                                aeffective[i,j] = 0.9*(2*math.randomreal()-1);
                                                aparam[i,j] = aeffective[i,j];
                                            }
                                            aeffective[i,i] = (2*math.randominteger(2)-1)*(0.8+math.randomreal());
                                            aparam[i,i] = aeffective[i,i];
                                        }
                                    }
                                    if( isunit )
                                    {
                                        for(i=0; i<=n-1; i++)
                                        {
                                            aeffective[i,i] = 1;
                                            aparam[i,i] = 0;
                                        }
                                    }
                                    if( istrans )
                                    {
                                        if( isupper )
                                        {
                                            for(i=0; i<=n-1; i++)
                                            {
                                                for(j=i+1; j<=n-1; j++)
                                                {
                                                    aeffective[j,i] = aeffective[i,j];
                                                    aeffective[i,j] = 0;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            for(i=0; i<=n-1; i++)
                                            {
                                                for(j=i+1; j<=n-1; j++)
                                                {
                                                    aeffective[i,j] = aeffective[j,i];
                                                    aeffective[j,i] = 0;
                                                }
                                            }
                                        }
                                    }
                                    
                                    //
                                    // Prepare task, solve, compare
                                    //
                                    for(i=0; i<=n-1; i++)
                                    {
                                        xe[i] = 2*math.randomreal()-1;
                                    }
                                    for(i=0; i<=n-1; i++)
                                    {
                                        v = 0.0;
                                        for(i_=0; i_<=n-1;i_++)
                                        {
                                            v += aeffective[i,i_]*xe[i_];
                                        }
                                        b[i] = v;
                                    }
                                    trlinsolve.rmatrixtrsafesolve(aparam, n, ref b, ref s, isupper, istrans, isunit);
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        xe[i_] = s*xe[i_];
                                    }
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        xe[i_] = xe[i_] - b[i_];
                                    }
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += xe[i_]*xe[i_];
                                    }
                                    v = Math.Sqrt(v);
                                    waserrors = waserrors | (double)(v)>(double)(threshold);
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // report
            //
            if( !silent )
            {
                System.Console.Write("TESTING RMatrixTRSafeSolve");
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void makeacopy(double[,] a,
            int m,
            int n,
            ref double[,] b)
        {
            int i = 0;
            int j = 0;

            b = new double[0,0];

            b = new double[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


    }
    public class testsafesolveunit
    {
        /*************************************************************************
        Main unittest subroutine
        *************************************************************************/
        public static bool testsafesolve(bool silent)
        {
            bool result = new bool();
            int maxmn = 0;
            double threshold = 0;
            bool rerrors = new bool();
            bool cerrors = new bool();
            bool waserrors = new bool();
            bool isupper = new bool();
            int trans = 0;
            bool isunit = new bool();
            double scalea = 0;
            double growth = 0;
            int i = 0;
            int j = 0;
            int n = 0;
            int j1 = 0;
            int j2 = 0;
            complex cv = 0;
            complex[,] ca = new complex[0,0];
            complex[,] cea = new complex[0,0];
            complex[,] ctmpa = new complex[0,0];
            complex[] cxs = new complex[0];
            complex[] cxe = new complex[0];
            double rv = 0;
            double[,] ra = new double[0,0];
            double[,] rea = new double[0,0];
            double[,] rtmpa = new double[0,0];
            double[] rxs = new double[0];
            double[] rxe = new double[0];
            int i_ = 0;

            maxmn = 30;
            threshold = 100000*math.machineepsilon;
            rerrors = false;
            cerrors = false;
            waserrors = false;
            
            //
            // Different problems: general tests
            //
            for(n=1; n<=maxmn; n++)
            {
                
                //
                // test complex solver with well-conditioned matrix:
                // 1. generate A: fill off-diagonal elements with small values,
                //    diagonal elements are filled with larger values
                // 2. generate 'effective' A
                // 3. prepare task (exact X is stored in CXE, right part - in CXS),
                //    solve and compare CXS and CXE
                //
                isupper = (double)(math.randomreal())>(double)(0.5);
                trans = math.randominteger(3);
                isunit = (double)(math.randomreal())>(double)(0.5);
                scalea = math.randomreal()+0.5;
                ca = new complex[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j )
                        {
                            ca[i,j].x = (2*math.randominteger(2)-1)*(5+math.randomreal());
                            ca[i,j].y = (2*math.randominteger(2)-1)*(5+math.randomreal());
                        }
                        else
                        {
                            ca[i,j].x = 0.2*math.randomreal()-0.1;
                            ca[i,j].y = 0.2*math.randomreal()-0.1;
                        }
                    }
                }
                cmatrixmakeacopy(ca, n, n, ref ctmpa);
                for(i=0; i<=n-1; i++)
                {
                    if( isupper )
                    {
                        j1 = 0;
                        j2 = i-1;
                    }
                    else
                    {
                        j1 = i+1;
                        j2 = n-1;
                    }
                    for(j=j1; j<=j2; j++)
                    {
                        ctmpa[i,j] = 0;
                    }
                    if( isunit )
                    {
                        ctmpa[i,i] = 1;
                    }
                }
                cea = new complex[n, n];
                for(i=0; i<=n-1; i++)
                {
                    if( trans==0 )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            cea[i,i_] = scalea*ctmpa[i,i_];
                        }
                    }
                    if( trans==1 )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            cea[i_,i] = scalea*ctmpa[i,i_];
                        }
                    }
                    if( trans==2 )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            cea[i_,i] = scalea*math.conj(ctmpa[i,i_]);
                        }
                    }
                }
                cxe = new complex[n];
                for(i=0; i<=n-1; i++)
                {
                    cxe[i].x = 2*math.randomreal()-1;
                    cxe[i].y = 2*math.randomreal()-1;
                }
                cxs = new complex[n];
                for(i=0; i<=n-1; i++)
                {
                    cv = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        cv += cea[i,i_]*cxe[i_];
                    }
                    cxs[i] = cv;
                }
                if( safesolve.cmatrixscaledtrsafesolve(ca, scalea, n, ref cxs, isupper, trans, isunit, Math.Sqrt(math.maxrealnumber)) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        cerrors = cerrors | (double)(math.abscomplex(cxs[i]-cxe[i]))>(double)(threshold);
                    }
                }
                else
                {
                    cerrors = true;
                }
                
                //
                // same with real
                //
                isupper = (double)(math.randomreal())>(double)(0.5);
                trans = math.randominteger(2);
                isunit = (double)(math.randomreal())>(double)(0.5);
                scalea = math.randomreal()+0.5;
                ra = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j )
                        {
                            ra[i,j] = (2*math.randominteger(2)-1)*(5+math.randomreal());
                        }
                        else
                        {
                            ra[i,j] = 0.2*math.randomreal()-0.1;
                        }
                    }
                }
                rmatrixmakeacopy(ra, n, n, ref rtmpa);
                for(i=0; i<=n-1; i++)
                {
                    if( isupper )
                    {
                        j1 = 0;
                        j2 = i-1;
                    }
                    else
                    {
                        j1 = i+1;
                        j2 = n-1;
                    }
                    for(j=j1; j<=j2; j++)
                    {
                        rtmpa[i,j] = 0;
                    }
                    if( isunit )
                    {
                        rtmpa[i,i] = 1;
                    }
                }
                rea = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    if( trans==0 )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            rea[i,i_] = scalea*rtmpa[i,i_];
                        }
                    }
                    if( trans==1 )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            rea[i_,i] = scalea*rtmpa[i,i_];
                        }
                    }
                }
                rxe = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    rxe[i] = 2*math.randomreal()-1;
                }
                rxs = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    rv = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        rv += rea[i,i_]*rxe[i_];
                    }
                    rxs[i] = rv;
                }
                if( safesolve.rmatrixscaledtrsafesolve(ra, scalea, n, ref rxs, isupper, trans, isunit, Math.Sqrt(math.maxrealnumber)) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        rerrors = rerrors | (double)(Math.Abs(rxs[i]-rxe[i]))>(double)(threshold);
                    }
                }
                else
                {
                    rerrors = true;
                }
            }
            
            //
            // Special test with diagonal ill-conditioned matrix:
            // * ability to solve it when resulting growth is less than threshold
            // * ability to stop solve when resulting growth is greater than threshold
            //
            // A = diag(1, 1/growth)
            // b = (1, 0.5)
            //
            n = 2;
            growth = 10;
            ca = new complex[n, n];
            ca[0,0] = 1;
            ca[0,1] = 0;
            ca[1,0] = 0;
            ca[1,1] = 1/growth;
            cxs = new complex[n];
            cxs[0] = 1.0;
            cxs[1] = 0.5;
            cerrors = cerrors | !safesolve.cmatrixscaledtrsafesolve(ca, 1.0, n, ref cxs, (double)(math.randomreal())>(double)(0.5), math.randominteger(3), false, 1.05*Math.Max(math.abscomplex(cxs[1])*growth, 1.0));
            cerrors = cerrors | !safesolve.cmatrixscaledtrsafesolve(ca, 1.0, n, ref cxs, (double)(math.randomreal())>(double)(0.5), math.randominteger(3), false, 0.95*Math.Max(math.abscomplex(cxs[1])*growth, 1.0));
            ra = new double[n, n];
            ra[0,0] = 1;
            ra[0,1] = 0;
            ra[1,0] = 0;
            ra[1,1] = 1/growth;
            rxs = new double[n];
            rxs[0] = 1.0;
            rxs[1] = 0.5;
            rerrors = rerrors | !safesolve.rmatrixscaledtrsafesolve(ra, 1.0, n, ref rxs, (double)(math.randomreal())>(double)(0.5), math.randominteger(2), false, 1.05*Math.Max(Math.Abs(rxs[1])*growth, 1.0));
            rerrors = rerrors | !safesolve.rmatrixscaledtrsafesolve(ra, 1.0, n, ref rxs, (double)(math.randomreal())>(double)(0.5), math.randominteger(2), false, 0.95*Math.Max(Math.Abs(rxs[1])*growth, 1.0));
            
            //
            // Special test with diagonal degenerate matrix:
            // * ability to solve it when resulting growth is less than threshold
            // * ability to stop solve when resulting growth is greater than threshold
            //
            // A = diag(1, 0)
            // b = (1, 0.5)
            //
            n = 2;
            ca = new complex[n, n];
            ca[0,0] = 1;
            ca[0,1] = 0;
            ca[1,0] = 0;
            ca[1,1] = 0;
            cxs = new complex[n];
            cxs[0] = 1.0;
            cxs[1] = 0.5;
            cerrors = cerrors | safesolve.cmatrixscaledtrsafesolve(ca, 1.0, n, ref cxs, (double)(math.randomreal())>(double)(0.5), math.randominteger(3), false, Math.Sqrt(math.maxrealnumber));
            ra = new double[n, n];
            ra[0,0] = 1;
            ra[0,1] = 0;
            ra[1,0] = 0;
            ra[1,1] = 0;
            rxs = new double[n];
            rxs[0] = 1.0;
            rxs[1] = 0.5;
            rerrors = rerrors | safesolve.rmatrixscaledtrsafesolve(ra, 1.0, n, ref rxs, (double)(math.randomreal())>(double)(0.5), math.randominteger(2), false, Math.Sqrt(math.maxrealnumber));
            
            //
            // report
            //
            waserrors = rerrors | cerrors;
            if( !silent )
            {
                System.Console.Write("TESTING SAFE TR SOLVER");
                System.Console.WriteLine();
                System.Console.Write("REAL:                                    ");
                if( !rerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("COMPLEX:                                 ");
                if( !cerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void rmatrixmakeacopy(double[,] a,
            int m,
            int n,
            ref double[,] b)
        {
            int i = 0;
            int j = 0;

            b = new double[0,0];

            b = new double[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void cmatrixmakeacopy(complex[,] a,
            int m,
            int n,
            ref complex[,] b)
        {
            int i = 0;
            int j = 0;

            b = new complex[0,0];

            b = new complex[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


    }
    public class testrcondunit
    {
        public const double threshold50 = 0.25;
        public const double threshold90 = 0.10;


        public static bool testrcond(bool silent)
        {
            bool result = new bool();
            int maxn = 0;
            int passcount = 0;
            bool waserrors = new bool();
            bool rtrerr = new bool();
            bool ctrerr = new bool();
            bool rerr = new bool();
            bool cerr = new bool();
            bool spderr = new bool();
            bool hpderr = new bool();

            maxn = 10;
            passcount = 100;
            
            //
            // report
            //
            rtrerr = !testrmatrixtrrcond(maxn, passcount);
            ctrerr = !testcmatrixtrrcond(maxn, passcount);
            rerr = !testrmatrixrcond(maxn, passcount);
            cerr = !testcmatrixrcond(maxn, passcount);
            spderr = !testspdmatrixrcond(maxn, passcount);
            hpderr = !testhpdmatrixrcond(maxn, passcount);
            waserrors = ((((rtrerr | ctrerr) | rerr) | cerr) | spderr) | hpderr;
            if( !silent )
            {
                System.Console.Write("TESTING RCOND");
                System.Console.WriteLine();
                System.Console.Write("REAL TRIANGULAR:                         ");
                if( !rtrerr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("COMPLEX TRIANGULAR:                      ");
                if( !ctrerr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("REAL:                                    ");
                if( !rerr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("SPD:                                     ");
                if( !spderr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("HPD:                                     ");
                if( !hpderr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("COMPLEX:                                 ");
                if( !cerr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void rmatrixmakeacopy(double[,] a,
            int m,
            int n,
            ref double[,] b)
        {
            int i = 0;
            int j = 0;

            b = new double[0,0];

            b = new double[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        Drops upper or lower half of the matrix - fills it by special pattern
        which may be used later to ensure that this part wasn't changed
        *************************************************************************/
        private static void rmatrixdrophalf(ref double[,] a,
            int n,
            bool droplower)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (droplower & i>j) | (!droplower & i<j) )
                    {
                        a[i,j] = 1+2*i+3*j;
                    }
                }
            }
        }


        /*************************************************************************
        Drops upper or lower half of the matrix - fills it by special pattern
        which may be used later to ensure that this part wasn't changed
        *************************************************************************/
        private static void cmatrixdrophalf(ref complex[,] a,
            int n,
            bool droplower)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (droplower & i>j) | (!droplower & i<j) )
                    {
                        a[i,j] = 1+2*i+3*j;
                    }
                }
            }
        }


        /*************************************************************************
        Generate matrix with given condition number C (2-norm)
        *************************************************************************/
        private static void rmatrixgenzero(ref double[,] a0,
            int n)
        {
            int i = 0;
            int j = 0;

            a0 = new double[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a0[i,j] = 0;
                }
            }
        }


        /*************************************************************************
        triangular inverse
        *************************************************************************/
        private static bool rmatrixinvmattr(ref double[,] a,
            int n,
            bool isupper,
            bool isunittriangular)
        {
            bool result = new bool();
            bool nounit = new bool();
            int i = 0;
            int j = 0;
            double v = 0;
            double ajj = 0;
            double[] t = new double[0];
            int i_ = 0;

            result = true;
            t = new double[n-1+1];
            
            //
            // Test the input parameters.
            //
            nounit = !isunittriangular;
            if( isupper )
            {
                
                //
                // Compute inverse of upper triangular matrix.
                //
                for(j=0; j<=n-1; j++)
                {
                    if( nounit )
                    {
                        if( (double)(a[j,j])==(double)(0) )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    
                    //
                    // Compute elements 1:j-1 of j-th column.
                    //
                    if( j>0 )
                    {
                        for(i_=0; i_<=j-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=0; i<=j-1; i++)
                        {
                            if( i<j-1 )
                            {
                                v = 0.0;
                                for(i_=i+1; i_<=j-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=0; i_<=j-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            else
            {
                
                //
                // Compute inverse of lower triangular matrix.
                //
                for(j=n-1; j>=0; j--)
                {
                    if( nounit )
                    {
                        if( (double)(a[j,j])==(double)(0) )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    if( j<n-1 )
                    {
                        
                        //
                        // Compute elements j+1:n of j-th column.
                        //
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=j+1; i<=n-1; i++)
                        {
                            if( i>j+1 )
                            {
                                v = 0.0;
                                for(i_=j+1; i_<=i-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        LU inverse
        *************************************************************************/
        private static bool rmatrixinvmatlu(ref double[,] a,
            int[] pivots,
            int n)
        {
            bool result = new bool();
            double[] work = new double[0];
            int i = 0;
            int j = 0;
            int jp = 0;
            double v = 0;
            int i_ = 0;

            result = true;
            
            //
            // Quick return if possible
            //
            if( n==0 )
            {
                return result;
            }
            work = new double[n-1+1];
            
            //
            // Form inv(U)
            //
            if( !rmatrixinvmattr(ref a, n, true, false) )
            {
                result = false;
                return result;
            }
            
            //
            // Solve the equation inv(A)*L = inv(U) for inv(A).
            //
            for(j=n-1; j>=0; j--)
            {
                
                //
                // Copy current column of L to WORK and replace with zeros.
                //
                for(i=j+1; i<=n-1; i++)
                {
                    work[i] = a[i,j];
                    a[i,j] = 0;
                }
                
                //
                // Compute current column of inv(A).
                //
                if( j<n-1 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*work[i_];
                        }
                        a[i,j] = a[i,j]-v;
                    }
                }
            }
            
            //
            // Apply column interchanges.
            //
            for(j=n-2; j>=0; j--)
            {
                jp = pivots[j];
                if( jp!=j )
                {
                    for(i_=0; i_<=n-1;i_++)
                    {
                        work[i_] = a[i_,j];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        a[i_,j] = a[i_,jp];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        a[i_,jp] = work[i_];
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Matrix inverse
        *************************************************************************/
        private static bool rmatrixinvmat(ref double[,] a,
            int n)
        {
            bool result = new bool();
            int[] pivots = new int[0];

            trfac.rmatrixlu(ref a, n, n, ref pivots);
            result = rmatrixinvmatlu(ref a, pivots, n);
            return result;
        }


        /*************************************************************************
        reference RCond
        *************************************************************************/
        private static void rmatrixrefrcond(double[,] a,
            int n,
            ref double rc1,
            ref double rcinf)
        {
            double[,] inva = new double[0,0];
            double nrm1a = 0;
            double nrminfa = 0;
            double nrm1inva = 0;
            double nrminfinva = 0;
            double v = 0;
            int k = 0;
            int i = 0;

            rc1 = 0;
            rcinf = 0;

            
            //
            // inv A
            //
            rmatrixmakeacopy(a, n, n, ref inva);
            if( !rmatrixinvmat(ref inva, n) )
            {
                rc1 = 0;
                rcinf = 0;
                return;
            }
            
            //
            // norm A
            //
            nrm1a = 0;
            nrminfa = 0;
            for(k=0; k<=n-1; k++)
            {
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+Math.Abs(a[i,k]);
                }
                nrm1a = Math.Max(nrm1a, v);
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+Math.Abs(a[k,i]);
                }
                nrminfa = Math.Max(nrminfa, v);
            }
            
            //
            // norm inv A
            //
            nrm1inva = 0;
            nrminfinva = 0;
            for(k=0; k<=n-1; k++)
            {
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+Math.Abs(inva[i,k]);
                }
                nrm1inva = Math.Max(nrm1inva, v);
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+Math.Abs(inva[k,i]);
                }
                nrminfinva = Math.Max(nrminfinva, v);
            }
            
            //
            // result
            //
            rc1 = nrm1inva*nrm1a;
            rcinf = nrminfinva*nrminfa;
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void cmatrixmakeacopy(complex[,] a,
            int m,
            int n,
            ref complex[,] b)
        {
            int i = 0;
            int j = 0;

            b = new complex[0,0];

            b = new complex[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        Generate matrix with given condition number C (2-norm)
        *************************************************************************/
        private static void cmatrixgenzero(ref complex[,] a0,
            int n)
        {
            int i = 0;
            int j = 0;

            a0 = new complex[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a0[i,j] = 0;
                }
            }
        }


        /*************************************************************************
        triangular inverse
        *************************************************************************/
        private static bool cmatrixinvmattr(ref complex[,] a,
            int n,
            bool isupper,
            bool isunittriangular)
        {
            bool result = new bool();
            bool nounit = new bool();
            int i = 0;
            int j = 0;
            complex v = 0;
            complex ajj = 0;
            complex[] t = new complex[0];
            int i_ = 0;

            result = true;
            t = new complex[n-1+1];
            
            //
            // Test the input parameters.
            //
            nounit = !isunittriangular;
            if( isupper )
            {
                
                //
                // Compute inverse of upper triangular matrix.
                //
                for(j=0; j<=n-1; j++)
                {
                    if( nounit )
                    {
                        if( a[j,j]==0 )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    
                    //
                    // Compute elements 1:j-1 of j-th column.
                    //
                    if( j>0 )
                    {
                        for(i_=0; i_<=j-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=0; i<=j-1; i++)
                        {
                            if( i<j-1 )
                            {
                                v = 0.0;
                                for(i_=i+1; i_<=j-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=0; i_<=j-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            else
            {
                
                //
                // Compute inverse of lower triangular matrix.
                //
                for(j=n-1; j>=0; j--)
                {
                    if( nounit )
                    {
                        if( a[j,j]==0 )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    if( j<n-1 )
                    {
                        
                        //
                        // Compute elements j+1:n of j-th column.
                        //
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=j+1; i<=n-1; i++)
                        {
                            if( i>j+1 )
                            {
                                v = 0.0;
                                for(i_=j+1; i_<=i-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        LU inverse
        *************************************************************************/
        private static bool cmatrixinvmatlu(ref complex[,] a,
            int[] pivots,
            int n)
        {
            bool result = new bool();
            complex[] work = new complex[0];
            int i = 0;
            int j = 0;
            int jp = 0;
            complex v = 0;
            int i_ = 0;

            result = true;
            
            //
            // Quick return if possible
            //
            if( n==0 )
            {
                return result;
            }
            work = new complex[n-1+1];
            
            //
            // Form inv(U)
            //
            if( !cmatrixinvmattr(ref a, n, true, false) )
            {
                result = false;
                return result;
            }
            
            //
            // Solve the equation inv(A)*L = inv(U) for inv(A).
            //
            for(j=n-1; j>=0; j--)
            {
                
                //
                // Copy current column of L to WORK and replace with zeros.
                //
                for(i=j+1; i<=n-1; i++)
                {
                    work[i] = a[i,j];
                    a[i,j] = 0;
                }
                
                //
                // Compute current column of inv(A).
                //
                if( j<n-1 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*work[i_];
                        }
                        a[i,j] = a[i,j]-v;
                    }
                }
            }
            
            //
            // Apply column interchanges.
            //
            for(j=n-2; j>=0; j--)
            {
                jp = pivots[j];
                if( jp!=j )
                {
                    for(i_=0; i_<=n-1;i_++)
                    {
                        work[i_] = a[i_,j];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        a[i_,j] = a[i_,jp];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        a[i_,jp] = work[i_];
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Matrix inverse
        *************************************************************************/
        private static bool cmatrixinvmat(ref complex[,] a,
            int n)
        {
            bool result = new bool();
            int[] pivots = new int[0];

            trfac.cmatrixlu(ref a, n, n, ref pivots);
            result = cmatrixinvmatlu(ref a, pivots, n);
            return result;
        }


        /*************************************************************************
        reference RCond
        *************************************************************************/
        private static void cmatrixrefrcond(complex[,] a,
            int n,
            ref double rc1,
            ref double rcinf)
        {
            complex[,] inva = new complex[0,0];
            double nrm1a = 0;
            double nrminfa = 0;
            double nrm1inva = 0;
            double nrminfinva = 0;
            double v = 0;
            int k = 0;
            int i = 0;

            rc1 = 0;
            rcinf = 0;

            
            //
            // inv A
            //
            cmatrixmakeacopy(a, n, n, ref inva);
            if( !cmatrixinvmat(ref inva, n) )
            {
                rc1 = 0;
                rcinf = 0;
                return;
            }
            
            //
            // norm A
            //
            nrm1a = 0;
            nrminfa = 0;
            for(k=0; k<=n-1; k++)
            {
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+math.abscomplex(a[i,k]);
                }
                nrm1a = Math.Max(nrm1a, v);
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+math.abscomplex(a[k,i]);
                }
                nrminfa = Math.Max(nrminfa, v);
            }
            
            //
            // norm inv A
            //
            nrm1inva = 0;
            nrminfinva = 0;
            for(k=0; k<=n-1; k++)
            {
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+math.abscomplex(inva[i,k]);
                }
                nrm1inva = Math.Max(nrm1inva, v);
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+math.abscomplex(inva[k,i]);
                }
                nrminfinva = Math.Max(nrminfinva, v);
            }
            
            //
            // result
            //
            rc1 = nrm1inva*nrm1a;
            rcinf = nrminfinva*nrminfa;
        }


        /*************************************************************************
        Returns True for successful test, False - for failed test
        *************************************************************************/
        private static bool testrmatrixtrrcond(int maxn,
            int passcount)
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            double[,] ea = new double[0,0];
            int[] p = new int[0];
            int n = 0;
            int i = 0;
            int j = 0;
            int j1 = 0;
            int j2 = 0;
            int pass = 0;
            bool err50 = new bool();
            bool err90 = new bool();
            bool errspec = new bool();
            bool errless = new bool();
            double erc1 = 0;
            double ercinf = 0;
            double[] q50 = new double[0];
            double[] q90 = new double[0];
            double v = 0;
            bool isupper = new bool();
            bool isunit = new bool();

            err50 = false;
            err90 = false;
            errless = false;
            errspec = false;
            q50 = new double[2];
            q90 = new double[2];
            for(n=1; n<=maxn; n++)
            {
                
                //
                // special test for zero matrix
                //
                rmatrixgenzero(ref a, n);
                errspec = errspec | (double)(rcond.rmatrixtrrcond1(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                errspec = errspec | (double)(rcond.rmatrixtrrcondinf(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                
                //
                // general test
                //
                a = new double[n, n];
                for(i=0; i<=1; i++)
                {
                    q50[i] = 0;
                    q90[i] = 0;
                }
                for(pass=1; pass<=passcount; pass++)
                {
                    isupper = (double)(math.randomreal())>(double)(0.5);
                    isunit = (double)(math.randomreal())>(double)(0.5);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = math.randomreal()-0.5;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 1+math.randomreal();
                    }
                    rmatrixmakeacopy(a, n, n, ref ea);
                    for(i=0; i<=n-1; i++)
                    {
                        if( isupper )
                        {
                            j1 = 0;
                            j2 = i-1;
                        }
                        else
                        {
                            j1 = i+1;
                            j2 = n-1;
                        }
                        for(j=j1; j<=j2; j++)
                        {
                            ea[i,j] = 0;
                        }
                        if( isunit )
                        {
                            ea[i,i] = 1;
                        }
                    }
                    rmatrixrefrcond(ea, n, ref erc1, ref ercinf);
                    
                    //
                    // 1-norm
                    //
                    v = 1/rcond.rmatrixtrrcond1(a, n, isupper, isunit);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[0] = q50[0]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[0] = q90[0]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                    
                    //
                    // Inf-norm
                    //
                    v = 1/rcond.rmatrixtrrcondinf(a, n, isupper, isunit);
                    if( (double)(v)>=(double)(threshold50*ercinf) )
                    {
                        q50[1] = q50[1]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*ercinf) )
                    {
                        q90[1] = q90[1]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(ercinf*1.001);
                }
                for(i=0; i<=1; i++)
                {
                    err50 = err50 | (double)(q50[i])<(double)(0.50);
                    err90 = err90 | (double)(q90[i])<(double)(0.90);
                }
                
                //
                // degenerate matrix test
                //
                if( n>=3 )
                {
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    a[0,0] = 1;
                    a[n-1,n-1] = 1;
                    errspec = errspec | (double)(rcond.rmatrixtrrcond1(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                    errspec = errspec | (double)(rcond.rmatrixtrrcondinf(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                }
                
                //
                // near-degenerate matrix test
                //
                if( n>=2 )
                {
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 1;
                    }
                    i = math.randominteger(n);
                    a[i,i] = 0.1*math.maxrealnumber;
                    errspec = errspec | (double)(rcond.rmatrixtrrcond1(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                    errspec = errspec | (double)(rcond.rmatrixtrrcondinf(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                }
            }
            
            //
            // report
            //
            result = !(((err50 | err90) | errless) | errspec);
            return result;
        }


        /*************************************************************************
        Returns True for successful test, False - for failed test
        *************************************************************************/
        private static bool testcmatrixtrrcond(int maxn,
            int passcount)
        {
            bool result = new bool();
            complex[,] a = new complex[0,0];
            complex[,] ea = new complex[0,0];
            int[] p = new int[0];
            int n = 0;
            int i = 0;
            int j = 0;
            int j1 = 0;
            int j2 = 0;
            int pass = 0;
            bool err50 = new bool();
            bool err90 = new bool();
            bool errspec = new bool();
            bool errless = new bool();
            double erc1 = 0;
            double ercinf = 0;
            double[] q50 = new double[0];
            double[] q90 = new double[0];
            double v = 0;
            bool isupper = new bool();
            bool isunit = new bool();

            err50 = false;
            err90 = false;
            errless = false;
            errspec = false;
            q50 = new double[2];
            q90 = new double[2];
            for(n=1; n<=maxn; n++)
            {
                
                //
                // special test for zero matrix
                //
                cmatrixgenzero(ref a, n);
                errspec = errspec | (double)(rcond.cmatrixtrrcond1(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                errspec = errspec | (double)(rcond.cmatrixtrrcondinf(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                
                //
                // general test
                //
                a = new complex[n, n];
                for(i=0; i<=1; i++)
                {
                    q50[i] = 0;
                    q90[i] = 0;
                }
                for(pass=1; pass<=passcount; pass++)
                {
                    isupper = (double)(math.randomreal())>(double)(0.5);
                    isunit = (double)(math.randomreal())>(double)(0.5);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j].x = math.randomreal()-0.5;
                            a[i,j].y = math.randomreal()-0.5;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i].x = 1+math.randomreal();
                        a[i,i].y = 1+math.randomreal();
                    }
                    cmatrixmakeacopy(a, n, n, ref ea);
                    for(i=0; i<=n-1; i++)
                    {
                        if( isupper )
                        {
                            j1 = 0;
                            j2 = i-1;
                        }
                        else
                        {
                            j1 = i+1;
                            j2 = n-1;
                        }
                        for(j=j1; j<=j2; j++)
                        {
                            ea[i,j] = 0;
                        }
                        if( isunit )
                        {
                            ea[i,i] = 1;
                        }
                    }
                    cmatrixrefrcond(ea, n, ref erc1, ref ercinf);
                    
                    //
                    // 1-norm
                    //
                    v = 1/rcond.cmatrixtrrcond1(a, n, isupper, isunit);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[0] = q50[0]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[0] = q90[0]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                    
                    //
                    // Inf-norm
                    //
                    v = 1/rcond.cmatrixtrrcondinf(a, n, isupper, isunit);
                    if( (double)(v)>=(double)(threshold50*ercinf) )
                    {
                        q50[1] = q50[1]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*ercinf) )
                    {
                        q90[1] = q90[1]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(ercinf*1.001);
                }
                for(i=0; i<=1; i++)
                {
                    err50 = err50 | (double)(q50[i])<(double)(0.50);
                    err90 = err90 | (double)(q90[i])<(double)(0.90);
                }
                
                //
                // degenerate matrix test
                //
                if( n>=3 )
                {
                    a = new complex[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    a[0,0] = 1;
                    a[n-1,n-1] = 1;
                    errspec = errspec | (double)(rcond.cmatrixtrrcond1(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                    errspec = errspec | (double)(rcond.cmatrixtrrcondinf(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                }
                
                //
                // near-degenerate matrix test
                //
                if( n>=2 )
                {
                    a = new complex[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 1;
                    }
                    i = math.randominteger(n);
                    a[i,i] = 0.1*math.maxrealnumber;
                    errspec = errspec | (double)(rcond.cmatrixtrrcond1(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                    errspec = errspec | (double)(rcond.cmatrixtrrcondinf(a, n, (double)(math.randomreal())>(double)(0.5), false))!=(double)(0);
                }
            }
            
            //
            // report
            //
            result = !(((err50 | err90) | errless) | errspec);
            return result;
        }


        /*************************************************************************
        Returns True for successful test, False - for failed test
        *************************************************************************/
        private static bool testrmatrixrcond(int maxn,
            int passcount)
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            double[,] lua = new double[0,0];
            int[] p = new int[0];
            int n = 0;
            int i = 0;
            int j = 0;
            int pass = 0;
            bool err50 = new bool();
            bool err90 = new bool();
            bool errspec = new bool();
            bool errless = new bool();
            double erc1 = 0;
            double ercinf = 0;
            double[] q50 = new double[0];
            double[] q90 = new double[0];
            double v = 0;

            err50 = false;
            err90 = false;
            errless = false;
            errspec = false;
            q50 = new double[3+1];
            q90 = new double[3+1];
            for(n=1; n<=maxn; n++)
            {
                
                //
                // special test for zero matrix
                //
                rmatrixgenzero(ref a, n);
                rmatrixmakeacopy(a, n, n, ref lua);
                trfac.rmatrixlu(ref lua, n, n, ref p);
                errspec = errspec | (double)(rcond.rmatrixrcond1(a, n))!=(double)(0);
                errspec = errspec | (double)(rcond.rmatrixrcondinf(a, n))!=(double)(0);
                errspec = errspec | (double)(rcond.rmatrixlurcond1(lua, n))!=(double)(0);
                errspec = errspec | (double)(rcond.rmatrixlurcondinf(lua, n))!=(double)(0);
                
                //
                // general test
                //
                a = new double[n-1+1, n-1+1];
                for(i=0; i<=3; i++)
                {
                    q50[i] = 0;
                    q90[i] = 0;
                }
                for(pass=1; pass<=passcount; pass++)
                {
                    matgen.rmatrixrndcond(n, Math.Exp(math.randomreal()*Math.Log(1000)), ref a);
                    rmatrixmakeacopy(a, n, n, ref lua);
                    trfac.rmatrixlu(ref lua, n, n, ref p);
                    rmatrixrefrcond(a, n, ref erc1, ref ercinf);
                    
                    //
                    // 1-norm, normal
                    //
                    v = 1/rcond.rmatrixrcond1(a, n);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[0] = q50[0]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[0] = q90[0]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                    
                    //
                    // 1-norm, LU
                    //
                    v = 1/rcond.rmatrixlurcond1(lua, n);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[1] = q50[1]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[1] = q90[1]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                    
                    //
                    // Inf-norm, normal
                    //
                    v = 1/rcond.rmatrixrcondinf(a, n);
                    if( (double)(v)>=(double)(threshold50*ercinf) )
                    {
                        q50[2] = q50[2]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*ercinf) )
                    {
                        q90[2] = q90[2]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(ercinf*1.001);
                    
                    //
                    // Inf-norm, LU
                    //
                    v = 1/rcond.rmatrixlurcondinf(lua, n);
                    if( (double)(v)>=(double)(threshold50*ercinf) )
                    {
                        q50[3] = q50[3]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*ercinf) )
                    {
                        q90[3] = q90[3]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(ercinf*1.001);
                }
                for(i=0; i<=3; i++)
                {
                    err50 = err50 | (double)(q50[i])<(double)(0.50);
                    err90 = err90 | (double)(q90[i])<(double)(0.90);
                }
                
                //
                // degenerate matrix test
                //
                if( n>=3 )
                {
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    a[0,0] = 1;
                    a[n-1,n-1] = 1;
                    errspec = errspec | (double)(rcond.rmatrixrcond1(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.rmatrixrcondinf(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.rmatrixlurcond1(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.rmatrixlurcondinf(a, n))!=(double)(0);
                }
                
                //
                // near-degenerate matrix test
                //
                if( n>=2 )
                {
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 1;
                    }
                    i = math.randominteger(n);
                    a[i,i] = 0.1*math.maxrealnumber;
                    errspec = errspec | (double)(rcond.rmatrixrcond1(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.rmatrixrcondinf(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.rmatrixlurcond1(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.rmatrixlurcondinf(a, n))!=(double)(0);
                }
            }
            
            //
            // report
            //
            result = !(((err50 | err90) | errless) | errspec);
            return result;
        }


        /*************************************************************************
        Returns True for successful test, False - for failed test
        *************************************************************************/
        private static bool testspdmatrixrcond(int maxn,
            int passcount)
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            double[,] cha = new double[0,0];
            int[] p = new int[0];
            int n = 0;
            int i = 0;
            int j = 0;
            int pass = 0;
            bool err50 = new bool();
            bool err90 = new bool();
            bool errspec = new bool();
            bool errless = new bool();
            bool isupper = new bool();
            double erc1 = 0;
            double ercinf = 0;
            double[] q50 = new double[0];
            double[] q90 = new double[0];
            double v = 0;

            err50 = false;
            err90 = false;
            errless = false;
            errspec = false;
            q50 = new double[2];
            q90 = new double[2];
            for(n=1; n<=maxn; n++)
            {
                isupper = (double)(math.randomreal())>(double)(0.5);
                
                //
                // general test
                //
                a = new double[n, n];
                for(i=0; i<=1; i++)
                {
                    q50[i] = 0;
                    q90[i] = 0;
                }
                for(pass=1; pass<=passcount; pass++)
                {
                    matgen.spdmatrixrndcond(n, Math.Exp(math.randomreal()*Math.Log(1000)), ref a);
                    rmatrixrefrcond(a, n, ref erc1, ref ercinf);
                    rmatrixdrophalf(ref a, n, isupper);
                    rmatrixmakeacopy(a, n, n, ref cha);
                    trfac.spdmatrixcholesky(ref cha, n, isupper);
                    
                    //
                    // normal
                    //
                    v = 1/rcond.spdmatrixrcond(a, n, isupper);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[0] = q50[0]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[0] = q90[0]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                    
                    //
                    // Cholesky
                    //
                    v = 1/rcond.spdmatrixcholeskyrcond(cha, n, isupper);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[1] = q50[1]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[1] = q90[1]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                }
                for(i=0; i<=1; i++)
                {
                    err50 = err50 | (double)(q50[i])<(double)(0.50);
                    err90 = err90 | (double)(q90[i])<(double)(0.90);
                }
                
                //
                // degenerate matrix test
                //
                if( n>=3 )
                {
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    a[0,0] = 1;
                    a[n-1,n-1] = 1;
                    errspec = errspec | (double)(rcond.spdmatrixrcond(a, n, isupper))!=(double)(-1);
                    errspec = errspec | (double)(rcond.spdmatrixcholeskyrcond(a, n, isupper))!=(double)(0);
                }
                
                //
                // near-degenerate matrix test
                //
                if( n>=2 )
                {
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 1;
                    }
                    i = math.randominteger(n);
                    a[i,i] = 0.1*math.maxrealnumber;
                    errspec = errspec | (double)(rcond.spdmatrixrcond(a, n, isupper))!=(double)(0);
                    errspec = errspec | (double)(rcond.spdmatrixcholeskyrcond(a, n, isupper))!=(double)(0);
                }
            }
            
            //
            // report
            //
            result = !(((err50 | err90) | errless) | errspec);
            return result;
        }


        /*************************************************************************
        Returns True for successful test, False - for failed test
        *************************************************************************/
        private static bool testcmatrixrcond(int maxn,
            int passcount)
        {
            bool result = new bool();
            complex[,] a = new complex[0,0];
            complex[,] lua = new complex[0,0];
            int[] p = new int[0];
            int n = 0;
            int i = 0;
            int j = 0;
            int pass = 0;
            bool err50 = new bool();
            bool err90 = new bool();
            bool errless = new bool();
            bool errspec = new bool();
            double erc1 = 0;
            double ercinf = 0;
            double[] q50 = new double[0];
            double[] q90 = new double[0];
            double v = 0;

            q50 = new double[3+1];
            q90 = new double[3+1];
            err50 = false;
            err90 = false;
            errless = false;
            errspec = false;
            
            //
            // process
            //
            for(n=1; n<=maxn; n++)
            {
                
                //
                // special test for zero matrix
                //
                cmatrixgenzero(ref a, n);
                cmatrixmakeacopy(a, n, n, ref lua);
                trfac.cmatrixlu(ref lua, n, n, ref p);
                errspec = errspec | (double)(rcond.cmatrixrcond1(a, n))!=(double)(0);
                errspec = errspec | (double)(rcond.cmatrixrcondinf(a, n))!=(double)(0);
                errspec = errspec | (double)(rcond.cmatrixlurcond1(lua, n))!=(double)(0);
                errspec = errspec | (double)(rcond.cmatrixlurcondinf(lua, n))!=(double)(0);
                
                //
                // general test
                //
                a = new complex[n-1+1, n-1+1];
                for(i=0; i<=3; i++)
                {
                    q50[i] = 0;
                    q90[i] = 0;
                }
                for(pass=1; pass<=passcount; pass++)
                {
                    matgen.cmatrixrndcond(n, Math.Exp(math.randomreal()*Math.Log(1000)), ref a);
                    cmatrixmakeacopy(a, n, n, ref lua);
                    trfac.cmatrixlu(ref lua, n, n, ref p);
                    cmatrixrefrcond(a, n, ref erc1, ref ercinf);
                    
                    //
                    // 1-norm, normal
                    //
                    v = 1/rcond.cmatrixrcond1(a, n);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[0] = q50[0]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[0] = q90[0]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                    
                    //
                    // 1-norm, LU
                    //
                    v = 1/rcond.cmatrixlurcond1(lua, n);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[1] = q50[1]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[1] = q90[1]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                    
                    //
                    // Inf-norm, normal
                    //
                    v = 1/rcond.cmatrixrcondinf(a, n);
                    if( (double)(v)>=(double)(threshold50*ercinf) )
                    {
                        q50[2] = q50[2]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*ercinf) )
                    {
                        q90[2] = q90[2]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(ercinf*1.001);
                    
                    //
                    // Inf-norm, LU
                    //
                    v = 1/rcond.cmatrixlurcondinf(lua, n);
                    if( (double)(v)>=(double)(threshold50*ercinf) )
                    {
                        q50[3] = q50[3]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*ercinf) )
                    {
                        q90[3] = q90[3]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(ercinf*1.001);
                }
                for(i=0; i<=3; i++)
                {
                    err50 = err50 | (double)(q50[i])<(double)(0.50);
                    err90 = err90 | (double)(q90[i])<(double)(0.90);
                }
                
                //
                // degenerate matrix test
                //
                if( n>=3 )
                {
                    a = new complex[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    a[0,0] = 1;
                    a[n-1,n-1] = 1;
                    errspec = errspec | (double)(rcond.cmatrixrcond1(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.cmatrixrcondinf(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.cmatrixlurcond1(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.cmatrixlurcondinf(a, n))!=(double)(0);
                }
                
                //
                // near-degenerate matrix test
                //
                if( n>=2 )
                {
                    a = new complex[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 1;
                    }
                    i = math.randominteger(n);
                    a[i,i] = 0.1*math.maxrealnumber;
                    errspec = errspec | (double)(rcond.cmatrixrcond1(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.cmatrixrcondinf(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.cmatrixlurcond1(a, n))!=(double)(0);
                    errspec = errspec | (double)(rcond.cmatrixlurcondinf(a, n))!=(double)(0);
                }
            }
            
            //
            // report
            //
            result = !(((err50 | err90) | errless) | errspec);
            return result;
        }


        /*************************************************************************
        Returns True for successful test, False - for failed test
        *************************************************************************/
        private static bool testhpdmatrixrcond(int maxn,
            int passcount)
        {
            bool result = new bool();
            complex[,] a = new complex[0,0];
            complex[,] cha = new complex[0,0];
            int[] p = new int[0];
            int n = 0;
            int i = 0;
            int j = 0;
            int pass = 0;
            bool err50 = new bool();
            bool err90 = new bool();
            bool errspec = new bool();
            bool errless = new bool();
            bool isupper = new bool();
            double erc1 = 0;
            double ercinf = 0;
            double[] q50 = new double[0];
            double[] q90 = new double[0];
            double v = 0;

            err50 = false;
            err90 = false;
            errless = false;
            errspec = false;
            q50 = new double[2];
            q90 = new double[2];
            for(n=1; n<=maxn; n++)
            {
                isupper = (double)(math.randomreal())>(double)(0.5);
                
                //
                // general test
                //
                a = new complex[n, n];
                for(i=0; i<=1; i++)
                {
                    q50[i] = 0;
                    q90[i] = 0;
                }
                for(pass=1; pass<=passcount; pass++)
                {
                    matgen.hpdmatrixrndcond(n, Math.Exp(math.randomreal()*Math.Log(1000)), ref a);
                    cmatrixrefrcond(a, n, ref erc1, ref ercinf);
                    cmatrixdrophalf(ref a, n, isupper);
                    cmatrixmakeacopy(a, n, n, ref cha);
                    trfac.hpdmatrixcholesky(ref cha, n, isupper);
                    
                    //
                    // normal
                    //
                    v = 1/rcond.hpdmatrixrcond(a, n, isupper);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[0] = q50[0]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[0] = q90[0]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                    
                    //
                    // Cholesky
                    //
                    v = 1/rcond.hpdmatrixcholeskyrcond(cha, n, isupper);
                    if( (double)(v)>=(double)(threshold50*erc1) )
                    {
                        q50[1] = q50[1]+(double)1/(double)passcount;
                    }
                    if( (double)(v)>=(double)(threshold90*erc1) )
                    {
                        q90[1] = q90[1]+(double)1/(double)passcount;
                    }
                    errless = errless | (double)(v)>(double)(erc1*1.001);
                }
                for(i=0; i<=1; i++)
                {
                    err50 = err50 | (double)(q50[i])<(double)(0.50);
                    err90 = err90 | (double)(q90[i])<(double)(0.90);
                }
                
                //
                // degenerate matrix test
                //
                if( n>=3 )
                {
                    a = new complex[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    a[0,0] = 1;
                    a[n-1,n-1] = 1;
                    errspec = errspec | (double)(rcond.hpdmatrixrcond(a, n, isupper))!=(double)(-1);
                    errspec = errspec | (double)(rcond.hpdmatrixcholeskyrcond(a, n, isupper))!=(double)(0);
                }
                
                //
                // near-degenerate matrix test
                //
                if( n>=2 )
                {
                    a = new complex[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 1;
                    }
                    i = math.randominteger(n);
                    a[i,i] = 0.1*math.maxrealnumber;
                    errspec = errspec | (double)(rcond.hpdmatrixrcond(a, n, isupper))!=(double)(0);
                    errspec = errspec | (double)(rcond.hpdmatrixcholeskyrcond(a, n, isupper))!=(double)(0);
                }
            }
            
            //
            // report
            //
            result = !(((err50 | err90) | errless) | errspec);
            return result;
        }


    }
    public class testmatinvunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testmatinv(bool silent)
        {
            bool result = new bool();
            int maxrn = 0;
            int maxcn = 0;
            int passcount = 0;
            double threshold = 0;
            double rcondtol = 0;
            bool rtrerrors = new bool();
            bool ctrerrors = new bool();
            bool rerrors = new bool();
            bool cerrors = new bool();
            bool spderrors = new bool();
            bool hpderrors = new bool();
            bool waserrors = new bool();
            double[,] emptyra = new double[0,0];
            double[,] emptyca = new double[0,0];

            maxrn = 3*ablas.ablasblocksize(emptyra)+1;
            maxcn = 3*ablas.ablasblocksize(emptyca)+1;
            passcount = 1;
            threshold = 10000*math.machineepsilon;
            rcondtol = 0.01;
            rtrerrors = false;
            ctrerrors = false;
            rerrors = false;
            cerrors = false;
            spderrors = false;
            hpderrors = false;
            testrtrinv(maxrn, passcount, threshold, ref rtrerrors);
            testctrinv(maxcn, passcount, threshold, ref ctrerrors);
            testrinv(maxrn, passcount, threshold, ref rerrors);
            testspdinv(maxrn, passcount, threshold, ref spderrors);
            testcinv(maxcn, passcount, threshold, ref cerrors);
            testhpdinv(maxcn, passcount, threshold, ref hpderrors);
            waserrors = ((((rtrerrors | ctrerrors) | rerrors) | cerrors) | spderrors) | hpderrors;
            if( !silent )
            {
                System.Console.Write("TESTING MATINV");
                System.Console.WriteLine();
                System.Console.Write("* REAL TRIANGULAR:                        ");
                if( rtrerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* COMPLEX TRIANGULAR:                     ");
                if( ctrerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* REAL:                                   ");
                if( rerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* COMPLEX:                                ");
                if( cerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SPD:                                    ");
                if( spderrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* HPD:                                    ");
                if( hpderrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void rmatrixmakeacopy(double[,] a,
            int m,
            int n,
            ref double[,] b)
        {
            int i = 0;
            int j = 0;

            b = new double[0,0];

            b = new double[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void cmatrixmakeacopy(complex[,] a,
            int m,
            int n,
            ref complex[,] b)
        {
            int i = 0;
            int j = 0;

            b = new complex[0,0];

            b = new complex[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        Checks whether inverse is correct
        Returns True on success.
        *************************************************************************/
        private static bool rmatrixcheckinverse(double[,] a,
            double[,] inva,
            int n,
            double threshold,
            int info,
            matinv.matinvreport rep)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;
            double v = 0;
            int i_ = 0;

            result = true;
            if( info<=0 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(100*math.machineepsilon) | (double)(rep.r1)>(double)(1+1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(100*math.machineepsilon) | (double)(rep.rinf)>(double)(1+1000*math.machineepsilon));
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*inva[i_,j];
                        }
                        if( i==j )
                        {
                            v = v-1;
                        }
                        result = result & (double)(Math.Abs(v))<=(double)(threshold);
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Checks whether inverse is correct
        Returns True on success.
        *************************************************************************/
        private static bool spdmatrixcheckinverse(double[,] a,
            double[,] inva,
            bool isupper,
            int n,
            double threshold,
            int info,
            matinv.matinvreport rep)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;
            double v = 0;
            int i_ = 0;

            a = (double[,])a.Clone();
            inva = (double[,])inva.Clone();

            for(i=0; i<=n-2; i++)
            {
                if( isupper )
                {
                    for(i_=i+1; i_<=n-1;i_++)
                    {
                        a[i_,i] = a[i,i_];
                    }
                    for(i_=i+1; i_<=n-1;i_++)
                    {
                        inva[i_,i] = inva[i,i_];
                    }
                }
                else
                {
                    for(i_=i+1; i_<=n-1;i_++)
                    {
                        a[i,i_] = a[i_,i];
                    }
                    for(i_=i+1; i_<=n-1;i_++)
                    {
                        inva[i,i_] = inva[i_,i];
                    }
                }
            }
            result = true;
            if( info<=0 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(100*math.machineepsilon) | (double)(rep.r1)>(double)(1+1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(100*math.machineepsilon) | (double)(rep.rinf)>(double)(1+1000*math.machineepsilon));
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*inva[i_,j];
                        }
                        if( i==j )
                        {
                            v = v-1;
                        }
                        result = result & (double)(Math.Abs(v))<=(double)(threshold);
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Checks whether inverse is correct
        Returns True on success.
        *************************************************************************/
        private static bool hpdmatrixcheckinverse(complex[,] a,
            complex[,] inva,
            bool isupper,
            int n,
            double threshold,
            int info,
            matinv.matinvreport rep)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;
            complex v = 0;
            int i_ = 0;

            a = (complex[,])a.Clone();
            inva = (complex[,])inva.Clone();

            for(i=0; i<=n-2; i++)
            {
                if( isupper )
                {
                    for(i_=i+1; i_<=n-1;i_++)
                    {
                        a[i_,i] = math.conj(a[i,i_]);
                    }
                    for(i_=i+1; i_<=n-1;i_++)
                    {
                        inva[i_,i] = math.conj(inva[i,i_]);
                    }
                }
                else
                {
                    for(i_=i+1; i_<=n-1;i_++)
                    {
                        a[i,i_] = math.conj(a[i_,i]);
                    }
                    for(i_=i+1; i_<=n-1;i_++)
                    {
                        inva[i,i_] = math.conj(inva[i_,i]);
                    }
                }
            }
            result = true;
            if( info<=0 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(100*math.machineepsilon) | (double)(rep.r1)>(double)(1+1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(100*math.machineepsilon) | (double)(rep.rinf)>(double)(1+1000*math.machineepsilon));
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*inva[i_,j];
                        }
                        if( i==j )
                        {
                            v = v-1;
                        }
                        result = result & (double)(math.abscomplex(v))<=(double)(threshold);
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Checks whether inversion result indicate singular matrix
        Returns True on success.
        *************************************************************************/
        private static bool rmatrixcheckinversesingular(double[,] inva,
            int n,
            double threshold,
            int info,
            matinv.matinvreport rep)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info!=-3 & info!=1 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(0) | (double)(rep.r1)>(double)(1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(0) | (double)(rep.rinf)>(double)(1000*math.machineepsilon));
                if( info==-3 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            result = result & (double)(inva[i,j])==(double)(0);
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Checks whether inverse is correct
        Returns True on success.
        *************************************************************************/
        private static bool cmatrixcheckinverse(complex[,] a,
            complex[,] inva,
            int n,
            double threshold,
            int info,
            matinv.matinvreport rep)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;
            complex v = 0;
            int i_ = 0;

            result = true;
            if( info<=0 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(100*math.machineepsilon) | (double)(rep.r1)>(double)(1+1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(100*math.machineepsilon) | (double)(rep.rinf)>(double)(1+1000*math.machineepsilon));
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*inva[i_,j];
                        }
                        if( i==j )
                        {
                            v = v-1;
                        }
                        result = result & (double)(math.abscomplex(v))<=(double)(threshold);
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Checks whether inversion result indicate singular matrix
        Returns True on success.
        *************************************************************************/
        private static bool cmatrixcheckinversesingular(complex[,] inva,
            int n,
            double threshold,
            int info,
            matinv.matinvreport rep)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info!=-3 & info!=1 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(0) | (double)(rep.r1)>(double)(1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(0) | (double)(rep.rinf)>(double)(1000*math.machineepsilon));
                if( info==-3 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            result = result & inva[i,j]==0;
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Drops upper or lower half of the matrix - fills it by special pattern
        which may be used later to ensure that this part wasn't changed
        *************************************************************************/
        private static void rmatrixdrophalf(ref double[,] a,
            int n,
            bool droplower)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (droplower & i>j) | (!droplower & i<j) )
                    {
                        a[i,j] = 1+2*i+3*j;
                    }
                }
            }
        }


        /*************************************************************************
        Drops upper or lower half of the matrix - fills it by special pattern
        which may be used later to ensure that this part wasn't changed
        *************************************************************************/
        private static void cmatrixdrophalf(ref complex[,] a,
            int n,
            bool droplower)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (droplower & i>j) | (!droplower & i<j) )
                    {
                        a[i,j] = 1+2*i+3*j;
                    }
                }
            }
        }


        /*************************************************************************
        Real TR inverse
        *************************************************************************/
        private static void testrtrinv(int maxn,
            int passcount,
            double threshold,
            ref bool rtrerrors)
        {
            double[,] a = new double[0,0];
            double[,] b = new double[0,0];
            int n = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int task = 0;
            bool isupper = new bool();
            bool isunit = new bool();
            double v = 0;
            bool waserrors = new bool();
            int info = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int i_ = 0;

            waserrors = false;
            
            //
            // Test
            //
            for(n=1; n<=maxn; n++)
            {
                a = new double[n, n];
                b = new double[n, n];
                for(task=0; task<=3; task++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        
                        //
                        // Determine task
                        //
                        isupper = task%2==0;
                        isunit = task/2%2==0;
                        
                        //
                        // Generate matrix
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( i==j )
                                {
                                    a[i,i] = 1+math.randomreal();
                                }
                                else
                                {
                                    a[i,j] = 0.2*math.randomreal()-0.1;
                                }
                                b[i,j] = a[i,j];
                            }
                        }
                        
                        //
                        // Inverse
                        //
                        matinv.rmatrixtrinverse(ref b, n, isupper, isunit, ref info, rep);
                        if( info<=0 )
                        {
                            rtrerrors = true;
                            return;
                        }
                        
                        //
                        // Structural test
                        //
                        if( isunit )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                rtrerrors = rtrerrors | (double)(a[i,i])!=(double)(b[i,i]);
                            }
                        }
                        if( isupper )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=i-1; j++)
                                {
                                    rtrerrors = rtrerrors | (double)(a[i,j])!=(double)(b[i,j]);
                                }
                            }
                        }
                        else
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=i+1; j<=n-1; j++)
                                {
                                    rtrerrors = rtrerrors | (double)(a[i,j])!=(double)(b[i,j]);
                                }
                            }
                        }
                        
                        //
                        // Inverse test
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j<i & isupper) | (j>i & !isupper) )
                                {
                                    a[i,j] = 0;
                                    b[i,j] = 0;
                                }
                            }
                        }
                        if( isunit )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                a[i,i] = 1;
                                b[i,i] = 1;
                            }
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*b[i_,j];
                                }
                                if( j!=i )
                                {
                                    rtrerrors = rtrerrors | (double)(Math.Abs(v))>(double)(threshold);
                                }
                                else
                                {
                                    rtrerrors = rtrerrors | (double)(Math.Abs(v-1))>(double)(threshold);
                                }
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Complex TR inverse
        *************************************************************************/
        private static void testctrinv(int maxn,
            int passcount,
            double threshold,
            ref bool ctrerrors)
        {
            complex[,] a = new complex[0,0];
            complex[,] b = new complex[0,0];
            int n = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int task = 0;
            bool isupper = new bool();
            bool isunit = new bool();
            complex v = 0;
            bool waserrors = new bool();
            int info = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int i_ = 0;

            waserrors = false;
            
            //
            // Test
            //
            for(n=1; n<=maxn; n++)
            {
                a = new complex[n, n];
                b = new complex[n, n];
                for(task=0; task<=3; task++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        
                        //
                        // Determine task
                        //
                        isupper = task%2==0;
                        isunit = task/2%2==0;
                        
                        //
                        // Generate matrix
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( i==j )
                                {
                                    a[i,i].x = 1+math.randomreal();
                                    a[i,i].y = 1+math.randomreal();
                                }
                                else
                                {
                                    a[i,j].x = 0.2*math.randomreal()-0.1;
                                    a[i,j].y = 0.2*math.randomreal()-0.1;
                                }
                                b[i,j] = a[i,j];
                            }
                        }
                        
                        //
                        // Inverse
                        //
                        matinv.cmatrixtrinverse(ref b, n, isupper, isunit, ref info, rep);
                        if( info<=0 )
                        {
                            ctrerrors = true;
                            return;
                        }
                        
                        //
                        // Structural test
                        //
                        if( isunit )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                ctrerrors = ctrerrors | a[i,i]!=b[i,i];
                            }
                        }
                        if( isupper )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=i-1; j++)
                                {
                                    ctrerrors = ctrerrors | a[i,j]!=b[i,j];
                                }
                            }
                        }
                        else
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=i+1; j<=n-1; j++)
                                {
                                    ctrerrors = ctrerrors | a[i,j]!=b[i,j];
                                }
                            }
                        }
                        
                        //
                        // Inverse test
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j<i & isupper) | (j>i & !isupper) )
                                {
                                    a[i,j] = 0;
                                    b[i,j] = 0;
                                }
                            }
                        }
                        if( isunit )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                a[i,i] = 1;
                                b[i,i] = 1;
                            }
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*b[i_,j];
                                }
                                if( j!=i )
                                {
                                    ctrerrors = ctrerrors | (double)(math.abscomplex(v))>(double)(threshold);
                                }
                                else
                                {
                                    ctrerrors = ctrerrors | (double)(math.abscomplex(v-1))>(double)(threshold);
                                }
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Real test
        *************************************************************************/
        private static void testrinv(int maxn,
            int passcount,
            double threshold,
            ref bool rerrors)
        {
            double[,] a = new double[0,0];
            double[,] lua = new double[0,0];
            double[,] inva = new double[0,0];
            double[,] invlua = new double[0,0];
            int[] p = new int[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int pass = 0;
            int taskkind = 0;
            int info = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int i_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // ********************************************************
                    // WELL CONDITIONED TASKS
                    // ability to find correct solution is tested
                    // ********************************************************
                    //
                    // 1. generate random well conditioned matrix A.
                    // 2. generate random solution vector xe
                    // 3. generate right part b=A*xe
                    // 4. test different methods on original A
                    //
                    matgen.rmatrixrndcond(n, 1000, ref a);
                    rmatrixmakeacopy(a, n, n, ref lua);
                    trfac.rmatrixlu(ref lua, n, n, ref p);
                    rmatrixmakeacopy(a, n, n, ref inva);
                    rmatrixmakeacopy(lua, n, n, ref invlua);
                    info = 0;
                    unsetrep(rep);
                    matinv.rmatrixinverse(ref inva, n, ref info, rep);
                    rerrors = rerrors | !rmatrixcheckinverse(a, inva, n, threshold, info, rep);
                    info = 0;
                    unsetrep(rep);
                    matinv.rmatrixluinverse(ref invlua, p, n, ref info, rep);
                    rerrors = rerrors | !rmatrixcheckinverse(a, invlua, n, threshold, info, rep);
                    
                    //
                    // ********************************************************
                    // EXACTLY SINGULAR MATRICES
                    // ability to detect singularity is tested
                    // ********************************************************
                    //
                    // 1. generate different types of singular matrices:
                    //    * zero
                    //    * with zero columns
                    //    * with zero rows
                    //    * with equal rows/columns
                    // 2. test different methods
                    //
                    for(taskkind=0; taskkind<=4; taskkind++)
                    {
                        unset2d(ref a);
                        if( taskkind==0 )
                        {
                            
                            //
                            // all zeros
                            //
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        if( taskkind==1 )
                        {
                            
                            //
                            // there is zero column
                            //
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            k = math.randominteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,k] = 0*a[i_,k];
                            }
                        }
                        if( taskkind==2 )
                        {
                            
                            //
                            // there is zero row
                            //
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            k = math.randominteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[k,i_] = 0*a[k,i_];
                            }
                        }
                        if( taskkind==3 )
                        {
                            
                            //
                            // equal columns
                            //
                            if( n<2 )
                            {
                                continue;
                            }
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            k = 1+math.randominteger(n-1);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,0] = a[i_,k];
                            }
                        }
                        if( taskkind==4 )
                        {
                            
                            //
                            // equal rows
                            //
                            if( n<2 )
                            {
                                continue;
                            }
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            k = 1+math.randominteger(n-1);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[0,i_] = a[k,i_];
                            }
                        }
                        rmatrixmakeacopy(a, n, n, ref lua);
                        trfac.rmatrixlu(ref lua, n, n, ref p);
                        info = 0;
                        unsetrep(rep);
                        matinv.rmatrixinverse(ref a, n, ref info, rep);
                        rerrors = rerrors | !rmatrixcheckinversesingular(a, n, threshold, info, rep);
                        info = 0;
                        unsetrep(rep);
                        matinv.rmatrixluinverse(ref lua, p, n, ref info, rep);
                        rerrors = rerrors | !rmatrixcheckinversesingular(lua, n, threshold, info, rep);
                    }
                }
            }
        }


        /*************************************************************************
        Complex test
        *************************************************************************/
        private static void testcinv(int maxn,
            int passcount,
            double threshold,
            ref bool cerrors)
        {
            complex[,] a = new complex[0,0];
            complex[,] lua = new complex[0,0];
            complex[,] inva = new complex[0,0];
            complex[,] invlua = new complex[0,0];
            int[] p = new int[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int pass = 0;
            int taskkind = 0;
            int info = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int i_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // ********************************************************
                    // WELL CONDITIONED TASKS
                    // ability to find correct solution is tested
                    // ********************************************************
                    //
                    // 1. generate random well conditioned matrix A.
                    // 2. generate random solution vector xe
                    // 3. generate right part b=A*xe
                    // 4. test different methods on original A
                    //
                    matgen.cmatrixrndcond(n, 1000, ref a);
                    cmatrixmakeacopy(a, n, n, ref lua);
                    trfac.cmatrixlu(ref lua, n, n, ref p);
                    cmatrixmakeacopy(a, n, n, ref inva);
                    cmatrixmakeacopy(lua, n, n, ref invlua);
                    info = 0;
                    unsetrep(rep);
                    matinv.cmatrixinverse(ref inva, n, ref info, rep);
                    cerrors = cerrors | !cmatrixcheckinverse(a, inva, n, threshold, info, rep);
                    info = 0;
                    unsetrep(rep);
                    matinv.cmatrixluinverse(ref invlua, p, n, ref info, rep);
                    cerrors = cerrors | !cmatrixcheckinverse(a, invlua, n, threshold, info, rep);
                    
                    //
                    // ********************************************************
                    // EXACTLY SINGULAR MATRICES
                    // ability to detect singularity is tested
                    // ********************************************************
                    //
                    // 1. generate different types of singular matrices:
                    //    * zero
                    //    * with zero columns
                    //    * with zero rows
                    //    * with equal rows/columns
                    // 2. test different methods
                    //
                    for(taskkind=0; taskkind<=4; taskkind++)
                    {
                        cunset2d(ref a);
                        if( taskkind==0 )
                        {
                            
                            //
                            // all zeros
                            //
                            a = new complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        if( taskkind==1 )
                        {
                            
                            //
                            // there is zero column
                            //
                            a = new complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j].x = 2*math.randomreal()-1;
                                    a[i,j].y = 2*math.randomreal()-1;
                                }
                            }
                            k = math.randominteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,k] = 0*a[i_,k];
                            }
                        }
                        if( taskkind==2 )
                        {
                            
                            //
                            // there is zero row
                            //
                            a = new complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j].x = 2*math.randomreal()-1;
                                    a[i,j].y = 2*math.randomreal()-1;
                                }
                            }
                            k = math.randominteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[k,i_] = 0*a[k,i_];
                            }
                        }
                        if( taskkind==3 )
                        {
                            
                            //
                            // equal columns
                            //
                            if( n<2 )
                            {
                                continue;
                            }
                            a = new complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j].x = 2*math.randomreal()-1;
                                    a[i,j].y = 2*math.randomreal()-1;
                                }
                            }
                            k = 1+math.randominteger(n-1);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,0] = a[i_,k];
                            }
                        }
                        if( taskkind==4 )
                        {
                            
                            //
                            // equal rows
                            //
                            if( n<2 )
                            {
                                continue;
                            }
                            a = new complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j].x = 2*math.randomreal()-1;
                                    a[i,j].y = 2*math.randomreal()-1;
                                }
                            }
                            k = 1+math.randominteger(n-1);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[0,i_] = a[k,i_];
                            }
                        }
                        cmatrixmakeacopy(a, n, n, ref lua);
                        trfac.cmatrixlu(ref lua, n, n, ref p);
                        info = 0;
                        unsetrep(rep);
                        matinv.cmatrixinverse(ref a, n, ref info, rep);
                        cerrors = cerrors | !cmatrixcheckinversesingular(a, n, threshold, info, rep);
                        info = 0;
                        unsetrep(rep);
                        matinv.cmatrixluinverse(ref lua, p, n, ref info, rep);
                        cerrors = cerrors | !cmatrixcheckinversesingular(lua, n, threshold, info, rep);
                    }
                }
            }
        }


        /*************************************************************************
        SPD test
        *************************************************************************/
        private static void testspdinv(int maxn,
            int passcount,
            double threshold,
            ref bool spderrors)
        {
            double[,] a = new double[0,0];
            double[,] cha = new double[0,0];
            double[,] inva = new double[0,0];
            double[,] invcha = new double[0,0];
            bool isupper = new bool();
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int pass = 0;
            int taskkind = 0;
            int info = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int i_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    isupper = (double)(math.randomreal())>(double)(0.5);
                    
                    //
                    // ********************************************************
                    // WELL CONDITIONED TASKS
                    // ability to find correct solution is tested
                    // ********************************************************
                    //
                    // 1. generate random well conditioned matrix A.
                    // 2. generate random solution vector xe
                    // 3. generate right part b=A*xe
                    // 4. test different methods on original A
                    //
                    matgen.spdmatrixrndcond(n, 1000, ref a);
                    rmatrixdrophalf(ref a, n, isupper);
                    rmatrixmakeacopy(a, n, n, ref cha);
                    if( !trfac.spdmatrixcholesky(ref cha, n, isupper) )
                    {
                        continue;
                    }
                    rmatrixmakeacopy(a, n, n, ref inva);
                    rmatrixmakeacopy(cha, n, n, ref invcha);
                    info = 0;
                    unsetrep(rep);
                    matinv.spdmatrixinverse(ref inva, n, isupper, ref info, rep);
                    spderrors = spderrors | !spdmatrixcheckinverse(a, inva, isupper, n, threshold, info, rep);
                    info = 0;
                    unsetrep(rep);
                    matinv.spdmatrixcholeskyinverse(ref invcha, n, isupper, ref info, rep);
                    spderrors = spderrors | !spdmatrixcheckinverse(a, invcha, isupper, n, threshold, info, rep);
                    
                    //
                    // ********************************************************
                    // EXACTLY SINGULAR MATRICES
                    // ability to detect singularity is tested
                    // ********************************************************
                    //
                    // 1. generate different types of singular matrices:
                    //    * zero
                    //    * with zero columns
                    //    * with zero rows
                    // 2. test different methods
                    //
                    for(taskkind=0; taskkind<=2; taskkind++)
                    {
                        unset2d(ref a);
                        if( taskkind==0 )
                        {
                            
                            //
                            // all zeros
                            //
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        if( taskkind==1 )
                        {
                            
                            //
                            // there is zero column
                            //
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            k = math.randominteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,k] = 0*a[i_,k];
                            }
                        }
                        if( taskkind==2 )
                        {
                            
                            //
                            // there is zero row
                            //
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            k = math.randominteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[k,i_] = 0*a[k,i_];
                            }
                        }
                        info = 0;
                        unsetrep(rep);
                        matinv.spdmatrixcholeskyinverse(ref a, n, isupper, ref info, rep);
                        if( info!=-3 & info!=1 )
                        {
                            spderrors = true;
                        }
                        else
                        {
                            spderrors = (spderrors | (double)(rep.r1)<(double)(0)) | (double)(rep.r1)>(double)(1000*math.machineepsilon);
                            spderrors = (spderrors | (double)(rep.rinf)<(double)(0)) | (double)(rep.rinf)>(double)(1000*math.machineepsilon);
                        }
                    }
                }
            }
        }


        /*************************************************************************
        HPD test
        *************************************************************************/
        private static void testhpdinv(int maxn,
            int passcount,
            double threshold,
            ref bool hpderrors)
        {
            complex[,] a = new complex[0,0];
            complex[,] cha = new complex[0,0];
            complex[,] inva = new complex[0,0];
            complex[,] invcha = new complex[0,0];
            bool isupper = new bool();
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int pass = 0;
            int taskkind = 0;
            int info = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int i_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    isupper = (double)(math.randomreal())>(double)(0.5);
                    
                    //
                    // ********************************************************
                    // WELL CONDITIONED TASKS
                    // ability to find correct solution is tested
                    // ********************************************************
                    //
                    // 1. generate random well conditioned matrix A.
                    // 2. generate random solution vector xe
                    // 3. generate right part b=A*xe
                    // 4. test different methods on original A
                    //
                    matgen.hpdmatrixrndcond(n, 1000, ref a);
                    cmatrixdrophalf(ref a, n, isupper);
                    cmatrixmakeacopy(a, n, n, ref cha);
                    if( !trfac.hpdmatrixcholesky(ref cha, n, isupper) )
                    {
                        continue;
                    }
                    cmatrixmakeacopy(a, n, n, ref inva);
                    cmatrixmakeacopy(cha, n, n, ref invcha);
                    info = 0;
                    unsetrep(rep);
                    matinv.hpdmatrixinverse(ref inva, n, isupper, ref info, rep);
                    hpderrors = hpderrors | !hpdmatrixcheckinverse(a, inva, isupper, n, threshold, info, rep);
                    info = 0;
                    unsetrep(rep);
                    matinv.hpdmatrixcholeskyinverse(ref invcha, n, isupper, ref info, rep);
                    hpderrors = hpderrors | !hpdmatrixcheckinverse(a, invcha, isupper, n, threshold, info, rep);
                    
                    //
                    // ********************************************************
                    // EXACTLY SINGULAR MATRICES
                    // ability to detect singularity is tested
                    // ********************************************************
                    //
                    // 1. generate different types of singular matrices:
                    //    * zero
                    //    * with zero columns
                    //    * with zero rows
                    // 2. test different methods
                    //
                    for(taskkind=0; taskkind<=2; taskkind++)
                    {
                        cunset2d(ref a);
                        if( taskkind==0 )
                        {
                            
                            //
                            // all zeros
                            //
                            a = new complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        if( taskkind==1 )
                        {
                            
                            //
                            // there is zero column
                            //
                            a = new complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j].x = 2*math.randomreal()-1;
                                    a[i,j].y = 2*math.randomreal()-1;
                                }
                            }
                            k = math.randominteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,k] = 0*a[i_,k];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[k,i_] = 0*a[k,i_];
                            }
                        }
                        if( taskkind==2 )
                        {
                            
                            //
                            // there is zero row
                            //
                            a = new complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j].x = 2*math.randomreal()-1;
                                    a[i,j].y = 2*math.randomreal()-1;
                                }
                            }
                            k = math.randominteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[k,i_] = 0*a[k,i_];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,k] = 0*a[i_,k];
                            }
                        }
                        info = 0;
                        unsetrep(rep);
                        matinv.hpdmatrixcholeskyinverse(ref a, n, isupper, ref info, rep);
                        if( info!=-3 & info!=1 )
                        {
                            hpderrors = true;
                        }
                        else
                        {
                            hpderrors = (hpderrors | (double)(rep.r1)<(double)(0)) | (double)(rep.r1)>(double)(1000*math.machineepsilon);
                            hpderrors = (hpderrors | (double)(rep.rinf)<(double)(0)) | (double)(rep.rinf)>(double)(1000*math.machineepsilon);
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Unsets real matrix
        *************************************************************************/
        private static void unset2d(ref double[,] x)
        {
            x = new double[1, 1];
            x[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets real vector
        *************************************************************************/
        private static void unset1d(ref double[] x)
        {
            x = new double[1];
            x[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets real matrix
        *************************************************************************/
        private static void cunset2d(ref complex[,] x)
        {
            x = new complex[1, 1];
            x[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets real vector
        *************************************************************************/
        private static void cunset1d(ref complex[] x)
        {
            x = new complex[1];
            x[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets report
        *************************************************************************/
        private static void unsetrep(matinv.matinvreport r)
        {
            r.r1 = -1;
            r.rinf = -1;
        }


    }
    public class testldaunit
    {
        public static bool testlda(bool silent)
        {
            bool result = new bool();
            int maxnf = 0;
            int maxns = 0;
            int maxnc = 0;
            int passcount = 0;
            bool ldanerrors = new bool();
            bool lda1errors = new bool();
            bool waserrors = new bool();
            int nf = 0;
            int nc = 0;
            int ns = 0;
            int i = 0;
            int info = 0;
            int pass = 0;
            int axis = 0;
            double[,] xy = new double[0,0];
            double[,] wn = new double[0,0];
            double[] w1 = new double[0];

            
            //
            // Primary settings
            //
            maxnf = 10;
            maxns = 1000;
            maxnc = 5;
            passcount = 1;
            waserrors = false;
            ldanerrors = false;
            lda1errors = false;
            
            //
            // General tests
            //
            for(nf=1; nf<=maxnf; nf++)
            {
                for(nc=2; nc<=maxnc; nc++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        
                        //
                        // Simple test for LDA-N/LDA-1
                        //
                        axis = math.randominteger(nf);
                        ns = maxns/2+math.randominteger(maxns/2);
                        gensimpleset(nf, nc, ns, axis, ref xy);
                        lda.fisherldan(xy, ns, nf, nc, ref info, ref wn);
                        if( info!=1 )
                        {
                            ldanerrors = true;
                            continue;
                        }
                        ldanerrors = ldanerrors | !testwn(xy, wn, ns, nf, nc, 0);
                        ldanerrors = ldanerrors | (double)(Math.Abs(wn[axis,0]))<=(double)(0.75);
                        lda.fisherlda(xy, ns, nf, nc, ref info, ref w1);
                        for(i=0; i<=nf-1; i++)
                        {
                            lda1errors = lda1errors | (double)(w1[i])!=(double)(wn[i,0]);
                        }
                        
                        //
                        // Degenerate test for LDA-N
                        //
                        if( nf>=3 )
                        {
                            ns = maxns/2+math.randominteger(maxns/2);
                            
                            //
                            // there are two duplicate features,
                            // axis is oriented along non-duplicate feature
                            //
                            axis = math.randominteger(nf-2);
                            gendeg1set(nf, nc, ns, axis, ref xy);
                            lda.fisherldan(xy, ns, nf, nc, ref info, ref wn);
                            if( info!=2 )
                            {
                                ldanerrors = true;
                                continue;
                            }
                            ldanerrors = ldanerrors | (double)(wn[axis,0])<=(double)(0.75);
                            lda.fisherlda(xy, ns, nf, nc, ref info, ref w1);
                            for(i=0; i<=nf-1; i++)
                            {
                                lda1errors = lda1errors | (double)(w1[i])!=(double)(wn[i,0]);
                            }
                        }
                    }
                }
            }
            
            //
            // Final report
            //
            waserrors = ldanerrors | lda1errors;
            if( !silent )
            {
                System.Console.Write("LDA TEST");
                System.Console.WriteLine();
                System.Console.Write("FISHER LDA-N:                            ");
                if( !ldanerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("FISHER LDA-1:                            ");
                if( !lda1errors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Generates 'simple' set - a sequence of unit 'balls' at (0,0), (1,0), (2,0)
        and so on.
        *************************************************************************/
        private static void gensimpleset(int nfeatures,
            int nclasses,
            int nsamples,
            int axis,
            ref double[,] xy)
        {
            int i = 0;
            int j = 0;
            int c = 0;

            xy = new double[0,0];

            ap.assert(axis>=0 & axis<nfeatures, "GenSimpleSet: wrong Axis!");
            xy = new double[nsamples-1+1, nfeatures+1];
            for(i=0; i<=nsamples-1; i++)
            {
                for(j=0; j<=nfeatures-1; j++)
                {
                    xy[i,j] = generatenormal(0.0, 1.0);
                }
                c = i%nclasses;
                xy[i,axis] = xy[i,axis]+c;
                xy[i,nfeatures] = c;
            }
        }


        /*************************************************************************
        Generates 'degenerate' set #1.
        NFeatures>=3.
        *************************************************************************/
        private static void gendeg1set(int nfeatures,
            int nclasses,
            int nsamples,
            int axis,
            ref double[,] xy)
        {
            int i = 0;
            int j = 0;
            int c = 0;

            xy = new double[0,0];

            ap.assert(axis>=0 & axis<nfeatures, "GenDeg1Set: wrong Axis!");
            ap.assert(nfeatures>=3, "GenDeg1Set: wrong NFeatures!");
            xy = new double[nsamples-1+1, nfeatures+1];
            if( axis>=nfeatures-2 )
            {
                axis = nfeatures-3;
            }
            for(i=0; i<=nsamples-1; i++)
            {
                for(j=0; j<=nfeatures-2; j++)
                {
                    xy[i,j] = generatenormal(0.0, 1.0);
                }
                xy[i,nfeatures-1] = xy[i,nfeatures-2];
                c = i%nclasses;
                xy[i,axis] = xy[i,axis]+c;
                xy[i,nfeatures] = c;
            }
        }


        /*************************************************************************
        Normal random number
        *************************************************************************/
        private static double generatenormal(double mean,
            double sigma)
        {
            double result = 0;
            double u = 0;
            double v = 0;
            double sum = 0;

            result = mean;
            while( true )
            {
                u = (2*math.randominteger(2)-1)*math.randomreal();
                v = (2*math.randominteger(2)-1)*math.randomreal();
                sum = u*u+v*v;
                if( (double)(sum)<(double)(1) & (double)(sum)>(double)(0) )
                {
                    sum = Math.Sqrt(-(2*Math.Log(sum)/sum));
                    result = sigma*u*sum+mean;
                    return result;
                }
            }
            return result;
        }


        /*************************************************************************
        Tests WN for correctness
        *************************************************************************/
        private static bool testwn(double[,] xy,
            double[,] wn,
            int ns,
            int nf,
            int nc,
            int ndeg)
        {
            bool result = new bool();
            double[,] st = new double[0,0];
            double[,] sw = new double[0,0];
            double[,] a = new double[0,0];
            double[,] z = new double[0,0];
            double[] tx = new double[0];
            double[] jp = new double[0];
            double[] jq = new double[0];
            double[] work = new double[0];
            int i = 0;
            int j = 0;
            double v = 0;
            double wprev = 0;
            double tol = 0;
            double p = 0;
            double q = 0;
            int i_ = 0;

            tol = 10000;
            result = true;
            fishers(xy, ns, nf, nc, ref st, ref sw);
            
            //
            // Test for decreasing of J
            //
            tx = new double[nf-1+1];
            jp = new double[nf-1+1];
            jq = new double[nf-1+1];
            for(j=0; j<=nf-1; j++)
            {
                for(i_=0; i_<=nf-1;i_++)
                {
                    tx[i_] = wn[i_,j];
                }
                v = calcj(nf, st, sw, tx, ref p, ref q);
                jp[j] = p;
                jq[j] = q;
            }
            for(i=1; i<=nf-1-ndeg; i++)
            {
                result = result & (double)(jp[i-1]/jq[i-1])>=(double)((1-tol*math.machineepsilon)*jp[i]/jq[i]);
            }
            for(i=nf-1-ndeg+1; i<=nf-1; i++)
            {
                result = result & (double)(jp[i])<=(double)(tol*math.machineepsilon*jp[0]);
            }
            
            //
            // Test for J optimality
            //
            for(i_=0; i_<=nf-1;i_++)
            {
                tx[i_] = wn[i_,0];
            }
            v = calcj(nf, st, sw, tx, ref p, ref q);
            for(i=0; i<=nf-1; i++)
            {
                wprev = tx[i];
                tx[i] = wprev+0.01;
                result = result & (double)(v)>=(double)((1-tol*math.machineepsilon)*calcj(nf, st, sw, tx, ref p, ref q));
                tx[i] = wprev-0.01;
                result = result & (double)(v)>=(double)((1-tol*math.machineepsilon)*calcj(nf, st, sw, tx, ref p, ref q));
                tx[i] = wprev;
            }
            
            //
            // Test for linear independence of W
            //
            work = new double[nf+1];
            a = new double[nf-1+1, nf-1+1];
            blas.matrixmatrixmultiply(wn, 0, nf-1, 0, nf-1, false, wn, 0, nf-1, 0, nf-1, true, 1.0, ref a, 0, nf-1, 0, nf-1, 0.0, ref work);
            if( evd.smatrixevd(a, nf, 1, true, ref tx, ref z) )
            {
                result = result & (double)(tx[0])>(double)(tx[nf-1]*1000*math.machineepsilon);
            }
            
            //
            // Test for other properties
            //
            for(j=0; j<=nf-1; j++)
            {
                v = 0.0;
                for(i_=0; i_<=nf-1;i_++)
                {
                    v += wn[i_,j]*wn[i_,j];
                }
                v = Math.Sqrt(v);
                result = result & (double)(Math.Abs(v-1))<=(double)(1000*math.machineepsilon);
                v = 0;
                for(i=0; i<=nf-1; i++)
                {
                    v = v+wn[i,j];
                }
                result = result & (double)(v)>=(double)(0);
            }
            return result;
        }


        /*************************************************************************
        Calculates J
        *************************************************************************/
        private static double calcj(int nf,
            double[,] st,
            double[,] sw,
            double[] w,
            ref double p,
            ref double q)
        {
            double result = 0;
            double[] tx = new double[0];
            int i = 0;
            double v = 0;
            int i_ = 0;

            p = 0;
            q = 0;

            tx = new double[nf-1+1];
            for(i=0; i<=nf-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=nf-1;i_++)
                {
                    v += st[i,i_]*w[i_];
                }
                tx[i] = v;
            }
            v = 0.0;
            for(i_=0; i_<=nf-1;i_++)
            {
                v += w[i_]*tx[i_];
            }
            p = v;
            for(i=0; i<=nf-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=nf-1;i_++)
                {
                    v += sw[i,i_]*w[i_];
                }
                tx[i] = v;
            }
            v = 0.0;
            for(i_=0; i_<=nf-1;i_++)
            {
                v += w[i_]*tx[i_];
            }
            q = v;
            result = p/q;
            return result;
        }


        /*************************************************************************
        Calculates ST/SW
        *************************************************************************/
        private static void fishers(double[,] xy,
            int npoints,
            int nfeatures,
            int nclasses,
            ref double[,] st,
            ref double[,] sw)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            int[] c = new int[0];
            double[] mu = new double[0];
            double[,] muc = new double[0,0];
            int[] nc = new int[0];
            double[] tf = new double[0];
            double[] work = new double[0];
            int i_ = 0;

            st = new double[0,0];
            sw = new double[0,0];

            
            //
            // Prepare temporaries
            //
            tf = new double[nfeatures-1+1];
            work = new double[nfeatures+1];
            
            //
            // Convert class labels from reals to integers (just for convenience)
            //
            c = new int[npoints-1+1];
            for(i=0; i<=npoints-1; i++)
            {
                c[i] = (int)Math.Round(xy[i,nfeatures]);
            }
            
            //
            // Calculate class sizes and means
            //
            mu = new double[nfeatures-1+1];
            muc = new double[nclasses-1+1, nfeatures-1+1];
            nc = new int[nclasses-1+1];
            for(j=0; j<=nfeatures-1; j++)
            {
                mu[j] = 0;
            }
            for(i=0; i<=nclasses-1; i++)
            {
                nc[i] = 0;
                for(j=0; j<=nfeatures-1; j++)
                {
                    muc[i,j] = 0;
                }
            }
            for(i=0; i<=npoints-1; i++)
            {
                for(i_=0; i_<=nfeatures-1;i_++)
                {
                    mu[i_] = mu[i_] + xy[i,i_];
                }
                for(i_=0; i_<=nfeatures-1;i_++)
                {
                    muc[c[i],i_] = muc[c[i],i_] + xy[i,i_];
                }
                nc[c[i]] = nc[c[i]]+1;
            }
            for(i=0; i<=nclasses-1; i++)
            {
                v = (double)1/(double)nc[i];
                for(i_=0; i_<=nfeatures-1;i_++)
                {
                    muc[i,i_] = v*muc[i,i_];
                }
            }
            v = (double)1/(double)npoints;
            for(i_=0; i_<=nfeatures-1;i_++)
            {
                mu[i_] = v*mu[i_];
            }
            
            //
            // Create ST matrix
            //
            st = new double[nfeatures-1+1, nfeatures-1+1];
            for(i=0; i<=nfeatures-1; i++)
            {
                for(j=0; j<=nfeatures-1; j++)
                {
                    st[i,j] = 0;
                }
            }
            for(k=0; k<=npoints-1; k++)
            {
                for(i_=0; i_<=nfeatures-1;i_++)
                {
                    tf[i_] = xy[k,i_];
                }
                for(i_=0; i_<=nfeatures-1;i_++)
                {
                    tf[i_] = tf[i_] - mu[i_];
                }
                for(i=0; i<=nfeatures-1; i++)
                {
                    v = tf[i];
                    for(i_=0; i_<=nfeatures-1;i_++)
                    {
                        st[i,i_] = st[i,i_] + v*tf[i_];
                    }
                }
            }
            
            //
            // Create SW matrix
            //
            sw = new double[nfeatures-1+1, nfeatures-1+1];
            for(i=0; i<=nfeatures-1; i++)
            {
                for(j=0; j<=nfeatures-1; j++)
                {
                    sw[i,j] = 0;
                }
            }
            for(k=0; k<=npoints-1; k++)
            {
                for(i_=0; i_<=nfeatures-1;i_++)
                {
                    tf[i_] = xy[k,i_];
                }
                for(i_=0; i_<=nfeatures-1;i_++)
                {
                    tf[i_] = tf[i_] - muc[c[k],i_];
                }
                for(i=0; i<=nfeatures-1; i++)
                {
                    v = tf[i];
                    for(i_=0; i_<=nfeatures-1;i_++)
                    {
                        sw[i,i_] = sw[i,i_] + v*tf[i_];
                    }
                }
            }
        }


    }
    public class testgammafuncunit
    {
        public static bool testgammafunc(bool silent)
        {
            bool result = new bool();
            double threshold = 0;
            double v = 0;
            double s = 0;
            bool waserrors = new bool();
            bool gammaerrors = new bool();
            bool lngammaerrors = new bool();

            gammaerrors = false;
            lngammaerrors = false;
            waserrors = false;
            threshold = 100*math.machineepsilon;
            
            //
            //
            //
            gammaerrors = gammaerrors | (double)(Math.Abs(gammafunc.gammafunction(0.5)-Math.Sqrt(Math.PI)))>(double)(threshold);
            gammaerrors = gammaerrors | (double)(Math.Abs(gammafunc.gammafunction(1.5)-0.5*Math.Sqrt(Math.PI)))>(double)(threshold);
            v = gammafunc.lngamma(0.5, ref s);
            lngammaerrors = (lngammaerrors | (double)(Math.Abs(v-Math.Log(Math.Sqrt(Math.PI))))>(double)(threshold)) | (double)(s)!=(double)(1);
            v = gammafunc.lngamma(1.5, ref s);
            lngammaerrors = (lngammaerrors | (double)(Math.Abs(v-Math.Log(0.5*Math.Sqrt(Math.PI))))>(double)(threshold)) | (double)(s)!=(double)(1);
            
            //
            // report
            //
            waserrors = gammaerrors | lngammaerrors;
            if( !silent )
            {
                System.Console.Write("TESTING GAMMA FUNCTION");
                System.Console.WriteLine();
                System.Console.Write("GAMMA:                                   ");
                if( gammaerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LN GAMMA:                                ");
                if( lngammaerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            
            //
            // end
            //
            result = !waserrors;
            return result;
        }


    }
    public class testbdsvdunit
    {
        /*************************************************************************
        Testing bidiagonal SVD decomposition subroutine
        *************************************************************************/
        public static bool testbdsvd(bool silent)
        {
            bool result = new bool();
            double[] d = new double[0];
            double[] e = new double[0];
            double[,] mempty = new double[0,0];
            int n = 0;
            int maxn = 0;
            int i = 0;
            int pass = 0;
            bool waserrors = new bool();
            bool wsorted = new bool();
            bool wfailed = new bool();
            bool failcase = new bool();
            double materr = 0;
            double orterr = 0;
            double threshold = 0;
            double failthreshold = 0;
            double failr = 0;
            int failcount = 0;
            int succcount = 0;

            failcount = 0;
            succcount = 0;
            materr = 0;
            orterr = 0;
            wsorted = true;
            wfailed = false;
            waserrors = false;
            maxn = 15;
            threshold = 5*100*math.machineepsilon;
            failthreshold = 1.0E-2;
            d = new double[maxn-1+1];
            e = new double[maxn-2+1];
            
            //
            // special case: fail matrix
            //
            n = 5;
            d[0] = -8.27448347422711894000e-01;
            d[1] = -8.16705832087160854600e-01;
            d[2] = -2.53974358904729382800e-17;
            d[3] = -1.24626684881972815700e+00;
            d[4] = -4.64744131545637651000e-01;
            e[0] = -3.25785088656270038800e-01;
            e[1] = -1.03732413708914436580e-01;
            e[2] = -9.57365642262031357700e-02;
            e[3] = -2.71564153973817390400e-01;
            failcase = bdsvd.rmatrixbdsvd(ref d, e, n, true, false, ref mempty, 0, ref mempty, 0, ref mempty, 0);
            
            //
            // special case: zero divide matrix
            // unfixed LAPACK routine should fail on this problem
            //
            n = 7;
            d[0] = -6.96462904751731892700e-01;
            d[1] = 0.00000000000000000000e+00;
            d[2] = -5.73827770385971991400e-01;
            d[3] = -6.62562624399371191700e-01;
            d[4] = 5.82737148001782223600e-01;
            d[5] = 3.84825263580925003300e-01;
            d[6] = 9.84087420830525472200e-01;
            e[0] = -7.30307931760612871800e-02;
            e[1] = -2.30079042939542843800e-01;
            e[2] = -6.87824621739351216300e-01;
            e[3] = -1.77306437707837570600e-02;
            e[4] = 1.78285126526551632000e-15;
            e[5] = -4.89434737751289969400e-02;
            bdsvd.rmatrixbdsvd(ref d, e, n, true, false, ref mempty, 0, ref mempty, 0, ref mempty, 0);
            
            //
            // zero matrix, several cases
            //
            for(i=0; i<=maxn-1; i++)
            {
                d[i] = 0;
            }
            for(i=0; i<=maxn-2; i++)
            {
                e[i] = 0;
            }
            for(n=1; n<=maxn; n++)
            {
                testbdsvdproblem(d, e, n, ref materr, ref orterr, ref wsorted, ref wfailed, ref failcount, ref succcount);
            }
            
            //
            // Dense matrix
            //
            for(n=1; n<=maxn; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    for(i=0; i<=maxn-1; i++)
                    {
                        d[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=maxn-2; i++)
                    {
                        e[i] = 2*math.randomreal()-1;
                    }
                    testbdsvdproblem(d, e, n, ref materr, ref orterr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                }
            }
            
            //
            // Sparse matrices, very sparse matrices, incredible sparse matrices
            //
            for(n=1; n<=maxn; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    fillsparsede(ref d, ref e, n, 0.5);
                    testbdsvdproblem(d, e, n, ref materr, ref orterr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                    fillsparsede(ref d, ref e, n, 0.8);
                    testbdsvdproblem(d, e, n, ref materr, ref orterr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                    fillsparsede(ref d, ref e, n, 0.9);
                    testbdsvdproblem(d, e, n, ref materr, ref orterr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                    fillsparsede(ref d, ref e, n, 0.95);
                    testbdsvdproblem(d, e, n, ref materr, ref orterr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                }
            }
            
            //
            // report
            //
            failr = (double)failcount/(double)(succcount+failcount);
            waserrors = (((double)(materr)>(double)(threshold) | (double)(orterr)>(double)(threshold)) | !wsorted) | (double)(failr)>(double)(failthreshold);
            if( !silent )
            {
                System.Console.Write("TESTING BIDIAGONAL SVD DECOMPOSITION");
                System.Console.WriteLine();
                System.Console.Write("SVD decomposition error:                 ");
                System.Console.Write("{0,5:E3}",materr);
                System.Console.WriteLine();
                System.Console.Write("SVD orthogonality error:                 ");
                System.Console.Write("{0,5:E3}",orterr);
                System.Console.WriteLine();
                System.Console.Write("Singular values order:                   ");
                if( wsorted )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("Always converged:                        ");
                if( !wfailed )
                {
                    System.Console.Write("YES");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("NO");
                    System.Console.WriteLine();
                    System.Console.Write("Fail ratio:                              ");
                    System.Console.Write("{0,5:F3}",failr);
                    System.Console.WriteLine();
                }
                System.Console.Write("Fail matrix test:                        ");
                if( !failcase )
                {
                    System.Console.Write("AS EXPECTED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("CONVERGED (UNEXPECTED)");
                    System.Console.WriteLine();
                }
                System.Console.Write("Threshold:                               ");
                System.Console.Write("{0,5:E3}",threshold);
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        private static void fillidentity(ref double[,] a,
            int n)
        {
            int i = 0;
            int j = 0;

            a = new double[n-1+1, n-1+1];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( i==j )
                    {
                        a[i,j] = 1;
                    }
                    else
                    {
                        a[i,j] = 0;
                    }
                }
            }
        }


        private static void fillsparsede(ref double[] d,
            ref double[] e,
            int n,
            double sparcity)
        {
            int i = 0;

            d = new double[n-1+1];
            e = new double[Math.Max(0, n-2)+1];
            for(i=0; i<=n-1; i++)
            {
                if( (double)(math.randomreal())>=(double)(sparcity) )
                {
                    d[i] = 2*math.randomreal()-1;
                }
                else
                {
                    d[i] = 0;
                }
            }
            for(i=0; i<=n-2; i++)
            {
                if( (double)(math.randomreal())>=(double)(sparcity) )
                {
                    e[i] = 2*math.randomreal()-1;
                }
                else
                {
                    e[i] = 0;
                }
            }
        }


        private static void getbdsvderror(double[] d,
            double[] e,
            int n,
            bool isupper,
            double[,] u,
            double[,] c,
            double[] w,
            double[,] vt,
            ref double materr,
            ref double orterr,
            ref bool wsorted)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            double locerr = 0;
            double sm = 0;
            int i_ = 0;

            
            //
            // decomposition error
            //
            locerr = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    sm = 0;
                    for(k=0; k<=n-1; k++)
                    {
                        sm = sm+w[k]*u[i,k]*vt[k,j];
                    }
                    if( isupper )
                    {
                        if( i==j )
                        {
                            locerr = Math.Max(locerr, Math.Abs(d[i]-sm));
                        }
                        else
                        {
                            if( i==j-1 )
                            {
                                locerr = Math.Max(locerr, Math.Abs(e[i]-sm));
                            }
                            else
                            {
                                locerr = Math.Max(locerr, Math.Abs(sm));
                            }
                        }
                    }
                    else
                    {
                        if( i==j )
                        {
                            locerr = Math.Max(locerr, Math.Abs(d[i]-sm));
                        }
                        else
                        {
                            if( i-1==j )
                            {
                                locerr = Math.Max(locerr, Math.Abs(e[j]-sm));
                            }
                            else
                            {
                                locerr = Math.Max(locerr, Math.Abs(sm));
                            }
                        }
                    }
                }
            }
            materr = Math.Max(materr, locerr);
            
            //
            // check for C = U'
            // we consider it as decomposition error
            //
            locerr = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    locerr = Math.Max(locerr, Math.Abs(u[i,j]-c[j,i]));
                }
            }
            materr = Math.Max(materr, locerr);
            
            //
            // orthogonality error
            //
            locerr = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=i; j<=n-1; j++)
                {
                    sm = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sm += u[i_,i]*u[i_,j];
                    }
                    if( i!=j )
                    {
                        locerr = Math.Max(locerr, Math.Abs(sm));
                    }
                    else
                    {
                        locerr = Math.Max(locerr, Math.Abs(sm-1));
                    }
                    sm = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sm += vt[i,i_]*vt[j,i_];
                    }
                    if( i!=j )
                    {
                        locerr = Math.Max(locerr, Math.Abs(sm));
                    }
                    else
                    {
                        locerr = Math.Max(locerr, Math.Abs(sm-1));
                    }
                }
            }
            orterr = Math.Max(orterr, locerr);
            
            //
            // values order error
            //
            for(i=1; i<=n-1; i++)
            {
                if( (double)(w[i])>(double)(w[i-1]) )
                {
                    wsorted = false;
                }
            }
        }


        private static void testbdsvdproblem(double[] d,
            double[] e,
            int n,
            ref double materr,
            ref double orterr,
            ref bool wsorted,
            ref bool wfailed,
            ref int failcount,
            ref int succcount)
        {
            double[,] u = new double[0,0];
            double[,] vt = new double[0,0];
            double[,] c = new double[0,0];
            double[] w = new double[0];
            int i = 0;
            double mx = 0;

            mx = 0;
            for(i=0; i<=n-1; i++)
            {
                if( (double)(Math.Abs(d[i]))>(double)(mx) )
                {
                    mx = Math.Abs(d[i]);
                }
            }
            for(i=0; i<=n-2; i++)
            {
                if( (double)(Math.Abs(e[i]))>(double)(mx) )
                {
                    mx = Math.Abs(e[i]);
                }
            }
            if( (double)(mx)==(double)(0) )
            {
                mx = 1;
            }
            
            //
            // Upper BDSVD tests
            //
            w = new double[n-1+1];
            fillidentity(ref u, n);
            fillidentity(ref vt, n);
            fillidentity(ref c, n);
            for(i=0; i<=n-1; i++)
            {
                w[i] = d[i];
            }
            if( !bdsvd.rmatrixbdsvd(ref w, e, n, true, false, ref u, n, ref c, n, ref vt, n) )
            {
                failcount = failcount+1;
                wfailed = true;
                return;
            }
            getbdsvderror(d, e, n, true, u, c, w, vt, ref materr, ref orterr, ref wsorted);
            fillidentity(ref u, n);
            fillidentity(ref vt, n);
            fillidentity(ref c, n);
            for(i=0; i<=n-1; i++)
            {
                w[i] = d[i];
            }
            if( !bdsvd.rmatrixbdsvd(ref w, e, n, true, true, ref u, n, ref c, n, ref vt, n) )
            {
                failcount = failcount+1;
                wfailed = true;
                return;
            }
            getbdsvderror(d, e, n, true, u, c, w, vt, ref materr, ref orterr, ref wsorted);
            
            //
            // Lower BDSVD tests
            //
            w = new double[n-1+1];
            fillidentity(ref u, n);
            fillidentity(ref vt, n);
            fillidentity(ref c, n);
            for(i=0; i<=n-1; i++)
            {
                w[i] = d[i];
            }
            if( !bdsvd.rmatrixbdsvd(ref w, e, n, false, false, ref u, n, ref c, n, ref vt, n) )
            {
                failcount = failcount+1;
                wfailed = true;
                return;
            }
            getbdsvderror(d, e, n, false, u, c, w, vt, ref materr, ref orterr, ref wsorted);
            fillidentity(ref u, n);
            fillidentity(ref vt, n);
            fillidentity(ref c, n);
            for(i=0; i<=n-1; i++)
            {
                w[i] = d[i];
            }
            if( !bdsvd.rmatrixbdsvd(ref w, e, n, false, true, ref u, n, ref c, n, ref vt, n) )
            {
                failcount = failcount+1;
                wfailed = true;
                return;
            }
            getbdsvderror(d, e, n, false, u, c, w, vt, ref materr, ref orterr, ref wsorted);
            
            //
            // update counter
            //
            succcount = succcount+1;
        }


    }
    public class testsvdunit
    {
        /*************************************************************************
        Testing SVD decomposition subroutine
        *************************************************************************/
        public static bool testsvd(bool silent)
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            int m = 0;
            int n = 0;
            int maxmn = 0;
            int i = 0;
            int j = 0;
            int gpass = 0;
            int pass = 0;
            bool waserrors = new bool();
            bool wsorted = new bool();
            bool wfailed = new bool();
            double materr = 0;
            double orterr = 0;
            double othererr = 0;
            double threshold = 0;
            double failthreshold = 0;
            double failr = 0;
            int failcount = 0;
            int succcount = 0;

            failcount = 0;
            succcount = 0;
            materr = 0;
            orterr = 0;
            othererr = 0;
            wsorted = true;
            wfailed = false;
            waserrors = false;
            maxmn = 30;
            threshold = 5*100*math.machineepsilon;
            failthreshold = 5.0E-3;
            a = new double[maxmn-1+1, maxmn-1+1];
            
            //
            // TODO: div by zero fail, convergence fail
            //
            for(gpass=1; gpass<=1; gpass++)
            {
                
                //
                // zero matrix, several cases
                //
                for(i=0; i<=maxmn-1; i++)
                {
                    for(j=0; j<=maxmn-1; j++)
                    {
                        a[i,j] = 0;
                    }
                }
                for(i=1; i<=Math.Min(5, maxmn); i++)
                {
                    for(j=1; j<=Math.Min(5, maxmn); j++)
                    {
                        testsvdproblem(a, i, j, ref materr, ref orterr, ref othererr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                    }
                }
                
                //
                // Long dense matrix
                //
                for(i=0; i<=maxmn-1; i++)
                {
                    for(j=0; j<=Math.Min(5, maxmn)-1; j++)
                    {
                        a[i,j] = 2*math.randomreal()-1;
                    }
                }
                for(i=1; i<=maxmn; i++)
                {
                    for(j=1; j<=Math.Min(5, maxmn); j++)
                    {
                        testsvdproblem(a, i, j, ref materr, ref orterr, ref othererr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                    }
                }
                for(i=0; i<=Math.Min(5, maxmn)-1; i++)
                {
                    for(j=0; j<=maxmn-1; j++)
                    {
                        a[i,j] = 2*math.randomreal()-1;
                    }
                }
                for(i=1; i<=Math.Min(5, maxmn); i++)
                {
                    for(j=1; j<=maxmn; j++)
                    {
                        testsvdproblem(a, i, j, ref materr, ref orterr, ref othererr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                    }
                }
                
                //
                // Dense matrices
                //
                for(m=1; m<=Math.Min(10, maxmn); m++)
                {
                    for(n=1; n<=Math.Min(10, maxmn); n++)
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a[i,j] = 2*math.randomreal()-1;
                            }
                        }
                        testsvdproblem(a, m, n, ref materr, ref orterr, ref othererr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                    }
                }
                
                //
                // Sparse matrices, very sparse matrices, incredible sparse matrices
                //
                for(m=1; m<=10; m++)
                {
                    for(n=1; n<=10; n++)
                    {
                        for(pass=1; pass<=2; pass++)
                        {
                            fillsparsea(ref a, m, n, 0.8);
                            testsvdproblem(a, m, n, ref materr, ref orterr, ref othererr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                            fillsparsea(ref a, m, n, 0.9);
                            testsvdproblem(a, m, n, ref materr, ref orterr, ref othererr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                            fillsparsea(ref a, m, n, 0.95);
                            testsvdproblem(a, m, n, ref materr, ref orterr, ref othererr, ref wsorted, ref wfailed, ref failcount, ref succcount);
                        }
                    }
                }
            }
            
            //
            // report
            //
            failr = (double)failcount/(double)(succcount+failcount);
            waserrors = ((((double)(materr)>(double)(threshold) | (double)(orterr)>(double)(threshold)) | (double)(othererr)>(double)(threshold)) | !wsorted) | (double)(failr)>(double)(failthreshold);
            if( !silent )
            {
                System.Console.Write("TESTING SVD DECOMPOSITION");
                System.Console.WriteLine();
                System.Console.Write("SVD decomposition error:                 ");
                System.Console.Write("{0,5:E3}",materr);
                System.Console.WriteLine();
                System.Console.Write("SVD orthogonality error:                 ");
                System.Console.Write("{0,5:E3}",orterr);
                System.Console.WriteLine();
                System.Console.Write("SVD with different parameters error:     ");
                System.Console.Write("{0,5:E3}",othererr);
                System.Console.WriteLine();
                System.Console.Write("Singular values order:                   ");
                if( wsorted )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("Always converged:                        ");
                if( !wfailed )
                {
                    System.Console.Write("YES");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("NO");
                    System.Console.WriteLine();
                    System.Console.Write("Fail ratio:                              ");
                    System.Console.Write("{0,5:F3}",failr);
                    System.Console.WriteLine();
                }
                System.Console.Write("Threshold:                               ");
                System.Console.Write("{0,5:E3}",threshold);
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        private static void fillsparsea(ref double[,] a,
            int m,
            int n,
            double sparcity)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (double)(math.randomreal())>=(double)(sparcity) )
                    {
                        a[i,j] = 2*math.randomreal()-1;
                    }
                    else
                    {
                        a[i,j] = 0;
                    }
                }
            }
        }


        private static void getsvderror(double[,] a,
            int m,
            int n,
            double[,] u,
            double[] w,
            double[,] vt,
            ref double materr,
            ref double orterr,
            ref bool wsorted)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            int minmn = 0;
            double locerr = 0;
            double sm = 0;
            int i_ = 0;

            minmn = Math.Min(m, n);
            
            //
            // decomposition error
            //
            locerr = 0;
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    sm = 0;
                    for(k=0; k<=minmn-1; k++)
                    {
                        sm = sm+w[k]*u[i,k]*vt[k,j];
                    }
                    locerr = Math.Max(locerr, Math.Abs(a[i,j]-sm));
                }
            }
            materr = Math.Max(materr, locerr);
            
            //
            // orthogonality error
            //
            locerr = 0;
            for(i=0; i<=minmn-1; i++)
            {
                for(j=i; j<=minmn-1; j++)
                {
                    sm = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        sm += u[i_,i]*u[i_,j];
                    }
                    if( i!=j )
                    {
                        locerr = Math.Max(locerr, Math.Abs(sm));
                    }
                    else
                    {
                        locerr = Math.Max(locerr, Math.Abs(sm-1));
                    }
                    sm = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        sm += vt[i,i_]*vt[j,i_];
                    }
                    if( i!=j )
                    {
                        locerr = Math.Max(locerr, Math.Abs(sm));
                    }
                    else
                    {
                        locerr = Math.Max(locerr, Math.Abs(sm-1));
                    }
                }
            }
            orterr = Math.Max(orterr, locerr);
            
            //
            // values order error
            //
            for(i=1; i<=minmn-1; i++)
            {
                if( (double)(w[i])>(double)(w[i-1]) )
                {
                    wsorted = false;
                }
            }
        }


        private static void testsvdproblem(double[,] a,
            int m,
            int n,
            ref double materr,
            ref double orterr,
            ref double othererr,
            ref bool wsorted,
            ref bool wfailed,
            ref int failcount,
            ref int succcount)
        {
            double[,] u = new double[0,0];
            double[,] vt = new double[0,0];
            double[,] u2 = new double[0,0];
            double[,] vt2 = new double[0,0];
            double[] w = new double[0];
            double[] w2 = new double[0];
            int i = 0;
            int j = 0;
            int ujob = 0;
            int vtjob = 0;
            int memjob = 0;
            int ucheck = 0;
            int vtcheck = 0;

            
            //
            // Main SVD test
            //
            if( !svd.rmatrixsvd(a, m, n, 2, 2, 2, ref w, ref u, ref vt) )
            {
                failcount = failcount+1;
                wfailed = true;
                return;
            }
            getsvderror(a, m, n, u, w, vt, ref materr, ref orterr, ref wsorted);
            
            //
            // Additional SVD tests
            //
            for(ujob=0; ujob<=2; ujob++)
            {
                for(vtjob=0; vtjob<=2; vtjob++)
                {
                    for(memjob=0; memjob<=2; memjob++)
                    {
                        if( !svd.rmatrixsvd(a, m, n, ujob, vtjob, memjob, ref w2, ref u2, ref vt2) )
                        {
                            failcount = failcount+1;
                            wfailed = true;
                            return;
                        }
                        ucheck = 0;
                        if( ujob==1 )
                        {
                            ucheck = Math.Min(m, n);
                        }
                        if( ujob==2 )
                        {
                            ucheck = m;
                        }
                        vtcheck = 0;
                        if( vtjob==1 )
                        {
                            vtcheck = Math.Min(m, n);
                        }
                        if( vtjob==2 )
                        {
                            vtcheck = n;
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=ucheck-1; j++)
                            {
                                othererr = Math.Max(othererr, Math.Abs(u[i,j]-u2[i,j]));
                            }
                        }
                        for(i=0; i<=vtcheck-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                othererr = Math.Max(othererr, Math.Abs(vt[i,j]-vt2[i,j]));
                            }
                        }
                        for(i=0; i<=Math.Min(m, n)-1; i++)
                        {
                            othererr = Math.Max(othererr, Math.Abs(w[i]-w2[i]));
                        }
                    }
                }
            }
            
            //
            // update counter
            //
            succcount = succcount+1;
        }


    }
    public class testlinregunit
    {
        public static bool testlinreg(bool silent)
        {
            bool result = new bool();
            double sigmathreshold = 0;
            int maxn = 0;
            int maxm = 0;
            int passcount = 0;
            int estpasscount = 0;
            double threshold = 0;
            int n = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int tmpi = 0;
            int pass = 0;
            int epass = 0;
            int m = 0;
            int tasktype = 0;
            int modeltype = 0;
            int m1 = 0;
            int m2 = 0;
            int n1 = 0;
            int n2 = 0;
            int info = 0;
            int info2 = 0;
            double[,] xy = new double[0,0];
            double[,] xy2 = new double[0,0];
            double[] s = new double[0];
            double[] s2 = new double[0];
            double[] w2 = new double[0];
            double[] x = new double[0];
            double[] ta = new double[0];
            double[] tb = new double[0];
            double[] tc = new double[0];
            double[] xy0 = new double[0];
            double[] tmpweights = new double[0];
            linreg.linearmodel w = new linreg.linearmodel();
            linreg.linearmodel wt = new linreg.linearmodel();
            linreg.linearmodel wt2 = new linreg.linearmodel();
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double y1 = 0;
            double y2 = 0;
            bool allsame = new bool();
            double ea = 0;
            double eb = 0;
            double varatested = 0;
            double varbtested = 0;
            double a = 0;
            double b = 0;
            double vara = 0;
            double varb = 0;
            double a2 = 0;
            double b2 = 0;
            double covab = 0;
            double corrab = 0;
            double p = 0;
            int qcnt = 0;
            double[] qtbl = new double[0];
            double[] qvals = new double[0];
            double[] qsigma = new double[0];
            linreg.lrreport ar = new linreg.lrreport();
            linreg.lrreport ar2 = new linreg.lrreport();
            double f = 0;
            double fp = 0;
            double fm = 0;
            double v = 0;
            double vv = 0;
            double cvrmserror = 0;
            double cvavgerror = 0;
            double cvavgrelerror = 0;
            double rmserror = 0;
            double avgerror = 0;
            double avgrelerror = 0;
            bool nondefect = new bool();
            double sinshift = 0;
            double tasklevel = 0;
            double noiselevel = 0;
            double hstep = 0;
            double sigma = 0;
            double mean = 0;
            double means = 0;
            double stddev = 0;
            double stddevs = 0;
            bool slcerrors = new bool();
            bool slerrors = new bool();
            bool grcoverrors = new bool();
            bool gropterrors = new bool();
            bool gresterrors = new bool();
            bool grothererrors = new bool();
            bool grconverrors = new bool();
            bool waserrors = new bool();
            int i_ = 0;

            
            //
            // Primary settings
            //
            maxn = 40;
            maxm = 5;
            passcount = 3;
            estpasscount = 1000;
            sigmathreshold = 7;
            threshold = 1000000*math.machineepsilon;
            slerrors = false;
            slcerrors = false;
            grcoverrors = false;
            gropterrors = false;
            gresterrors = false;
            grothererrors = false;
            grconverrors = false;
            waserrors = false;
            
            //
            // Quantiles table setup
            //
            qcnt = 5;
            qtbl = new double[qcnt-1+1];
            qvals = new double[qcnt-1+1];
            qsigma = new double[qcnt-1+1];
            qtbl[0] = 0.5;
            qtbl[1] = 0.25;
            qtbl[2] = 0.10;
            qtbl[3] = 0.05;
            qtbl[4] = 0.025;
            for(i=0; i<=qcnt-1; i++)
            {
                qsigma[i] = Math.Sqrt(qtbl[i]*(1-qtbl[i])/estpasscount);
            }
            
            //
            // Other setup
            //
            ta = new double[estpasscount-1+1];
            tb = new double[estpasscount-1+1];
            
            //
            // Test straight line regression
            //
            for(n=2; n<=maxn; n++)
            {
                
                //
                // Fail/pass test
                //
                generaterandomtask(-1, 1, false, -1, 1, 1, 2, n, ref xy, ref s);
                linreg.lrlines(xy, s, n, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                slcerrors = slcerrors | info!=1;
                generaterandomtask(1, 1, false, -1, 1, 1, 2, n, ref xy, ref s);
                linreg.lrlines(xy, s, n, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                slcerrors = slcerrors | info!=-3;
                generaterandomtask(-1, 1, false, -1, 1, -1, -1, n, ref xy, ref s);
                linreg.lrlines(xy, s, n, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                slcerrors = slcerrors | info!=-2;
                generaterandomtask(-1, 1, false, -1, 1, 2, 1, 2, ref xy, ref s);
                linreg.lrlines(xy, s, 1, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                slcerrors = slcerrors | info!=-1;
                
                //
                // Multipass tests
                //
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // Test S variant against non-S variant
                    //
                    ea = 2*math.randomreal()-1;
                    eb = 2*math.randomreal()-1;
                    generatetask(ea, eb, -(5*math.randomreal()), 5*math.randomreal(), (double)(math.randomreal())>(double)(0.5), 1, 1, n, ref xy, ref s);
                    linreg.lrlines(xy, s, n, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                    linreg.lrline(xy, n, ref info2, ref a2, ref b2);
                    if( info!=1 | info2!=1 )
                    {
                        slcerrors = true;
                    }
                    else
                    {
                        slerrors = (slerrors | (double)(Math.Abs(a-a2))>(double)(threshold)) | (double)(Math.Abs(b-b2))>(double)(threshold);
                    }
                    
                    //
                    // Test for A/B
                    //
                    // Generate task with exact, non-perturbed y[i],
                    // then make non-zero s[i]
                    //
                    ea = 2*math.randomreal()-1;
                    eb = 2*math.randomreal()-1;
                    generatetask(ea, eb, -(5*math.randomreal()), 5*math.randomreal(), n>4, 0.0, 0.0, n, ref xy, ref s);
                    for(i=0; i<=n-1; i++)
                    {
                        s[i] = 1+math.randomreal();
                    }
                    linreg.lrlines(xy, s, n, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                    if( info!=1 )
                    {
                        slcerrors = true;
                    }
                    else
                    {
                        slerrors = (slerrors | (double)(Math.Abs(a-ea))>(double)(0.001)) | (double)(Math.Abs(b-eb))>(double)(0.001);
                    }
                    
                    //
                    // Test for VarA, VarB, P (P is being tested only for N>2)
                    //
                    for(i=0; i<=qcnt-1; i++)
                    {
                        qvals[i] = 0;
                    }
                    ea = 2*math.randomreal()-1;
                    eb = 2*math.randomreal()-1;
                    generatetask(ea, eb, -(5*math.randomreal()), 5*math.randomreal(), n>4, 1.0, 2.0, n, ref xy, ref s);
                    linreg.lrlines(xy, s, n, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                    if( info!=1 )
                    {
                        slcerrors = true;
                        continue;
                    }
                    varatested = vara;
                    varbtested = varb;
                    for(epass=0; epass<=estpasscount-1; epass++)
                    {
                        
                        //
                        // Generate
                        //
                        filltaskwithy(ea, eb, n, ref xy, ref s);
                        linreg.lrlines(xy, s, n, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                        if( info!=1 )
                        {
                            slcerrors = true;
                            continue;
                        }
                        
                        //
                        // A, B, P
                        // (P is being tested for uniformity, additional p-tests are below)
                        //
                        ta[epass] = a;
                        tb[epass] = b;
                        for(i=0; i<=qcnt-1; i++)
                        {
                            if( (double)(p)<=(double)(qtbl[i]) )
                            {
                                qvals[i] = qvals[i]+(double)1/(double)estpasscount;
                            }
                        }
                    }
                    calculatemv(ta, estpasscount, ref mean, ref means, ref stddev, ref stddevs);
                    slerrors = slerrors | (double)(Math.Abs(mean-ea)/means)>=(double)(sigmathreshold);
                    slerrors = slerrors | (double)(Math.Abs(stddev-Math.Sqrt(varatested))/stddevs)>=(double)(sigmathreshold);
                    calculatemv(tb, estpasscount, ref mean, ref means, ref stddev, ref stddevs);
                    slerrors = slerrors | (double)(Math.Abs(mean-eb)/means)>=(double)(sigmathreshold);
                    slerrors = slerrors | (double)(Math.Abs(stddev-Math.Sqrt(varbtested))/stddevs)>=(double)(sigmathreshold);
                    if( n>2 )
                    {
                        for(i=0; i<=qcnt-1; i++)
                        {
                            if( (double)(Math.Abs(qtbl[i]-qvals[i])/qsigma[i])>(double)(sigmathreshold) )
                            {
                                slerrors = true;
                            }
                        }
                    }
                    
                    //
                    // Additional tests for P: correlation with fit quality
                    //
                    if( n>2 )
                    {
                        generatetask(ea, eb, -(5*math.randomreal()), 5*math.randomreal(), false, 0.0, 0.0, n, ref xy, ref s);
                        for(i=0; i<=n-1; i++)
                        {
                            s[i] = 1+math.randomreal();
                        }
                        linreg.lrlines(xy, s, n, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                        if( info!=1 )
                        {
                            slcerrors = true;
                            continue;
                        }
                        slerrors = slerrors | (double)(p)<(double)(0.999);
                        generatetask(0, 0, -(5*math.randomreal()), 5*math.randomreal(), false, 1.0, 1.0, n, ref xy, ref s);
                        for(i=0; i<=n-1; i++)
                        {
                            if( i%2==0 )
                            {
                                xy[i,1] = 5.0;
                            }
                            else
                            {
                                xy[i,1] = -5.0;
                            }
                        }
                        if( n%2!=0 )
                        {
                            xy[n-1,1] = 0;
                        }
                        linreg.lrlines(xy, s, n, ref info, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                        if( info!=1 )
                        {
                            slcerrors = true;
                            continue;
                        }
                        slerrors = slerrors | (double)(p)>(double)(0.001);
                    }
                }
            }
            
            //
            // General regression tests:
            //
            
            //
            // Simple linear tests (small sample, optimum point, covariance)
            //
            for(n=3; n<=maxn; n++)
            {
                s = new double[n-1+1];
                
                //
                // Linear tests:
                // a. random points, sigmas
                // b. no sigmas
                //
                xy = new double[n-1+1, 1+1];
                for(i=0; i<=n-1; i++)
                {
                    xy[i,0] = 2*math.randomreal()-1;
                    xy[i,1] = 2*math.randomreal()-1;
                    s[i] = 1+math.randomreal();
                }
                linreg.lrbuilds(xy, s, n, 1, ref info, wt, ar);
                if( info!=1 )
                {
                    grconverrors = true;
                    continue;
                }
                linreg.lrunpack(wt, ref tmpweights, ref tmpi);
                linreg.lrlines(xy, s, n, ref info2, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
                gropterrors = gropterrors | (double)(Math.Abs(a-tmpweights[1]))>(double)(threshold);
                gropterrors = gropterrors | (double)(Math.Abs(b-tmpweights[0]))>(double)(threshold);
                grcoverrors = grcoverrors | (double)(Math.Abs(vara-ar.c[1,1]))>(double)(threshold);
                grcoverrors = grcoverrors | (double)(Math.Abs(varb-ar.c[0,0]))>(double)(threshold);
                grcoverrors = grcoverrors | (double)(Math.Abs(covab-ar.c[1,0]))>(double)(threshold);
                grcoverrors = grcoverrors | (double)(Math.Abs(covab-ar.c[0,1]))>(double)(threshold);
                linreg.lrbuild(xy, n, 1, ref info, wt, ar);
                if( info!=1 )
                {
                    grconverrors = true;
                    continue;
                }
                linreg.lrunpack(wt, ref tmpweights, ref tmpi);
                linreg.lrline(xy, n, ref info2, ref a, ref b);
                gropterrors = gropterrors | (double)(Math.Abs(a-tmpweights[1]))>(double)(threshold);
                gropterrors = gropterrors | (double)(Math.Abs(b-tmpweights[0]))>(double)(threshold);
            }
            
            //
            // S covariance versus S-less covariance.
            // Slightly skewed task, large sample size.
            // Will S-less subroutine estimate covariance matrix good enough?
            //
            n = 1000+math.randominteger(3000);
            sigma = 0.1+math.randomreal()*1.9;
            xy = new double[n-1+1, 1+1];
            s = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                xy[i,0] = 1.5*math.randomreal()-0.5;
                xy[i,1] = 1.2*xy[i,0]-0.3+generatenormal(0, sigma);
                s[i] = sigma;
            }
            linreg.lrbuild(xy, n, 1, ref info, wt, ar);
            linreg.lrlines(xy, s, n, ref info2, ref a, ref b, ref vara, ref varb, ref covab, ref corrab, ref p);
            if( info!=1 | info2!=1 )
            {
                grconverrors = true;
            }
            else
            {
                grcoverrors = grcoverrors | (double)(Math.Abs(Math.Log(ar.c[0,0]/varb)))>(double)(Math.Log(1.2));
                grcoverrors = grcoverrors | (double)(Math.Abs(Math.Log(ar.c[1,1]/vara)))>(double)(Math.Log(1.2));
                grcoverrors = grcoverrors | (double)(Math.Abs(Math.Log(ar.c[0,1]/covab)))>(double)(Math.Log(1.2));
                grcoverrors = grcoverrors | (double)(Math.Abs(Math.Log(ar.c[1,0]/covab)))>(double)(Math.Log(1.2));
            }
            
            //
            // General tests:
            // * basis functions - up to cubic
            // * task types:
            // * data set is noisy sine half-period with random shift
            // * tests:
            //   unpacking/packing
            //   optimality
            //   error estimates
            // * tasks:
            //   0 = noised sine
            //   1 = degenerate task with 1-of-n encoded categorical variables
            //   2 = random task with large variation (for 1-type models)
            //   3 = random task with small variation (for 1-type models)
            //
            //   Additional tasks TODO
            //   specially designed task with defective vectors which leads to
            //   the failure of the fast CV formula.
            //
            //
            for(modeltype=0; modeltype<=1; modeltype++)
            {
                for(tasktype=0; tasktype<=3; tasktype++)
                {
                    if( tasktype==0 )
                    {
                        m1 = 1;
                        m2 = 3;
                    }
                    if( tasktype==1 )
                    {
                        m1 = 9;
                        m2 = 9;
                    }
                    if( tasktype==2 | tasktype==3 )
                    {
                        m1 = 9;
                        m2 = 9;
                    }
                    for(m=m1; m<=m2; m++)
                    {
                        if( tasktype==0 )
                        {
                            n1 = m+3;
                            n2 = m+20;
                        }
                        if( tasktype==1 )
                        {
                            n1 = 70+math.randominteger(70);
                            n2 = n1;
                        }
                        if( tasktype==2 | tasktype==3 )
                        {
                            n1 = 100;
                            n2 = n1;
                        }
                        for(n=n1; n<=n2; n++)
                        {
                            xy = new double[n-1+1, m+1];
                            xy0 = new double[n-1+1];
                            s = new double[n-1+1];
                            hstep = 0.001;
                            noiselevel = 0.2;
                            
                            //
                            // Prepare task
                            //
                            if( tasktype==0 )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    xy[i,0] = 2*math.randomreal()-1;
                                }
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=1; j<=m-1; j++)
                                    {
                                        xy[i,j] = xy[i,0]*xy[i,j-1];
                                    }
                                }
                                sinshift = math.randomreal()*Math.PI;
                                for(i=0; i<=n-1; i++)
                                {
                                    xy0[i] = Math.Sin(sinshift+Math.PI*0.5*(xy[i,0]+1));
                                    xy[i,m] = xy0[i]+noiselevel*generatenormal(0, 1);
                                }
                            }
                            if( tasktype==1 )
                            {
                                ap.assert(m==9);
                                ta = new double[8+1];
                                ta[0] = 1;
                                ta[1] = 2;
                                ta[2] = 3;
                                ta[3] = 0.25;
                                ta[4] = 0.5;
                                ta[5] = 0.75;
                                ta[6] = 0.06;
                                ta[7] = 0.12;
                                ta[8] = 0.18;
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=m-1; j++)
                                    {
                                        xy[i,j] = 0;
                                    }
                                    xy[i,0+i%3] = 1;
                                    xy[i,3+i/3%3] = 1;
                                    xy[i,6+i/9%3] = 1;
                                    v = 0.0;
                                    for(i_=0; i_<=8;i_++)
                                    {
                                        v += xy[i,i_]*ta[i_];
                                    }
                                    xy0[i] = v;
                                    xy[i,m] = v+noiselevel*generatenormal(0, 1);
                                }
                            }
                            if( tasktype==2 | tasktype==3 )
                            {
                                ap.assert(m==9);
                                ta = new double[8+1];
                                ta[0] = 1;
                                ta[1] = -2;
                                ta[2] = 3;
                                ta[3] = 0.25;
                                ta[4] = -0.5;
                                ta[5] = 0.75;
                                ta[6] = -0.06;
                                ta[7] = 0.12;
                                ta[8] = -0.18;
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=m-1; j++)
                                    {
                                        if( tasktype==2 )
                                        {
                                            xy[i,j] = 1+generatenormal(0, 3);
                                        }
                                        else
                                        {
                                            xy[i,j] = 1+generatenormal(0, 0.05);
                                        }
                                    }
                                    v = 0.0;
                                    for(i_=0; i_<=8;i_++)
                                    {
                                        v += xy[i,i_]*ta[i_];
                                    }
                                    xy0[i] = v;
                                    xy[i,m] = v+noiselevel*generatenormal(0, 1);
                                }
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                s[i] = 1+math.randomreal();
                            }
                            
                            //
                            // Solve (using S-variant, non-S-variant is not tested)
                            //
                            if( modeltype==0 )
                            {
                                linreg.lrbuilds(xy, s, n, m, ref info, wt, ar);
                            }
                            else
                            {
                                linreg.lrbuildzs(xy, s, n, m, ref info, wt, ar);
                            }
                            if( info!=1 )
                            {
                                grconverrors = true;
                                continue;
                            }
                            linreg.lrunpack(wt, ref tmpweights, ref tmpi);
                            
                            //
                            // LRProcess test
                            //
                            x = new double[m-1+1];
                            v = tmpweights[m];
                            for(i=0; i<=m-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                                v = v+tmpweights[i]*x[i];
                            }
                            grothererrors = grothererrors | (double)(Math.Abs(v-linreg.lrprocess(wt, x))/Math.Max(Math.Abs(v), 1))>(double)(threshold);
                            
                            //
                            // LRPack test
                            //
                            linreg.lrpack(tmpweights, m, wt2);
                            x = new double[m-1+1];
                            for(i=0; i<=m-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                            v = linreg.lrprocess(wt, x);
                            grothererrors = grothererrors | (double)(Math.Abs(v-linreg.lrprocess(wt2, x))/Math.Abs(v))>(double)(threshold);
                            
                            //
                            // Optimality test
                            //
                            for(k=0; k<=m; k++)
                            {
                                if( modeltype==1 & k==m )
                                {
                                    
                                    //
                                    // 0-type models (with non-zero constant term)
                                    // are tested for optimality of all coefficients.
                                    //
                                    // 1-type models (with zero constant term)
                                    // are tested for optimality of non-constant terms only.
                                    //
                                    continue;
                                }
                                f = 0;
                                fp = 0;
                                fm = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    v = tmpweights[m];
                                    for(j=0; j<=m-1; j++)
                                    {
                                        v = v+xy[i,j]*tmpweights[j];
                                    }
                                    f = f+math.sqr((v-xy[i,m])/s[i]);
                                    if( k<m )
                                    {
                                        vv = xy[i,k];
                                    }
                                    else
                                    {
                                        vv = 1;
                                    }
                                    fp = fp+math.sqr((v+vv*hstep-xy[i,m])/s[i]);
                                    fm = fm+math.sqr((v-vv*hstep-xy[i,m])/s[i]);
                                }
                                gropterrors = (gropterrors | (double)(f)>(double)(fp)) | (double)(f)>(double)(fm);
                            }
                            
                            //
                            // Covariance matrix test:
                            // generate random vector, project coefficients on it,
                            // compare variance of projection with estimate provided
                            // by cov.matrix
                            //
                            ta = new double[estpasscount-1+1];
                            tb = new double[m+1];
                            tc = new double[m+1];
                            xy2 = new double[n-1+1, m+1];
                            for(i=0; i<=m; i++)
                            {
                                tb[i] = generatenormal(0, 1);
                            }
                            for(epass=0; epass<=estpasscount-1; epass++)
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    for(i_=0; i_<=m-1;i_++)
                                    {
                                        xy2[i,i_] = xy[i,i_];
                                    }
                                    xy2[i,m] = xy0[i]+s[i]*generatenormal(0, 1);
                                }
                                if( modeltype==0 )
                                {
                                    linreg.lrbuilds(xy2, s, n, m, ref info, wt, ar2);
                                }
                                else
                                {
                                    linreg.lrbuildzs(xy2, s, n, m, ref info, wt, ar2);
                                }
                                if( info!=1 )
                                {
                                    ta[epass] = 0;
                                    grconverrors = true;
                                    continue;
                                }
                                linreg.lrunpack(wt, ref w2, ref tmpi);
                                v = 0.0;
                                for(i_=0; i_<=m;i_++)
                                {
                                    v += tb[i_]*w2[i_];
                                }
                                ta[epass] = v;
                            }
                            calculatemv(ta, estpasscount, ref mean, ref means, ref stddev, ref stddevs);
                            for(i=0; i<=m; i++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=m;i_++)
                                {
                                    v += tb[i_]*ar.c[i_,i];
                                }
                                tc[i] = v;
                            }
                            v = 0.0;
                            for(i_=0; i_<=m;i_++)
                            {
                                v += tc[i_]*tb[i_];
                            }
                            grcoverrors = grcoverrors | (double)(Math.Abs((Math.Sqrt(v)-stddev)/stddevs))>=(double)(sigmathreshold);
                            
                            //
                            // Test for the fast CV error:
                            // calculate CV error by definition (leaving out N
                            // points and recalculating solution).
                            //
                            // Test for the training set error
                            //
                            cvrmserror = 0;
                            cvavgerror = 0;
                            cvavgrelerror = 0;
                            rmserror = 0;
                            avgerror = 0;
                            avgrelerror = 0;
                            xy2 = new double[n-2+1, m+1];
                            s2 = new double[n-2+1];
                            for(i=0; i<=n-2; i++)
                            {
                                for(i_=0; i_<=m;i_++)
                                {
                                    xy2[i,i_] = xy[i+1,i_];
                                }
                                s2[i] = s[i+1];
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                
                                //
                                // Trn
                                //
                                v = 0.0;
                                for(i_=0; i_<=m-1;i_++)
                                {
                                    v += xy[i,i_]*tmpweights[i_];
                                }
                                v = v+tmpweights[m];
                                rmserror = rmserror+math.sqr(v-xy[i,m]);
                                avgerror = avgerror+Math.Abs(v-xy[i,m]);
                                avgrelerror = avgrelerror+Math.Abs((v-xy[i,m])/xy[i,m]);
                                
                                //
                                // CV: non-defect vectors only
                                //
                                nondefect = true;
                                for(k=0; k<=ar.ncvdefects-1; k++)
                                {
                                    if( ar.cvdefects[k]==i )
                                    {
                                        nondefect = false;
                                    }
                                }
                                if( nondefect )
                                {
                                    if( modeltype==0 )
                                    {
                                        linreg.lrbuilds(xy2, s2, n-1, m, ref info2, wt, ar2);
                                    }
                                    else
                                    {
                                        linreg.lrbuildzs(xy2, s2, n-1, m, ref info2, wt, ar2);
                                    }
                                    if( info2!=1 )
                                    {
                                        grconverrors = true;
                                        continue;
                                    }
                                    linreg.lrunpack(wt, ref w2, ref tmpi);
                                    v = 0.0;
                                    for(i_=0; i_<=m-1;i_++)
                                    {
                                        v += xy[i,i_]*w2[i_];
                                    }
                                    v = v+w2[m];
                                    cvrmserror = cvrmserror+math.sqr(v-xy[i,m]);
                                    cvavgerror = cvavgerror+Math.Abs(v-xy[i,m]);
                                    cvavgrelerror = cvavgrelerror+Math.Abs((v-xy[i,m])/xy[i,m]);
                                }
                                
                                //
                                // Next set
                                //
                                if( i!=n-1 )
                                {
                                    for(i_=0; i_<=m;i_++)
                                    {
                                        xy2[i,i_] = xy[i,i_];
                                    }
                                    s2[i] = s[i];
                                }
                            }
                            cvrmserror = Math.Sqrt(cvrmserror/(n-ar.ncvdefects));
                            cvavgerror = cvavgerror/(n-ar.ncvdefects);
                            cvavgrelerror = cvavgrelerror/(n-ar.ncvdefects);
                            rmserror = Math.Sqrt(rmserror/n);
                            avgerror = avgerror/n;
                            avgrelerror = avgrelerror/n;
                            gresterrors = gresterrors | (double)(Math.Abs(Math.Log(ar.cvrmserror/cvrmserror)))>(double)(Math.Log(1+1.0E-5));
                            gresterrors = gresterrors | (double)(Math.Abs(Math.Log(ar.cvavgerror/cvavgerror)))>(double)(Math.Log(1+1.0E-5));
                            gresterrors = gresterrors | (double)(Math.Abs(Math.Log(ar.cvavgrelerror/cvavgrelerror)))>(double)(Math.Log(1+1.0E-5));
                            gresterrors = gresterrors | (double)(Math.Abs(Math.Log(ar.rmserror/rmserror)))>(double)(Math.Log(1+1.0E-5));
                            gresterrors = gresterrors | (double)(Math.Abs(Math.Log(ar.avgerror/avgerror)))>(double)(Math.Log(1+1.0E-5));
                            gresterrors = gresterrors | (double)(Math.Abs(Math.Log(ar.avgrelerror/avgrelerror)))>(double)(Math.Log(1+1.0E-5));
                        }
                    }
                }
            }
            
            //
            // Additional subroutines
            //
            for(pass=1; pass<=50; pass++)
            {
                n = 2;
                do
                {
                    noiselevel = math.randomreal()+0.1;
                    tasklevel = 2*math.randomreal()-1;
                }
                while( (double)(Math.Abs(noiselevel-tasklevel))<=(double)(0.05) );
                xy = new double[3*n-1+1, 1+1];
                for(i=0; i<=n-1; i++)
                {
                    xy[3*i+0,0] = i;
                    xy[3*i+1,0] = i;
                    xy[3*i+2,0] = i;
                    xy[3*i+0,1] = tasklevel-noiselevel;
                    xy[3*i+1,1] = tasklevel;
                    xy[3*i+2,1] = tasklevel+noiselevel;
                }
                linreg.lrbuild(xy, 3*n, 1, ref info, wt, ar);
                if( info==1 )
                {
                    linreg.lrunpack(wt, ref tmpweights, ref tmpi);
                    v = linreg.lrrmserror(wt, xy, 3*n);
                    grothererrors = grothererrors | (double)(Math.Abs(v-noiselevel*Math.Sqrt((double)2/(double)3)))>(double)(threshold);
                    v = linreg.lravgerror(wt, xy, 3*n);
                    grothererrors = grothererrors | (double)(Math.Abs(v-noiselevel*((double)2/(double)3)))>(double)(threshold);
                    v = linreg.lravgrelerror(wt, xy, 3*n);
                    vv = (Math.Abs(noiselevel/(tasklevel-noiselevel))+Math.Abs(noiselevel/(tasklevel+noiselevel)))/3;
                    grothererrors = grothererrors | (double)(Math.Abs(v-vv))>(double)(threshold*vv);
                }
                else
                {
                    grothererrors = true;
                }
                for(i=0; i<=n-1; i++)
                {
                    xy[3*i+0,0] = i;
                    xy[3*i+1,0] = i;
                    xy[3*i+2,0] = i;
                    xy[3*i+0,1] = -noiselevel;
                    xy[3*i+1,1] = 0;
                    xy[3*i+2,1] = noiselevel;
                }
                linreg.lrbuild(xy, 3*n, 1, ref info, wt, ar);
                if( info==1 )
                {
                    linreg.lrunpack(wt, ref tmpweights, ref tmpi);
                    v = linreg.lravgrelerror(wt, xy, 3*n);
                    grothererrors = grothererrors | (double)(Math.Abs(v-1))>(double)(threshold);
                }
                else
                {
                    grothererrors = true;
                }
            }
            for(pass=1; pass<=10; pass++)
            {
                m = 1+math.randominteger(5);
                n = 10+math.randominteger(10);
                xy = new double[n-1+1, m+1];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m; j++)
                    {
                        xy[i,j] = 2*math.randomreal()-1;
                    }
                }
                linreg.lrbuild(xy, n, m, ref info, w, ar);
                if( info<0 )
                {
                    grothererrors = true;
                    break;
                }
                x1 = new double[m-1+1];
                x2 = new double[m-1+1];
                
                //
                // Same inputs on original leads to same outputs
                // on copy created using LRCopy
                //
                unsetlr(wt);
                linreg.lrcopy(w, wt);
                for(i=0; i<=m-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                y1 = linreg.lrprocess(w, x1);
                y2 = linreg.lrprocess(wt, x2);
                allsame = (double)(y1)==(double)(y2);
                grothererrors = grothererrors | !allsame;
            }
            
            //
            // TODO: Degenerate tests (when design matrix and right part are zero)
            //
            
            //
            // Final report
            //
            waserrors = (((((slerrors | slcerrors) | gropterrors) | grcoverrors) | gresterrors) | grothererrors) | grconverrors;
            if( !silent )
            {
                System.Console.Write("REGRESSION TEST");
                System.Console.WriteLine();
                System.Console.Write("STRAIGHT LINE REGRESSION:                ");
                if( !slerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("STRAIGHT LINE REGRESSION CONVERGENCE:    ");
                if( !slcerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("GENERAL LINEAR REGRESSION:               ");
                if( !((((gropterrors | grcoverrors) | gresterrors) | grothererrors) | grconverrors) )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* OPTIMALITY:                            ");
                if( !gropterrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* COV. MATRIX:                           ");
                if( !grcoverrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* ERROR ESTIMATES:                       ");
                if( !gresterrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* CONVERGENCE:                           ");
                if( !grconverrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* OTHER SUBROUTINES:                     ");
                if( !grothererrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Task generation. Meaningless task, just random numbers.
        *************************************************************************/
        private static void generaterandomtask(double xl,
            double xr,
            bool randomx,
            double ymin,
            double ymax,
            double smin,
            double smax,
            int n,
            ref double[,] xy,
            ref double[] s)
        {
            int i = 0;

            xy = new double[n-1+1, 1+1];
            s = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                if( randomx )
                {
                    xy[i,0] = xl+(xr-xl)*math.randomreal();
                }
                else
                {
                    xy[i,0] = xl+(xr-xl)*i/(n-1);
                }
                xy[i,1] = ymin+(ymax-ymin)*math.randomreal();
                s[i] = smin+(smax-smin)*math.randomreal();
            }
        }


        /*************************************************************************
        Task generation.
        *************************************************************************/
        private static void generatetask(double a,
            double b,
            double xl,
            double xr,
            bool randomx,
            double smin,
            double smax,
            int n,
            ref double[,] xy,
            ref double[] s)
        {
            int i = 0;

            xy = new double[n-1+1, 1+1];
            s = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                if( randomx )
                {
                    xy[i,0] = xl+(xr-xl)*math.randomreal();
                }
                else
                {
                    xy[i,0] = xl+(xr-xl)*i/(n-1);
                }
                s[i] = smin+(smax-smin)*math.randomreal();
                xy[i,1] = a+b*xy[i,0]+generatenormal(0, s[i]);
            }
        }


        /*************************************************************************
        Task generation.
        y[i] are filled based on A, B, X[I], S[I]
        *************************************************************************/
        private static void filltaskwithy(double a,
            double b,
            int n,
            ref double[,] xy,
            ref double[] s)
        {
            int i = 0;

            for(i=0; i<=n-1; i++)
            {
                xy[i,1] = a+b*xy[i,0]+generatenormal(0, s[i]);
            }
        }


        /*************************************************************************
        Normal random numbers
        *************************************************************************/
        private static double generatenormal(double mean,
            double sigma)
        {
            double result = 0;
            double u = 0;
            double v = 0;
            double sum = 0;

            result = mean;
            while( true )
            {
                u = (2*math.randominteger(2)-1)*math.randomreal();
                v = (2*math.randominteger(2)-1)*math.randomreal();
                sum = u*u+v*v;
                if( (double)(sum)<(double)(1) & (double)(sum)>(double)(0) )
                {
                    sum = Math.Sqrt(-(2*Math.Log(sum)/sum));
                    result = sigma*u*sum+mean;
                    return result;
                }
            }
            return result;
        }


        /*************************************************************************
        Moments estimates and their errors
        *************************************************************************/
        private static void calculatemv(double[] x,
            int n,
            ref double mean,
            ref double means,
            ref double stddev,
            ref double stddevs)
        {
            int i = 0;
            double v1 = 0;
            double v2 = 0;
            double variance = 0;

            mean = 0;
            means = 0;
            stddev = 0;
            stddevs = 0;

            mean = 0;
            means = 1;
            stddev = 0;
            stddevs = 1;
            variance = 0;
            if( n<=1 )
            {
                return;
            }
            
            //
            // Mean
            //
            for(i=0; i<=n-1; i++)
            {
                mean = mean+x[i];
            }
            mean = mean/n;
            
            //
            // Variance (using corrected two-pass algorithm)
            //
            if( n!=1 )
            {
                v1 = 0;
                for(i=0; i<=n-1; i++)
                {
                    v1 = v1+math.sqr(x[i]-mean);
                }
                v2 = 0;
                for(i=0; i<=n-1; i++)
                {
                    v2 = v2+(x[i]-mean);
                }
                v2 = math.sqr(v2)/n;
                variance = (v1-v2)/(n-1);
                if( (double)(variance)<(double)(0) )
                {
                    variance = 0;
                }
                stddev = Math.Sqrt(variance);
            }
            
            //
            // Errors
            //
            means = stddev/Math.Sqrt(n);
            stddevs = stddev*Math.Sqrt(2)/Math.Sqrt(n-1);
        }


        /*************************************************************************
        Unsets LR
        *************************************************************************/
        private static void unsetlr(linreg.linearmodel lr)
        {
            double[,] xy = new double[0,0];
            int info = 0;
            linreg.lrreport rep = new linreg.lrreport();
            int i = 0;

            xy = new double[5+1, 1+1];
            for(i=0; i<=5; i++)
            {
                xy[i,0] = 0;
                xy[i,1] = 0;
            }
            linreg.lrbuild(xy, 6, 1, ref info, lr, rep);
            ap.assert(info>0);
        }


    }
    public class testxblasunit
    {
        public static bool testxblas(bool silent)
        {
            bool result = new bool();
            bool approxerrors = new bool();
            bool exactnesserrors = new bool();
            bool waserrors = new bool();
            double approxthreshold = 0;
            int maxn = 0;
            int passcount = 0;
            int n = 0;
            int i = 0;
            int pass = 0;
            double rv1 = 0;
            double rv2 = 0;
            double rv2err = 0;
            complex cv1 = 0;
            complex cv2 = 0;
            double cv2err = 0;
            double[] rx = new double[0];
            double[] ry = new double[0];
            complex[] cx = new complex[0];
            complex[] cy = new complex[0];
            double[] temp = new double[0];
            double s = 0;
            int i_ = 0;

            approxerrors = false;
            exactnesserrors = false;
            waserrors = false;
            approxthreshold = 1000*math.machineepsilon;
            maxn = 1000;
            passcount = 10;
            
            //
            // tests:
            // 1. ability to calculate dot product
            // 2. higher precision
            //
            for(n=1; n<=maxn; n++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    //  ability to approximately calculate real dot product
                    //
                    rx = new double[n];
                    ry = new double[n];
                    temp = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        if( (double)(math.randomreal())>(double)(0.2) )
                        {
                            rx[i] = 2*math.randomreal()-1;
                        }
                        else
                        {
                            rx[i] = 0;
                        }
                        if( (double)(math.randomreal())>(double)(0.2) )
                        {
                            ry[i] = 2*math.randomreal()-1;
                        }
                        else
                        {
                            ry[i] = 0;
                        }
                    }
                    rv1 = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        rv1 += rx[i_]*ry[i_];
                    }
                    xblas.xdot(rx, ry, n, ref temp, ref rv2, ref rv2err);
                    approxerrors = approxerrors | (double)(Math.Abs(rv1-rv2))>(double)(approxthreshold);
                    
                    //
                    //  ability to approximately calculate complex dot product
                    //
                    cx = new complex[n];
                    cy = new complex[n];
                    temp = new double[2*n];
                    for(i=0; i<=n-1; i++)
                    {
                        if( (double)(math.randomreal())>(double)(0.2) )
                        {
                            cx[i].x = 2*math.randomreal()-1;
                            cx[i].y = 2*math.randomreal()-1;
                        }
                        else
                        {
                            cx[i] = 0;
                        }
                        if( (double)(math.randomreal())>(double)(0.2) )
                        {
                            cy[i].x = 2*math.randomreal()-1;
                            cy[i].y = 2*math.randomreal()-1;
                        }
                        else
                        {
                            cy[i] = 0;
                        }
                    }
                    cv1 = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        cv1 += cx[i_]*cy[i_];
                    }
                    xblas.xcdot(cx, cy, n, ref temp, ref cv2, ref cv2err);
                    approxerrors = approxerrors | (double)(math.abscomplex(cv1-cv2))>(double)(approxthreshold);
                }
            }
            
            //
            // test of precision: real
            //
            n = 50000;
            rx = new double[n];
            ry = new double[n];
            temp = new double[n];
            for(pass=0; pass<=passcount-1; pass++)
            {
                ap.assert(n%2==0);
                
                //
                // First test: X + X + ... + X - X - X - ... - X = 1*X
                //
                s = Math.Exp(Math.Max(pass, 50));
                if( pass==passcount-1 & pass>1 )
                {
                    s = math.maxrealnumber;
                }
                ry[0] = (2*math.randomreal()-1)*s*Math.Sqrt(2*math.randomreal());
                for(i=1; i<=n-1; i++)
                {
                    ry[i] = ry[0];
                }
                for(i=0; i<=n/2-1; i++)
                {
                    rx[i] = 1;
                }
                for(i=n/2; i<=n-2; i++)
                {
                    rx[i] = -1;
                }
                rx[n-1] = 0;
                xblas.xdot(rx, ry, n, ref temp, ref rv2, ref rv2err);
                exactnesserrors = exactnesserrors | (double)(rv2err)<(double)(0);
                exactnesserrors = exactnesserrors | (double)(rv2err)>(double)(4*math.machineepsilon*Math.Abs(ry[0]));
                exactnesserrors = exactnesserrors | (double)(Math.Abs(rv2-ry[0]))>(double)(rv2err);
                
                //
                // First test: X + X + ... + X = N*X
                //
                s = Math.Exp(Math.Max(pass, 50));
                if( pass==passcount-1 & pass>1 )
                {
                    s = math.maxrealnumber;
                }
                ry[0] = (2*math.randomreal()-1)*s*Math.Sqrt(2*math.randomreal());
                for(i=1; i<=n-1; i++)
                {
                    ry[i] = ry[0];
                }
                for(i=0; i<=n-1; i++)
                {
                    rx[i] = 1;
                }
                xblas.xdot(rx, ry, n, ref temp, ref rv2, ref rv2err);
                exactnesserrors = exactnesserrors | (double)(rv2err)<(double)(0);
                exactnesserrors = exactnesserrors | (double)(rv2err)>(double)(4*math.machineepsilon*Math.Abs(ry[0])*n);
                exactnesserrors = exactnesserrors | (double)(Math.Abs(rv2-n*ry[0]))>(double)(rv2err);
            }
            
            //
            // test of precision: complex
            //
            n = 50000;
            cx = new complex[n];
            cy = new complex[n];
            temp = new double[2*n];
            for(pass=0; pass<=passcount-1; pass++)
            {
                ap.assert(n%2==0);
                
                //
                // First test: X + X + ... + X - X - X - ... - X = 1*X
                //
                s = Math.Exp(Math.Max(pass, 50));
                if( pass==passcount-1 & pass>1 )
                {
                    s = math.maxrealnumber;
                }
                cy[0].x = (2*math.randomreal()-1)*s*Math.Sqrt(2*math.randomreal());
                cy[0].y = (2*math.randomreal()-1)*s*Math.Sqrt(2*math.randomreal());
                for(i=1; i<=n-1; i++)
                {
                    cy[i] = cy[0];
                }
                for(i=0; i<=n/2-1; i++)
                {
                    cx[i] = 1;
                }
                for(i=n/2; i<=n-2; i++)
                {
                    cx[i] = -1;
                }
                cx[n-1] = 0;
                xblas.xcdot(cx, cy, n, ref temp, ref cv2, ref cv2err);
                exactnesserrors = exactnesserrors | (double)(cv2err)<(double)(0);
                exactnesserrors = exactnesserrors | (double)(cv2err)>(double)(4*math.machineepsilon*math.abscomplex(cy[0]));
                exactnesserrors = exactnesserrors | (double)(math.abscomplex(cv2-cy[0]))>(double)(cv2err);
                
                //
                // First test: X + X + ... + X = N*X
                //
                s = Math.Exp(Math.Max(pass, 50));
                if( pass==passcount-1 & pass>1 )
                {
                    s = math.maxrealnumber;
                }
                cy[0] = (2*math.randomreal()-1)*s*Math.Sqrt(2*math.randomreal());
                for(i=1; i<=n-1; i++)
                {
                    cy[i] = cy[0];
                }
                for(i=0; i<=n-1; i++)
                {
                    cx[i] = 1;
                }
                xblas.xcdot(cx, cy, n, ref temp, ref cv2, ref cv2err);
                exactnesserrors = exactnesserrors | (double)(cv2err)<(double)(0);
                exactnesserrors = exactnesserrors | (double)(cv2err)>(double)(4*math.machineepsilon*math.abscomplex(cy[0])*n);
                exactnesserrors = exactnesserrors | (double)(math.abscomplex(cv2-n*cy[0]))>(double)(cv2err);
            }
            
            //
            // report
            //
            waserrors = approxerrors | exactnesserrors;
            if( !silent )
            {
                System.Console.Write("TESTING XBLAS");
                System.Console.WriteLine();
                System.Console.Write("APPROX.TESTS:                            ");
                if( approxerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("EXACT TESTS:                             ");
                if( exactnesserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            
            //
            // end
            //
            result = !waserrors;
            return result;
        }


    }
    public class testdensesolverunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testdensesolver(bool silent)
        {
            bool result = new bool();
            int maxn = 0;
            int maxm = 0;
            int passcount = 0;
            double threshold = 0;
            bool rerrors = new bool();
            bool cerrors = new bool();
            bool spderrors = new bool();
            bool hpderrors = new bool();
            bool rfserrors = new bool();
            bool waserrors = new bool();

            maxn = 10;
            maxm = 5;
            passcount = 5;
            threshold = 10000*math.machineepsilon;
            rfserrors = false;
            rerrors = false;
            cerrors = false;
            spderrors = false;
            hpderrors = false;
            testrsolver(maxn, maxm, passcount, threshold, ref rerrors, ref rfserrors);
            testspdsolver(maxn, maxm, passcount, threshold, ref spderrors, ref rfserrors);
            testcsolver(maxn, maxm, passcount, threshold, ref cerrors, ref rfserrors);
            testhpdsolver(maxn, maxm, passcount, threshold, ref hpderrors, ref rfserrors);
            waserrors = (((rerrors | cerrors) | spderrors) | hpderrors) | rfserrors;
            if( !silent )
            {
                System.Console.Write("TESTING DENSE SOLVER");
                System.Console.WriteLine();
                System.Console.Write("* REAL:                                   ");
                if( rerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* COMPLEX:                                ");
                if( cerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SPD:                                    ");
                if( spderrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* HPD:                                    ");
                if( hpderrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* ITERATIVE IMPROVEMENT:                  ");
                if( rfserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Checks whether solver results are correct solution.
        Returns True on success.
        *************************************************************************/
        private static bool rmatrixchecksolutionm(double[,] xe,
            int n,
            int m,
            double threshold,
            int info,
            densesolver.densesolverreport rep,
            double[,] xs)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info<=0 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(100*math.machineepsilon) | (double)(rep.r1)>(double)(1+1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(100*math.machineepsilon) | (double)(rep.rinf)>(double)(1+1000*math.machineepsilon));
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        result = result & (double)(Math.Abs(xe[i,j]-xs[i,j]))<=(double)(threshold);
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Checks whether solver results are correct solution.
        Returns True on success.
        *************************************************************************/
        private static bool rmatrixchecksolution(double[,] xe,
            int n,
            double threshold,
            int info,
            densesolver.densesolverreport rep,
            double[] xs)
        {
            bool result = new bool();
            double[,] xsm = new double[0,0];
            int i_ = 0;

            xsm = new double[n, 1];
            for(i_=0; i_<=n-1;i_++)
            {
                xsm[i_,0] = xs[i_];
            }
            result = rmatrixchecksolutionm(xe, n, 1, threshold, info, rep, xsm);
            return result;
        }


        /*************************************************************************
        Checks whether solver results indicate singular matrix.
        Returns True on success.
        *************************************************************************/
        private static bool rmatrixchecksingularm(int n,
            int m,
            int info,
            densesolver.densesolverreport rep,
            double[,] xs)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info!=-3 & info!=1 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(0) | (double)(rep.r1)>(double)(1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(0) | (double)(rep.rinf)>(double)(1000*math.machineepsilon));
                if( info==-3 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            result = result & (double)(xs[i,j])==(double)(0);
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Checks whether solver results indicate singular matrix.
        Returns True on success.
        *************************************************************************/
        private static bool rmatrixchecksingular(int n,
            int info,
            densesolver.densesolverreport rep,
            double[] xs)
        {
            bool result = new bool();
            double[,] xsm = new double[0,0];
            int i_ = 0;

            xsm = new double[n, 1];
            for(i_=0; i_<=n-1;i_++)
            {
                xsm[i_,0] = xs[i_];
            }
            result = rmatrixchecksingularm(n, 1, info, rep, xsm);
            return result;
        }


        /*************************************************************************
        Checks whether solver results are correct solution.
        Returns True on success.
        *************************************************************************/
        private static bool cmatrixchecksolutionm(complex[,] xe,
            int n,
            int m,
            double threshold,
            int info,
            densesolver.densesolverreport rep,
            complex[,] xs)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info<=0 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(100*math.machineepsilon) | (double)(rep.r1)>(double)(1+1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(100*math.machineepsilon) | (double)(rep.rinf)>(double)(1+1000*math.machineepsilon));
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        result = result & (double)(math.abscomplex(xe[i,j]-xs[i,j]))<=(double)(threshold);
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Checks whether solver results are correct solution.
        Returns True on success.
        *************************************************************************/
        private static bool cmatrixchecksolution(complex[,] xe,
            int n,
            double threshold,
            int info,
            densesolver.densesolverreport rep,
            complex[] xs)
        {
            bool result = new bool();
            complex[,] xsm = new complex[0,0];
            int i_ = 0;

            xsm = new complex[n, 1];
            for(i_=0; i_<=n-1;i_++)
            {
                xsm[i_,0] = xs[i_];
            }
            result = cmatrixchecksolutionm(xe, n, 1, threshold, info, rep, xsm);
            return result;
        }


        /*************************************************************************
        Checks whether solver results indicate singular matrix.
        Returns True on success.
        *************************************************************************/
        private static bool cmatrixchecksingularm(int n,
            int m,
            int info,
            densesolver.densesolverreport rep,
            complex[,] xs)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info!=-3 & info!=1 )
            {
                result = false;
            }
            else
            {
                result = result & !((double)(rep.r1)<(double)(0) | (double)(rep.r1)>(double)(1000*math.machineepsilon));
                result = result & !((double)(rep.rinf)<(double)(0) | (double)(rep.rinf)>(double)(1000*math.machineepsilon));
                if( info==-3 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            result = result & xs[i,j]==0;
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Checks whether solver results indicate singular matrix.
        Returns True on success.
        *************************************************************************/
        private static bool cmatrixchecksingular(int n,
            int info,
            densesolver.densesolverreport rep,
            complex[] xs)
        {
            bool result = new bool();
            complex[,] xsm = new complex[0,0];
            int i_ = 0;

            xsm = new complex[n, 1];
            for(i_=0; i_<=n-1;i_++)
            {
                xsm[i_,0] = xs[i_];
            }
            result = cmatrixchecksingularm(n, 1, info, rep, xsm);
            return result;
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void rmatrixmakeacopy(double[,] a,
            int m,
            int n,
            ref double[,] b)
        {
            int i = 0;
            int j = 0;

            b = new double[0,0];

            b = new double[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void cmatrixmakeacopy(complex[,] a,
            int m,
            int n,
            ref complex[,] b)
        {
            int i = 0;
            int j = 0;

            b = new complex[0,0];

            b = new complex[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        Drops upper or lower half of the matrix - fills it by special pattern
        which may be used later to ensure that this part wasn't changed
        *************************************************************************/
        private static void rmatrixdrophalf(ref double[,] a,
            int n,
            bool droplower)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (droplower & i>j) | (!droplower & i<j) )
                    {
                        a[i,j] = 1+2*i+3*j;
                    }
                }
            }
        }


        /*************************************************************************
        Drops upper or lower half of the matrix - fills it by special pattern
        which may be used later to ensure that this part wasn't changed
        *************************************************************************/
        private static void cmatrixdrophalf(ref complex[,] a,
            int n,
            bool droplower)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (droplower & i>j) | (!droplower & i<j) )
                    {
                        a[i,j] = 1+2*i+3*j;
                    }
                }
            }
        }


        /*************************************************************************
        Real test
        *************************************************************************/
        private static void testrsolver(int maxn,
            int maxm,
            int passcount,
            double threshold,
            ref bool rerrors,
            ref bool rfserrors)
        {
            double[,] a = new double[0,0];
            double[,] lua = new double[0,0];
            double[,] atmp = new double[0,0];
            int[] p = new int[0];
            double[,] xe = new double[0,0];
            double[,] b = new double[0,0];
            double[] bv = new double[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int m = 0;
            int pass = 0;
            int taskkind = 0;
            double v = 0;
            double verr = 0;
            int info = 0;
            densesolver.densesolverreport rep = new densesolver.densesolverreport();
            densesolver.densesolverlsreport repls = new densesolver.densesolverlsreport();
            double[,] x = new double[0,0];
            double[] xv = new double[0];
            double[] y = new double[0];
            double[] tx = new double[0];
            int i_ = 0;
            int i1_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    for(m=1; m<=maxm; m++)
                    {
                        
                        //
                        // ********************************************************
                        // WELL CONDITIONED TASKS
                        // ability to find correct solution is tested
                        // ********************************************************
                        //
                        // 1. generate random well conditioned matrix A.
                        // 2. generate random solution vector xe
                        // 3. generate right part b=A*xe
                        // 4. test different methods on original A
                        //
                        matgen.rmatrixrndcond(n, 1000, ref a);
                        rmatrixmakeacopy(a, n, n, ref lua);
                        trfac.rmatrixlu(ref lua, n, n, ref p);
                        xe = new double[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                xe[i,j] = 2*math.randomreal()-1;
                            }
                        }
                        b = new double[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*xe[i_,j];
                                }
                                b[i,j] = v;
                            }
                        }
                        
                        //
                        // Test solvers
                        //
                        info = 0;
                        unsetrep(rep);
                        unset2d(ref x);
                        densesolver.rmatrixsolvem(a, n, b, m, (double)(math.randomreal())>(double)(0.5), ref info, rep, ref x);
                        rerrors = rerrors | !rmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        unset1d(ref xv);
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.rmatrixsolve(a, n, bv, ref info, rep, ref xv);
                        rerrors = rerrors | !rmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        info = 0;
                        unsetrep(rep);
                        unset2d(ref x);
                        densesolver.rmatrixlusolvem(lua, p, n, b, m, ref info, rep, ref x);
                        rerrors = rerrors | !rmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        unset1d(ref xv);
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.rmatrixlusolve(lua, p, n, bv, ref info, rep, ref xv);
                        rerrors = rerrors | !rmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        info = 0;
                        unsetrep(rep);
                        unset2d(ref x);
                        densesolver.rmatrixmixedsolvem(a, lua, p, n, b, m, ref info, rep, ref x);
                        rerrors = rerrors | !rmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        unset1d(ref xv);
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.rmatrixmixedsolve(a, lua, p, n, bv, ref info, rep, ref xv);
                        rerrors = rerrors | !rmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        
                        //
                        // Test DenseSolverRLS():
                        // * test on original system A*x = b
                        // * test on overdetermined system with the same solution: (A' A')'*x = (b' b')'
                        // * test on underdetermined system with the same solution: (A 0 0 0 ) * z = b
                        //
                        info = 0;
                        unsetlsrep(repls);
                        unset1d(ref xv);
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.rmatrixsolvels(a, n, n, bv, 0.0, ref info, repls, ref xv);
                        if( info<=0 )
                        {
                            rerrors = true;
                        }
                        else
                        {
                            rerrors = (rerrors | (double)(repls.r2)<(double)(100*math.machineepsilon)) | (double)(repls.r2)>(double)(1+1000*math.machineepsilon);
                            rerrors = (rerrors | repls.n!=n) | repls.k!=0;
                            for(i=0; i<=n-1; i++)
                            {
                                rerrors = rerrors | (double)(Math.Abs(xe[i,0]-xv[i]))>(double)(threshold);
                            }
                        }
                        info = 0;
                        unsetlsrep(repls);
                        unset1d(ref xv);
                        bv = new double[2*n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        i1_ = (0) - (n);
                        for(i_=n; i_<=2*n-1;i_++)
                        {
                            bv[i_] = b[i_+i1_,0];
                        }
                        atmp = new double[2*n, n];
                        blas.copymatrix(a, 0, n-1, 0, n-1, ref atmp, 0, n-1, 0, n-1);
                        blas.copymatrix(a, 0, n-1, 0, n-1, ref atmp, n, 2*n-1, 0, n-1);
                        densesolver.rmatrixsolvels(atmp, 2*n, n, bv, 0.0, ref info, repls, ref xv);
                        if( info<=0 )
                        {
                            rerrors = true;
                        }
                        else
                        {
                            rerrors = (rerrors | (double)(repls.r2)<(double)(100*math.machineepsilon)) | (double)(repls.r2)>(double)(1+1000*math.machineepsilon);
                            rerrors = (rerrors | repls.n!=n) | repls.k!=0;
                            for(i=0; i<=n-1; i++)
                            {
                                rerrors = rerrors | (double)(Math.Abs(xe[i,0]-xv[i]))>(double)(threshold);
                            }
                        }
                        info = 0;
                        unsetlsrep(repls);
                        unset1d(ref xv);
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        atmp = new double[n, 2*n];
                        blas.copymatrix(a, 0, n-1, 0, n-1, ref atmp, 0, n-1, 0, n-1);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=n; j<=2*n-1; j++)
                            {
                                atmp[i,j] = 0;
                            }
                        }
                        densesolver.rmatrixsolvels(atmp, n, 2*n, bv, 0.0, ref info, repls, ref xv);
                        if( info<=0 )
                        {
                            rerrors = true;
                        }
                        else
                        {
                            rerrors = rerrors | (double)(repls.r2)!=(double)(0);
                            rerrors = (rerrors | repls.n!=2*n) | repls.k!=n;
                            for(i=0; i<=n-1; i++)
                            {
                                rerrors = rerrors | (double)(Math.Abs(xe[i,0]-xv[i]))>(double)(threshold);
                            }
                            for(i=n; i<=2*n-1; i++)
                            {
                                rerrors = rerrors | (double)(Math.Abs(xv[i]))>(double)(threshold);
                            }
                        }
                        
                        //
                        // ********************************************************
                        // EXACTLY SINGULAR MATRICES
                        // ability to detect singularity is tested
                        // ********************************************************
                        //
                        // 1. generate different types of singular matrices:
                        //    * zero
                        //    * with zero columns
                        //    * with zero rows
                        //    * with equal rows/columns
                        // 2. generate random solution vector xe
                        // 3. generate right part b=A*xe
                        // 4. test different methods
                        //
                        for(taskkind=0; taskkind<=4; taskkind++)
                        {
                            unset2d(ref a);
                            if( taskkind==0 )
                            {
                                
                                //
                                // all zeros
                                //
                                a = new double[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 0;
                                    }
                                }
                            }
                            if( taskkind==1 )
                            {
                                
                                //
                                // there is zero column
                                //
                                a = new double[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 2*math.randomreal()-1;
                                    }
                                }
                                k = math.randominteger(n);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,k] = 0*a[i_,k];
                                }
                            }
                            if( taskkind==2 )
                            {
                                
                                //
                                // there is zero row
                                //
                                a = new double[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 2*math.randomreal()-1;
                                    }
                                }
                                k = math.randominteger(n);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[k,i_] = 0*a[k,i_];
                                }
                            }
                            if( taskkind==3 )
                            {
                                
                                //
                                // equal columns
                                //
                                if( n<2 )
                                {
                                    continue;
                                }
                                a = new double[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 2*math.randomreal()-1;
                                    }
                                }
                                k = 1+math.randominteger(n-1);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,0] = a[i_,k];
                                }
                            }
                            if( taskkind==4 )
                            {
                                
                                //
                                // equal rows
                                //
                                if( n<2 )
                                {
                                    continue;
                                }
                                a = new double[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 2*math.randomreal()-1;
                                    }
                                }
                                k = 1+math.randominteger(n-1);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[0,i_] = a[k,i_];
                                }
                            }
                            xe = new double[n, m];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    xe[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            b = new double[n, m];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += a[i,i_]*xe[i_,j];
                                    }
                                    b[i,j] = v;
                                }
                            }
                            rmatrixmakeacopy(a, n, n, ref lua);
                            trfac.rmatrixlu(ref lua, n, n, ref p);
                            
                            //
                            // Test RMatrixSolveM()
                            //
                            info = 0;
                            unsetrep(rep);
                            unset2d(ref x);
                            densesolver.rmatrixsolvem(a, n, b, m, (double)(math.randomreal())>(double)(0.5), ref info, rep, ref x);
                            rerrors = rerrors | !rmatrixchecksingularm(n, m, info, rep, x);
                            
                            //
                            // Test RMatrixSolve()
                            //
                            info = 0;
                            unsetrep(rep);
                            unset2d(ref x);
                            bv = new double[n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                bv[i_] = b[i_,0];
                            }
                            densesolver.rmatrixsolve(a, n, bv, ref info, rep, ref xv);
                            rerrors = rerrors | !rmatrixchecksingular(n, info, rep, xv);
                            
                            //
                            // Test RMatrixLUSolveM()
                            //
                            info = 0;
                            unsetrep(rep);
                            unset2d(ref x);
                            densesolver.rmatrixlusolvem(lua, p, n, b, m, ref info, rep, ref x);
                            rerrors = rerrors | !rmatrixchecksingularm(n, m, info, rep, x);
                            
                            //
                            // Test RMatrixLUSolve()
                            //
                            info = 0;
                            unsetrep(rep);
                            unset2d(ref x);
                            bv = new double[n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                bv[i_] = b[i_,0];
                            }
                            densesolver.rmatrixlusolve(lua, p, n, bv, ref info, rep, ref xv);
                            rerrors = rerrors | !rmatrixchecksingular(n, info, rep, xv);
                            
                            //
                            // Test RMatrixMixedSolveM()
                            //
                            info = 0;
                            unsetrep(rep);
                            unset2d(ref x);
                            densesolver.rmatrixmixedsolvem(a, lua, p, n, b, m, ref info, rep, ref x);
                            rerrors = rerrors | !rmatrixchecksingularm(n, m, info, rep, x);
                            
                            //
                            // Test RMatrixMixedSolve()
                            //
                            info = 0;
                            unsetrep(rep);
                            unset2d(ref x);
                            bv = new double[n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                bv[i_] = b[i_,0];
                            }
                            densesolver.rmatrixmixedsolve(a, lua, p, n, bv, ref info, rep, ref xv);
                            rerrors = rerrors | !rmatrixchecksingular(n, info, rep, xv);
                        }
                    }
                }
            }
            
            //
            // test iterative improvement
            //
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Test iterative improvement matrices
                //
                // A matrix/right part are constructed such that both matrix
                // and solution components are within (-1,+1). Such matrix/right part
                // have nice properties - system can be solved using iterative
                // improvement with |A*x-b| about several ulps of max(1,|b|).
                //
                n = 100;
                a = new double[n, n];
                b = new double[n, 1];
                bv = new double[n];
                tx = new double[n];
                xv = new double[n];
                y = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    xv[i] = 2*math.randomreal()-1;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a[i,j] = 2*math.randomreal()-1;
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        y[i_] = a[i,i_];
                    }
                    xblas.xdot(y, xv, n, ref tx, ref v, ref verr);
                    bv[i] = v;
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    b[i_,0] = bv[i_];
                }
                
                //
                // Test RMatrixSolveM()
                //
                unset2d(ref x);
                densesolver.rmatrixsolvem(a, n, b, 1, true, ref info, rep, ref x);
                if( info<=0 )
                {
                    rfserrors = true;
                }
                else
                {
                    xv = new double[n];
                    for(i_=0; i_<=n-1;i_++)
                    {
                        xv[i_] = x[i_,0];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            y[i_] = a[i,i_];
                        }
                        xblas.xdot(y, xv, n, ref tx, ref v, ref verr);
                        rfserrors = rfserrors | (double)(Math.Abs(v-b[i,0]))>(double)(8*math.machineepsilon*Math.Max(1, Math.Abs(b[i,0])));
                    }
                }
                
                //
                // Test RMatrixSolve()
                //
                unset1d(ref xv);
                densesolver.rmatrixsolve(a, n, bv, ref info, rep, ref xv);
                if( info<=0 )
                {
                    rfserrors = true;
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            y[i_] = a[i,i_];
                        }
                        xblas.xdot(y, xv, n, ref tx, ref v, ref verr);
                        rfserrors = rfserrors | (double)(Math.Abs(v-bv[i]))>(double)(8*math.machineepsilon*Math.Max(1, Math.Abs(bv[i])));
                    }
                }
                
                //
                // Test LS-solver on the same matrix
                //
                densesolver.rmatrixsolvels(a, n, n, bv, 0.0, ref info, repls, ref xv);
                if( info<=0 )
                {
                    rfserrors = true;
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            y[i_] = a[i,i_];
                        }
                        xblas.xdot(y, xv, n, ref tx, ref v, ref verr);
                        rfserrors = rfserrors | (double)(Math.Abs(v-bv[i]))>(double)(8*math.machineepsilon*Math.Max(1, Math.Abs(bv[i])));
                    }
                }
            }
        }


        /*************************************************************************
        SPD test
        *************************************************************************/
        private static void testspdsolver(int maxn,
            int maxm,
            int passcount,
            double threshold,
            ref bool spderrors,
            ref bool rfserrors)
        {
            double[,] a = new double[0,0];
            double[,] cha = new double[0,0];
            double[,] atmp = new double[0,0];
            int[] p = new int[0];
            double[,] xe = new double[0,0];
            double[,] b = new double[0,0];
            double[] bv = new double[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int m = 0;
            int pass = 0;
            int taskkind = 0;
            double v = 0;
            bool isupper = new bool();
            int info = 0;
            densesolver.densesolverreport rep = new densesolver.densesolverreport();
            densesolver.densesolverlsreport repls = new densesolver.densesolverlsreport();
            double[,] x = new double[0,0];
            double[] xv = new double[0];
            double[] y = new double[0];
            double[] tx = new double[0];
            int i_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    for(m=1; m<=maxm; m++)
                    {
                        
                        //
                        // ********************************************************
                        // WELL CONDITIONED TASKS
                        // ability to find correct solution is tested
                        // ********************************************************
                        //
                        // 1. generate random well conditioned matrix A.
                        // 2. generate random solution vector xe
                        // 3. generate right part b=A*xe
                        // 4. test different methods on original A
                        //
                        isupper = (double)(math.randomreal())>(double)(0.5);
                        matgen.spdmatrixrndcond(n, 1000, ref a);
                        rmatrixmakeacopy(a, n, n, ref cha);
                        if( !trfac.spdmatrixcholesky(ref cha, n, isupper) )
                        {
                            spderrors = true;
                            return;
                        }
                        xe = new double[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                xe[i,j] = 2*math.randomreal()-1;
                            }
                        }
                        b = new double[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*xe[i_,j];
                                }
                                b[i,j] = v;
                            }
                        }
                        rmatrixdrophalf(ref a, n, isupper);
                        rmatrixdrophalf(ref cha, n, isupper);
                        
                        //
                        // Test solvers
                        //
                        info = 0;
                        unsetrep(rep);
                        unset2d(ref x);
                        densesolver.spdmatrixsolvem(a, n, isupper, b, m, ref info, rep, ref x);
                        spderrors = spderrors | !rmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        unset1d(ref xv);
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.spdmatrixsolve(a, n, isupper, bv, ref info, rep, ref xv);
                        spderrors = spderrors | !rmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        info = 0;
                        unsetrep(rep);
                        unset2d(ref x);
                        densesolver.spdmatrixcholeskysolvem(cha, n, isupper, b, m, ref info, rep, ref x);
                        spderrors = spderrors | !rmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        unset1d(ref xv);
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.spdmatrixcholeskysolve(cha, n, isupper, bv, ref info, rep, ref xv);
                        spderrors = spderrors | !rmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        
                        //
                        // ********************************************************
                        // EXACTLY SINGULAR MATRICES
                        // ability to detect singularity is tested
                        // ********************************************************
                        //
                        // 1. generate different types of singular matrices:
                        //    * zero
                        //    * with zero columns
                        //    * with zero rows
                        //    * with equal rows/columns
                        // 2. generate random solution vector xe
                        // 3. generate right part b=A*xe
                        // 4. test different methods
                        //
                        for(taskkind=0; taskkind<=3; taskkind++)
                        {
                            unset2d(ref a);
                            if( taskkind==0 )
                            {
                                
                                //
                                // all zeros
                                //
                                a = new double[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 0;
                                    }
                                }
                            }
                            if( taskkind==1 )
                            {
                                
                                //
                                // there is zero column
                                //
                                a = new double[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i; j<=n-1; j++)
                                    {
                                        a[i,j] = 2*math.randomreal()-1;
                                        a[j,i] = a[i,j];
                                    }
                                }
                                k = math.randominteger(n);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,k] = 0*a[i_,k];
                                }
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[k,i_] = 0*a[k,i_];
                                }
                            }
                            if( taskkind==2 )
                            {
                                
                                //
                                // there is zero row
                                //
                                a = new double[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i; j<=n-1; j++)
                                    {
                                        a[i,j] = 2*math.randomreal()-1;
                                        a[j,i] = a[i,j];
                                    }
                                }
                                k = math.randominteger(n);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[k,i_] = 0*a[k,i_];
                                }
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,k] = 0*a[i_,k];
                                }
                            }
                            if( taskkind==3 )
                            {
                                
                                //
                                // equal columns/rows
                                //
                                if( n<2 )
                                {
                                    continue;
                                }
                                a = new double[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i; j<=n-1; j++)
                                    {
                                        a[i,j] = 2*math.randomreal()-1;
                                        a[j,i] = a[i,j];
                                    }
                                }
                                k = 1+math.randominteger(n-1);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,0] = a[i_,k];
                                }
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[0,i_] = a[k,i_];
                                }
                            }
                            xe = new double[n, m];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    xe[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            b = new double[n, m];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += a[i,i_]*xe[i_,j];
                                    }
                                    b[i,j] = v;
                                }
                            }
                            rmatrixmakeacopy(a, n, n, ref cha);
                            rmatrixdrophalf(ref a, n, isupper);
                            rmatrixdrophalf(ref cha, n, isupper);
                            
                            //
                            // Test SPDMatrixSolveM()
                            //
                            info = 0;
                            unsetrep(rep);
                            unset2d(ref x);
                            densesolver.spdmatrixsolvem(a, n, isupper, b, m, ref info, rep, ref x);
                            spderrors = spderrors | !rmatrixchecksingularm(n, m, info, rep, x);
                            
                            //
                            // Test SPDMatrixSolve()
                            //
                            info = 0;
                            unsetrep(rep);
                            unset2d(ref x);
                            bv = new double[n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                bv[i_] = b[i_,0];
                            }
                            densesolver.spdmatrixsolve(a, n, isupper, bv, ref info, rep, ref xv);
                            spderrors = spderrors | !rmatrixchecksingular(n, info, rep, xv);
                            
                            //
                            // 'equal columns/rows' are degenerate, but
                            // Cholesky matrix with equal columns/rows IS NOT degenerate,
                            // so it is not used for testing purposes.
                            //
                            if( taskkind!=3 )
                            {
                                
                                //
                                // Test SPDMatrixLUSolveM()
                                //
                                info = 0;
                                unsetrep(rep);
                                unset2d(ref x);
                                densesolver.spdmatrixcholeskysolvem(cha, n, isupper, b, m, ref info, rep, ref x);
                                spderrors = spderrors | !rmatrixchecksingularm(n, m, info, rep, x);
                                
                                //
                                // Test SPDMatrixLUSolve()
                                //
                                info = 0;
                                unsetrep(rep);
                                unset2d(ref x);
                                bv = new double[n];
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    bv[i_] = b[i_,0];
                                }
                                densesolver.spdmatrixcholeskysolve(cha, n, isupper, bv, ref info, rep, ref xv);
                                spderrors = spderrors | !rmatrixchecksingular(n, info, rep, xv);
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Real test
        *************************************************************************/
        private static void testcsolver(int maxn,
            int maxm,
            int passcount,
            double threshold,
            ref bool cerrors,
            ref bool rfserrors)
        {
            complex[,] a = new complex[0,0];
            complex[,] lua = new complex[0,0];
            complex[,] atmp = new complex[0,0];
            int[] p = new int[0];
            complex[,] xe = new complex[0,0];
            complex[,] b = new complex[0,0];
            complex[] bv = new complex[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int m = 0;
            int pass = 0;
            int taskkind = 0;
            double verr = 0;
            complex v = 0;
            int info = 0;
            densesolver.densesolverreport rep = new densesolver.densesolverreport();
            densesolver.densesolverlsreport repls = new densesolver.densesolverlsreport();
            complex[,] x = new complex[0,0];
            complex[] xv = new complex[0];
            complex[] y = new complex[0];
            double[] tx = new double[0];
            int i_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    for(m=1; m<=maxm; m++)
                    {
                        
                        //
                        // ********************************************************
                        // WELL CONDITIONED TASKS
                        // ability to find correct solution is tested
                        // ********************************************************
                        //
                        // 1. generate random well conditioned matrix A.
                        // 2. generate random solution vector xe
                        // 3. generate right part b=A*xe
                        // 4. test different methods on original A
                        //
                        matgen.cmatrixrndcond(n, 1000, ref a);
                        cmatrixmakeacopy(a, n, n, ref lua);
                        trfac.cmatrixlu(ref lua, n, n, ref p);
                        xe = new complex[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                xe[i,j].x = 2*math.randomreal()-1;
                                xe[i,j].y = 2*math.randomreal()-1;
                            }
                        }
                        b = new complex[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*xe[i_,j];
                                }
                                b[i,j] = v;
                            }
                        }
                        
                        //
                        // Test solvers
                        //
                        info = 0;
                        unsetrep(rep);
                        cunset2d(ref x);
                        densesolver.cmatrixsolvem(a, n, b, m, (double)(math.randomreal())>(double)(0.5), ref info, rep, ref x);
                        cerrors = cerrors | !cmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        cunset1d(ref xv);
                        bv = new complex[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.cmatrixsolve(a, n, bv, ref info, rep, ref xv);
                        cerrors = cerrors | !cmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        info = 0;
                        unsetrep(rep);
                        cunset2d(ref x);
                        densesolver.cmatrixlusolvem(lua, p, n, b, m, ref info, rep, ref x);
                        cerrors = cerrors | !cmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        cunset1d(ref xv);
                        bv = new complex[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.cmatrixlusolve(lua, p, n, bv, ref info, rep, ref xv);
                        cerrors = cerrors | !cmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        info = 0;
                        unsetrep(rep);
                        cunset2d(ref x);
                        densesolver.cmatrixmixedsolvem(a, lua, p, n, b, m, ref info, rep, ref x);
                        cerrors = cerrors | !cmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        cunset1d(ref xv);
                        bv = new complex[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.cmatrixmixedsolve(a, lua, p, n, bv, ref info, rep, ref xv);
                        cerrors = cerrors | !cmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        
                        //
                        // ********************************************************
                        // EXACTLY SINGULAR MATRICES
                        // ability to detect singularity is tested
                        // ********************************************************
                        //
                        // 1. generate different types of singular matrices:
                        //    * zero
                        //    * with zero columns
                        //    * with zero rows
                        //    * with equal rows/columns
                        // 2. generate random solution vector xe
                        // 3. generate right part b=A*xe
                        // 4. test different methods
                        //
                        for(taskkind=0; taskkind<=4; taskkind++)
                        {
                            cunset2d(ref a);
                            if( taskkind==0 )
                            {
                                
                                //
                                // all zeros
                                //
                                a = new complex[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 0;
                                    }
                                }
                            }
                            if( taskkind==1 )
                            {
                                
                                //
                                // there is zero column
                                //
                                a = new complex[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j].x = 2*math.randomreal()-1;
                                        a[i,j].y = 2*math.randomreal()-1;
                                    }
                                }
                                k = math.randominteger(n);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,k] = 0*a[i_,k];
                                }
                            }
                            if( taskkind==2 )
                            {
                                
                                //
                                // there is zero row
                                //
                                a = new complex[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j].x = 2*math.randomreal()-1;
                                        a[i,j].y = 2*math.randomreal()-1;
                                    }
                                }
                                k = math.randominteger(n);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[k,i_] = 0*a[k,i_];
                                }
                            }
                            if( taskkind==3 )
                            {
                                
                                //
                                // equal columns
                                //
                                if( n<2 )
                                {
                                    continue;
                                }
                                a = new complex[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j].x = 2*math.randomreal()-1;
                                        a[i,j].y = 2*math.randomreal()-1;
                                    }
                                }
                                k = 1+math.randominteger(n-1);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,0] = a[i_,k];
                                }
                            }
                            if( taskkind==4 )
                            {
                                
                                //
                                // equal rows
                                //
                                if( n<2 )
                                {
                                    continue;
                                }
                                a = new complex[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j].x = 2*math.randomreal()-1;
                                        a[i,j].y = 2*math.randomreal()-1;
                                    }
                                }
                                k = 1+math.randominteger(n-1);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[0,i_] = a[k,i_];
                                }
                            }
                            xe = new complex[n, m];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    xe[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            b = new complex[n, m];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += a[i,i_]*xe[i_,j];
                                    }
                                    b[i,j] = v;
                                }
                            }
                            cmatrixmakeacopy(a, n, n, ref lua);
                            trfac.cmatrixlu(ref lua, n, n, ref p);
                            
                            //
                            // Test CMatrixSolveM()
                            //
                            info = 0;
                            unsetrep(rep);
                            cunset2d(ref x);
                            densesolver.cmatrixsolvem(a, n, b, m, (double)(math.randomreal())>(double)(0.5), ref info, rep, ref x);
                            cerrors = cerrors | !cmatrixchecksingularm(n, m, info, rep, x);
                            
                            //
                            // Test CMatrixSolve()
                            //
                            info = 0;
                            unsetrep(rep);
                            cunset2d(ref x);
                            bv = new complex[n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                bv[i_] = b[i_,0];
                            }
                            densesolver.cmatrixsolve(a, n, bv, ref info, rep, ref xv);
                            cerrors = cerrors | !cmatrixchecksingular(n, info, rep, xv);
                            
                            //
                            // Test CMatrixLUSolveM()
                            //
                            info = 0;
                            unsetrep(rep);
                            cunset2d(ref x);
                            densesolver.cmatrixlusolvem(lua, p, n, b, m, ref info, rep, ref x);
                            cerrors = cerrors | !cmatrixchecksingularm(n, m, info, rep, x);
                            
                            //
                            // Test CMatrixLUSolve()
                            //
                            info = 0;
                            unsetrep(rep);
                            cunset2d(ref x);
                            bv = new complex[n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                bv[i_] = b[i_,0];
                            }
                            densesolver.cmatrixlusolve(lua, p, n, bv, ref info, rep, ref xv);
                            cerrors = cerrors | !cmatrixchecksingular(n, info, rep, xv);
                            
                            //
                            // Test CMatrixMixedSolveM()
                            //
                            info = 0;
                            unsetrep(rep);
                            cunset2d(ref x);
                            densesolver.cmatrixmixedsolvem(a, lua, p, n, b, m, ref info, rep, ref x);
                            cerrors = cerrors | !cmatrixchecksingularm(n, m, info, rep, x);
                            
                            //
                            // Test CMatrixMixedSolve()
                            //
                            info = 0;
                            unsetrep(rep);
                            cunset2d(ref x);
                            bv = new complex[n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                bv[i_] = b[i_,0];
                            }
                            densesolver.cmatrixmixedsolve(a, lua, p, n, bv, ref info, rep, ref xv);
                            cerrors = cerrors | !cmatrixchecksingular(n, info, rep, xv);
                        }
                    }
                }
            }
            
            //
            // test iterative improvement
            //
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Test iterative improvement matrices
                //
                // A matrix/right part are constructed such that both matrix
                // and solution components magnitudes are within (-1,+1).
                // Such matrix/right part have nice properties - system can
                // be solved using iterative improvement with |A*x-b| about
                // several ulps of max(1,|b|).
                //
                n = 100;
                a = new complex[n, n];
                b = new complex[n, 1];
                bv = new complex[n];
                tx = new double[2*n];
                xv = new complex[n];
                y = new complex[n];
                for(i=0; i<=n-1; i++)
                {
                    xv[i].x = 2*math.randomreal()-1;
                    xv[i].y = 2*math.randomreal()-1;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a[i,j].x = 2*math.randomreal()-1;
                        a[i,j].y = 2*math.randomreal()-1;
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        y[i_] = a[i,i_];
                    }
                    xblas.xcdot(y, xv, n, ref tx, ref v, ref verr);
                    bv[i] = v;
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    b[i_,0] = bv[i_];
                }
                
                //
                // Test CMatrixSolveM()
                //
                cunset2d(ref x);
                densesolver.cmatrixsolvem(a, n, b, 1, true, ref info, rep, ref x);
                if( info<=0 )
                {
                    rfserrors = true;
                }
                else
                {
                    xv = new complex[n];
                    for(i_=0; i_<=n-1;i_++)
                    {
                        xv[i_] = x[i_,0];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            y[i_] = a[i,i_];
                        }
                        xblas.xcdot(y, xv, n, ref tx, ref v, ref verr);
                        rfserrors = rfserrors | (double)(math.abscomplex(v-b[i,0]))>(double)(8*math.machineepsilon*Math.Max(1, math.abscomplex(b[i,0])));
                    }
                }
                
                //
                // Test CMatrixSolve()
                //
                cunset1d(ref xv);
                densesolver.cmatrixsolve(a, n, bv, ref info, rep, ref xv);
                if( info<=0 )
                {
                    rfserrors = true;
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            y[i_] = a[i,i_];
                        }
                        xblas.xcdot(y, xv, n, ref tx, ref v, ref verr);
                        rfserrors = rfserrors | (double)(math.abscomplex(v-bv[i]))>(double)(8*math.machineepsilon*Math.Max(1, math.abscomplex(bv[i])));
                    }
                }
                
                //
                // TODO: Test LS-solver on the same matrix
                //
            }
        }


        /*************************************************************************
        HPD test
        *************************************************************************/
        private static void testhpdsolver(int maxn,
            int maxm,
            int passcount,
            double threshold,
            ref bool hpderrors,
            ref bool rfserrors)
        {
            complex[,] a = new complex[0,0];
            complex[,] cha = new complex[0,0];
            complex[,] atmp = new complex[0,0];
            int[] p = new int[0];
            complex[,] xe = new complex[0,0];
            complex[,] b = new complex[0,0];
            complex[] bv = new complex[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int m = 0;
            int pass = 0;
            int taskkind = 0;
            complex v = 0;
            bool isupper = new bool();
            int info = 0;
            densesolver.densesolverreport rep = new densesolver.densesolverreport();
            densesolver.densesolverlsreport repls = new densesolver.densesolverlsreport();
            complex[,] x = new complex[0,0];
            complex[] xv = new complex[0];
            complex[] y = new complex[0];
            complex[] tx = new complex[0];
            int i_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    for(m=1; m<=maxm; m++)
                    {
                        
                        //
                        // ********************************************************
                        // WELL CONDITIONED TASKS
                        // ability to find correct solution is tested
                        // ********************************************************
                        //
                        // 1. generate random well conditioned matrix A.
                        // 2. generate random solution vector xe
                        // 3. generate right part b=A*xe
                        // 4. test different methods on original A
                        //
                        isupper = (double)(math.randomreal())>(double)(0.5);
                        matgen.hpdmatrixrndcond(n, 1000, ref a);
                        cmatrixmakeacopy(a, n, n, ref cha);
                        if( !trfac.hpdmatrixcholesky(ref cha, n, isupper) )
                        {
                            hpderrors = true;
                            return;
                        }
                        xe = new complex[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                xe[i,j].x = 2*math.randomreal()-1;
                                xe[i,j].y = 2*math.randomreal()-1;
                            }
                        }
                        b = new complex[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*xe[i_,j];
                                }
                                b[i,j] = v;
                            }
                        }
                        cmatrixdrophalf(ref a, n, isupper);
                        cmatrixdrophalf(ref cha, n, isupper);
                        
                        //
                        // Test solvers
                        //
                        info = 0;
                        unsetrep(rep);
                        cunset2d(ref x);
                        densesolver.hpdmatrixsolvem(a, n, isupper, b, m, ref info, rep, ref x);
                        hpderrors = hpderrors | !cmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        cunset1d(ref xv);
                        bv = new complex[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.hpdmatrixsolve(a, n, isupper, bv, ref info, rep, ref xv);
                        hpderrors = hpderrors | !cmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        info = 0;
                        unsetrep(rep);
                        cunset2d(ref x);
                        densesolver.hpdmatrixcholeskysolvem(cha, n, isupper, b, m, ref info, rep, ref x);
                        hpderrors = hpderrors | !cmatrixchecksolutionm(xe, n, m, threshold, info, rep, x);
                        info = 0;
                        unsetrep(rep);
                        cunset1d(ref xv);
                        bv = new complex[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.hpdmatrixcholeskysolve(cha, n, isupper, bv, ref info, rep, ref xv);
                        hpderrors = hpderrors | !cmatrixchecksolution(xe, n, threshold, info, rep, xv);
                        
                        //
                        // ********************************************************
                        // EXACTLY SINGULAR MATRICES
                        // ability to detect singularity is tested
                        // ********************************************************
                        //
                        // 1. generate different types of singular matrices:
                        //    * zero
                        //    * with zero columns
                        //    * with zero rows
                        //    * with equal rows/columns
                        // 2. generate random solution vector xe
                        // 3. generate right part b=A*xe
                        // 4. test different methods
                        //
                        for(taskkind=0; taskkind<=3; taskkind++)
                        {
                            cunset2d(ref a);
                            if( taskkind==0 )
                            {
                                
                                //
                                // all zeros
                                //
                                a = new complex[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 0;
                                    }
                                }
                            }
                            if( taskkind==1 )
                            {
                                
                                //
                                // there is zero column
                                //
                                a = new complex[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i; j<=n-1; j++)
                                    {
                                        a[i,j].x = 2*math.randomreal()-1;
                                        a[i,j].y = 2*math.randomreal()-1;
                                        if( i==j )
                                        {
                                            a[i,j].y = 0;
                                        }
                                        a[j,i] = a[i,j];
                                    }
                                }
                                k = math.randominteger(n);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,k] = 0*a[i_,k];
                                }
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[k,i_] = 0*a[k,i_];
                                }
                            }
                            if( taskkind==2 )
                            {
                                
                                //
                                // there is zero row
                                //
                                a = new complex[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i; j<=n-1; j++)
                                    {
                                        a[i,j].x = 2*math.randomreal()-1;
                                        a[i,j].y = 2*math.randomreal()-1;
                                        if( i==j )
                                        {
                                            a[i,j].y = 0;
                                        }
                                        a[j,i] = a[i,j];
                                    }
                                }
                                k = math.randominteger(n);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[k,i_] = 0*a[k,i_];
                                }
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,k] = 0*a[i_,k];
                                }
                            }
                            if( taskkind==3 )
                            {
                                
                                //
                                // equal columns/rows
                                //
                                if( n<2 )
                                {
                                    continue;
                                }
                                a = new complex[n, n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i; j<=n-1; j++)
                                    {
                                        a[i,j].x = 2*math.randomreal()-1;
                                        a[i,j].y = 2*math.randomreal()-1;
                                        if( i==j )
                                        {
                                            a[i,j].y = 0;
                                        }
                                        a[j,i] = a[i,j];
                                    }
                                }
                                k = 1+math.randominteger(n-1);
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[i_,0] = a[i_,k];
                                }
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    a[0,i_] = a[k,i_];
                                }
                            }
                            xe = new complex[n, m];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    xe[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            b = new complex[n, m];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=m-1; j++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += a[i,i_]*xe[i_,j];
                                    }
                                    b[i,j] = v;
                                }
                            }
                            cmatrixmakeacopy(a, n, n, ref cha);
                            cmatrixdrophalf(ref a, n, isupper);
                            cmatrixdrophalf(ref cha, n, isupper);
                            
                            //
                            // Test SPDMatrixSolveM()
                            //
                            info = 0;
                            unsetrep(rep);
                            cunset2d(ref x);
                            densesolver.hpdmatrixsolvem(a, n, isupper, b, m, ref info, rep, ref x);
                            hpderrors = hpderrors | !cmatrixchecksingularm(n, m, info, rep, x);
                            
                            //
                            // Test SPDMatrixSolve()
                            //
                            info = 0;
                            unsetrep(rep);
                            cunset2d(ref x);
                            bv = new complex[n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                bv[i_] = b[i_,0];
                            }
                            densesolver.hpdmatrixsolve(a, n, isupper, bv, ref info, rep, ref xv);
                            hpderrors = hpderrors | !cmatrixchecksingular(n, info, rep, xv);
                            
                            //
                            // 'equal columns/rows' are degenerate, but
                            // Cholesky matrix with equal columns/rows IS NOT degenerate,
                            // so it is not used for testing purposes.
                            //
                            if( taskkind!=3 )
                            {
                                
                                //
                                // Test SPDMatrixLUSolveM()
                                //
                                info = 0;
                                unsetrep(rep);
                                cunset2d(ref x);
                                densesolver.hpdmatrixcholeskysolvem(cha, n, isupper, b, m, ref info, rep, ref x);
                                hpderrors = hpderrors | !cmatrixchecksingularm(n, m, info, rep, x);
                                
                                //
                                // Test SPDMatrixLUSolve()
                                //
                                info = 0;
                                unsetrep(rep);
                                cunset2d(ref x);
                                bv = new complex[n];
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    bv[i_] = b[i_,0];
                                }
                                densesolver.hpdmatrixcholeskysolve(cha, n, isupper, bv, ref info, rep, ref xv);
                                hpderrors = hpderrors | !cmatrixchecksingular(n, info, rep, xv);
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Unsets real matrix
        *************************************************************************/
        private static void unset2d(ref double[,] x)
        {
            x = new double[1, 1];
            x[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets real vector
        *************************************************************************/
        private static void unset1d(ref double[] x)
        {
            x = new double[1];
            x[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets real matrix
        *************************************************************************/
        private static void cunset2d(ref complex[,] x)
        {
            x = new complex[1, 1];
            x[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets real vector
        *************************************************************************/
        private static void cunset1d(ref complex[] x)
        {
            x = new complex[1];
            x[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets report
        *************************************************************************/
        private static void unsetrep(densesolver.densesolverreport r)
        {
            r.r1 = -1;
            r.rinf = -1;
        }


        /*************************************************************************
        Unsets report
        *************************************************************************/
        private static void unsetlsrep(densesolver.densesolverlsreport r)
        {
            r.r2 = -1;
            r.n = -1;
            r.k = -1;
            unset2d(ref r.cx);
        }


    }
    public class testlinminunit
    {
        public static bool testlinmin(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();

            waserrors = false;
            if( !silent )
            {
                System.Console.Write("TESTING LINMIN");
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


    }
    public class testmincgunit
    {
        public static bool testmincg(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool referror = new bool();
            bool eqerror = new bool();
            bool linerror1 = new bool();
            bool linerror2 = new bool();
            bool restartserror = new bool();
            bool precerror = new bool();
            bool converror = new bool();
            bool othererrors = new bool();
            int n = 0;
            double[] x = new double[0];
            double[] xe = new double[0];
            double[] b = new double[0];
            double[] xlast = new double[0];
            int i = 0;
            int j = 0;
            double v = 0;
            double[,] a = new double[0,0];
            double[] diagh = new double[0];
            mincg.mincgstate state = new mincg.mincgstate();
            mincg.mincgreport rep = new mincg.mincgreport();
            int cgtype = 0;
            int difftype = 0;
            double diffstep = 0;
            int i_ = 0;

            waserrors = false;
            referror = false;
            linerror1 = false;
            linerror2 = false;
            eqerror = false;
            converror = false;
            restartserror = false;
            othererrors = false;
            precerror = false;
            testpreconditioning(ref precerror);
            testother(ref othererrors);
            for(difftype=0; difftype<=1; difftype++)
            {
                for(cgtype=-1; cgtype<=1; cgtype++)
                {
                    
                    //
                    // Reference problem
                    //
                    x = new double[2+1];
                    n = 3;
                    diffstep = 1.0E-6;
                    x[0] = 100*math.randomreal()-50;
                    x[1] = 100*math.randomreal()-50;
                    x[2] = 100*math.randomreal()-50;
                    if( difftype==0 )
                    {
                        mincg.mincgcreate(n, x, state);
                    }
                    if( difftype==1 )
                    {
                        mincg.mincgcreatef(n, x, diffstep, state);
                    }
                    mincg.mincgsetcgtype(state, cgtype);
                    while( mincg.mincgiteration(state) )
                    {
                        if( state.needf | state.needfg )
                        {
                            state.f = math.sqr(state.x[0]-2)+math.sqr(state.x[1])+math.sqr(state.x[2]-state.x[0]);
                        }
                        if( state.needfg )
                        {
                            state.g[0] = 2*(state.x[0]-2)+2*(state.x[0]-state.x[2]);
                            state.g[1] = 2*state.x[1];
                            state.g[2] = 2*(state.x[2]-state.x[0]);
                        }
                    }
                    mincg.mincgresults(state, ref x, rep);
                    referror = (((referror | rep.terminationtype<=0) | (double)(Math.Abs(x[0]-2))>(double)(0.001)) | (double)(Math.Abs(x[1]))>(double)(0.001)) | (double)(Math.Abs(x[2]-2))>(double)(0.001);
                    
                    //
                    // F2 problem with restarts:
                    // * make several iterations and restart BEFORE termination
                    // * iterate and restart AFTER termination
                    //
                    // NOTE: step is bounded from above to avoid premature convergence
                    //
                    x = new double[3];
                    n = 3;
                    diffstep = 1.0E-6;
                    x[0] = 10+10*math.randomreal();
                    x[1] = 10+10*math.randomreal();
                    x[2] = 10+10*math.randomreal();
                    if( difftype==0 )
                    {
                        mincg.mincgcreate(n, x, state);
                    }
                    if( difftype==1 )
                    {
                        mincg.mincgcreatef(n, x, diffstep, state);
                    }
                    mincg.mincgsetcgtype(state, cgtype);
                    mincg.mincgsetstpmax(state, 0.1);
                    mincg.mincgsetcond(state, 0.0000001, 0.0, 0.0, 0);
                    for(i=0; i<=10; i++)
                    {
                        if( !mincg.mincgiteration(state) )
                        {
                            break;
                        }
                        testfunc2(state);
                    }
                    x[0] = 10+10*math.randomreal();
                    x[1] = 10+10*math.randomreal();
                    x[2] = 10+10*math.randomreal();
                    mincg.mincgrestartfrom(state, x);
                    while( mincg.mincgiteration(state) )
                    {
                        testfunc2(state);
                    }
                    mincg.mincgresults(state, ref x, rep);
                    restartserror = (((restartserror | rep.terminationtype<=0) | (double)(Math.Abs(x[0]-Math.Log(2)))>(double)(0.01)) | (double)(Math.Abs(x[1]))>(double)(0.01)) | (double)(Math.Abs(x[2]-Math.Log(2)))>(double)(0.01);
                    x[0] = 10+10*math.randomreal();
                    x[1] = 10+10*math.randomreal();
                    x[2] = 10+10*math.randomreal();
                    mincg.mincgrestartfrom(state, x);
                    while( mincg.mincgiteration(state) )
                    {
                        testfunc2(state);
                    }
                    mincg.mincgresults(state, ref x, rep);
                    restartserror = (((restartserror | rep.terminationtype<=0) | (double)(Math.Abs(x[0]-Math.Log(2)))>(double)(0.01)) | (double)(Math.Abs(x[1]))>(double)(0.01)) | (double)(Math.Abs(x[2]-Math.Log(2)))>(double)(0.01);
                    
                    //
                    // 1D problem #1
                    //
                    x = new double[0+1];
                    n = 1;
                    diffstep = 1.0E-6;
                    x[0] = 100*math.randomreal()-50;
                    if( difftype==0 )
                    {
                        mincg.mincgcreate(n, x, state);
                    }
                    if( difftype==1 )
                    {
                        mincg.mincgcreatef(n, x, diffstep, state);
                    }
                    mincg.mincgsetcgtype(state, cgtype);
                    while( mincg.mincgiteration(state) )
                    {
                        if( state.needf | state.needfg )
                        {
                            state.f = -Math.Cos(state.x[0]);
                        }
                        if( state.needfg )
                        {
                            state.g[0] = Math.Sin(state.x[0]);
                        }
                    }
                    mincg.mincgresults(state, ref x, rep);
                    linerror1 = (linerror1 | rep.terminationtype<=0) | (double)(Math.Abs(x[0]/Math.PI-(int)Math.Round(x[0]/Math.PI)))>(double)(0.001);
                    
                    //
                    // 1D problem #2
                    //
                    x = new double[0+1];
                    n = 1;
                    diffstep = 1.0E-6;
                    x[0] = 100*math.randomreal()-50;
                    if( difftype==0 )
                    {
                        mincg.mincgcreate(n, x, state);
                    }
                    if( difftype==1 )
                    {
                        mincg.mincgcreatef(n, x, diffstep, state);
                    }
                    mincg.mincgsetcgtype(state, cgtype);
                    while( mincg.mincgiteration(state) )
                    {
                        if( state.needf | state.needfg )
                        {
                            state.f = math.sqr(state.x[0])/(1+math.sqr(state.x[0]));
                        }
                        if( state.needfg )
                        {
                            state.g[0] = (2*state.x[0]*(1+math.sqr(state.x[0]))-math.sqr(state.x[0])*2*state.x[0])/math.sqr(1+math.sqr(state.x[0]));
                        }
                    }
                    mincg.mincgresults(state, ref x, rep);
                    linerror2 = (linerror2 | rep.terminationtype<=0) | (double)(Math.Abs(x[0]))>(double)(0.001);
                    
                    //
                    // Linear equations
                    //
                    diffstep = 1.0E-6;
                    for(n=1; n<=10; n++)
                    {
                        
                        //
                        // Prepare task
                        //
                        a = new double[n-1+1, n-1+1];
                        x = new double[n-1+1];
                        xe = new double[n-1+1];
                        b = new double[n-1+1];
                        for(i=0; i<=n-1; i++)
                        {
                            xe[i] = 2*math.randomreal()-1;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a[i,j] = 2*math.randomreal()-1;
                            }
                            a[i,i] = a[i,i]+3*Math.Sign(a[i,i]);
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*xe[i_];
                            }
                            b[i] = v;
                        }
                        
                        //
                        // Solve task
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = 2*math.randomreal()-1;
                        }
                        if( difftype==0 )
                        {
                            mincg.mincgcreate(n, x, state);
                        }
                        if( difftype==1 )
                        {
                            mincg.mincgcreatef(n, x, diffstep, state);
                        }
                        mincg.mincgsetcgtype(state, cgtype);
                        while( mincg.mincgiteration(state) )
                        {
                            if( state.needf | state.needfg )
                            {
                                state.f = 0;
                            }
                            if( state.needfg )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    state.g[i] = 0;
                                }
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*state.x[i_];
                                }
                                if( state.needf | state.needfg )
                                {
                                    state.f = state.f+math.sqr(v-b[i]);
                                }
                                if( state.needfg )
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        state.g[j] = state.g[j]+2*(v-b[i])*a[i,j];
                                    }
                                }
                            }
                        }
                        mincg.mincgresults(state, ref x, rep);
                        eqerror = eqerror | rep.terminationtype<=0;
                        for(i=0; i<=n-1; i++)
                        {
                            eqerror = eqerror | (double)(Math.Abs(x[i]-xe[i]))>(double)(0.001);
                        }
                    }
                    
                    //
                    // Testing convergence properties
                    //
                    diffstep = 1.0E-6;
                    n = 3;
                    x = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 6*math.randomreal()-3;
                    }
                    if( difftype==0 )
                    {
                        mincg.mincgcreate(n, x, state);
                    }
                    if( difftype==1 )
                    {
                        mincg.mincgcreatef(n, x, diffstep, state);
                    }
                    mincg.mincgsetcond(state, 0.001, 0.0, 0.0, 0);
                    mincg.mincgsetcgtype(state, cgtype);
                    while( mincg.mincgiteration(state) )
                    {
                        testfunc3(state);
                    }
                    mincg.mincgresults(state, ref x, rep);
                    converror = converror | rep.terminationtype!=4;
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 6*math.randomreal()-3;
                    }
                    if( difftype==0 )
                    {
                        mincg.mincgcreate(n, x, state);
                    }
                    if( difftype==1 )
                    {
                        mincg.mincgcreatef(n, x, diffstep, state);
                    }
                    mincg.mincgsetcond(state, 0.0, 0.001, 0.0, 0);
                    mincg.mincgsetcgtype(state, cgtype);
                    while( mincg.mincgiteration(state) )
                    {
                        testfunc3(state);
                    }
                    mincg.mincgresults(state, ref x, rep);
                    converror = converror | rep.terminationtype!=1;
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 6*math.randomreal()-3;
                    }
                    if( difftype==0 )
                    {
                        mincg.mincgcreate(n, x, state);
                    }
                    if( difftype==1 )
                    {
                        mincg.mincgcreatef(n, x, diffstep, state);
                    }
                    mincg.mincgsetcond(state, 0.0, 0.0, 0.001, 0);
                    mincg.mincgsetcgtype(state, cgtype);
                    while( mincg.mincgiteration(state) )
                    {
                        testfunc3(state);
                    }
                    mincg.mincgresults(state, ref x, rep);
                    converror = converror | rep.terminationtype!=2;
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    if( difftype==0 )
                    {
                        mincg.mincgcreate(n, x, state);
                    }
                    if( difftype==1 )
                    {
                        mincg.mincgcreatef(n, x, diffstep, state);
                    }
                    mincg.mincgsetcond(state, 0.0, 0.0, 0.0, 10);
                    mincg.mincgsetcgtype(state, cgtype);
                    while( mincg.mincgiteration(state) )
                    {
                        testfunc3(state);
                    }
                    mincg.mincgresults(state, ref x, rep);
                    converror = converror | !((rep.terminationtype==5 & rep.iterationscount==10) | rep.terminationtype==7);
                }
            }
            
            //
            // end
            //
            waserrors = ((((((referror | eqerror) | linerror1) | linerror2) | converror) | othererrors) | restartserror) | precerror;
            if( !silent )
            {
                System.Console.Write("TESTING CG OPTIMIZATION");
                System.Console.WriteLine();
                System.Console.Write("REFERENCE PROBLEM:                        ");
                if( referror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LIN-1 PROBLEM:                            ");
                if( linerror1 )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LIN-2 PROBLEM:                            ");
                if( linerror2 )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LINEAR EQUATIONS:                         ");
                if( eqerror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("RESTARTS:                                 ");
                if( restartserror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("PRECONDITIONING:                          ");
                if( precerror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("CONVERGENCE PROPERTIES:                   ");
                if( converror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("OTHER PROPERTIES:                         ");
                if( othererrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Other properties
        *************************************************************************/
        public static void testother(ref bool err)
        {
            int n = 0;
            double[] x = new double[0];
            double[] s = new double[0];
            double[] a = new double[0];
            double[] h = new double[0];
            double[] xlast = new double[0];
            double fprev = 0;
            double xprev = 0;
            double stpmax = 0;
            int i = 0;
            mincg.mincgstate state = new mincg.mincgstate();
            mincg.mincgreport rep = new mincg.mincgreport();
            int cgtype = 0;
            double tmpeps = 0;
            double epsg = 0;
            double v = 0;
            double r = 0;
            bool hasxlast = new bool();
            double lastscaledstep = 0;
            int pkind = 0;
            int ckind = 0;
            int mkind = 0;
            int dkind = 0;
            double diffstep = 0;
            double vc = 0;
            double vm = 0;
            bool wasf = new bool();
            bool wasfg = new bool();
            int i_ = 0;

            for(cgtype=-1; cgtype<=1; cgtype++)
            {
                
                //
                // Test reports (F should form monotone sequence)
                //
                n = 50;
                x = new double[n];
                xlast = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 1;
                }
                mincg.mincgcreate(n, x, state);
                mincg.mincgsetcond(state, 0, 0, 0, 100);
                mincg.mincgsetxrep(state, true);
                fprev = math.maxrealnumber;
                while( mincg.mincgiteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+math.sqr((1+i)*state.x[i]);
                            state.g[i] = 2*(1+i)*state.x[i];
                        }
                    }
                    if( state.xupdated )
                    {
                        err = err | (double)(state.f)>(double)(fprev);
                        if( (double)(fprev)==(double)(math.maxrealnumber) )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                err = err | (double)(state.x[i])!=(double)(x[i]);
                            }
                        }
                        fprev = state.f;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            xlast[i_] = state.x[i_];
                        }
                    }
                }
                mincg.mincgresults(state, ref x, rep);
                for(i=0; i<=n-1; i++)
                {
                    err = err | (double)(x[i])!=(double)(xlast[i]);
                }
                
                //
                // Test differentiation vs. analytic gradient
                // (first one issues NeedF requests, second one issues NeedFG requests)
                //
                n = 50;
                diffstep = 1.0E-6;
                for(dkind=0; dkind<=1; dkind++)
                {
                    x = new double[n];
                    xlast = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 1;
                    }
                    if( dkind==0 )
                    {
                        mincg.mincgcreate(n, x, state);
                    }
                    if( dkind==1 )
                    {
                        mincg.mincgcreatef(n, x, diffstep, state);
                    }
                    mincg.mincgsetcond(state, 0, 0, 0, n/2);
                    wasf = false;
                    wasfg = false;
                    while( mincg.mincgiteration(state) )
                    {
                        if( state.needf | state.needfg )
                        {
                            state.f = 0;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            if( state.needf | state.needfg )
                            {
                                state.f = state.f+math.sqr((1+i)*state.x[i]);
                            }
                            if( state.needfg )
                            {
                                state.g[i] = 2*(1+i)*state.x[i];
                            }
                        }
                        wasf = wasf | state.needf;
                        wasfg = wasfg | state.needfg;
                    }
                    mincg.mincgresults(state, ref x, rep);
                    if( dkind==0 )
                    {
                        err = (err | wasf) | !wasfg;
                    }
                    if( dkind==1 )
                    {
                        err = (err | !wasf) | wasfg;
                    }
                }
                
                //
                // Test that numerical differentiation uses scaling.
                //
                // In order to test that we solve simple optimization
                // problem: min(x^2) with initial x equal to 0.0.
                //
                // We choose random DiffStep and S, then we check that
                // optimizer evaluates function at +-DiffStep*S only.
                //
                x = new double[1];
                s = new double[1];
                diffstep = math.randomreal()*1.0E-6;
                s[0] = Math.Exp(math.randomreal()*4-2);
                x[0] = 0;
                mincg.mincgcreatef(1, x, diffstep, state);
                mincg.mincgsetcond(state, 1.0E-6, 0, 0, 0);
                mincg.mincgsetscale(state, s);
                v = 0;
                while( mincg.mincgiteration(state) )
                {
                    state.f = math.sqr(state.x[0]);
                    v = Math.Max(v, Math.Abs(state.x[0]));
                }
                mincg.mincgresults(state, ref x, rep);
                r = v/(s[0]*diffstep);
                err = err | (double)(Math.Abs(Math.Log(r)))>(double)(Math.Log(1+1000*math.machineepsilon));
                
                //
                // Test maximum step
                //
                n = 1;
                x = new double[n];
                x[0] = 100;
                stpmax = 0.05+0.05*math.randomreal();
                mincg.mincgcreate(n, x, state);
                mincg.mincgsetcond(state, 1.0E-9, 0, 0, 0);
                mincg.mincgsetstpmax(state, stpmax);
                mincg.mincgsetxrep(state, true);
                xprev = x[0];
                while( mincg.mincgiteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = Math.Exp(state.x[0])+Math.Exp(-state.x[0]);
                        state.g[0] = Math.Exp(state.x[0])-Math.Exp(-state.x[0]);
                        err = err | (double)(Math.Abs(state.x[0]-xprev))>(double)((1+Math.Sqrt(math.machineepsilon))*stpmax);
                    }
                    if( state.xupdated )
                    {
                        err = err | (double)(Math.Abs(state.x[0]-xprev))>(double)((1+Math.Sqrt(math.machineepsilon))*stpmax);
                        xprev = state.x[0];
                    }
                }
                
                //
                // Test correctness of the scaling:
                // * initial point is random point from [+1,+2]^N
                // * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
                // * we use random scaling matrix
                // * we test different variants of the preconditioning:
                //   0) unit preconditioner
                //   1) random diagonal from [0.01,100]
                //   2) scale preconditioner
                // * we set stringent stopping conditions (we try EpsG and EpsX)
                // * and we test that in the extremum stopping conditions are
                //   satisfied subject to the current scaling coefficients.
                //
                tmpeps = 1.0E-10;
                for(n=1; n<=10; n++)
                {
                    for(pkind=0; pkind<=2; pkind++)
                    {
                        x = new double[n];
                        xlast = new double[n];
                        a = new double[n];
                        s = new double[n];
                        h = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = math.randomreal()+1;
                            a[i] = Math.Exp(Math.Log(100)*(2*math.randomreal()-1));
                            s[i] = Math.Exp(Math.Log(100)*(2*math.randomreal()-1));
                            h[i] = Math.Exp(Math.Log(100)*(2*math.randomreal()-1));
                        }
                        mincg.mincgcreate(n, x, state);
                        mincg.mincgsetscale(state, s);
                        mincg.mincgsetxrep(state, true);
                        if( pkind==1 )
                        {
                            mincg.mincgsetprecdiag(state, h);
                        }
                        if( pkind==2 )
                        {
                            mincg.mincgsetprecscale(state);
                        }
                        
                        //
                        // Test gradient-based stopping condition
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = math.randomreal()+1;
                        }
                        mincg.mincgsetcond(state, tmpeps, 0, 0, 0);
                        mincg.mincgrestartfrom(state, x);
                        while( mincg.mincgiteration(state) )
                        {
                            if( state.needfg )
                            {
                                state.f = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    state.f = state.f+a[i]*Math.Pow(state.x[i], 4);
                                    state.g[i] = 4*a[i]*Math.Pow(state.x[i], 3);
                                }
                            }
                        }
                        mincg.mincgresults(state, ref x, rep);
                        if( rep.terminationtype<=0 )
                        {
                            err = true;
                            return;
                        }
                        v = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            v = v+math.sqr(s[i]*4*a[i]*Math.Pow(x[i], 3));
                        }
                        v = Math.Sqrt(v);
                        err = err | (double)(v)>(double)(tmpeps);
                        
                        //
                        // Test step-based stopping condition
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = math.randomreal()+1;
                        }
                        hasxlast = false;
                        mincg.mincgsetcond(state, 0, 0, tmpeps, 0);
                        mincg.mincgrestartfrom(state, x);
                        while( mincg.mincgiteration(state) )
                        {
                            if( state.needfg )
                            {
                                state.f = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    state.f = state.f+a[i]*Math.Pow(state.x[i], 4);
                                    state.g[i] = 4*a[i]*Math.Pow(state.x[i], 3);
                                }
                            }
                            if( state.xupdated )
                            {
                                if( hasxlast )
                                {
                                    lastscaledstep = 0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        lastscaledstep = lastscaledstep+math.sqr(state.x[i]-xlast[i])/math.sqr(s[i]);
                                    }
                                    lastscaledstep = Math.Sqrt(lastscaledstep);
                                }
                                else
                                {
                                    lastscaledstep = 0;
                                }
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    xlast[i_] = state.x[i_];
                                }
                                hasxlast = true;
                            }
                        }
                        mincg.mincgresults(state, ref x, rep);
                        if( rep.terminationtype<=0 )
                        {
                            err = true;
                            return;
                        }
                        err = err | (double)(lastscaledstep)>(double)(tmpeps);
                    }
                }
                
                //
                // Check correctness of the "trimming".
                //
                // Trimming is a technique which is used to help algorithm
                // cope with unbounded functions. In order to check this
                // technique we will try to solve following optimization
                // problem:
                //
                //     min f(x) subject to no constraints on X
                //            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
                //     f(x) = {
                //            { M, if x<=-0.999999 or x>=0.999999
                //
                // where c is either 1.0 or 1.0E+6, M is either 1.0E8, 1.0E20 or +INF
                // (we try different combinations)
                //
                for(ckind=0; ckind<=1; ckind++)
                {
                    for(mkind=0; mkind<=2; mkind++)
                    {
                        
                        //
                        // Choose c and M
                        //
                        if( ckind==0 )
                        {
                            vc = 1.0;
                        }
                        if( ckind==1 )
                        {
                            vc = 1.0E+6;
                        }
                        if( mkind==0 )
                        {
                            vm = 1.0E+8;
                        }
                        if( mkind==1 )
                        {
                            vm = 1.0E+20;
                        }
                        if( mkind==2 )
                        {
                            vm = Double.PositiveInfinity;
                        }
                        
                        //
                        // Create optimizer, solve optimization problem
                        //
                        epsg = 1.0E-6*vc;
                        x = new double[1];
                        x[0] = 0.0;
                        mincg.mincgcreate(1, x, state);
                        mincg.mincgsetcond(state, epsg, 0, 0, 0);
                        mincg.mincgsetcgtype(state, cgtype);
                        while( mincg.mincgiteration(state) )
                        {
                            if( state.needfg )
                            {
                                if( (double)(-0.999999)<(double)(state.x[0]) & (double)(state.x[0])<(double)(0.999999) )
                                {
                                    state.f = 1/(1-state.x[0])+1/(1+state.x[0])+vc*state.x[0];
                                    state.g[0] = 1/math.sqr(1-state.x[0])-1/math.sqr(1+state.x[0])+vc;
                                }
                                else
                                {
                                    state.f = vm;
                                }
                            }
                        }
                        mincg.mincgresults(state, ref x, rep);
                        if( rep.terminationtype<=0 )
                        {
                            err = true;
                            return;
                        }
                        err = err | (double)(Math.Abs(1/math.sqr(1-x[0])-1/math.sqr(1+x[0])+vc))>(double)(epsg);
                    }
                }
            }
        }


        /*************************************************************************
        Calculate test function #1
        *************************************************************************/
        private static void testfunc1(mincg.mincgstate state)
        {
            if( (double)(state.x[0])<(double)(100) )
            {
                if( state.needf | state.needfg )
                {
                    state.f = math.sqr(Math.Exp(state.x[0])-2)+math.sqr(state.x[1])+math.sqr(state.x[2]-state.x[0]);
                }
                if( state.needfg )
                {
                    state.g[0] = 2*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[0]-state.x[2]);
                    state.g[1] = 2*state.x[1];
                    state.g[2] = 2*(state.x[2]-state.x[0]);
                }
            }
            else
            {
                if( state.needf | state.needfg )
                {
                    state.f = Math.Sqrt(math.maxrealnumber);
                }
                if( state.needfg )
                {
                    state.g[0] = Math.Sqrt(math.maxrealnumber);
                    state.g[1] = 0;
                    state.g[2] = 0;
                }
            }
        }


        /*************************************************************************
        Calculate test function #2

        Simple variation of #1, much more nonlinear, which makes unlikely premature
        convergence of algorithm .
        *************************************************************************/
        private static void testfunc2(mincg.mincgstate state)
        {
            if( (double)(state.x[0])<(double)(100) )
            {
                if( state.needf | state.needfg )
                {
                    state.f = math.sqr(Math.Exp(state.x[0])-2)+math.sqr(math.sqr(state.x[1]))+math.sqr(state.x[2]-state.x[0]);
                }
                if( state.needfg )
                {
                    state.g[0] = 2*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[0]-state.x[2]);
                    state.g[1] = 4*state.x[1]*math.sqr(state.x[1]);
                    state.g[2] = 2*(state.x[2]-state.x[0]);
                }
            }
            else
            {
                if( state.needf | state.needfg )
                {
                    state.f = Math.Sqrt(math.maxrealnumber);
                }
                if( state.needfg )
                {
                    state.g[0] = Math.Sqrt(math.maxrealnumber);
                    state.g[1] = 0;
                    state.g[2] = 0;
                }
            }
        }


        /*************************************************************************
        Calculate test function #3

        Simple variation of #1, much more nonlinear, with non-zero value at minimum.
        It achieve two goals:
        * makes unlikely premature convergence of algorithm .
        * solves some issues with EpsF stopping condition which arise when
          F(minimum) is zero

        *************************************************************************/
        private static void testfunc3(mincg.mincgstate state)
        {
            double s = 0;

            s = 0.001;
            if( (double)(state.x[0])<(double)(100) )
            {
                if( state.needf | state.needfg )
                {
                    state.f = math.sqr(Math.Exp(state.x[0])-2)+math.sqr(math.sqr(state.x[1])+s)+math.sqr(state.x[2]-state.x[0]);
                }
                if( state.needfg )
                {
                    state.g[0] = 2*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[0]-state.x[2]);
                    state.g[1] = 2*(math.sqr(state.x[1])+s)*2*state.x[1];
                    state.g[2] = 2*(state.x[2]-state.x[0]);
                }
            }
            else
            {
                if( state.needf | state.needfg )
                {
                    state.f = Math.Sqrt(math.maxrealnumber);
                }
                if( state.needfg )
                {
                    state.g[0] = Math.Sqrt(math.maxrealnumber);
                    state.g[1] = 0;
                    state.g[2] = 0;
                }
            }
        }


        /*************************************************************************
        Calculate test function IIP2

        f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1)

        It has high condition number which makes fast convergence unlikely without
        good preconditioner.

        *************************************************************************/
        private static void calciip2(mincg.mincgstate state,
            int n)
        {
            int i = 0;

            if( state.needf | state.needfg )
            {
                state.f = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                if( state.needf | state.needfg )
                {
                    state.f = state.f+math.sqr(i*i+1)*math.sqr(state.x[i]);
                }
                if( state.needfg )
                {
                    state.g[i] = math.sqr(i*i+1)*2*state.x[i];
                }
            }
        }


        /*************************************************************************
        Calculate test function f(x) = 0.5*(x-x0)'*A*(x-x0), A = D+V'*Vd*V
        *************************************************************************/
        private static void calclowrank(mincg.mincgstate state,
            int n,
            int vcnt,
            double[] d,
            double[,] v,
            double[] vd,
            double[] x0)
        {
            int i = 0;
            int j = 0;
            double dx = 0;
            double t = 0;
            double t2 = 0;
            int i_ = 0;

            state.f = 0;
            for(i=0; i<=n-1; i++)
            {
                state.g[i] = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                dx = state.x[i]-x0[i];
                state.f = state.f+0.5*dx*d[i]*dx;
                state.g[i] = state.g[i]+d[i]*dx;
            }
            for(i=0; i<=vcnt-1; i++)
            {
                t = 0;
                for(j=0; j<=n-1; j++)
                {
                    t = t+v[i,j]*(state.x[j]-x0[j]);
                }
                state.f = state.f+0.5*t*vd[i]*t;
                t2 = t*vd[i];
                for(i_=0; i_<=n-1;i_++)
                {
                    state.g[i_] = state.g[i_] + t2*v[i,i_];
                }
            }
        }


        /*************************************************************************
        This function tests preconditioning

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testpreconditioning(ref bool err)
        {
            int pass = 0;
            int n = 0;
            double[] x = new double[0];
            double[] x0 = new double[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int vs = 0;
            double[,] v = new double[0,0];
            double[] vd = new double[0];
            double[] d = new double[0];
            double[] s = new double[0];
            int cntb1 = 0;
            int cntg1 = 0;
            int cntb2 = 0;
            int cntg2 = 0;
            double epsg = 0;
            double[] diagh = new double[0];
            mincg.mincgstate state = new mincg.mincgstate();
            mincg.mincgreport rep = new mincg.mincgreport();
            int cgtype = 0;

            k = 50;
            epsg = 1.0E-10;
            for(cgtype=-1; cgtype<=1; cgtype++)
            {
                
                //
                // Preconditioner test 1.
                //
                // If
                // * B1 is default preconditioner
                // * G1 is diagonal precomditioner based on approximate diagonal of Hessian matrix
                // then "bad" preconditioner is worse than "good" one.
                // "Worse" means more iterations to converge.
                //
                //
                // We test it using f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1).
                //
                // N        - problem size
                // K        - number of repeated passes (should be large enough to average out random factors)
                //
                for(n=10; n<=15; n++)
                {
                    x = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 0;
                    }
                    mincg.mincgcreate(n, x, state);
                    mincg.mincgsetcgtype(state, cgtype);
                    
                    //
                    // Test it with default preconditioner
                    //
                    mincg.mincgsetprecdefault(state);
                    cntb1 = 0;
                    for(pass=0; pass<=k-1; pass++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = 2*math.randomreal()-1;
                        }
                        mincg.mincgrestartfrom(state, x);
                        while( mincg.mincgiteration(state) )
                        {
                            calciip2(state, n);
                        }
                        mincg.mincgresults(state, ref x, rep);
                        cntb1 = cntb1+rep.iterationscount;
                        err = err | rep.terminationtype<=0;
                    }
                    
                    //
                    // Test it with perturbed diagonal preconditioner
                    //
                    diagh = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        diagh[i] = 2*math.sqr(i*i+1)*(0.8+0.4*math.randomreal());
                    }
                    mincg.mincgsetprecdiag(state, diagh);
                    cntg1 = 0;
                    for(pass=0; pass<=k-1; pass++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = 2*math.randomreal()-1;
                        }
                        mincg.mincgrestartfrom(state, x);
                        while( mincg.mincgiteration(state) )
                        {
                            calciip2(state, n);
                        }
                        mincg.mincgresults(state, ref x, rep);
                        cntg1 = cntg1+rep.iterationscount;
                        err = err | rep.terminationtype<=0;
                    }
                    
                    //
                    // Compare
                    //
                    err = err | cntb1<cntg1;
                }
                
                //
                // Preconditioner test 2.
                //
                // If
                // * B1 is default preconditioner
                // * G1 is low rank exact preconditioner
                // then "bad" preconditioner is worse than "good" one.
                // "Worse" means more iterations to converge.
                //
                // Target function is f(x) = 0.5*(x-x0)'*A*(x-x0), A = D+V'*Vd*V
                //
                // N        - problem size
                // K        - number of repeated passes (should be large enough to average out random factors)
                //
                for(n=10; n<=15; n++)
                {
                    for(vs=0; vs<=5; vs++)
                    {
                        x = new double[n];
                        x0 = new double[n];
                        d = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = 0;
                            x0[i] = 2*math.randomreal()-1;
                            d[i] = Math.Exp(2*math.randomreal());
                        }
                        if( vs>0 )
                        {
                            v = new double[vs, n];
                            vd = new double[vs];
                            for(i=0; i<=vs-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    v[i,j] = 2*math.randomreal()-1;
                                }
                                vd[i] = Math.Exp(2*math.randomreal());
                            }
                        }
                        mincg.mincgcreate(n, x, state);
                        mincg.mincgsetcgtype(state, cgtype);
                        
                        //
                        // Test it with default preconditioner
                        //
                        mincg.mincgsetprecdefault(state);
                        cntb1 = 0;
                        for(pass=0; pass<=k-1; pass++)
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                            mincg.mincgrestartfrom(state, x);
                            while( mincg.mincgiteration(state) )
                            {
                                calclowrank(state, n, vs, d, v, vd, x0);
                            }
                            mincg.mincgresults(state, ref x, rep);
                            cntb1 = cntb1+rep.iterationscount;
                            err = err | rep.terminationtype<=0;
                        }
                        
                        //
                        // Test it with low rank preconditioner
                        //
                        mincg.mincgsetpreclowrankfast(state, d, vd, v, vs);
                        cntg1 = 0;
                        for(pass=0; pass<=k-1; pass++)
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                            mincg.mincgrestartfrom(state, x);
                            while( mincg.mincgiteration(state) )
                            {
                                calclowrank(state, n, vs, d, v, vd, x0);
                            }
                            mincg.mincgresults(state, ref x, rep);
                            cntg1 = cntg1+rep.iterationscount;
                            err = err | rep.terminationtype<=0;
                        }
                        
                        //
                        // Compare
                        //
                        err = err | cntb1<cntg1;
                    }
                }
                
                //
                // Preconditioner test 3.
                //
                // If
                // * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
                // * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
                // then B2 is worse than G2.
                // "Worse" means more iterations to converge.
                //
                for(n=10; n<=15; n++)
                {
                    x = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 0;
                    }
                    mincg.mincgcreate(n, x, state);
                    s = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        s[i] = 1/Math.Sqrt(2*Math.Pow(i*i+1, 2)*(0.8+0.4*math.randomreal()));
                    }
                    mincg.mincgsetprecdefault(state);
                    mincg.mincgsetscale(state, s);
                    cntb2 = 0;
                    for(pass=0; pass<=k-1; pass++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = 2*math.randomreal()-1;
                        }
                        mincg.mincgrestartfrom(state, x);
                        while( mincg.mincgiteration(state) )
                        {
                            calciip2(state, n);
                        }
                        mincg.mincgresults(state, ref x, rep);
                        cntb2 = cntb2+rep.iterationscount;
                        err = err | rep.terminationtype<=0;
                    }
                    mincg.mincgsetprecscale(state);
                    mincg.mincgsetscale(state, s);
                    cntg2 = 0;
                    for(pass=0; pass<=k-1; pass++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = 2*math.randomreal()-1;
                        }
                        mincg.mincgrestartfrom(state, x);
                        while( mincg.mincgiteration(state) )
                        {
                            calciip2(state, n);
                        }
                        mincg.mincgresults(state, ref x, rep);
                        cntg2 = cntg2+rep.iterationscount;
                        err = err | rep.terminationtype<=0;
                    }
                    err = err | cntb2<cntg2;
                }
            }
        }


    }
    public class testminbleicunit
    {
        public static bool testminbleic(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool feasibilityerrors = new bool();
            bool othererrors = new bool();
            bool precerrors = new bool();
            bool interrors = new bool();
            bool converrors = new bool();

            waserrors = false;
            feasibilityerrors = false;
            othererrors = false;
            precerrors = false;
            interrors = false;
            converrors = false;
            testfeasibility(ref feasibilityerrors, ref converrors, ref interrors);
            testother(ref othererrors);
            testconv(ref converrors);
            testpreconditioning(ref precerrors);
            
            //
            // end
            //
            waserrors = (((feasibilityerrors | othererrors) | converrors) | interrors) | precerrors;
            if( !silent )
            {
                System.Console.Write("TESTING BLEIC OPTIMIZATION");
                System.Console.WriteLine();
                System.Console.Write("FEASIBILITY PROPERTIES:                   ");
                if( feasibilityerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("PRECONDITIONING:                          ");
                if( precerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("OTHER PROPERTIES:                         ");
                if( othererrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("CONVERGENCE PROPERTIES:                   ");
                if( converrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("INTERNAL ERRORS:                          ");
                if( interrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Checks that X is bounded with respect to BndL/BndU.

        If it is not, True is assigned to the Err variable (which is not changed
        otherwise).
        *************************************************************************/
        private static void checkbounds(double[] x,
            double[] bndl,
            double[] bndu,
            int n,
            ref bool err)
        {
            int i = 0;

            for(i=0; i<=n-1; i++)
            {
                if( (double)(x[i])<(double)(bndl[i]) | (double)(x[i])>(double)(bndu[i]) )
                {
                    err = true;
                }
            }
        }


        /*************************************************************************
        Calculate test function IIP2

        f(x) = sum( ((i*i+1)^FK*x[i])^2, i=0..N-1)

        It has high condition number which makes fast convergence unlikely without
        good preconditioner.

        *************************************************************************/
        private static void calciip2(minbleic.minbleicstate state,
            int n,
            int fk)
        {
            int i = 0;

            if( state.needfg )
            {
                state.f = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                if( state.needfg )
                {
                    state.f = state.f+Math.Pow(i*i+1, 2*fk)*math.sqr(state.x[i]);
                    state.g[i] = Math.Pow(i*i+1, 2*fk)*2*state.x[i];
                }
            }
        }


        /*************************************************************************
        This function test feasibility properties.
        It launches a sequence of problems and examines their solutions.
        Most of the attention is directed towards feasibility properties,
        although we make some quick checks to ensure that actual solution is found.

        On failure sets FeasErr (or ConvErr, depending on failure type) to True,
        or leaves it unchanged otherwise.

        IntErr is set to True on internal errors (errors in the control flow).
        *************************************************************************/
        private static void testfeasibility(ref bool feaserr,
            ref bool converr,
            ref bool interr)
        {
            int pkind = 0;
            int preckind = 0;
            int passcount = 0;
            int pass = 0;
            int n = 0;
            int nmax = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int p = 0;
            double v = 0;
            double v2 = 0;
            double v3 = 0;
            double vv = 0;
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] x = new double[0];
            double[] g = new double[0];
            double[] x0 = new double[0];
            double[] xs = new double[0];
            double[,] c = new double[0,0];
            int[] ct = new int[0];
            minbleic.minbleicstate state = new minbleic.minbleicstate();
            double epsc = 0;
            double epsg = 0;
            minbleic.minbleicreport rep = new minbleic.minbleicreport();
            int dkind = 0;
            double diffstep = 0;
            int i_ = 0;

            nmax = 5;
            epsc = 1.0E-4;
            epsg = 1.0E-8;
            passcount = 10;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Test problem 1:
                // * no boundary and inequality constraints
                // * randomly generated plane as equality constraint
                // * random point (not necessarily on the plane)
                // * f = |x|^P, P = {2, 4} is used as target function
                // * preconditioner is chosen at random (we just want to be
                //   sure that preconditioning won't prevent us from converging
                //   to the feasible point):
                //   * unit preconditioner
                //   * random diagonal-based preconditioner
                //   * random scale-based preconditioner
                // * either analytic gradient or numerical differentiation are used
                // * we check that after work is over we are on the plane and
                //   that we are in the stationary point of constrained F
                //
                diffstep = 1.0E-6;
                for(dkind=0; dkind<=1; dkind++)
                {
                    for(preckind=0; preckind<=2; preckind++)
                    {
                        for(pkind=1; pkind<=2; pkind++)
                        {
                            for(n=1; n<=nmax; n++)
                            {
                                
                                //
                                // Generate X, BL, BU, CT and left part of C.
                                //
                                // Right part of C is generated using somewhat complex algo:
                                // * we generate random vector and multiply it by C.
                                // * result is used as the right part.
                                // * calculations are done on the fly, vector itself is not stored
                                // We use such algo to be sure that our system is consistent.
                                //
                                p = 2*pkind;
                                x = new double[n];
                                g = new double[n];
                                c = new double[1, n+1];
                                ct = new int[1];
                                c[0,n] = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    x[i] = 2*math.randomreal()-1;
                                    c[0,i] = 2*math.randomreal()-1;
                                    v = 2*math.randomreal()-1;
                                    c[0,n] = c[0,n]+c[0,i]*v;
                                }
                                ct[0] = 0;
                                
                                //
                                // Create and optimize
                                //
                                if( dkind==0 )
                                {
                                    minbleic.minbleiccreate(n, x, state);
                                }
                                if( dkind==1 )
                                {
                                    minbleic.minbleiccreatef(n, x, diffstep, state);
                                }
                                minbleic.minbleicsetlc(state, c, ct, 1);
                                minbleic.minbleicsetinnercond(state, epsg, 0.0, 0.0);
                                minbleic.minbleicsetoutercond(state, epsc, epsc);
                                setrandompreconditioner(state, n, preckind);
                                while( minbleic.minbleiciteration(state) )
                                {
                                    if( state.needf | state.needfg )
                                    {
                                        state.f = 0;
                                    }
                                    for(i=0; i<=n-1; i++)
                                    {
                                        if( state.needf | state.needfg )
                                        {
                                            state.f = state.f+Math.Pow(state.x[i], p);
                                        }
                                        if( state.needfg )
                                        {
                                            state.g[i] = p*Math.Pow(state.x[i], p-1);
                                        }
                                    }
                                }
                                minbleic.minbleicresults(state, ref x, rep);
                                if( rep.terminationtype<=0 )
                                {
                                    converr = true;
                                    return;
                                }
                                
                                //
                                // Test feasibility of solution
                                //
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += c[0,i_]*x[i_];
                                }
                                feaserr = feaserr | (double)(Math.Abs(v-c[0,n]))>(double)(epsc);
                                
                                //
                                // if C is nonzero, test that result is
                                // a stationary point of constrained F.
                                //
                                // NOTE: this check is done only if C is nonzero
                                //
                                vv = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    vv += c[0,i_]*c[0,i_];
                                }
                                if( (double)(vv)!=(double)(0) )
                                {
                                    
                                    //
                                    // Calculate gradient at the result
                                    // Project gradient into C
                                    // Check projected norm
                                    //
                                    for(i=0; i<=n-1; i++)
                                    {
                                        g[i] = p*Math.Pow(x[i], p-1);
                                    }
                                    v2 = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v2 += c[0,i_]*c[0,i_];
                                    }
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += c[0,i_]*g[i_];
                                    }
                                    vv = v/v2;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        g[i_] = g[i_] - vv*c[0,i_];
                                    }
                                    v3 = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v3 += g[i_]*g[i_];
                                    }
                                    converr = converr | (double)(Math.Sqrt(v3))>(double)(0.001);
                                }
                            }
                        }
                    }
                }
                
                //
                // Test problem 2 (multiple equality constraints):
                // * 1<=N<=NMax, 1<=K<=N
                // * no boundary constraints
                // * N-dimensional space
                // * randomly generated point xs
                // * K randomly generated hyperplanes which all pass through xs
                //   define K equality constraints: (a[k],x)=b[k]
                // * preconditioner is chosen at random (we just want to be
                //   sure that preconditioning won't prevent us from converging
                //   to the feasible point):
                //   * unit preconditioner
                //   * random diagonal-based preconditioner
                //   * random scale-based preconditioner
                // * f(x) = |x-x0|^2, x0 = xs+a[0]
                // * either analytic gradient or numerical differentiation are used
                // * extremum of f(x) is exactly xs because:
                //   * xs is the closest point in the plane defined by (a[0],x)=b[0]
                //   * xs is feasible by definition
                //
                diffstep = 1.0E-6;
                for(dkind=0; dkind<=1; dkind++)
                {
                    for(preckind=0; preckind<=2; preckind++)
                    {
                        for(n=2; n<=nmax; n++)
                        {
                            for(k=1; k<=n; k++)
                            {
                                
                                //
                                // Generate X, X0, XS, BL, BU, CT and left part of C.
                                //
                                // Right part of C is generated using somewhat complex algo:
                                // * we generate random vector and multiply it by C.
                                // * result is used as the right part.
                                // * calculations are done on the fly, vector itself is not stored
                                // We use such algo to be sure that our system is consistent.
                                //
                                p = 2*pkind;
                                x = new double[n];
                                x0 = new double[n];
                                xs = new double[n];
                                g = new double[n];
                                c = new double[k, n+1];
                                ct = new int[k];
                                c[0,n] = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    x[i] = 2*math.randomreal()-1;
                                    xs[i] = 2*math.randomreal()-1;
                                }
                                for(i=0; i<=k-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        c[i,j] = 2*math.randomreal()-1;
                                    }
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += c[i,i_]*xs[i_];
                                    }
                                    c[i,n] = v;
                                    ct[i] = 0;
                                }
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    x0[i_] = xs[i_];
                                }
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    x0[i_] = x0[i_] + c[0,i_];
                                }
                                
                                //
                                // Create and optimize
                                //
                                if( dkind==0 )
                                {
                                    minbleic.minbleiccreate(n, x, state);
                                }
                                if( dkind==1 )
                                {
                                    minbleic.minbleiccreatef(n, x, diffstep, state);
                                }
                                minbleic.minbleicsetlc(state, c, ct, k);
                                minbleic.minbleicsetinnercond(state, epsg, 0.0, 0.0);
                                minbleic.minbleicsetoutercond(state, epsc, epsc);
                                setrandompreconditioner(state, n, preckind);
                                while( minbleic.minbleiciteration(state) )
                                {
                                    if( state.needf | state.needfg )
                                    {
                                        state.f = 0;
                                    }
                                    for(i=0; i<=n-1; i++)
                                    {
                                        if( state.needf | state.needfg )
                                        {
                                            state.f = state.f+math.sqr(state.x[i]-x0[i]);
                                        }
                                        if( state.needfg )
                                        {
                                            state.g[i] = 2*(state.x[i]-x0[i]);
                                        }
                                    }
                                }
                                minbleic.minbleicresults(state, ref x, rep);
                                if( rep.terminationtype<=0 )
                                {
                                    converr = true;
                                    return;
                                }
                                
                                //
                                // check feasiblity properties
                                //
                                for(i=0; i<=k-1; i++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += c[i,i_]*x[i_];
                                    }
                                    feaserr = feaserr | (double)(Math.Abs(v-c[i,n]))>(double)(epsc);
                                }
                                
                                //
                                // Compare with XS
                                //
                                v = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    v = v+math.sqr(x[i]-xs[i]);
                                }
                                v = Math.Sqrt(v);
                                converr = converr | (double)(Math.Abs(v))>(double)(0.001);
                            }
                        }
                    }
                }
                
                //
                // Another simple problem:
                // * bound constraints 0 <= x[i] <= 1
                // * no linear constraints
                // * preconditioner is chosen at random (we just want to be
                //   sure that preconditioning won't prevent us from converging
                //   to the feasible point):
                //   * unit preconditioner
                //   * random diagonal-based preconditioner
                //   * random scale-based preconditioner
                // * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
                // * with such simple boundaries and function it is easy to find
                //   analytic form of solution: S[i] = bound(x0[i], 0, 1)
                // * we also check that both final solution and subsequent iterates
                //   are strictly feasible
                //
                diffstep = 1.0E-6;
                for(dkind=0; dkind<=1; dkind++)
                {
                    for(preckind=0; preckind<=2; preckind++)
                    {
                        for(pkind=1; pkind<=2; pkind++)
                        {
                            for(n=1; n<=nmax; n++)
                            {
                                
                                //
                                // Generate X, BL, BU.
                                //
                                p = 2*pkind;
                                bl = new double[n];
                                bu = new double[n];
                                x = new double[n];
                                x0 = new double[n];
                                for(i=0; i<=n-1; i++)
                                {
                                    bl[i] = 0;
                                    bu[i] = 1;
                                    x[i] = math.randomreal();
                                    x0[i] = 3*math.randomreal()-1;
                                }
                                
                                //
                                // Create and optimize
                                //
                                if( dkind==0 )
                                {
                                    minbleic.minbleiccreate(n, x, state);
                                }
                                if( dkind==1 )
                                {
                                    minbleic.minbleiccreatef(n, x, diffstep, state);
                                }
                                minbleic.minbleicsetbc(state, bl, bu);
                                minbleic.minbleicsetinnercond(state, epsg, 0.0, 0.0);
                                minbleic.minbleicsetoutercond(state, epsc, epsc);
                                setrandompreconditioner(state, n, preckind);
                                while( minbleic.minbleiciteration(state) )
                                {
                                    if( state.needf | state.needfg )
                                    {
                                        state.f = 0;
                                    }
                                    for(i=0; i<=n-1; i++)
                                    {
                                        if( state.needf | state.needfg )
                                        {
                                            state.f = state.f+Math.Pow(state.x[i]-x0[i], p);
                                        }
                                        if( state.needfg )
                                        {
                                            state.g[i] = p*Math.Pow(state.x[i]-x0[i], p-1);
                                        }
                                        feaserr = feaserr | (double)(state.x[i])<(double)(0.0);
                                        feaserr = feaserr | (double)(state.x[i])>(double)(1.0);
                                    }
                                }
                                minbleic.minbleicresults(state, ref x, rep);
                                if( rep.terminationtype<=0 )
                                {
                                    converr = true;
                                    return;
                                }
                                
                                //
                                // * compare solution with analytic one
                                // * check feasibility
                                //
                                for(i=0; i<=n-1; i++)
                                {
                                    converr = converr | (double)(Math.Abs(x[i]-apserv.boundval(x0[i], 0.0, 1.0)))>(double)(0.01);
                                    feaserr = feaserr | (double)(x[i])<(double)(0.0);
                                    feaserr = feaserr | (double)(x[i])>(double)(1.0);
                                }
                            }
                        }
                    }
                }
                
                //
                // Same as previous one, but with bound constraints posed
                // as general linear ones:
                // * no bound constraints
                // * 2*N linear constraints 0 <= x[i] <= 1
                // * preconditioner is chosen at random (we just want to be
                //   sure that preconditioning won't prevent us from converging
                //   to the feasible point):
                //   * unit preconditioner
                //   * random diagonal-based preconditioner
                //   * random scale-based preconditioner
                // * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
                // * with such simple constraints and function it is easy to find
                //   analytic form of solution: S[i] = bound(x0[i], 0, 1).
                // * however, we can't guarantee that solution is strictly feasible
                //   with respect to nonlinearity constraint, so we check
                //   for approximate feasibility.
                //
                for(preckind=0; preckind<=2; preckind++)
                {
                    for(pkind=1; pkind<=2; pkind++)
                    {
                        for(n=1; n<=nmax; n++)
                        {
                            
                            //
                            // Generate X, BL, BU.
                            //
                            p = 2*pkind;
                            x = new double[n];
                            x0 = new double[n];
                            c = new double[2*n, n+1];
                            ct = new int[2*n];
                            for(i=0; i<=n-1; i++)
                            {
                                x[i] = math.randomreal();
                                x0[i] = 3*math.randomreal()-1;
                                for(j=0; j<=n; j++)
                                {
                                    c[2*i+0,j] = 0;
                                    c[2*i+1,j] = 0;
                                }
                                c[2*i+0,i] = 1;
                                c[2*i+0,n] = 0;
                                ct[2*i+0] = 1;
                                c[2*i+1,i] = 1;
                                c[2*i+1,n] = 1;
                                ct[2*i+1] = -1;
                            }
                            
                            //
                            // Create and optimize
                            //
                            minbleic.minbleiccreate(n, x, state);
                            minbleic.minbleicsetlc(state, c, ct, 2*n);
                            minbleic.minbleicsetinnercond(state, epsg, 0.0, 0.0);
                            minbleic.minbleicsetoutercond(state, epsc, epsc);
                            setrandompreconditioner(state, n, preckind);
                            while( minbleic.minbleiciteration(state) )
                            {
                                if( state.needfg )
                                {
                                    state.f = 0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        state.f = state.f+Math.Pow(state.x[i]-x0[i], p);
                                        state.g[i] = p*Math.Pow(state.x[i]-x0[i], p-1);
                                    }
                                    continue;
                                }
                                
                                //
                                // Unknown protocol specified
                                //
                                interr = true;
                                return;
                            }
                            minbleic.minbleicresults(state, ref x, rep);
                            if( rep.terminationtype<=0 )
                            {
                                converr = true;
                                return;
                            }
                            
                            //
                            // * compare solution with analytic one
                            // * check feasibility
                            //
                            for(i=0; i<=n-1; i++)
                            {
                                converr = converr | (double)(Math.Abs(x[i]-apserv.boundval(x0[i], 0.0, 1.0)))>(double)(0.05);
                                feaserr = feaserr | (double)(x[i])<(double)(0.0-epsc);
                                feaserr = feaserr | (double)(x[i])>(double)(1.0+epsc);
                            }
                        }
                    }
                }
                
                //
                // Infeasible problem:
                // * all bound constraints are 0 <= x[i] <= 1 except for one
                // * that one is 0 >= x[i] >= 1
                // * no linear constraints
                // * preconditioner is chosen at random (we just want to be
                //   sure that preconditioning won't prevent us from detecting
                //   infeasible point):
                //   * unit preconditioner
                //   * random diagonal-based preconditioner
                //   * random scale-based preconditioner
                // * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
                // * algorithm must return correct error code on such problem
                //
                for(preckind=0; preckind<=2; preckind++)
                {
                    for(pkind=1; pkind<=2; pkind++)
                    {
                        for(n=1; n<=nmax; n++)
                        {
                            
                            //
                            // Generate X, BL, BU.
                            //
                            p = 2*pkind;
                            bl = new double[n];
                            bu = new double[n];
                            x = new double[n];
                            x0 = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                bl[i] = 0;
                                bu[i] = 1;
                                x[i] = math.randomreal();
                                x0[i] = 3*math.randomreal()-1;
                            }
                            i = math.randominteger(n);
                            bl[i] = 1;
                            bu[i] = 0;
                            
                            //
                            // Create and optimize
                            //
                            minbleic.minbleiccreate(n, x, state);
                            minbleic.minbleicsetbc(state, bl, bu);
                            minbleic.minbleicsetinnercond(state, epsg, 0.0, 0.0);
                            minbleic.minbleicsetoutercond(state, epsc, epsc);
                            setrandompreconditioner(state, n, preckind);
                            while( minbleic.minbleiciteration(state) )
                            {
                                if( state.needfg )
                                {
                                    state.f = 0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        state.f = state.f+Math.Pow(state.x[i]-x0[i], p);
                                        state.g[i] = p*Math.Pow(state.x[i]-x0[i], p-1);
                                    }
                                    continue;
                                }
                                
                                //
                                // Unknown protocol specified
                                //
                                interr = true;
                                return;
                            }
                            minbleic.minbleicresults(state, ref x, rep);
                            feaserr = feaserr | rep.terminationtype!=-3;
                        }
                    }
                }
                
                //
                // Infeasible problem (2):
                // * no bound and inequality constraints
                // * 1<=K<=N arbitrary equality constraints
                // * (K+1)th constraint which is equal to the first constraint a*x=c,
                //   but with c:=c+1. I.e. we have both a*x=c and a*x=c+1, which can't
                //   be true (other constraints may be inconsistent too, but we don't
                //   have to check it).
                // * preconditioner is chosen at random (we just want to be
                //   sure that preconditioning won't prevent us from detecting
                //   infeasible point):
                //   * unit preconditioner
                //   * random diagonal-based preconditioner
                //   * random scale-based preconditioner
                // * F(x) = |x|^P, where P={2,4}
                // * algorithm must return correct error code on such problem
                //
                for(preckind=0; preckind<=2; preckind++)
                {
                    for(pkind=1; pkind<=2; pkind++)
                    {
                        for(n=1; n<=nmax; n++)
                        {
                            for(k=1; k<=n; k++)
                            {
                                
                                //
                                // Generate X, BL, BU.
                                //
                                p = 2*pkind;
                                x = new double[n];
                                c = new double[k+1, n+1];
                                ct = new int[k+1];
                                for(i=0; i<=n-1; i++)
                                {
                                    x[i] = math.randomreal();
                                }
                                for(i=0; i<=k-1; i++)
                                {
                                    for(j=0; j<=n; j++)
                                    {
                                        c[i,j] = 2*math.randomreal()-1;
                                    }
                                    ct[i] = 0;
                                }
                                ct[k] = 0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    c[k,i_] = c[0,i_];
                                }
                                c[k,n] = c[0,n]+1;
                                
                                //
                                // Create and optimize
                                //
                                minbleic.minbleiccreate(n, x, state);
                                minbleic.minbleicsetlc(state, c, ct, k+1);
                                minbleic.minbleicsetinnercond(state, epsg, 0.0, 0.0);
                                minbleic.minbleicsetoutercond(state, epsc, epsc);
                                setrandompreconditioner(state, n, preckind);
                                while( minbleic.minbleiciteration(state) )
                                {
                                    if( state.needfg )
                                    {
                                        state.f = 0;
                                        for(i=0; i<=n-1; i++)
                                        {
                                            state.f = state.f+Math.Pow(state.x[i], p);
                                            state.g[i] = p*Math.Pow(state.x[i], p-1);
                                        }
                                        continue;
                                    }
                                    
                                    //
                                    // Unknown protocol specified
                                    //
                                    interr = true;
                                    return;
                                }
                                minbleic.minbleicresults(state, ref x, rep);
                                feaserr = feaserr | rep.terminationtype!=-3;
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        This function additional properties.

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testother(ref bool err)
        {
            int passcount = 0;
            int pass = 0;
            int n = 0;
            int nmax = 0;
            int i = 0;
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] x = new double[0];
            double[] xf = new double[0];
            double[] xlast = new double[0];
            double[] a = new double[0];
            double[] s = new double[0];
            double[] h = new double[0];
            double[,] c = new double[0,0];
            int[] ct = new int[0];
            double fprev = 0;
            double xprev = 0;
            double stpmax = 0;
            double v = 0;
            int pkind = 0;
            int ckind = 0;
            int mkind = 0;
            double vc = 0;
            double vm = 0;
            minbleic.minbleicstate state = new minbleic.minbleicstate();
            double epsc = 0;
            double epsg = 0;
            double tmpeps = 0;
            minbleic.minbleicreport rep = new minbleic.minbleicreport();
            double diffstep = 0;
            int dkind = 0;
            bool wasf = new bool();
            bool wasfg = new bool();
            double r = 0;
            int i_ = 0;

            nmax = 5;
            epsc = 1.0E-4;
            epsg = 1.0E-8;
            passcount = 10;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Test reports:
                // * first value must be starting point
                // * last value must be last point
                //
                n = 50;
                x = new double[n];
                xlast = new double[n];
                bl = new double[n];
                bu = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 10;
                    bl[i] = 2*math.randomreal()-1;
                    bu[i] = Double.PositiveInfinity;
                }
                minbleic.minbleiccreate(n, x, state);
                minbleic.minbleicsetbc(state, bl, bu);
                minbleic.minbleicsetinnercond(state, 0, 0, 0);
                minbleic.minbleicsetmaxits(state, 10);
                minbleic.minbleicsetoutercond(state, 1.0E-64, 1.0E-64);
                minbleic.minbleicsetxrep(state, true);
                fprev = math.maxrealnumber;
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+math.sqr((1+i)*state.x[i]);
                            state.g[i] = 2*(1+i)*state.x[i];
                        }
                    }
                    if( state.xupdated )
                    {
                        if( (double)(fprev)==(double)(math.maxrealnumber) )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                err = err | (double)(state.x[i])!=(double)(x[i]);
                            }
                        }
                        fprev = state.f;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            xlast[i_] = state.x[i_];
                        }
                    }
                }
                minbleic.minbleicresults(state, ref x, rep);
                for(i=0; i<=n-1; i++)
                {
                    err = err | (double)(x[i])!=(double)(xlast[i]);
                }
                
                //
                // Test differentiation vs. analytic gradient
                // (first one issues NeedF requests, second one issues NeedFG requests)
                //
                n = 50;
                diffstep = 1.0E-6;
                for(dkind=0; dkind<=1; dkind++)
                {
                    x = new double[n];
                    xlast = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 1;
                    }
                    if( dkind==0 )
                    {
                        minbleic.minbleiccreate(n, x, state);
                    }
                    if( dkind==1 )
                    {
                        minbleic.minbleiccreatef(n, x, diffstep, state);
                    }
                    minbleic.minbleicsetinnercond(state, 1.0E-10, 0, 0);
                    minbleic.minbleicsetoutercond(state, 1.0E-6, 1.0E-6);
                    wasf = false;
                    wasfg = false;
                    while( minbleic.minbleiciteration(state) )
                    {
                        if( state.needf | state.needfg )
                        {
                            state.f = 0;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            if( state.needf | state.needfg )
                            {
                                state.f = state.f+math.sqr((1+i)*state.x[i]);
                            }
                            if( state.needfg )
                            {
                                state.g[i] = 2*(1+i)*state.x[i];
                            }
                        }
                        wasf = wasf | state.needf;
                        wasfg = wasfg | state.needfg;
                    }
                    minbleic.minbleicresults(state, ref x, rep);
                    if( dkind==0 )
                    {
                        err = (err | wasf) | !wasfg;
                    }
                    if( dkind==1 )
                    {
                        err = (err | !wasf) | wasfg;
                    }
                }
                
                //
                // Test that numerical differentiation uses scaling.
                //
                // In order to test that we solve simple optimization
                // problem: min(x^2) with initial x equal to 0.0.
                //
                // We choose random DiffStep and S, then we check that
                // optimizer evaluates function at +-DiffStep*S only.
                //
                x = new double[1];
                s = new double[1];
                diffstep = math.randomreal()*1.0E-6;
                s[0] = Math.Exp(math.randomreal()*4-2);
                x[0] = 0;
                minbleic.minbleiccreatef(1, x, diffstep, state);
                minbleic.minbleicsetinnercond(state, 1.0E-6, 0, 0);
                minbleic.minbleicsetscale(state, s);
                v = 0;
                while( minbleic.minbleiciteration(state) )
                {
                    state.f = math.sqr(state.x[0]);
                    v = Math.Max(v, Math.Abs(state.x[0]));
                }
                minbleic.minbleicresults(state, ref x, rep);
                r = v/(s[0]*diffstep);
                err = err | (double)(Math.Abs(Math.Log(r)))>(double)(Math.Log(1+1000*math.machineepsilon));
                
                //
                // Test stpmax
                //
                n = 1;
                x = new double[n];
                bl = new double[n];
                bu = new double[n];
                x[0] = 100;
                bl[0] = 2*math.randomreal()-1;
                bu[0] = Double.PositiveInfinity;
                stpmax = 0.05+0.05*math.randomreal();
                minbleic.minbleiccreate(n, x, state);
                minbleic.minbleicsetbc(state, bl, bu);
                minbleic.minbleicsetinnercond(state, epsg, 0, 0);
                minbleic.minbleicsetoutercond(state, epsc, epsc);
                minbleic.minbleicsetxrep(state, true);
                minbleic.minbleicsetstpmax(state, stpmax);
                xprev = x[0];
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = Math.Exp(state.x[0])+Math.Exp(-state.x[0]);
                        state.g[0] = Math.Exp(state.x[0])-Math.Exp(-state.x[0]);
                        err = err | (double)(Math.Abs(state.x[0]-xprev))>(double)((1+Math.Sqrt(math.machineepsilon))*stpmax);
                    }
                    if( state.xupdated )
                    {
                        err = err | (double)(Math.Abs(state.x[0]-xprev))>(double)((1+Math.Sqrt(math.machineepsilon))*stpmax);
                        xprev = state.x[0];
                    }
                }
                
                //
                // Ability to solve problems with function which is unbounded from below
                //
                n = 1;
                x = new double[n];
                bl = new double[n];
                bu = new double[n];
                bl[0] = 4*math.randomreal()+1;
                bu[0] = bl[0]+1;
                x[0] = 0.5*(bl[0]+bu[0]);
                minbleic.minbleiccreate(n, x, state);
                minbleic.minbleicsetbc(state, bl, bu);
                minbleic.minbleicsetinnercond(state, epsg, 0, 0);
                minbleic.minbleicsetoutercond(state, epsc, epsc);
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = -(1.0E8*math.sqr(state.x[0]));
                        state.g[0] = -(2.0E8*state.x[0]);
                    }
                }
                minbleic.minbleicresults(state, ref x, rep);
                err = err | (double)(Math.Abs(x[0]-bu[0]))>(double)(epsc);
                
                //
                // Test correctness of the scaling:
                // * initial point is random point from [+1,+2]^N
                // * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
                // * function is EFFECTIVELY unconstrained; it has formal constraints,
                //   but they are inactive at the solution; we try different variants
                //   in order to explore different control paths of the optimizer:
                //   0) absense of constraints
                //   1) bound constraints -100000<=x[i]<=100000
                //   2) one linear constraint 0*x=0
                //   3) combination of (1) and (2)
                // * we use random scaling matrix
                // * we test different variants of the preconditioning:
                //   0) unit preconditioner
                //   1) random diagonal from [0.01,100]
                //   2) scale preconditioner
                // * we set very mild outer stopping conditions - OuterEpsX=1.0, but
                //   inner conditions are very stringent
                // * and we test that in the extremum inner stopping conditions are
                //   satisfied subject to the current scaling coefficients.
                //
                tmpeps = 1.0E-10;
                for(n=1; n<=10; n++)
                {
                    for(ckind=0; ckind<=3; ckind++)
                    {
                        for(pkind=0; pkind<=2; pkind++)
                        {
                            x = new double[n];
                            a = new double[n];
                            s = new double[n];
                            h = new double[n];
                            bl = new double[n];
                            bu = new double[n];
                            c = new double[1, n+1];
                            ct = new int[1];
                            ct[0] = 0;
                            c[0,n] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                x[i] = math.randomreal()+1;
                                bl[i] = -100000;
                                bu[i] = 100000;
                                c[0,i] = 0;
                                a[i] = Math.Exp(Math.Log(100)*(2*math.randomreal()-1));
                                s[i] = Math.Exp(Math.Log(100)*(2*math.randomreal()-1));
                                h[i] = Math.Exp(Math.Log(100)*(2*math.randomreal()-1));
                            }
                            minbleic.minbleiccreate(n, x, state);
                            if( ckind==1 | ckind==3 )
                            {
                                minbleic.minbleicsetbc(state, bl, bu);
                            }
                            if( ckind==2 | ckind==3 )
                            {
                                minbleic.minbleicsetlc(state, c, ct, 1);
                            }
                            if( pkind==1 )
                            {
                                minbleic.minbleicsetprecdiag(state, h);
                            }
                            if( pkind==2 )
                            {
                                minbleic.minbleicsetprecscale(state);
                            }
                            minbleic.minbleicsetinnercond(state, tmpeps, 0, 0);
                            minbleic.minbleicsetoutercond(state, 1.0, 1.0E-8);
                            minbleic.minbleicsetscale(state, s);
                            while( minbleic.minbleiciteration(state) )
                            {
                                if( state.needfg )
                                {
                                    state.f = 0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        state.f = state.f+a[i]*Math.Pow(state.x[i], 4);
                                        state.g[i] = 4*a[i]*Math.Pow(state.x[i], 3);
                                    }
                                }
                            }
                            minbleic.minbleicresults(state, ref x, rep);
                            if( rep.terminationtype<=0 )
                            {
                                err = true;
                                return;
                            }
                            v = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                v = v+math.sqr(s[i]*4*a[i]*Math.Pow(x[i], 3));
                            }
                            v = Math.Sqrt(v);
                            err = err | (double)(v)>(double)(tmpeps);
                        }
                    }
                }
                
                //
                // Check correctness of the "trimming".
                //
                // Trimming is a technique which is used to help algorithm
                // cope with unbounded functions. In order to check this
                // technique we will try to solve following optimization
                // problem:
                //
                //     min f(x) subject to no constraints on X
                //            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
                //     f(x) = {
                //            { M, if x<=-0.999999 or x>=0.999999
                //
                // where c is either 1.0 or 1.0E+6, M is either 1.0E8, 1.0E20 or +INF
                // (we try different combinations)
                //
                for(ckind=0; ckind<=1; ckind++)
                {
                    for(mkind=0; mkind<=2; mkind++)
                    {
                        
                        //
                        // Choose c and M
                        //
                        if( ckind==0 )
                        {
                            vc = 1.0;
                        }
                        if( ckind==1 )
                        {
                            vc = 1.0E+6;
                        }
                        if( mkind==0 )
                        {
                            vm = 1.0E+8;
                        }
                        if( mkind==1 )
                        {
                            vm = 1.0E+20;
                        }
                        if( mkind==2 )
                        {
                            vm = Double.PositiveInfinity;
                        }
                        
                        //
                        // Create optimizer, solve optimization problem
                        //
                        epsg = 1.0E-6*vc;
                        x = new double[1];
                        x[0] = 0.0;
                        minbleic.minbleiccreate(1, x, state);
                        minbleic.minbleicsetinnercond(state, epsg, 0, 0);
                        minbleic.minbleicsetoutercond(state, 1.0E-6, 1.0E-6);
                        while( minbleic.minbleiciteration(state) )
                        {
                            if( state.needfg )
                            {
                                if( (double)(-0.999999)<(double)(state.x[0]) & (double)(state.x[0])<(double)(0.999999) )
                                {
                                    state.f = 1/(1-state.x[0])+1/(1+state.x[0])+vc*state.x[0];
                                    state.g[0] = 1/math.sqr(1-state.x[0])-1/math.sqr(1+state.x[0])+vc;
                                }
                                else
                                {
                                    state.f = vm;
                                }
                            }
                        }
                        minbleic.minbleicresults(state, ref x, rep);
                        if( rep.terminationtype<=0 )
                        {
                            err = true;
                            return;
                        }
                        err = err | (double)(Math.Abs(1/math.sqr(1-x[0])-1/math.sqr(1+x[0])+vc))>(double)(epsg);
                    }
                }
            }
        }


        /*************************************************************************
        This function tests convergence properties.
        We solve several simple problems with different combinations of constraints

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testconv(ref bool err)
        {
            int passcount = 0;
            int pass = 0;
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] x = new double[0];
            double[,] c = new double[0,0];
            int[] ct = new int[0];
            minbleic.minbleicstate state = new minbleic.minbleicstate();
            double epsc = 0;
            double epsg = 0;
            double tol = 0;
            minbleic.minbleicreport rep = new minbleic.minbleicreport();

            epsc = 1.0E-4;
            epsg = 1.0E-8;
            tol = 0.001;
            passcount = 10;
            
            //
            // Three closely connected problems:
            // * 2-dimensional space
            // * octagonal area bounded by:
            //   * -1<=x<=+1
            //   * -1<=y<=+1
            //   * x+y<=1.5
            //   * x-y<=1.5
            //   * -x+y<=1.5
            //   * -x-y<=1.5
            // * several target functions:
            //   * f0=x+0.001*y, minimum at x=-1, y=-0.5
            //   * f1=(x+10)^2+y^2, minimum at x=-1, y=0
            //   * f2=(x+10)^2+(y-0.6)^2, minimum at x=-1, y=0.5
            //
            x = new double[2];
            bl = new double[2];
            bu = new double[2];
            c = new double[4, 3];
            ct = new int[4];
            bl[0] = -1;
            bl[1] = -1;
            bu[0] = 1;
            bu[1] = 1;
            c[0,0] = 1;
            c[0,1] = 1;
            c[0,2] = 1.5;
            ct[0] = -1;
            c[1,0] = 1;
            c[1,1] = -1;
            c[1,2] = 1.5;
            ct[1] = -1;
            c[2,0] = -1;
            c[2,1] = 1;
            c[2,2] = 1.5;
            ct[2] = -1;
            c[3,0] = -1;
            c[3,1] = -1;
            c[3,2] = 1.5;
            ct[3] = -1;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // f0
                //
                x[0] = 0.2*math.randomreal()-0.1;
                x[1] = 0.2*math.randomreal()-0.1;
                minbleic.minbleiccreate(2, x, state);
                minbleic.minbleicsetbc(state, bl, bu);
                minbleic.minbleicsetlc(state, c, ct, 4);
                minbleic.minbleicsetinnercond(state, epsg, 0, 0);
                minbleic.minbleicsetoutercond(state, epsc, epsc);
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = state.x[0]+0.001*state.x[1];
                        state.g[0] = 1;
                        state.g[1] = 0.001;
                    }
                }
                minbleic.minbleicresults(state, ref x, rep);
                if( rep.terminationtype>0 )
                {
                    err = err | (double)(Math.Abs(x[0]+1))>(double)(tol);
                    err = err | (double)(Math.Abs(x[1]+0.5))>(double)(tol);
                }
                else
                {
                    err = true;
                }
                
                //
                // f1
                //
                x[0] = 0.2*math.randomreal()-0.1;
                x[1] = 0.2*math.randomreal()-0.1;
                minbleic.minbleiccreate(2, x, state);
                minbleic.minbleicsetbc(state, bl, bu);
                minbleic.minbleicsetlc(state, c, ct, 4);
                minbleic.minbleicsetinnercond(state, epsg, 0, 0);
                minbleic.minbleicsetoutercond(state, epsc, epsc);
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = math.sqr(state.x[0]+10)+math.sqr(state.x[1]);
                        state.g[0] = 2*(state.x[0]+10);
                        state.g[1] = 2*state.x[1];
                    }
                }
                minbleic.minbleicresults(state, ref x, rep);
                if( rep.terminationtype>0 )
                {
                    err = err | (double)(Math.Abs(x[0]+1))>(double)(tol);
                    err = err | (double)(Math.Abs(x[1]))>(double)(tol);
                }
                else
                {
                    err = true;
                }
                
                //
                // f2
                //
                x[0] = 0.2*math.randomreal()-0.1;
                x[1] = 0.2*math.randomreal()-0.1;
                minbleic.minbleiccreate(2, x, state);
                minbleic.minbleicsetbc(state, bl, bu);
                minbleic.minbleicsetlc(state, c, ct, 4);
                minbleic.minbleicsetinnercond(state, epsg, 0, 0);
                minbleic.minbleicsetoutercond(state, epsc, epsc);
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = math.sqr(state.x[0]+10)+math.sqr(state.x[1]-0.6);
                        state.g[0] = 2*(state.x[0]+10);
                        state.g[1] = 2*(state.x[1]-0.6);
                    }
                }
                minbleic.minbleicresults(state, ref x, rep);
                if( rep.terminationtype>0 )
                {
                    err = err | (double)(Math.Abs(x[0]+1))>(double)(tol);
                    err = err | (double)(Math.Abs(x[1]-0.5))>(double)(tol);
                }
                else
                {
                    err = true;
                }
            }
        }


        /*************************************************************************
        This function tests preconditioning

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testpreconditioning(ref bool err)
        {
            int pass = 0;
            int n = 0;
            double[] x = new double[0];
            double[] x0 = new double[0];
            int i = 0;
            int k = 0;
            double[,] v = new double[0,0];
            double[,] c = new double[0,0];
            int[] ct = new int[0];
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] vd = new double[0];
            double[] d = new double[0];
            double[] units = new double[0];
            double[] s = new double[0];
            int cntb1 = 0;
            int cntb2 = 0;
            int cntg1 = 0;
            int cntg2 = 0;
            double epsg = 0;
            double[] diagh = new double[0];
            minbleic.minbleicstate state = new minbleic.minbleicstate();
            minbleic.minbleicreport rep = new minbleic.minbleicreport();
            int fkind = 0;
            int ckind = 0;
            int fk = 0;

            
            //
            // Preconditioner test 1.
            //
            // If
            // * B1 is default preconditioner with unit scale
            // * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix
            // * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
            // * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
            // then B1 is worse than G1, B2 is worse than G2.
            // "Worse" means more iterations to converge.
            //
            // Test problem setup:
            // * f(x) = sum( ((i*i+1)^FK*x[i])^2, i=0..N-1)
            // * FK is either +1 or -1 (we try both to test different aspects of preconditioning)
            // * constraints:
            //   0) absent
            //   1) boundary only
            //   2) linear equality only
            //   3) combination of boundary and linear equality constraints
            //
            // N        - problem size
            // K        - number of repeated passes (should be large enough to average out random factors)
            //
            k = 30;
            epsg = 1.0E-10;
            for(n=5; n<=8; n++)
            {
                for(fkind=0; fkind<=1; fkind++)
                {
                    for(ckind=0; ckind<=3; ckind++)
                    {
                        fk = 1-2*fkind;
                        x = new double[n];
                        units = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = 0;
                            units[i] = 1;
                        }
                        minbleic.minbleiccreate(n, x, state);
                        if( ckind==1 | ckind==3 )
                        {
                            bl = new double[n];
                            bu = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                bl[i] = -1;
                                bu[i] = 1;
                            }
                            minbleic.minbleicsetbc(state, bl, bu);
                        }
                        if( ckind==2 | ckind==3 )
                        {
                            c = new double[1, n+1];
                            ct = new int[1];
                            ct[0] = math.randominteger(3)-1;
                            for(i=0; i<=n-1; i++)
                            {
                                c[0,i] = 2*math.randomreal()-1;
                            }
                            c[0,n] = 0;
                            minbleic.minbleicsetlc(state, c, ct, 1);
                        }
                        
                        //
                        // Test it with default preconditioner VS. perturbed diagonal preconditioner
                        //
                        minbleic.minbleicsetprecdefault(state);
                        minbleic.minbleicsetscale(state, units);
                        cntb1 = 0;
                        for(pass=0; pass<=k-1; pass++)
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                            minbleic.minbleicrestartfrom(state, x);
                            while( minbleic.minbleiciteration(state) )
                            {
                                calciip2(state, n, fk);
                            }
                            minbleic.minbleicresults(state, ref x, rep);
                            cntb1 = cntb1+rep.inneriterationscount;
                            err = err | rep.terminationtype<=0;
                        }
                        diagh = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            diagh[i] = 2*Math.Pow(i*i+1, 2*fk)*(0.8+0.4*math.randomreal());
                        }
                        minbleic.minbleicsetprecdiag(state, diagh);
                        minbleic.minbleicsetscale(state, units);
                        cntg1 = 0;
                        for(pass=0; pass<=k-1; pass++)
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                            minbleic.minbleicrestartfrom(state, x);
                            while( minbleic.minbleiciteration(state) )
                            {
                                calciip2(state, n, fk);
                            }
                            minbleic.minbleicresults(state, ref x, rep);
                            cntg1 = cntg1+rep.inneriterationscount;
                            err = err | rep.terminationtype<=0;
                        }
                        err = err | cntb1<cntg1;
                        
                        //
                        // Test it with scale-based preconditioner
                        //
                        s = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            s[i] = 1/Math.Sqrt(2*Math.Pow(i*i+1, 2*fk)*(0.8+0.4*math.randomreal()));
                        }
                        minbleic.minbleicsetprecdefault(state);
                        minbleic.minbleicsetscale(state, s);
                        cntb2 = 0;
                        for(pass=0; pass<=k-1; pass++)
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                            minbleic.minbleicrestartfrom(state, x);
                            while( minbleic.minbleiciteration(state) )
                            {
                                calciip2(state, n, fk);
                            }
                            minbleic.minbleicresults(state, ref x, rep);
                            cntb2 = cntb2+rep.inneriterationscount;
                            err = err | rep.terminationtype<=0;
                        }
                        minbleic.minbleicsetprecscale(state);
                        minbleic.minbleicsetscale(state, s);
                        cntg2 = 0;
                        for(pass=0; pass<=k-1; pass++)
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                            minbleic.minbleicrestartfrom(state, x);
                            while( minbleic.minbleiciteration(state) )
                            {
                                calciip2(state, n, fk);
                            }
                            minbleic.minbleicresults(state, ref x, rep);
                            cntg2 = cntg2+rep.inneriterationscount;
                            err = err | rep.terminationtype<=0;
                        }
                        err = err | cntb2<cntg2;
                    }
                }
            }
        }


        /*************************************************************************
        This function sets random preconditioner:
        * unit one, for PrecKind=0
        * diagonal-based one, for PrecKind=1
        * scale-based one, for PrecKind=2
        *************************************************************************/
        private static void setrandompreconditioner(minbleic.minbleicstate state,
            int n,
            int preckind)
        {
            double[] p = new double[0];
            int i = 0;

            if( preckind==1 )
            {
                p = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    p[i] = Math.Exp(10*math.randomreal()-5);
                }
                minbleic.minbleicsetprecdiag(state, p);
            }
            else
            {
                minbleic.minbleicsetprecdefault(state);
            }
        }


    }
    public class testmcpdunit
    {
        public static bool testmcpd(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool simpleerrors = new bool();
            bool entryexiterrors = new bool();
            bool ecerrors = new bool();
            bool bcerrors = new bool();
            bool lcerrors = new bool();
            bool othererrors = new bool();

            
            //
            // Init
            //
            waserrors = false;
            othererrors = false;
            simpleerrors = false;
            entryexiterrors = false;
            ecerrors = false;
            bcerrors = false;
            lcerrors = false;
            
            //
            // Test
            //
            testsimple(ref simpleerrors);
            testentryexit(ref entryexiterrors);
            testec(ref ecerrors);
            testbc(ref bcerrors);
            testlc(ref lcerrors);
            
            //
            // Final report
            //
            waserrors = ((((othererrors | simpleerrors) | entryexiterrors) | ecerrors) | bcerrors) | lcerrors;
            if( !silent )
            {
                System.Console.Write("MCPD TEST");
                System.Console.WriteLine();
                System.Console.Write("TOTAL RESULTS:                           ");
                if( !waserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SIMPLE:                                ");
                if( !simpleerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* ENTRY/EXIT:                            ");
                if( !entryexiterrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* EQUALITY CONSTRAINTS:                  ");
                if( !ecerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* BOUND CONSTRAINTS:                     ");
                if( !bcerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* LINEAR CONSTRAINTS:                    ");
                if( !lcerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* OTHER PROPERTIES:                      ");
                if( !othererrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Simple test with no "entry"/"exit" states

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testsimple(ref bool err)
        {
            int n = 0;
            double[,] pexact = new double[0,0];
            double[,] xy = new double[0,0];
            double threshold = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            double v0 = 0;
            double[,] p = new double[0,0];
            mcpd.mcpdstate s = new mcpd.mcpdstate();
            mcpd.mcpdreport rep = new mcpd.mcpdreport();
            double offdiagonal = 0;

            threshold = 1.0E-2;
            
            //
            // First test:
            // * N-dimensional problem
            // * proportional data
            // * no "entry"/"exit" states
            // * N tracks, each includes only two states
            // * first record in I-th track is [0 ... 1 ... 0] with 1 is in I-th position
            // * all tracks are modelled using randomly generated transition matrix P
            //
            for(n=1; n<=5; n++)
            {
                
                //
                // Initialize "exact" P:
                // * fill by random values
                // * make sure that each column sums to non-zero value
                // * normalize
                //
                pexact = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        pexact[i,j] = math.randomreal();
                    }
                }
                for(j=0; j<=n-1; j++)
                {
                    i = math.randominteger(n);
                    pexact[i,j] = pexact[i,j]+0.1;
                }
                for(j=0; j<=n-1; j++)
                {
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+pexact[i,j];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        pexact[i,j] = pexact[i,j]/v;
                    }
                }
                
                //
                // Initialize solver:
                // * create object
                // * add tracks
                //
                mcpd.mcpdcreate(n, s);
                for(i=0; i<=n-1; i++)
                {
                    xy = new double[2, n];
                    for(j=0; j<=n-1; j++)
                    {
                        xy[0,j] = 0;
                    }
                    xy[0,i] = 1;
                    for(j=0; j<=n-1; j++)
                    {
                        xy[1,j] = pexact[j,i];
                    }
                    mcpd.mcpdaddtrack(s, xy, 2);
                }
                
                //
                // Solve and test
                //
                mcpd.mcpdsolve(s);
                mcpd.mcpdresults(s, ref p, rep);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            err = err | (double)(Math.Abs(p[i,j]-pexact[i,j]))>(double)(threshold);
                        }
                    }
                }
                else
                {
                    err = true;
                }
            }
            
            //
            // Second test:
            // * N-dimensional problem
            // * proportional data
            // * no "entry"/"exit" states
            // * N tracks, each includes only two states
            // * first record in I-th track is [0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position
            // * all tracks are modelled using randomly generated transition matrix P
            //
            offdiagonal = 0.1;
            for(n=1; n<=5; n++)
            {
                
                //
                // Initialize "exact" P:
                // * fill by random values
                // * make sure that each column sums to non-zero value
                // * normalize
                //
                pexact = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        pexact[i,j] = math.randomreal();
                    }
                }
                for(j=0; j<=n-1; j++)
                {
                    i = math.randominteger(n);
                    pexact[i,j] = pexact[i,j]+0.1;
                }
                for(j=0; j<=n-1; j++)
                {
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+pexact[i,j];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        pexact[i,j] = pexact[i,j]/v;
                    }
                }
                
                //
                // Initialize solver:
                // * create object
                // * add tracks
                //
                mcpd.mcpdcreate(n, s);
                for(i=0; i<=n-1; i++)
                {
                    xy = new double[2, n];
                    for(j=0; j<=n-1; j++)
                    {
                        xy[0,j] = 0;
                    }
                    
                    //
                    // "main" element
                    //
                    xy[0,i] = 1.0-2*offdiagonal;
                    for(j=0; j<=n-1; j++)
                    {
                        xy[1,j] = (1.0-2*offdiagonal)*pexact[j,i];
                    }
                    
                    //
                    // off-diagonal ones
                    //
                    if( i>0 )
                    {
                        xy[0,i-1] = offdiagonal;
                        for(j=0; j<=n-1; j++)
                        {
                            xy[1,j] = xy[1,j]+offdiagonal*pexact[j,i-1];
                        }
                    }
                    if( i<n-1 )
                    {
                        xy[0,i+1] = offdiagonal;
                        for(j=0; j<=n-1; j++)
                        {
                            xy[1,j] = xy[1,j]+offdiagonal*pexact[j,i+1];
                        }
                    }
                    mcpd.mcpdaddtrack(s, xy, 2);
                }
                
                //
                // Solve and test
                //
                mcpd.mcpdsolve(s);
                mcpd.mcpdresults(s, ref p, rep);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            err = err | (double)(Math.Abs(p[i,j]-pexact[i,j]))>(double)(threshold);
                        }
                    }
                }
                else
                {
                    err = true;
                }
            }
            
            //
            // Third test:
            // * N-dimensional problem
            // * population data
            // * no "entry"/"exit" states
            // * N tracks, each includes only two states
            // * first record in I-th track is V*[0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position, V in [1,10]
            // * all tracks are modelled using randomly generated transition matrix P
            //
            offdiagonal = 0.1;
            for(n=1; n<=5; n++)
            {
                
                //
                // Initialize "exact" P:
                // * fill by random values
                // * make sure that each column sums to non-zero value
                // * normalize
                //
                pexact = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        pexact[i,j] = math.randomreal();
                    }
                }
                for(j=0; j<=n-1; j++)
                {
                    i = math.randominteger(n);
                    pexact[i,j] = pexact[i,j]+0.1;
                }
                for(j=0; j<=n-1; j++)
                {
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+pexact[i,j];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        pexact[i,j] = pexact[i,j]/v;
                    }
                }
                
                //
                // Initialize solver:
                // * create object
                // * add tracks
                //
                mcpd.mcpdcreate(n, s);
                for(i=0; i<=n-1; i++)
                {
                    xy = new double[2, n];
                    for(j=0; j<=n-1; j++)
                    {
                        xy[0,j] = 0;
                    }
                    
                    //
                    // "main" element
                    //
                    v0 = 9*math.randomreal()+1;
                    xy[0,i] = v0*(1.0-2*offdiagonal);
                    for(j=0; j<=n-1; j++)
                    {
                        xy[1,j] = v0*(1.0-2*offdiagonal)*pexact[j,i];
                    }
                    
                    //
                    // off-diagonal ones
                    //
                    if( i>0 )
                    {
                        xy[0,i-1] = v0*offdiagonal;
                        for(j=0; j<=n-1; j++)
                        {
                            xy[1,j] = xy[1,j]+v0*offdiagonal*pexact[j,i-1];
                        }
                    }
                    if( i<n-1 )
                    {
                        xy[0,i+1] = v0*offdiagonal;
                        for(j=0; j<=n-1; j++)
                        {
                            xy[1,j] = xy[1,j]+v0*offdiagonal*pexact[j,i+1];
                        }
                    }
                    mcpd.mcpdaddtrack(s, xy, 2);
                }
                
                //
                // Solve and test
                //
                mcpd.mcpdsolve(s);
                mcpd.mcpdresults(s, ref p, rep);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            err = err | (double)(Math.Abs(p[i,j]-pexact[i,j]))>(double)(threshold);
                        }
                    }
                }
                else
                {
                    err = true;
                }
            }
        }


        /*************************************************************************
        Test for different combinations of "entry"/"exit" models

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testentryexit(ref bool err)
        {
            int n = 0;
            double[,] p = new double[0,0];
            double[,] pexact = new double[0,0];
            double[,] xy = new double[0,0];
            double threshold = 0;
            int entrystate = 0;
            int exitstate = 0;
            int entrykind = 0;
            int exitkind = 0;
            int popkind = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            mcpd.mcpdstate s = new mcpd.mcpdstate();
            mcpd.mcpdreport rep = new mcpd.mcpdreport();
            int i_ = 0;

            threshold = 1.0E-3;
            
            //
            //
            //
            for(n=2; n<=5; n++)
            {
                for(entrykind=0; entrykind<=1; entrykind++)
                {
                    for(exitkind=0; exitkind<=1; exitkind++)
                    {
                        for(popkind=0; popkind<=1; popkind++)
                        {
                            
                            //
                            // Generate EntryState/ExitState such that one of the following is True:
                            // * EntryState<>ExitState
                            // * EntryState=-1 or ExitState=-1
                            //
                            do
                            {
                                if( entrykind==0 )
                                {
                                    entrystate = -1;
                                }
                                else
                                {
                                    entrystate = math.randominteger(n);
                                }
                                if( exitkind==0 )
                                {
                                    exitstate = -1;
                                }
                                else
                                {
                                    exitstate = math.randominteger(n);
                                }
                            }
                            while( !((entrystate==-1 | exitstate==-1) | entrystate!=exitstate) );
                            
                            //
                            // Generate transition matrix P such that:
                            // * columns corresponding to non-exit states sums to 1.0
                            // * columns corresponding to exit states sums to 0.0
                            // * rows corresponding to entry states are zero
                            //
                            pexact = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    pexact[i,j] = 1+math.randominteger(5);
                                    if( i==entrystate )
                                    {
                                        pexact[i,j] = 0.0;
                                    }
                                    if( j==exitstate )
                                    {
                                        pexact[i,j] = 0.0;
                                    }
                                }
                            }
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(i=0; i<=n-1; i++)
                                {
                                    v = v+pexact[i,j];
                                }
                                if( (double)(v)!=(double)(0) )
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        pexact[i,j] = pexact[i,j]/v;
                                    }
                                }
                            }
                            
                            //
                            // Create MCPD solver
                            //
                            if( entrystate<0 & exitstate<0 )
                            {
                                mcpd.mcpdcreate(n, s);
                            }
                            if( entrystate>=0 & exitstate<0 )
                            {
                                mcpd.mcpdcreateentry(n, entrystate, s);
                            }
                            if( entrystate<0 & exitstate>=0 )
                            {
                                mcpd.mcpdcreateexit(n, exitstate, s);
                            }
                            if( entrystate>=0 & exitstate>=0 )
                            {
                                mcpd.mcpdcreateentryexit(n, entrystate, exitstate, s);
                            }
                            
                            //
                            // Add N tracks.
                            //
                            // K-th track starts from vector with large value of
                            // K-th component and small random noise in other components.
                            //
                            // Track contains from 2 to 4 elements.
                            //
                            // Tracks contain proportional (normalized) or
                            // population data, depending on PopKind variable.
                            //
                            for(k=0; k<=n-1; k++)
                            {
                                
                                //
                                // Generate track whose length is in 2..4
                                //
                                xy = new double[2+math.randominteger(3), n];
                                for(j=0; j<=n-1; j++)
                                {
                                    xy[0,j] = 0.05*math.randomreal();
                                }
                                xy[0,k] = 1+math.randomreal();
                                for(i=1; i<=ap.rows(xy)-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        if( j!=entrystate )
                                        {
                                            v = 0.0;
                                            for(i_=0; i_<=n-1;i_++)
                                            {
                                                v += pexact[j,i_]*xy[i-1,i_];
                                            }
                                            xy[i,j] = v;
                                        }
                                        else
                                        {
                                            xy[i,j] = math.randomreal();
                                        }
                                    }
                                }
                                
                                //
                                // Normalize, if needed
                                //
                                if( popkind==1 )
                                {
                                    for(i=0; i<=ap.rows(xy)-1; i++)
                                    {
                                        v = 0.0;
                                        for(j=0; j<=n-1; j++)
                                        {
                                            v = v+xy[i,j];
                                        }
                                        if( (double)(v)>(double)(0) )
                                        {
                                            for(j=0; j<=n-1; j++)
                                            {
                                                xy[i,j] = xy[i,j]/v;
                                            }
                                        }
                                    }
                                }
                                
                                //
                                // Add track
                                //
                                mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            }
                            
                            //
                            // Solve and test
                            //
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            if( rep.terminationtype>0 )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        err = err | (double)(Math.Abs(p[i,j]-pexact[i,j]))>(double)(threshold);
                                    }
                                }
                            }
                            else
                            {
                                err = true;
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Test equality constraints.

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testec(ref bool err)
        {
            int n = 0;
            double[,] p = new double[0,0];
            double[,] ec = new double[0,0];
            double[,] xy = new double[0,0];
            int entrystate = 0;
            int exitstate = 0;
            int entrykind = 0;
            int exitkind = 0;
            int i = 0;
            int j = 0;
            int ic = 0;
            int jc = 0;
            double vc = 0;
            mcpd.mcpdstate s = new mcpd.mcpdstate();
            mcpd.mcpdreport rep = new mcpd.mcpdreport();

            
            //
            // We try different problems with following properties:
            // * N is large enough - we won't have problems with inconsistent constraints
            // * first state is either "entry" or "normal"
            // * last state is either "exit" or "normal"
            // * we have one long random track
            //
            // We test several properties which are described in comments below
            //
            for(n=4; n<=6; n++)
            {
                for(entrykind=0; entrykind<=1; entrykind++)
                {
                    for(exitkind=0; exitkind<=1; exitkind++)
                    {
                        
                        //
                        // Prepare problem
                        //
                        if( entrykind==0 )
                        {
                            entrystate = -1;
                        }
                        else
                        {
                            entrystate = 0;
                        }
                        if( exitkind==0 )
                        {
                            exitstate = -1;
                        }
                        else
                        {
                            exitstate = n-1;
                        }
                        xy = new double[2*n, n];
                        for(i=0; i<=ap.rows(xy)-1; i++)
                        {
                            for(j=0; j<=ap.cols(xy)-1; j++)
                            {
                                xy[i,j] = math.randomreal();
                            }
                        }
                        
                        //
                        // Test that single equality constraint on non-entry
                        // non-exit elements of P is satisfied.
                        //
                        // NOTE: this test needs N>=4 because smaller values
                        // can give us inconsistent constraints
                        //
                        ap.assert(n>=4, "TestEC: expectation failed");
                        ic = 1+math.randominteger(n-2);
                        jc = 1+math.randominteger(n-2);
                        vc = math.randomreal();
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdaddec(s, ic, jc, vc);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        if( rep.terminationtype>0 )
                        {
                            err = err | (double)(p[ic,jc])!=(double)(vc);
                        }
                        else
                        {
                            err = true;
                        }
                        
                        //
                        // Test interaction with default "sum-to-one" constraint
                        // on columns of P.
                        //
                        // We set N-1 equality constraints on random non-exit column
                        // of P, which are inconsistent with this default constraint
                        // (sum will be greater that 1.0).
                        //
                        // Algorithm must detect inconsistency.
                        //
                        // NOTE:
                        // 1. we do not set constraints for the first element of
                        //    the column, because this element may be constrained by
                        //    "exit state" constraint.
                        // 2. this test needs N>=3
                        //
                        ap.assert(n>=3, "TestEC: expectation failed");
                        jc = math.randominteger(n-1);
                        vc = 0.95;
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        for(i=1; i<=n-1; i++)
                        {
                            mcpd.mcpdaddec(s, i, jc, vc);
                        }
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        err = err | rep.terminationtype!=-3;
                        
                        //
                        // Test interaction with constrains on entry states.
                        //
                        // When model has entry state, corresponding row of P
                        // must be zero. We try to set two kinds of constraints
                        // on random element of this row:
                        // * zero equality constraint, which must be consistent
                        // * non-zero equality constraint, which must be inconsistent
                        //
                        if( entrystate>=0 )
                        {
                            jc = math.randominteger(n);
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddec(s, entrystate, jc, 0.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddec(s, entrystate, jc, 0.5);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                        
                        //
                        // Test interaction with constrains on exit states.
                        //
                        // When model has exit state, corresponding column of P
                        // must be zero. We try to set two kinds of constraints
                        // on random element of this column:
                        // * zero equality constraint, which must be consistent
                        // * non-zero equality constraint, which must be inconsistent
                        //
                        if( exitstate>=0 )
                        {
                            ic = math.randominteger(n);
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddec(s, ic, exitstate, 0.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddec(s, ic, exitstate, 0.5);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                        
                        //
                        // Test SetEC() call - we constrain subset of non-entry
                        // non-exit elements and test it.
                        //
                        ap.assert(n>=4, "TestEC: expectation failed");
                        ec = new double[n, n];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ec[i,j] = Double.NaN;
                            }
                        }
                        for(j=1; j<=n-2; j++)
                        {
                            ec[1+math.randominteger(n-2),j] = 0.1+0.1*math.randomreal();
                        }
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdsetec(s, ec);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        if( rep.terminationtype>0 )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    if( math.isfinite(ec[i,j]) )
                                    {
                                        err = err | (double)(p[i,j])!=(double)(ec[i,j]);
                                    }
                                }
                            }
                        }
                        else
                        {
                            err = true;
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Test bound constraints.

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testbc(ref bool err)
        {
            int n = 0;
            double[,] p = new double[0,0];
            double[,] bndl = new double[0,0];
            double[,] bndu = new double[0,0];
            double[,] xy = new double[0,0];
            int entrystate = 0;
            int exitstate = 0;
            int entrykind = 0;
            int exitkind = 0;
            int i = 0;
            int j = 0;
            int ic = 0;
            int jc = 0;
            double vl = 0;
            double vu = 0;
            mcpd.mcpdstate s = new mcpd.mcpdstate();
            mcpd.mcpdreport rep = new mcpd.mcpdreport();

            
            //
            // We try different problems with following properties:
            // * N is large enough - we won't have problems with inconsistent constraints
            // * first state is either "entry" or "normal"
            // * last state is either "exit" or "normal"
            // * we have one long random track
            //
            // We test several properties which are described in comments below
            //
            for(n=4; n<=6; n++)
            {
                for(entrykind=0; entrykind<=1; entrykind++)
                {
                    for(exitkind=0; exitkind<=1; exitkind++)
                    {
                        
                        //
                        // Prepare problem
                        //
                        if( entrykind==0 )
                        {
                            entrystate = -1;
                        }
                        else
                        {
                            entrystate = 0;
                        }
                        if( exitkind==0 )
                        {
                            exitstate = -1;
                        }
                        else
                        {
                            exitstate = n-1;
                        }
                        xy = new double[2*n, n];
                        for(i=0; i<=ap.rows(xy)-1; i++)
                        {
                            for(j=0; j<=ap.cols(xy)-1; j++)
                            {
                                xy[i,j] = math.randomreal();
                            }
                        }
                        
                        //
                        // Test that single bound constraint on non-entry
                        // non-exit elements of P is satisfied.
                        //
                        // NOTE 1: this test needs N>=4 because smaller values
                        // can give us inconsistent constraints
                        //
                        ap.assert(n>=4, "TestBC: expectation failed");
                        ic = 1+math.randominteger(n-2);
                        jc = 1+math.randominteger(n-2);
                        if( (double)(math.randomreal())>(double)(0.5) )
                        {
                            vl = 0.3*math.randomreal();
                        }
                        else
                        {
                            vl = Double.NegativeInfinity;
                        }
                        if( (double)(math.randomreal())>(double)(0.5) )
                        {
                            vu = 0.5+0.3*math.randomreal();
                        }
                        else
                        {
                            vu = Double.PositiveInfinity;
                        }
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdaddbc(s, ic, jc, vl, vu);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        if( rep.terminationtype>0 )
                        {
                            err = err | (double)(p[ic,jc])<(double)(vl);
                            err = err | (double)(p[ic,jc])>(double)(vu);
                        }
                        else
                        {
                            err = true;
                        }
                        
                        //
                        // Test interaction with default "sum-to-one" constraint
                        // on columns of P.
                        //
                        // We set N-1 bound constraints on random non-exit column
                        // of P, which are inconsistent with this default constraint
                        // (sum will be greater that 1.0).
                        //
                        // Algorithm must detect inconsistency.
                        //
                        // NOTE:
                        // 1. we do not set constraints for the first element of
                        //    the column, because this element may be constrained by
                        //    "exit state" constraint.
                        // 2. this test needs N>=3
                        //
                        ap.assert(n>=3, "TestEC: expectation failed");
                        jc = math.randominteger(n-1);
                        vl = 0.85;
                        vu = 0.95;
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        for(i=1; i<=n-1; i++)
                        {
                            mcpd.mcpdaddbc(s, i, jc, vl, vu);
                        }
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        err = err | rep.terminationtype!=-3;
                        
                        //
                        // Test interaction with constrains on entry states.
                        //
                        // When model has entry state, corresponding row of P
                        // must be zero. We try to set two kinds of constraints
                        // on random element of this row:
                        // * bound constraint with zero lower bound, which must be consistent
                        // * bound constraint with non-zero lower bound, which must be inconsistent
                        //
                        if( entrystate>=0 )
                        {
                            jc = math.randominteger(n);
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddbc(s, entrystate, jc, 0.0, 1.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddbc(s, entrystate, jc, 0.5, 1.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                        
                        //
                        // Test interaction with constrains on exit states.
                        //
                        // When model has exit state, corresponding column of P
                        // must be zero. We try to set two kinds of constraints
                        // on random element of this column:
                        // * bound constraint with zero lower bound, which must be consistent
                        // * bound constraint with non-zero lower bound, which must be inconsistent
                        //
                        if( exitstate>=0 )
                        {
                            ic = math.randominteger(n);
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddbc(s, ic, exitstate, 0.0, 1.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddbc(s, ic, exitstate, 0.5, 1.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                        
                        //
                        // Test SetBC() call - we constrain subset of non-entry
                        // non-exit elements and test it.
                        //
                        ap.assert(n>=4, "TestBC: expectation failed");
                        bndl = new double[n, n];
                        bndu = new double[n, n];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                bndl[i,j] = Double.NegativeInfinity;
                                bndu[i,j] = Double.PositiveInfinity;
                            }
                        }
                        for(j=1; j<=n-2; j++)
                        {
                            i = 1+math.randominteger(n-2);
                            bndl[i,j] = 0.5-0.1*math.randomreal();
                            bndu[i,j] = 0.5+0.1*math.randomreal();
                        }
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdsetbc(s, bndl, bndu);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        if( rep.terminationtype>0 )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    err = err | (double)(p[i,j])<(double)(bndl[i,j]);
                                    err = err | (double)(p[i,j])>(double)(bndu[i,j]);
                                }
                            }
                        }
                        else
                        {
                            err = true;
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Test bound constraints.

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testlc(ref bool err)
        {
            int n = 0;
            double[,] p = new double[0,0];
            double[,] c = new double[0,0];
            double[,] xy = new double[0,0];
            int[] ct = new int[0];
            int entrystate = 0;
            int exitstate = 0;
            int entrykind = 0;
            int exitkind = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int t = 0;
            int jc = 0;
            double v = 0;
            double threshold = 0;
            mcpd.mcpdstate s = new mcpd.mcpdstate();
            mcpd.mcpdreport rep = new mcpd.mcpdreport();

            threshold = 1.0E5*math.machineepsilon;
            
            //
            // We try different problems with following properties:
            // * N is large enough - we won't have problems with inconsistent constraints
            // * first state is either "entry" or "normal"
            // * last state is either "exit" or "normal"
            // * we have one long random track
            //
            // We test several properties which are described in comments below
            //
            for(n=4; n<=6; n++)
            {
                for(entrykind=0; entrykind<=1; entrykind++)
                {
                    for(exitkind=0; exitkind<=1; exitkind++)
                    {
                        
                        //
                        // Prepare problem
                        //
                        if( entrykind==0 )
                        {
                            entrystate = -1;
                        }
                        else
                        {
                            entrystate = 0;
                        }
                        if( exitkind==0 )
                        {
                            exitstate = -1;
                        }
                        else
                        {
                            exitstate = n-1;
                        }
                        xy = new double[2*n, n];
                        for(i=0; i<=ap.rows(xy)-1; i++)
                        {
                            for(j=0; j<=ap.cols(xy)-1; j++)
                            {
                                xy[i,j] = math.randomreal();
                            }
                        }
                        
                        //
                        // Test that single linear equality/inequality constraint
                        // on non-entry non-exit elements of P is satisfied.
                        //
                        // NOTE 1: this test needs N>=4 because smaller values
                        //         can give us inconsistent constraints
                        // NOTE 2: Constraints are generated is such a way that P=(1/N ... 1/N)
                        //         is always feasible. It guarantees that there always exists
                        //         at least one feasible point
                        // NOTE 3: If we have inequality constraint, we "shift" right part
                        //         in order to make feasible some neighborhood of P=(1/N ... 1/N).
                        //
                        ap.assert(n>=4, "TestLC: expectation failed");
                        c = new double[1, n*n+1];
                        ct = new int[1];
                        v = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( ((i==0 | i==n-1) | j==0) | j==n-1 )
                                {
                                    c[0,i*n+j] = 0;
                                }
                                else
                                {
                                    c[0,i*n+j] = math.randomreal();
                                    v = v+c[0,i*n+j]*((double)1/(double)n);
                                }
                            }
                        }
                        c[0,n*n] = v;
                        ct[0] = math.randominteger(3)-1;
                        if( ct[0]<0 )
                        {
                            c[0,n*n] = c[0,n*n]+0.1;
                        }
                        if( ct[0]>0 )
                        {
                            c[0,n*n] = c[0,n*n]-0.1;
                        }
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdsetlc(s, c, ct, 1);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        if( rep.terminationtype>0 )
                        {
                            v = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    v = v+p[i,j]*c[0,i*n+j];
                                }
                            }
                            if( ct[0]<0 )
                            {
                                err = err | (double)(v)>=(double)(c[0,n*n]+threshold);
                            }
                            if( ct[0]==0 )
                            {
                                err = err | (double)(Math.Abs(v-c[0,n*n]))>=(double)(threshold);
                            }
                            if( ct[0]>0 )
                            {
                                err = err | (double)(v)<=(double)(c[0,n*n]-threshold);
                            }
                        }
                        else
                        {
                            err = true;
                        }
                        
                        //
                        // Test interaction with default "sum-to-one" constraint
                        // on columns of P.
                        //
                        // We set linear constraint which has for "sum-to-X" on
                        // on random non-exit column of P. This constraint can be
                        // either consistent (X=1.0) or inconsistent (X<>1.0) with
                        // this default constraint.
                        //
                        // Algorithm must detect inconsistency.
                        //
                        // NOTE:
                        // 1. this test needs N>=2
                        //
                        ap.assert(n>=2, "TestLC: expectation failed");
                        jc = math.randominteger(n-1);
                        c = new double[1, n*n+1];
                        ct = new int[1];
                        for(i=0; i<=n*n-1; i++)
                        {
                            c[0,i] = 0.0;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            c[0,n*i+jc] = 1.0;
                        }
                        c[0,n*n] = 1.0;
                        ct[0] = 0;
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdsetlc(s, c, ct, 1);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        err = err | rep.terminationtype<=0;
                        c[0,n*n] = 2.0;
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdsetlc(s, c, ct, 1);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        err = err | rep.terminationtype!=-3;
                        
                        //
                        // Test interaction with constrains on entry states.
                        //
                        // When model has entry state, corresponding row of P
                        // must be zero. We try to set two kinds of constraints
                        // on elements of this row:
                        // * sums-to-zero constraint, which must be consistent
                        // * sums-to-one constraint, which must be inconsistent
                        //
                        if( entrystate>=0 )
                        {
                            c = new double[1, n*n+1];
                            ct = new int[1];
                            for(i=0; i<=n*n-1; i++)
                            {
                                c[0,i] = 0.0;
                            }
                            for(j=0; j<=n-1; j++)
                            {
                                c[0,n*entrystate+j] = 1.0;
                            }
                            ct[0] = 0;
                            c[0,n*n] = 0.0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdsetlc(s, c, ct, 1);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            c[0,n*n] = 1.0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdsetlc(s, c, ct, 1);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                        
                        //
                        // Test interaction with constrains on exit states.
                        //
                        // When model has exit state, corresponding column of P
                        // must be zero. We try to set two kinds of constraints
                        // on elements of this column:
                        // * sums-to-zero constraint, which must be consistent
                        // * sums-to-one constraint, which must be inconsistent
                        //
                        if( exitstate>=0 )
                        {
                            c = new double[1, n*n+1];
                            ct = new int[1];
                            for(i=0; i<=n*n-1; i++)
                            {
                                c[0,i] = 0.0;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                c[0,n*i+exitstate] = 1.0;
                            }
                            ct[0] = 0;
                            c[0,n*n] = 0.0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdsetlc(s, c, ct, 1);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            c[0,n*n] = 1.0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdsetlc(s, c, ct, 1);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                    }
                }
            }
            
            //
            // Final test - we generate several random constraints and
            // test SetLC() function.
            //
            // NOTES:
            //
            // 1. Constraints are generated is such a way that P=(1/N ... 1/N)
            //    is always feasible. It guarantees that there always exists
            //    at least one feasible point
            // 2. For simplicity of the test we do not use entry/exit states
            //    in our model
            //
            for(n=1; n<=4; n++)
            {
                for(k=1; k<=2*n; k++)
                {
                    
                    //
                    // Generate track
                    //
                    xy = new double[2*n, n];
                    for(i=0; i<=ap.rows(xy)-1; i++)
                    {
                        for(j=0; j<=ap.cols(xy)-1; j++)
                        {
                            xy[i,j] = math.randomreal();
                        }
                    }
                    
                    //
                    // Generate random constraints
                    //
                    c = new double[k, n*n+1];
                    ct = new int[k];
                    for(i=0; i<=k-1; i++)
                    {
                        
                        //
                        // Generate constraint and its right part
                        //
                        c[i,n*n] = 0;
                        for(j=0; j<=n*n-1; j++)
                        {
                            c[i,j] = 2*math.randomreal()-1;
                            c[i,n*n] = c[i,n*n]+c[i,j]*((double)1/(double)n);
                        }
                        ct[i] = math.randominteger(3)-1;
                        
                        //
                        // If we have inequality constraint, we "shift" right part
                        // in order to make feasible some neighborhood of P=(1/N ... 1/N).
                        //
                        if( ct[i]<0 )
                        {
                            c[i,n*n] = c[i,n*n]+0.1;
                        }
                        if( ct[i]>0 )
                        {
                            c[i,n*n] = c[i,n*n]-0.1;
                        }
                    }
                    
                    //
                    // Test
                    //
                    createee(n, -1, -1, s);
                    mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                    mcpd.mcpdsetlc(s, c, ct, k);
                    mcpd.mcpdsolve(s);
                    mcpd.mcpdresults(s, ref p, rep);
                    if( rep.terminationtype>0 )
                    {
                        for(t=0; t<=k-1; t++)
                        {
                            v = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    v = v+p[i,j]*c[t,i*n+j];
                                }
                            }
                            if( ct[t]<0 )
                            {
                                err = err | (double)(v)>=(double)(c[t,n*n]+threshold);
                            }
                            if( ct[t]==0 )
                            {
                                err = err | (double)(Math.Abs(v-c[t,n*n]))>=(double)(threshold);
                            }
                            if( ct[t]>0 )
                            {
                                err = err | (double)(v)<=(double)(c[t,n*n]-threshold);
                            }
                        }
                    }
                    else
                    {
                        err = true;
                    }
                }
            }
        }


        /*************************************************************************
        This function is used to create MCPD object with arbitrary combination of
        entry and exit states
        *************************************************************************/
        private static void createee(int n,
            int entrystate,
            int exitstate,
            mcpd.mcpdstate s)
        {
            if( entrystate<0 & exitstate<0 )
            {
                mcpd.mcpdcreate(n, s);
            }
            if( entrystate>=0 & exitstate<0 )
            {
                mcpd.mcpdcreateentry(n, entrystate, s);
            }
            if( entrystate<0 & exitstate>=0 )
            {
                mcpd.mcpdcreateexit(n, exitstate, s);
            }
            if( entrystate>=0 & exitstate>=0 )
            {
                mcpd.mcpdcreateentryexit(n, entrystate, exitstate, s);
            }
        }


    }
    public class testfblsunit
    {
        /*************************************************************************
        Testing
        *************************************************************************/
        public static bool testfbls(bool silent)
        {
            bool result = new bool();
            int n = 0;
            int m = 0;
            int mx = 0;
            int i = 0;
            int j = 0;
            bool waserrors = new bool();
            bool cgerrors = new bool();
            double v = 0;
            double v1 = 0;
            double v2 = 0;
            double[] tmp1 = new double[0];
            double[] tmp2 = new double[0];
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double[] x = new double[0];
            double[] xe = new double[0];
            double[] buf = new double[0];
            double alpha = 0;
            double e1 = 0;
            double e2 = 0;
            fbls.fblslincgstate cgstate = new fbls.fblslincgstate();
            int i_ = 0;

            mx = 10;
            waserrors = false;
            cgerrors = false;
            
            //
            // Test CG solver:
            // * generate problem (A, B, Alpha, XE - exact solution) and initial approximation X
            // * E1 = ||A'A*x-b||
            // * solve
            // * E2 = ||A'A*x-b||
            // * test that E2<0.001*E1
            //
            for(n=1; n<=mx; n++)
            {
                for(m=1; m<=mx; m++)
                {
                    a = new double[m, n];
                    b = new double[n];
                    x = new double[n];
                    xe = new double[n];
                    tmp1 = new double[m];
                    tmp2 = new double[n];
                    
                    //
                    // init A, alpha, B, X (initial approximation), XE (exact solution)
                    // X is initialized in such way that is has no chances to be equal to XE.
                    //
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 2*math.randomreal()-1;
                        }
                    }
                    alpha = math.randomreal()+0.1;
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = 2*math.randomreal()-1;
                        xe[i] = 2*math.randomreal()-1;
                        x[i] = (2*math.randominteger(2)-1)*(2+math.randomreal());
                    }
                    
                    //
                    // Test dense CG (which solves A'A*x=b and accepts dense A)
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = (2*math.randominteger(2)-1)*(2+math.randomreal());
                    }
                    ablas.rmatrixmv(m, n, a, 0, 0, 0, x, 0, ref tmp1, 0);
                    ablas.rmatrixmv(n, m, a, 0, 0, 1, tmp1, 0, ref tmp2, 0);
                    for(i_=0; i_<=n-1;i_++)
                    {
                        tmp2[i_] = tmp2[i_] + alpha*x[i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        tmp2[i_] = tmp2[i_] - b[i_];
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += tmp2[i_]*tmp2[i_];
                    }
                    e1 = Math.Sqrt(v);
                    fbls.fblssolvecgx(a, m, n, alpha, b, ref x, ref buf);
                    ablas.rmatrixmv(m, n, a, 0, 0, 0, x, 0, ref tmp1, 0);
                    ablas.rmatrixmv(n, m, a, 0, 0, 1, tmp1, 0, ref tmp2, 0);
                    for(i_=0; i_<=n-1;i_++)
                    {
                        tmp2[i_] = tmp2[i_] + alpha*x[i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        tmp2[i_] = tmp2[i_] - b[i_];
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += tmp2[i_]*tmp2[i_];
                    }
                    e2 = Math.Sqrt(v);
                    cgerrors = cgerrors | (double)(e2)>(double)(0.001*e1);
                    
                    //
                    // Test sparse CG (which relies on reverse communication)
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = (2*math.randominteger(2)-1)*(2+math.randomreal());
                    }
                    ablas.rmatrixmv(m, n, a, 0, 0, 0, x, 0, ref tmp1, 0);
                    ablas.rmatrixmv(n, m, a, 0, 0, 1, tmp1, 0, ref tmp2, 0);
                    for(i_=0; i_<=n-1;i_++)
                    {
                        tmp2[i_] = tmp2[i_] + alpha*x[i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        tmp2[i_] = tmp2[i_] - b[i_];
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += tmp2[i_]*tmp2[i_];
                    }
                    e1 = Math.Sqrt(v);
                    fbls.fblscgcreate(x, b, n, cgstate);
                    while( fbls.fblscgiteration(cgstate) )
                    {
                        ablas.rmatrixmv(m, n, a, 0, 0, 0, cgstate.x, 0, ref tmp1, 0);
                        ablas.rmatrixmv(n, m, a, 0, 0, 1, tmp1, 0, ref cgstate.ax, 0);
                        for(i_=0; i_<=n-1;i_++)
                        {
                            cgstate.ax[i_] = cgstate.ax[i_] + alpha*cgstate.x[i_];
                        }
                        v1 = 0.0;
                        for(i_=0; i_<=m-1;i_++)
                        {
                            v1 += tmp1[i_]*tmp1[i_];
                        }
                        v2 = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v2 += cgstate.x[i_]*cgstate.x[i_];
                        }
                        cgstate.xax = v1+alpha*v2;
                    }
                    ablas.rmatrixmv(m, n, a, 0, 0, 0, cgstate.xk, 0, ref tmp1, 0);
                    ablas.rmatrixmv(n, m, a, 0, 0, 1, tmp1, 0, ref tmp2, 0);
                    for(i_=0; i_<=n-1;i_++)
                    {
                        tmp2[i_] = tmp2[i_] + alpha*cgstate.xk[i_];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        tmp2[i_] = tmp2[i_] - b[i_];
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += tmp2[i_]*tmp2[i_];
                    }
                    e2 = Math.Sqrt(v);
                    cgerrors = cgerrors | (double)(Math.Abs(e1-cgstate.e1))>(double)(100*math.machineepsilon*e1);
                    cgerrors = cgerrors | (double)(Math.Abs(e2-cgstate.e2))>(double)(100*math.machineepsilon*e1);
                    cgerrors = cgerrors | (double)(e2)>(double)(0.001*e1);
                }
            }
            
            //
            // report
            //
            waserrors = cgerrors;
            if( !silent )
            {
                System.Console.Write("TESTING FBLS");
                System.Console.WriteLine();
                System.Console.Write("CG ERRORS:                               ");
                if( cgerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


    }
    public class testminlbfgsunit
    {
        public static bool testminlbfgs(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool referror = new bool();
            bool nonconverror = new bool();
            bool eqerror = new bool();
            bool converror = new bool();
            bool crashtest = new bool();
            bool othererrors = new bool();
            bool restartserror = new bool();
            bool precerror = new bool();
            int n = 0;
            int m = 0;
            double[] x = new double[0];
            double[] xe = new double[0];
            double[] b = new double[0];
            double[] xlast = new double[0];
            int i = 0;
            int j = 0;
            double v = 0;
            double[,] a = new double[0,0];
            double[] diagh = new double[0];
            int maxits = 0;
            minlbfgs.minlbfgsstate state = new minlbfgs.minlbfgsstate();
            minlbfgs.minlbfgsreport rep = new minlbfgs.minlbfgsreport();
            double diffstep = 0;
            int dkind = 0;
            int i_ = 0;

            waserrors = false;
            precerror = false;
            nonconverror = false;
            restartserror = false;
            eqerror = false;
            converror = false;
            crashtest = false;
            othererrors = false;
            testpreconditioning(ref precerror);
            testother(ref othererrors);
            
            //
            // Reference problem
            //
            diffstep = 1.0E-6;
            for(dkind=0; dkind<=1; dkind++)
            {
                x = new double[3];
                n = 3;
                m = 2;
                x[0] = 100*math.randomreal()-50;
                x[1] = 100*math.randomreal()-50;
                x[2] = 100*math.randomreal()-50;
                if( dkind==0 )
                {
                    minlbfgs.minlbfgscreate(n, m, x, state);
                }
                if( dkind==1 )
                {
                    minlbfgs.minlbfgscreatef(n, m, x, diffstep, state);
                }
                minlbfgs.minlbfgssetcond(state, 0, 0, 0, 0);
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    if( state.needf | state.needfg )
                    {
                        state.f = math.sqr(state.x[0]-2)+math.sqr(state.x[1])+math.sqr(state.x[2]-state.x[0]);
                    }
                    if( state.needfg )
                    {
                        state.g[0] = 2*(state.x[0]-2)+2*(state.x[0]-state.x[2]);
                        state.g[1] = 2*state.x[1];
                        state.g[2] = 2*(state.x[2]-state.x[0]);
                    }
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                referror = ((rep.terminationtype<=0 | (double)(Math.Abs(x[0]-2))>(double)(0.001)) | (double)(Math.Abs(x[1]))>(double)(0.001)) | (double)(Math.Abs(x[2]-2))>(double)(0.001);
            }
            
            //
            // nonconvex problems with complex surface: we start from point with very small
            // gradient, but we need ever smaller gradient in the next step due to
            // Wolfe conditions.
            //
            diffstep = 1.0E-6;
            for(dkind=0; dkind<=1; dkind++)
            {
                x = new double[1];
                n = 1;
                m = 1;
                v = -100;
                while( (double)(v)<(double)(0.1) )
                {
                    x[0] = v;
                    if( dkind==0 )
                    {
                        minlbfgs.minlbfgscreate(n, m, x, state);
                    }
                    if( dkind==1 )
                    {
                        minlbfgs.minlbfgscreatef(n, m, x, diffstep, state);
                    }
                    minlbfgs.minlbfgssetcond(state, 1.0E-9, 0, 0, 0);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        if( state.needf | state.needfg )
                        {
                            state.f = math.sqr(state.x[0])/(1+math.sqr(state.x[0]));
                        }
                        if( state.needfg )
                        {
                            state.g[0] = (2*state.x[0]*(1+math.sqr(state.x[0]))-math.sqr(state.x[0])*2*state.x[0])/math.sqr(1+math.sqr(state.x[0]));
                        }
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    nonconverror = (nonconverror | rep.terminationtype<=0) | (double)(Math.Abs(x[0]))>(double)(0.001);
                    v = v+0.1;
                }
            }
            
            //
            // F2 problem with restarts:
            // * make several iterations and restart BEFORE termination
            // * iterate and restart AFTER termination
            //
            // NOTE: step is bounded from above to avoid premature convergence
            //
            diffstep = 1.0E-6;
            for(dkind=0; dkind<=1; dkind++)
            {
                x = new double[3];
                n = 3;
                m = 2;
                x[0] = 10+10*math.randomreal();
                x[1] = 10+10*math.randomreal();
                x[2] = 10+10*math.randomreal();
                if( dkind==0 )
                {
                    minlbfgs.minlbfgscreate(n, m, x, state);
                }
                if( dkind==1 )
                {
                    minlbfgs.minlbfgscreatef(n, m, x, diffstep, state);
                }
                minlbfgs.minlbfgssetstpmax(state, 0.1);
                minlbfgs.minlbfgssetcond(state, 0.0000001, 0.0, 0.0, 0);
                for(i=0; i<=10; i++)
                {
                    if( !minlbfgs.minlbfgsiteration(state) )
                    {
                        break;
                    }
                    testfunc2(state);
                }
                x[0] = 10+10*math.randomreal();
                x[1] = 10+10*math.randomreal();
                x[2] = 10+10*math.randomreal();
                minlbfgs.minlbfgsrestartfrom(state, x);
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    testfunc2(state);
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                restartserror = (((restartserror | rep.terminationtype<=0) | (double)(Math.Abs(x[0]-Math.Log(2)))>(double)(0.01)) | (double)(Math.Abs(x[1]))>(double)(0.01)) | (double)(Math.Abs(x[2]-Math.Log(2)))>(double)(0.01);
                x[0] = 10+10*math.randomreal();
                x[1] = 10+10*math.randomreal();
                x[2] = 10+10*math.randomreal();
                minlbfgs.minlbfgsrestartfrom(state, x);
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    testfunc2(state);
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                restartserror = (((restartserror | rep.terminationtype<=0) | (double)(Math.Abs(x[0]-Math.Log(2)))>(double)(0.01)) | (double)(Math.Abs(x[1]))>(double)(0.01)) | (double)(Math.Abs(x[2]-Math.Log(2)))>(double)(0.01);
            }
            
            //
            // Linear equations
            //
            diffstep = 1.0E-6;
            for(n=1; n<=10; n++)
            {
                
                //
                // Prepare task
                //
                a = new double[n-1+1, n-1+1];
                x = new double[n-1+1];
                xe = new double[n-1+1];
                b = new double[n-1+1];
                for(i=0; i<=n-1; i++)
                {
                    xe[i] = 2*math.randomreal()-1;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a[i,j] = 2*math.randomreal()-1;
                    }
                    a[i,i] = a[i,i]+3*Math.Sign(a[i,i]);
                }
                for(i=0; i<=n-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += a[i,i_]*xe[i_];
                    }
                    b[i] = v;
                }
                
                //
                // Test different M/DKind
                //
                for(m=1; m<=n; m++)
                {
                    for(dkind=0; dkind<=1; dkind++)
                    {
                        
                        //
                        // Solve task
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = 2*math.randomreal()-1;
                        }
                        if( dkind==0 )
                        {
                            minlbfgs.minlbfgscreate(n, m, x, state);
                        }
                        if( dkind==1 )
                        {
                            minlbfgs.minlbfgscreatef(n, m, x, diffstep, state);
                        }
                        minlbfgs.minlbfgssetcond(state, 0, 0, 0, 0);
                        while( minlbfgs.minlbfgsiteration(state) )
                        {
                            if( state.needf | state.needfg )
                            {
                                state.f = 0;
                            }
                            if( state.needfg )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    state.g[i] = 0;
                                }
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*state.x[i_];
                                }
                                if( state.needf | state.needfg )
                                {
                                    state.f = state.f+math.sqr(v-b[i]);
                                }
                                if( state.needfg )
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        state.g[j] = state.g[j]+2*(v-b[i])*a[i,j];
                                    }
                                }
                            }
                        }
                        minlbfgs.minlbfgsresults(state, ref x, rep);
                        eqerror = eqerror | rep.terminationtype<=0;
                        for(i=0; i<=n-1; i++)
                        {
                            eqerror = eqerror | (double)(Math.Abs(x[i]-xe[i]))>(double)(0.001);
                        }
                    }
                }
            }
            
            //
            // Testing convergence properties
            //
            diffstep = 1.0E-6;
            for(dkind=0; dkind<=1; dkind++)
            {
                x = new double[3];
                n = 3;
                m = 2;
                for(i=0; i<=2; i++)
                {
                    x[i] = 6*math.randomreal()-3;
                }
                if( dkind==0 )
                {
                    minlbfgs.minlbfgscreate(n, m, x, state);
                }
                if( dkind==1 )
                {
                    minlbfgs.minlbfgscreatef(n, m, x, diffstep, state);
                }
                minlbfgs.minlbfgssetcond(state, 0.001, 0, 0, 0);
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    testfunc3(state);
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                converror = converror | rep.terminationtype!=4;
                for(i=0; i<=2; i++)
                {
                    x[i] = 6*math.randomreal()-3;
                }
                if( dkind==0 )
                {
                    minlbfgs.minlbfgscreate(n, m, x, state);
                }
                if( dkind==1 )
                {
                    minlbfgs.minlbfgscreatef(n, m, x, diffstep, state);
                }
                minlbfgs.minlbfgssetcond(state, 0, 0.001, 0, 0);
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    testfunc3(state);
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                converror = converror | rep.terminationtype!=1;
                for(i=0; i<=2; i++)
                {
                    x[i] = 6*math.randomreal()-3;
                }
                if( dkind==0 )
                {
                    minlbfgs.minlbfgscreate(n, m, x, state);
                }
                if( dkind==1 )
                {
                    minlbfgs.minlbfgscreatef(n, m, x, diffstep, state);
                }
                minlbfgs.minlbfgssetcond(state, 0, 0, 0.001, 0);
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    testfunc3(state);
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                converror = converror | rep.terminationtype!=2;
                for(i=0; i<=2; i++)
                {
                    x[i] = 2*math.randomreal()-1;
                }
                if( dkind==0 )
                {
                    minlbfgs.minlbfgscreate(n, m, x, state);
                }
                if( dkind==1 )
                {
                    minlbfgs.minlbfgscreatef(n, m, x, diffstep, state);
                }
                minlbfgs.minlbfgssetcond(state, 0, 0, 0, 10);
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    testfunc3(state);
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                converror = (converror | rep.terminationtype!=5) | rep.iterationscount!=10;
            }
            
            //
            // Crash test: too many iterations on a simple tasks
            // May fail when encounter zero step, underflow or something like that
            //
            x = new double[2+1];
            n = 3;
            m = 2;
            maxits = 10000;
            for(i=0; i<=2; i++)
            {
                x[i] = 6*math.randomreal()-3;
            }
            minlbfgs.minlbfgscreate(n, m, x, state);
            minlbfgs.minlbfgssetcond(state, 0, 0, 0, maxits);
            while( minlbfgs.minlbfgsiteration(state) )
            {
                state.f = math.sqr(Math.Exp(state.x[0])-2)+math.sqr(state.x[1])+math.sqr(state.x[2]-state.x[0]);
                state.g[0] = 2*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[0]-state.x[2]);
                state.g[1] = 2*state.x[1];
                state.g[2] = 2*(state.x[2]-state.x[0]);
            }
            minlbfgs.minlbfgsresults(state, ref x, rep);
            crashtest = crashtest | rep.terminationtype<=0;
            
            //
            // end
            //
            waserrors = ((((((referror | nonconverror) | eqerror) | converror) | crashtest) | othererrors) | restartserror) | precerror;
            if( !silent )
            {
                System.Console.Write("TESTING L-BFGS OPTIMIZATION");
                System.Console.WriteLine();
                System.Console.Write("REFERENCE PROBLEM:                        ");
                if( referror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("NON-CONVEX PROBLEM:                       ");
                if( nonconverror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LINEAR EQUATIONS:                         ");
                if( eqerror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("RESTARTS:                                 ");
                if( restartserror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("PRECONDITIONER:                           ");
                if( precerror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("CONVERGENCE PROPERTIES:                   ");
                if( converror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("CRASH TEST:                               ");
                if( crashtest )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("OTHER PROPERTIES:                         ");
                if( othererrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Calculate test function #1

        It may show very interesting behavior when optimized with 'x[0]>=ln(2)'
        constraint.
        *************************************************************************/
        private static void testfunc1(minlbfgs.minlbfgsstate state)
        {
            if( (double)(state.x[0])<(double)(100) )
            {
                if( state.needf | state.needfg )
                {
                    state.f = math.sqr(Math.Exp(state.x[0])-2)+math.sqr(state.x[1])+math.sqr(state.x[2]-state.x[0]);
                }
                if( state.needfg )
                {
                    state.g[0] = 2*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[0]-state.x[2]);
                    state.g[1] = 2*state.x[1];
                    state.g[2] = 2*(state.x[2]-state.x[0]);
                }
            }
            else
            {
                if( state.needf | state.needfg )
                {
                    state.f = Math.Sqrt(math.maxrealnumber);
                }
                if( state.needfg )
                {
                    state.g[0] = Math.Sqrt(math.maxrealnumber);
                    state.g[1] = 0;
                    state.g[2] = 0;
                }
            }
        }


        /*************************************************************************
        Calculate test function #2

        Simple variation of #1, much more nonlinear, which makes unlikely premature
        convergence of algorithm .
        *************************************************************************/
        private static void testfunc2(minlbfgs.minlbfgsstate state)
        {
            if( (double)(state.x[0])<(double)(100) )
            {
                if( state.needf | state.needfg )
                {
                    state.f = math.sqr(Math.Exp(state.x[0])-2)+math.sqr(math.sqr(state.x[1]))+math.sqr(state.x[2]-state.x[0]);
                }
                if( state.needfg )
                {
                    state.g[0] = 2*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[0]-state.x[2]);
                    state.g[1] = 4*state.x[1]*math.sqr(state.x[1]);
                    state.g[2] = 2*(state.x[2]-state.x[0]);
                }
            }
            else
            {
                if( state.needf | state.needfg )
                {
                    state.f = Math.Sqrt(math.maxrealnumber);
                }
                if( state.needfg )
                {
                    state.g[0] = Math.Sqrt(math.maxrealnumber);
                    state.g[1] = 0;
                    state.g[2] = 0;
                }
            }
        }


        /*************************************************************************
        Calculate test function #3

        Simple variation of #1, much more nonlinear, with non-zero value at minimum.
        It achieve two goals:
        * makes unlikely premature convergence of algorithm .
        * solves some issues with EpsF stopping condition which arise when
          F(minimum) is zero

        *************************************************************************/
        private static void testfunc3(minlbfgs.minlbfgsstate state)
        {
            double s = 0;

            s = 0.001;
            if( (double)(state.x[0])<(double)(100) )
            {
                if( state.needf | state.needfg )
                {
                    state.f = math.sqr(Math.Exp(state.x[0])-2)+math.sqr(math.sqr(state.x[1])+s)+math.sqr(state.x[2]-state.x[0]);
                }
                if( state.needfg )
                {
                    state.g[0] = 2*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[0]-state.x[2]);
                    state.g[1] = 2*(math.sqr(state.x[1])+s)*2*state.x[1];
                    state.g[2] = 2*(state.x[2]-state.x[0]);
                }
            }
            else
            {
                if( state.needf | state.needfg )
                {
                    state.f = Math.Sqrt(math.maxrealnumber);
                }
                if( state.needfg )
                {
                    state.g[0] = Math.Sqrt(math.maxrealnumber);
                    state.g[1] = 0;
                    state.g[2] = 0;
                }
            }
        }


        /*************************************************************************
        Calculate test function IIP2

        f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1)

        It has high condition number which makes fast convergence unlikely without
        good preconditioner.

        *************************************************************************/
        private static void calciip2(minlbfgs.minlbfgsstate state,
            int n)
        {
            int i = 0;

            if( state.needf | state.needfg )
            {
                state.f = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                if( state.needf | state.needfg )
                {
                    state.f = state.f+math.sqr(i*i+1)*math.sqr(state.x[i]);
                }
                if( state.needfg )
                {
                    state.g[i] = math.sqr(i*i+1)*2*state.x[i];
                }
            }
        }


        /*************************************************************************
        This function tests preconditioning

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testpreconditioning(ref bool err)
        {
            int pass = 0;
            int n = 0;
            int m = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int cntb1 = 0;
            int cntb2 = 0;
            int cntg1 = 0;
            int cntg2 = 0;
            double epsg = 0;
            int pkind = 0;
            minlbfgs.minlbfgsstate state = new minlbfgs.minlbfgsstate();
            minlbfgs.minlbfgsreport rep = new minlbfgs.minlbfgsreport();
            double[] x = new double[0];
            double[] s = new double[0];
            double[,] a = new double[0,0];
            double[] diagh = new double[0];

            m = 1;
            k = 50;
            epsg = 1.0E-10;
            
            //
            // Preconditioner test1.
            //
            // If
            // * B1 is default preconditioner
            // * B2 is Cholesky preconditioner with unit diagonal
            // * G1 is Cholesky preconditioner based on exact Hessian with perturbations
            // * G2 is diagonal precomditioner based on approximate diagonal of Hessian matrix
            // then "bad" preconditioners (B1/B2/..) are worse than "good" ones (G1/G2/..).
            // "Worse" means more iterations to converge.
            //
            // We test it using f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1) and L-BFGS
            // optimizer with deliberately small M=1.
            //
            // N        - problem size
            // PKind    - zero for upper triangular preconditioner, one for lower triangular.
            // K        - number of repeated passes (should be large enough to average out random factors)
            //
            for(n=10; n<=15; n++)
            {
                pkind = math.randominteger(2);
                x = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 0;
                }
                minlbfgs.minlbfgscreate(n, m, x, state);
                
                //
                // Test it with default preconditioner
                //
                minlbfgs.minlbfgssetprecdefault(state);
                cntb1 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    minlbfgs.minlbfgsrestartfrom(state, x);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        calciip2(state, n);
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    cntb1 = cntb1+rep.iterationscount;
                    err = err | rep.terminationtype<=0;
                }
                
                //
                // Test it with unit preconditioner
                //
                a = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j )
                        {
                            a[i,i] = 1;
                        }
                        else
                        {
                            a[i,j] = 0;
                        }
                    }
                }
                minlbfgs.minlbfgssetpreccholesky(state, a, pkind==0);
                cntb2 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    minlbfgs.minlbfgsrestartfrom(state, x);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        calciip2(state, n);
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    cntb2 = cntb2+rep.iterationscount;
                    err = err | rep.terminationtype<=0;
                }
                
                //
                // Test it with perturbed Hessian preconditioner
                //
                a = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j )
                        {
                            a[i,i] = (i*i+1)*(0.8+0.4*math.randomreal());
                        }
                        else
                        {
                            if( (pkind==0 & j>i) | (pkind==1 & j<i) )
                            {
                                a[i,j] = 0.1*math.randomreal()-0.05;
                            }
                            else
                            {
                                a[i,j] = Double.NaN;
                            }
                        }
                    }
                }
                minlbfgs.minlbfgssetpreccholesky(state, a, pkind==0);
                cntg1 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    minlbfgs.minlbfgsrestartfrom(state, x);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        calciip2(state, n);
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    cntg1 = cntg1+rep.iterationscount;
                    err = err | rep.terminationtype<=0;
                }
                
                //
                // Test it with perturbed diagonal preconditioner
                //
                diagh = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    diagh[i] = 2*math.sqr(i*i+1)*(0.8+0.4*math.randomreal());
                }
                minlbfgs.minlbfgssetprecdiag(state, diagh);
                cntg2 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    minlbfgs.minlbfgsrestartfrom(state, x);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        calciip2(state, n);
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    cntg2 = cntg2+rep.iterationscount;
                    err = err | rep.terminationtype<=0;
                }
                
                //
                // Compare
                //
                err = err | cntb1<cntg1;
                err = err | cntb2<cntg1;
                err = err | cntb1<cntg2;
                err = err | cntb2<cntg2;
            }
            
            //
            // Preconditioner test 2.
            //
            // If
            // * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
            // * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
            // then B2 is worse than G2.
            // "Worse" means more iterations to converge.
            //
            for(n=10; n<=15; n++)
            {
                x = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 0;
                }
                minlbfgs.minlbfgscreate(n, m, x, state);
                s = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    s[i] = 1/Math.Sqrt(2*Math.Pow(i*i+1, 2)*(0.8+0.4*math.randomreal()));
                }
                minlbfgs.minlbfgssetprecdefault(state);
                minlbfgs.minlbfgssetscale(state, s);
                cntb2 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    minlbfgs.minlbfgsrestartfrom(state, x);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        calciip2(state, n);
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    cntb2 = cntb2+rep.iterationscount;
                    err = err | rep.terminationtype<=0;
                }
                minlbfgs.minlbfgssetprecscale(state);
                minlbfgs.minlbfgssetscale(state, s);
                cntg2 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    minlbfgs.minlbfgsrestartfrom(state, x);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        calciip2(state, n);
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    cntg2 = cntg2+rep.iterationscount;
                    err = err | rep.terminationtype<=0;
                }
                err = err | cntb2<cntg2;
            }
        }


        /*************************************************************************
        This function tests other properties

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testother(ref bool err)
        {
            int n = 0;
            int m = 0;
            double[] x = new double[0];
            double[] a = new double[0];
            double[] s = new double[0];
            double[] h = new double[0];
            double[] xlast = new double[0];
            bool hasxlast = new bool();
            double lastscaledstep = 0;
            int i = 0;
            minlbfgs.minlbfgsstate state = new minlbfgs.minlbfgsstate();
            minlbfgs.minlbfgsreport rep = new minlbfgs.minlbfgsreport();
            double fprev = 0;
            double xprev = 0;
            double v = 0;
            double stpmax = 0;
            double tmpeps = 0;
            double epsg = 0;
            int pkind = 0;
            int ckind = 0;
            int mkind = 0;
            double vc = 0;
            double vm = 0;
            double diffstep = 0;
            int dkind = 0;
            bool wasf = new bool();
            bool wasfg = new bool();
            double r = 0;
            int i_ = 0;

            
            //
            // Test reports (F should form monotone sequence)
            //
            n = 50;
            m = 2;
            x = new double[n];
            xlast = new double[n];
            for(i=0; i<=n-1; i++)
            {
                x[i] = 1;
            }
            minlbfgs.minlbfgscreate(n, m, x, state);
            minlbfgs.minlbfgssetcond(state, 0, 0, 0, 100);
            minlbfgs.minlbfgssetxrep(state, true);
            fprev = math.maxrealnumber;
            while( minlbfgs.minlbfgsiteration(state) )
            {
                if( state.needfg )
                {
                    state.f = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+math.sqr((1+i)*state.x[i]);
                        state.g[i] = 2*(1+i)*state.x[i];
                    }
                }
                if( state.xupdated )
                {
                    err = err | (double)(state.f)>(double)(fprev);
                    if( (double)(fprev)==(double)(math.maxrealnumber) )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            err = err | (double)(state.x[i])!=(double)(x[i]);
                        }
                    }
                    fprev = state.f;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        xlast[i_] = state.x[i_];
                    }
                }
            }
            minlbfgs.minlbfgsresults(state, ref x, rep);
            for(i=0; i<=n-1; i++)
            {
                err = err | (double)(x[i])!=(double)(xlast[i]);
            }
            
            //
            // Test differentiation vs. analytic gradient
            // (first one issues NeedF requests, second one issues NeedFG requests)
            //
            n = 50;
            m = 5;
            diffstep = 1.0E-6;
            for(dkind=0; dkind<=1; dkind++)
            {
                x = new double[n];
                xlast = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 1;
                }
                if( dkind==0 )
                {
                    minlbfgs.minlbfgscreate(n, m, x, state);
                }
                if( dkind==1 )
                {
                    minlbfgs.minlbfgscreatef(n, m, x, diffstep, state);
                }
                minlbfgs.minlbfgssetcond(state, 0, 0, 0, n/2);
                wasf = false;
                wasfg = false;
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    if( state.needf | state.needfg )
                    {
                        state.f = 0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        if( state.needf | state.needfg )
                        {
                            state.f = state.f+math.sqr((1+i)*state.x[i]);
                        }
                        if( state.needfg )
                        {
                            state.g[i] = 2*(1+i)*state.x[i];
                        }
                    }
                    wasf = wasf | state.needf;
                    wasfg = wasfg | state.needfg;
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                if( dkind==0 )
                {
                    err = (err | wasf) | !wasfg;
                }
                if( dkind==1 )
                {
                    err = (err | !wasf) | wasfg;
                }
            }
            
            //
            // Test that numerical differentiation uses scaling.
            //
            // In order to test that we solve simple optimization
            // problem: min(x^2) with initial x equal to 0.0.
            //
            // We choose random DiffStep and S, then we check that
            // optimizer evaluates function at +-DiffStep*S only.
            //
            x = new double[1];
            s = new double[1];
            diffstep = math.randomreal()*1.0E-6;
            s[0] = Math.Exp(math.randomreal()*4-2);
            x[0] = 0;
            minlbfgs.minlbfgscreatef(1, 1, x, diffstep, state);
            minlbfgs.minlbfgssetcond(state, 1.0E-6, 0, 0, 0);
            minlbfgs.minlbfgssetscale(state, s);
            v = 0;
            while( minlbfgs.minlbfgsiteration(state) )
            {
                state.f = math.sqr(state.x[0]);
                v = Math.Max(v, Math.Abs(state.x[0]));
            }
            minlbfgs.minlbfgsresults(state, ref x, rep);
            r = v/(s[0]*diffstep);
            err = err | (double)(Math.Abs(Math.Log(r)))>(double)(Math.Log(1+1000*math.machineepsilon));
            
            //
            // test maximum step
            //
            n = 1;
            m = 1;
            x = new double[n];
            x[0] = 100;
            stpmax = 0.05+0.05*math.randomreal();
            minlbfgs.minlbfgscreate(n, m, x, state);
            minlbfgs.minlbfgssetcond(state, 1.0E-9, 0, 0, 0);
            minlbfgs.minlbfgssetstpmax(state, stpmax);
            minlbfgs.minlbfgssetxrep(state, true);
            xprev = x[0];
            while( minlbfgs.minlbfgsiteration(state) )
            {
                if( state.needfg )
                {
                    state.f = Math.Exp(state.x[0])+Math.Exp(-state.x[0]);
                    state.g[0] = Math.Exp(state.x[0])-Math.Exp(-state.x[0]);
                    err = err | (double)(Math.Abs(state.x[0]-xprev))>(double)((1+Math.Sqrt(math.machineepsilon))*stpmax);
                }
                if( state.xupdated )
                {
                    err = err | (double)(Math.Abs(state.x[0]-xprev))>(double)((1+Math.Sqrt(math.machineepsilon))*stpmax);
                    xprev = state.x[0];
                }
            }
            
            //
            // Test correctness of the scaling:
            // * initial point is random point from [+1,+2]^N
            // * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
            // * we use random scaling matrix
            // * we test different variants of the preconditioning:
            //   0) unit preconditioner
            //   1) random diagonal from [0.01,100]
            //   2) scale preconditioner
            // * we set stringent stopping conditions (we try EpsG and EpsX)
            // * and we test that in the extremum stopping conditions are
            //   satisfied subject to the current scaling coefficients.
            //
            tmpeps = 1.0E-10;
            m = 1;
            for(n=1; n<=10; n++)
            {
                for(pkind=0; pkind<=2; pkind++)
                {
                    x = new double[n];
                    xlast = new double[n];
                    a = new double[n];
                    s = new double[n];
                    h = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = math.randomreal()+1;
                        a[i] = Math.Exp(Math.Log(100)*(2*math.randomreal()-1));
                        s[i] = Math.Exp(Math.Log(100)*(2*math.randomreal()-1));
                        h[i] = Math.Exp(Math.Log(100)*(2*math.randomreal()-1));
                    }
                    minlbfgs.minlbfgscreate(n, m, x, state);
                    minlbfgs.minlbfgssetscale(state, s);
                    minlbfgs.minlbfgssetxrep(state, true);
                    if( pkind==1 )
                    {
                        minlbfgs.minlbfgssetprecdiag(state, h);
                    }
                    if( pkind==2 )
                    {
                        minlbfgs.minlbfgssetprecscale(state);
                    }
                    
                    //
                    // Test gradient-based stopping condition
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = math.randomreal()+1;
                    }
                    minlbfgs.minlbfgssetcond(state, tmpeps, 0, 0, 0);
                    minlbfgs.minlbfgsrestartfrom(state, x);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        if( state.needfg )
                        {
                            state.f = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+a[i]*Math.Pow(state.x[i], 4);
                                state.g[i] = 4*a[i]*Math.Pow(state.x[i], 3);
                            }
                        }
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    if( rep.terminationtype<=0 )
                    {
                        err = true;
                        return;
                    }
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+math.sqr(s[i]*4*a[i]*Math.Pow(x[i], 3));
                    }
                    v = Math.Sqrt(v);
                    err = err | (double)(v)>(double)(tmpeps);
                    
                    //
                    // Test step-based stopping condition
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = math.randomreal()+1;
                    }
                    hasxlast = false;
                    minlbfgs.minlbfgssetcond(state, 0, 0, tmpeps, 0);
                    minlbfgs.minlbfgsrestartfrom(state, x);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        if( state.needfg )
                        {
                            state.f = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+a[i]*Math.Pow(state.x[i], 4);
                                state.g[i] = 4*a[i]*Math.Pow(state.x[i], 3);
                            }
                        }
                        if( state.xupdated )
                        {
                            if( hasxlast )
                            {
                                lastscaledstep = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    lastscaledstep = lastscaledstep+math.sqr(state.x[i]-xlast[i])/math.sqr(s[i]);
                                }
                                lastscaledstep = Math.Sqrt(lastscaledstep);
                            }
                            else
                            {
                                lastscaledstep = 0;
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xlast[i_] = state.x[i_];
                            }
                            hasxlast = true;
                        }
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    if( rep.terminationtype<=0 )
                    {
                        err = true;
                        return;
                    }
                    err = err | (double)(lastscaledstep)>(double)(tmpeps);
                }
            }
            
            //
            // Check correctness of the "trimming".
            //
            // Trimming is a technique which is used to help algorithm
            // cope with unbounded functions. In order to check this
            // technique we will try to solve following optimization
            // problem:
            //
            //     min f(x) subject to no constraints on X
            //            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
            //     f(x) = {
            //            { M, if x<=-0.999999 or x>=0.999999
            //
            // where c is either 1.0 or 1.0E+6, M is either 1.0E8, 1.0E20 or +INF
            // (we try different combinations)
            //
            for(ckind=0; ckind<=1; ckind++)
            {
                for(mkind=0; mkind<=2; mkind++)
                {
                    
                    //
                    // Choose c and M
                    //
                    if( ckind==0 )
                    {
                        vc = 1.0;
                    }
                    if( ckind==1 )
                    {
                        vc = 1.0E+6;
                    }
                    if( mkind==0 )
                    {
                        vm = 1.0E+8;
                    }
                    if( mkind==1 )
                    {
                        vm = 1.0E+20;
                    }
                    if( mkind==2 )
                    {
                        vm = Double.PositiveInfinity;
                    }
                    
                    //
                    // Create optimizer, solve optimization problem
                    //
                    epsg = 1.0E-6*vc;
                    x = new double[1];
                    x[0] = 0.0;
                    minlbfgs.minlbfgscreate(1, 1, x, state);
                    minlbfgs.minlbfgssetcond(state, epsg, 0, 0, 0);
                    while( minlbfgs.minlbfgsiteration(state) )
                    {
                        if( state.needfg )
                        {
                            if( (double)(-0.999999)<(double)(state.x[0]) & (double)(state.x[0])<(double)(0.999999) )
                            {
                                state.f = 1/(1-state.x[0])+1/(1+state.x[0])+vc*state.x[0];
                                state.g[0] = 1/math.sqr(1-state.x[0])-1/math.sqr(1+state.x[0])+vc;
                            }
                            else
                            {
                                state.f = vm;
                            }
                        }
                    }
                    minlbfgs.minlbfgsresults(state, ref x, rep);
                    if( rep.terminationtype<=0 )
                    {
                        err = true;
                        return;
                    }
                    err = err | (double)(Math.Abs(1/math.sqr(1-x[0])-1/math.sqr(1+x[0])+vc))>(double)(epsg);
                }
            }
        }


    }
    public class testmlptrainunit
    {
        public static bool testmlptrain(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            int passcount = 0;
            int maxn = 0;
            int maxhid = 0;
            int info = 0;
            int nf = 0;
            int nl = 0;
            int nhid1 = 0;
            int nhid2 = 0;
            int nkind = 0;
            int i = 0;
            mlpbase.multilayerperceptron network = new mlpbase.multilayerperceptron();
            mlpbase.multilayerperceptron network2 = new mlpbase.multilayerperceptron();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            mlptrain.mlpcvreport cvrep = new mlptrain.mlpcvreport();
            int ncount = 0;
            double[,] xy = new double[0,0];
            double[,] valxy = new double[0,0];
            bool inferrors = new bool();
            bool procerrors = new bool();
            bool graderrors = new bool();
            bool hesserrors = new bool();
            bool trnerrors = new bool();

            waserrors = false;
            inferrors = false;
            procerrors = false;
            graderrors = false;
            hesserrors = false;
            trnerrors = false;
            passcount = 10;
            maxn = 4;
            maxhid = 4;
            
            //
            // General multilayer network tests
            //
            for(nf=1; nf<=maxn; nf++)
            {
                for(nl=1; nl<=maxn; nl++)
                {
                    for(nhid1=0; nhid1<=maxhid; nhid1++)
                    {
                        for(nhid2=0; nhid2<=0; nhid2++)
                        {
                            for(nkind=0; nkind<=3; nkind++)
                            {
                                
                                //
                                //  Skip meaningless parameters combinations
                                //
                                if( nkind==1 & nl<2 )
                                {
                                    continue;
                                }
                                if( nhid1==0 & nhid2!=0 )
                                {
                                    continue;
                                }
                                
                                //
                                // Tests
                                //
                                testinformational(nkind, nf, nhid1, nhid2, nl, passcount, ref inferrors);
                                testprocessing(nkind, nf, nhid1, nhid2, nl, passcount, ref procerrors);
                                testgradient(nkind, nf, nhid1, nhid2, nl, passcount, ref graderrors);
                                testhessian(nkind, nf, nhid1, nhid2, nl, passcount, ref hesserrors);
                            }
                        }
                    }
                }
            }
            
            //
            // Test network training on simple XOR problem
            //
            xy = new double[3+1, 2+1];
            xy[0,0] = -1;
            xy[0,1] = -1;
            xy[0,2] = -1;
            xy[1,0] = 1;
            xy[1,1] = -1;
            xy[1,2] = 1;
            xy[2,0] = -1;
            xy[2,1] = 1;
            xy[2,2] = 1;
            xy[3,0] = 1;
            xy[3,1] = 1;
            xy[3,2] = -1;
            mlpbase.mlpcreate1(2, 2, 1, network);
            mlptrain.mlptrainlm(network, xy, 4, 0.001, 10, ref info, rep);
            trnerrors = trnerrors | (double)(mlpbase.mlprmserror(network, xy, 4))>(double)(0.1);
            
            //
            // Test CV on random noisy problem
            //
            ncount = 100;
            xy = new double[ncount-1+1, 1+1];
            for(i=0; i<=ncount-1; i++)
            {
                xy[i,0] = 2*math.randomreal()-1;
                xy[i,1] = math.randominteger(4);
            }
            mlpbase.mlpcreatec0(1, 4, network);
            mlptrain.mlpkfoldcvlm(network, xy, ncount, 0.001, 5, 10, ref info, rep, cvrep);
            
            //
            // Final report
            //
            waserrors = (((inferrors | procerrors) | graderrors) | hesserrors) | trnerrors;
            if( !silent )
            {
                System.Console.Write("MLP TEST");
                System.Console.WriteLine();
                System.Console.Write("INFORMATIONAL FUNCTIONS:                 ");
                if( !inferrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("BASIC PROCESSING:                        ");
                if( !procerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("GRADIENT CALCULATION:                    ");
                if( !graderrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("HESSIAN CALCULATION:                     ");
                if( !hesserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("TRAINING:                                ");
                if( !trnerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Network creation

        This function creates network with desired structure. Network  is  created
        using one of the three methods:
        a) straighforward creation using MLPCreate???()
        b) MLPCreate???() for proxy object, which is copied with PassThroughSerializer()
        c) MLPCreate???() for proxy object, which is copied with MLPCopy()
        One of these methods is chosen with probability 1/3.
        *************************************************************************/
        private static void createnetwork(mlpbase.multilayerperceptron network,
            int nkind,
            double a1,
            double a2,
            int nin,
            int nhid1,
            int nhid2,
            int nout)
        {
            int mkind = 0;
            mlpbase.multilayerperceptron tmp = new mlpbase.multilayerperceptron();

            ap.assert(((nin>0 & nhid1>=0) & nhid2>=0) & nout>0, "CreateNetwork error");
            ap.assert(nhid1!=0 | nhid2==0, "CreateNetwork error");
            ap.assert(nkind!=1 | nout>=2, "CreateNetwork error");
            mkind = math.randominteger(3);
            if( nhid1==0 )
            {
                
                //
                // No hidden layers
                //
                if( nkind==0 )
                {
                    if( mkind==0 )
                    {
                        mlpbase.mlpcreate0(nin, nout, network);
                    }
                    if( mkind==1 )
                    {
                        mlpbase.mlpcreate0(nin, nout, tmp);
                        {
                            //
                            // This code passes data structure through serializers
                            // (serializes it to string and loads back)
                            //
                            serializer _local_serializer;
                            string _local_str;
                            
                            _local_serializer = new serializer();
                            _local_serializer.alloc_start();
                            mlpbase.mlpalloc(_local_serializer, tmp);
                            _local_serializer.sstart_str();
                            mlpbase.mlpserialize(_local_serializer, tmp);
                            _local_serializer.stop();
                            _local_str = _local_serializer.get_string();
                            
                            _local_serializer = new serializer();
                            _local_serializer.ustart_str(_local_str);
                            mlpbase.mlpunserialize(_local_serializer, network);
                            _local_serializer.stop();
                        }
                    }
                    if( mkind==2 )
                    {
                        mlpbase.mlpcreate0(nin, nout, tmp);
                        mlpbase.mlpcopy(tmp, network);
                    }
                }
                else
                {
                    if( nkind==1 )
                    {
                        if( mkind==0 )
                        {
                            mlpbase.mlpcreatec0(nin, nout, network);
                        }
                        if( mkind==1 )
                        {
                            mlpbase.mlpcreatec0(nin, nout, tmp);
                            {
                                //
                                // This code passes data structure through serializers
                                // (serializes it to string and loads back)
                                //
                                serializer _local_serializer;
                                string _local_str;
                                
                                _local_serializer = new serializer();
                                _local_serializer.alloc_start();
                                mlpbase.mlpalloc(_local_serializer, tmp);
                                _local_serializer.sstart_str();
                                mlpbase.mlpserialize(_local_serializer, tmp);
                                _local_serializer.stop();
                                _local_str = _local_serializer.get_string();
                                
                                _local_serializer = new serializer();
                                _local_serializer.ustart_str(_local_str);
                                mlpbase.mlpunserialize(_local_serializer, network);
                                _local_serializer.stop();
                            }
                        }
                        if( mkind==2 )
                        {
                            mlpbase.mlpcreatec0(nin, nout, tmp);
                            mlpbase.mlpcopy(tmp, network);
                        }
                    }
                    else
                    {
                        if( nkind==2 )
                        {
                            if( mkind==0 )
                            {
                                mlpbase.mlpcreateb0(nin, nout, a1, a2, network);
                            }
                            if( mkind==1 )
                            {
                                mlpbase.mlpcreateb0(nin, nout, a1, a2, tmp);
                                {
                                    //
                                    // This code passes data structure through serializers
                                    // (serializes it to string and loads back)
                                    //
                                    serializer _local_serializer;
                                    string _local_str;
                                    
                                    _local_serializer = new serializer();
                                    _local_serializer.alloc_start();
                                    mlpbase.mlpalloc(_local_serializer, tmp);
                                    _local_serializer.sstart_str();
                                    mlpbase.mlpserialize(_local_serializer, tmp);
                                    _local_serializer.stop();
                                    _local_str = _local_serializer.get_string();
                                    
                                    _local_serializer = new serializer();
                                    _local_serializer.ustart_str(_local_str);
                                    mlpbase.mlpunserialize(_local_serializer, network);
                                    _local_serializer.stop();
                                }
                            }
                            if( mkind==2 )
                            {
                                mlpbase.mlpcreateb0(nin, nout, a1, a2, tmp);
                                mlpbase.mlpcopy(tmp, network);
                            }
                        }
                        else
                        {
                            if( nkind==3 )
                            {
                                if( mkind==0 )
                                {
                                    mlpbase.mlpcreater0(nin, nout, a1, a2, network);
                                }
                                if( mkind==1 )
                                {
                                    mlpbase.mlpcreater0(nin, nout, a1, a2, tmp);
                                    {
                                        //
                                        // This code passes data structure through serializers
                                        // (serializes it to string and loads back)
                                        //
                                        serializer _local_serializer;
                                        string _local_str;
                                        
                                        _local_serializer = new serializer();
                                        _local_serializer.alloc_start();
                                        mlpbase.mlpalloc(_local_serializer, tmp);
                                        _local_serializer.sstart_str();
                                        mlpbase.mlpserialize(_local_serializer, tmp);
                                        _local_serializer.stop();
                                        _local_str = _local_serializer.get_string();
                                        
                                        _local_serializer = new serializer();
                                        _local_serializer.ustart_str(_local_str);
                                        mlpbase.mlpunserialize(_local_serializer, network);
                                        _local_serializer.stop();
                                    }
                                }
                                if( mkind==2 )
                                {
                                    mlpbase.mlpcreater0(nin, nout, a1, a2, tmp);
                                    mlpbase.mlpcopy(tmp, network);
                                }
                            }
                        }
                    }
                }
                mlpbase.mlprandomizefull(network);
                return;
            }
            if( nhid2==0 )
            {
                
                //
                // One hidden layer
                //
                if( nkind==0 )
                {
                    if( mkind==0 )
                    {
                        mlpbase.mlpcreate1(nin, nhid1, nout, network);
                    }
                    if( mkind==1 )
                    {
                        mlpbase.mlpcreate1(nin, nhid1, nout, tmp);
                        {
                            //
                            // This code passes data structure through serializers
                            // (serializes it to string and loads back)
                            //
                            serializer _local_serializer;
                            string _local_str;
                            
                            _local_serializer = new serializer();
                            _local_serializer.alloc_start();
                            mlpbase.mlpalloc(_local_serializer, tmp);
                            _local_serializer.sstart_str();
                            mlpbase.mlpserialize(_local_serializer, tmp);
                            _local_serializer.stop();
                            _local_str = _local_serializer.get_string();
                            
                            _local_serializer = new serializer();
                            _local_serializer.ustart_str(_local_str);
                            mlpbase.mlpunserialize(_local_serializer, network);
                            _local_serializer.stop();
                        }
                    }
                    if( mkind==2 )
                    {
                        mlpbase.mlpcreate1(nin, nhid1, nout, tmp);
                        mlpbase.mlpcopy(tmp, network);
                    }
                }
                else
                {
                    if( nkind==1 )
                    {
                        if( mkind==0 )
                        {
                            mlpbase.mlpcreatec1(nin, nhid1, nout, network);
                        }
                        if( mkind==1 )
                        {
                            mlpbase.mlpcreatec1(nin, nhid1, nout, tmp);
                            {
                                //
                                // This code passes data structure through serializers
                                // (serializes it to string and loads back)
                                //
                                serializer _local_serializer;
                                string _local_str;
                                
                                _local_serializer = new serializer();
                                _local_serializer.alloc_start();
                                mlpbase.mlpalloc(_local_serializer, tmp);
                                _local_serializer.sstart_str();
                                mlpbase.mlpserialize(_local_serializer, tmp);
                                _local_serializer.stop();
                                _local_str = _local_serializer.get_string();
                                
                                _local_serializer = new serializer();
                                _local_serializer.ustart_str(_local_str);
                                mlpbase.mlpunserialize(_local_serializer, network);
                                _local_serializer.stop();
                            }
                        }
                        if( mkind==2 )
                        {
                            mlpbase.mlpcreatec1(nin, nhid1, nout, tmp);
                            mlpbase.mlpcopy(tmp, network);
                        }
                    }
                    else
                    {
                        if( nkind==2 )
                        {
                            if( mkind==0 )
                            {
                                mlpbase.mlpcreateb1(nin, nhid1, nout, a1, a2, network);
                            }
                            if( mkind==1 )
                            {
                                mlpbase.mlpcreateb1(nin, nhid1, nout, a1, a2, tmp);
                                {
                                    //
                                    // This code passes data structure through serializers
                                    // (serializes it to string and loads back)
                                    //
                                    serializer _local_serializer;
                                    string _local_str;
                                    
                                    _local_serializer = new serializer();
                                    _local_serializer.alloc_start();
                                    mlpbase.mlpalloc(_local_serializer, tmp);
                                    _local_serializer.sstart_str();
                                    mlpbase.mlpserialize(_local_serializer, tmp);
                                    _local_serializer.stop();
                                    _local_str = _local_serializer.get_string();
                                    
                                    _local_serializer = new serializer();
                                    _local_serializer.ustart_str(_local_str);
                                    mlpbase.mlpunserialize(_local_serializer, network);
                                    _local_serializer.stop();
                                }
                            }
                            if( mkind==2 )
                            {
                                mlpbase.mlpcreateb1(nin, nhid1, nout, a1, a2, tmp);
                                mlpbase.mlpcopy(tmp, network);
                            }
                        }
                        else
                        {
                            if( nkind==3 )
                            {
                                if( mkind==0 )
                                {
                                    mlpbase.mlpcreater1(nin, nhid1, nout, a1, a2, network);
                                }
                                if( mkind==1 )
                                {
                                    mlpbase.mlpcreater1(nin, nhid1, nout, a1, a2, tmp);
                                    {
                                        //
                                        // This code passes data structure through serializers
                                        // (serializes it to string and loads back)
                                        //
                                        serializer _local_serializer;
                                        string _local_str;
                                        
                                        _local_serializer = new serializer();
                                        _local_serializer.alloc_start();
                                        mlpbase.mlpalloc(_local_serializer, tmp);
                                        _local_serializer.sstart_str();
                                        mlpbase.mlpserialize(_local_serializer, tmp);
                                        _local_serializer.stop();
                                        _local_str = _local_serializer.get_string();
                                        
                                        _local_serializer = new serializer();
                                        _local_serializer.ustart_str(_local_str);
                                        mlpbase.mlpunserialize(_local_serializer, network);
                                        _local_serializer.stop();
                                    }
                                }
                                if( mkind==2 )
                                {
                                    mlpbase.mlpcreater1(nin, nhid1, nout, a1, a2, tmp);
                                    mlpbase.mlpcopy(tmp, network);
                                }
                            }
                        }
                    }
                }
                mlpbase.mlprandomizefull(network);
                return;
            }
            
            //
            // Two hidden layers
            //
            if( nkind==0 )
            {
                if( mkind==0 )
                {
                    mlpbase.mlpcreate2(nin, nhid1, nhid2, nout, network);
                }
                if( mkind==1 )
                {
                    mlpbase.mlpcreate2(nin, nhid1, nhid2, nout, tmp);
                    {
                        //
                        // This code passes data structure through serializers
                        // (serializes it to string and loads back)
                        //
                        serializer _local_serializer;
                        string _local_str;
                        
                        _local_serializer = new serializer();
                        _local_serializer.alloc_start();
                        mlpbase.mlpalloc(_local_serializer, tmp);
                        _local_serializer.sstart_str();
                        mlpbase.mlpserialize(_local_serializer, tmp);
                        _local_serializer.stop();
                        _local_str = _local_serializer.get_string();
                        
                        _local_serializer = new serializer();
                        _local_serializer.ustart_str(_local_str);
                        mlpbase.mlpunserialize(_local_serializer, network);
                        _local_serializer.stop();
                    }
                }
                if( mkind==2 )
                {
                    mlpbase.mlpcreate2(nin, nhid1, nhid2, nout, tmp);
                    mlpbase.mlpcopy(tmp, network);
                }
            }
            else
            {
                if( nkind==1 )
                {
                    if( mkind==0 )
                    {
                        mlpbase.mlpcreatec2(nin, nhid1, nhid2, nout, network);
                    }
                    if( mkind==1 )
                    {
                        mlpbase.mlpcreatec2(nin, nhid1, nhid2, nout, tmp);
                        {
                            //
                            // This code passes data structure through serializers
                            // (serializes it to string and loads back)
                            //
                            serializer _local_serializer;
                            string _local_str;
                            
                            _local_serializer = new serializer();
                            _local_serializer.alloc_start();
                            mlpbase.mlpalloc(_local_serializer, tmp);
                            _local_serializer.sstart_str();
                            mlpbase.mlpserialize(_local_serializer, tmp);
                            _local_serializer.stop();
                            _local_str = _local_serializer.get_string();
                            
                            _local_serializer = new serializer();
                            _local_serializer.ustart_str(_local_str);
                            mlpbase.mlpunserialize(_local_serializer, network);
                            _local_serializer.stop();
                        }
                    }
                    if( mkind==2 )
                    {
                        mlpbase.mlpcreatec2(nin, nhid1, nhid2, nout, tmp);
                        mlpbase.mlpcopy(tmp, network);
                    }
                }
                else
                {
                    if( nkind==2 )
                    {
                        if( mkind==0 )
                        {
                            mlpbase.mlpcreateb2(nin, nhid1, nhid2, nout, a1, a2, network);
                        }
                        if( mkind==1 )
                        {
                            mlpbase.mlpcreateb2(nin, nhid1, nhid2, nout, a1, a2, tmp);
                            {
                                //
                                // This code passes data structure through serializers
                                // (serializes it to string and loads back)
                                //
                                serializer _local_serializer;
                                string _local_str;
                                
                                _local_serializer = new serializer();
                                _local_serializer.alloc_start();
                                mlpbase.mlpalloc(_local_serializer, tmp);
                                _local_serializer.sstart_str();
                                mlpbase.mlpserialize(_local_serializer, tmp);
                                _local_serializer.stop();
                                _local_str = _local_serializer.get_string();
                                
                                _local_serializer = new serializer();
                                _local_serializer.ustart_str(_local_str);
                                mlpbase.mlpunserialize(_local_serializer, network);
                                _local_serializer.stop();
                            }
                        }
                        if( mkind==2 )
                        {
                            mlpbase.mlpcreateb2(nin, nhid1, nhid2, nout, a1, a2, tmp);
                            mlpbase.mlpcopy(tmp, network);
                        }
                    }
                    else
                    {
                        if( nkind==3 )
                        {
                            if( mkind==0 )
                            {
                                mlpbase.mlpcreater2(nin, nhid1, nhid2, nout, a1, a2, network);
                            }
                            if( mkind==1 )
                            {
                                mlpbase.mlpcreater2(nin, nhid1, nhid2, nout, a1, a2, tmp);
                                {
                                    //
                                    // This code passes data structure through serializers
                                    // (serializes it to string and loads back)
                                    //
                                    serializer _local_serializer;
                                    string _local_str;
                                    
                                    _local_serializer = new serializer();
                                    _local_serializer.alloc_start();
                                    mlpbase.mlpalloc(_local_serializer, tmp);
                                    _local_serializer.sstart_str();
                                    mlpbase.mlpserialize(_local_serializer, tmp);
                                    _local_serializer.stop();
                                    _local_str = _local_serializer.get_string();
                                    
                                    _local_serializer = new serializer();
                                    _local_serializer.ustart_str(_local_str);
                                    mlpbase.mlpunserialize(_local_serializer, network);
                                    _local_serializer.stop();
                                }
                            }
                            if( mkind==2 )
                            {
                                mlpbase.mlpcreater2(nin, nhid1, nhid2, nout, a1, a2, tmp);
                                mlpbase.mlpcopy(tmp, network);
                            }
                        }
                    }
                }
            }
            mlpbase.mlprandomizefull(network);
        }


        /*************************************************************************
        Unsets network (initialize it to smallest network possible
        *************************************************************************/
        private static void unsetnetwork(mlpbase.multilayerperceptron network)
        {
            mlpbase.mlpcreate0(1, 1, network);
        }


        /*************************************************************************
        Informational functions test
        *************************************************************************/
        private static void testinformational(int nkind,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int passcount,
            ref bool err)
        {
            mlpbase.multilayerperceptron network = new mlpbase.multilayerperceptron();
            int n1 = 0;
            int n2 = 0;
            int wcount = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double threshold = 0;
            int nlayers = 0;
            int nmax = 0;
            bool issoftmax = new bool();
            double[,] neurons = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            double mean = 0;
            double sigma = 0;
            int fkind = 0;
            double c = 0;
            double f = 0;
            double df = 0;
            double d2f = 0;
            double s = 0;

            threshold = 100000*math.machineepsilon;
            createnetwork(network, nkind, 0.0, 0.0, nin, nhid1, nhid2, nout);
            
            //
            // test MLPProperties()
            //
            mlpbase.mlpproperties(network, ref n1, ref n2, ref wcount);
            err = ((err | n1!=nin) | n2!=nout) | wcount<=0;
            
            //
            // Test network geometry functions
            //
            // In order to do this we calculate neural network output using
            // informational functions only, and compare results with ones
            // obtained with MLPProcess():
            // 1. we allocate 2-dimensional array of neurons and fill it by zeros
            // 2. we full first layer of neurons by input values
            // 3. we move through array, calculating values of subsequent layers
            // 4. if we have classification network, we SOFTMAX-normalize output layer
            // 5. we apply scaling to the outputs
            // 6. we compare results with ones obtained by MLPProcess()
            //
            // NOTE: it is important to do (4) before (5), because on SOFTMAX network
            //       MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order
            //       to test it implicitly, we apply it to the classifier results
            //       (already normalized). If one of the coefficients deviates from
            //       expected values, we will get error during (6).
            //
            nlayers = 2;
            nmax = Math.Max(nin, nout);
            issoftmax = nkind==1;
            if( nhid1!=0 )
            {
                nlayers = 3;
                nmax = Math.Max(nmax, nhid1);
            }
            if( nhid2!=0 )
            {
                nlayers = 4;
                nmax = Math.Max(nmax, nhid2);
            }
            neurons = new double[nlayers, nmax];
            for(i=0; i<=nlayers-1; i++)
            {
                for(j=0; j<=nmax-1; j++)
                {
                    neurons[i,j] = 0;
                }
            }
            x = new double[nin];
            for(i=0; i<=nin-1; i++)
            {
                x[i] = 2*math.randomreal()-1;
            }
            y = new double[nout];
            for(i=0; i<=nout-1; i++)
            {
                y[i] = 2*math.randomreal()-1;
            }
            for(j=0; j<=nin-1; j++)
            {
                mlpbase.mlpgetinputscaling(network, j, ref mean, ref sigma);
                neurons[0,j] = (x[j]-mean)/sigma;
            }
            for(i=1; i<=nlayers-1; i++)
            {
                for(j=0; j<=mlpbase.mlpgetlayersize(network, i)-1; j++)
                {
                    for(k=0; k<=mlpbase.mlpgetlayersize(network, i-1)-1; k++)
                    {
                        neurons[i,j] = neurons[i,j]+mlpbase.mlpgetweight(network, i-1, k, i, j)*neurons[i-1,k];
                    }
                    mlpbase.mlpgetneuroninfo(network, i, j, ref fkind, ref c);
                    mlpbase.mlpactivationfunction(neurons[i,j]-c, fkind, ref f, ref df, ref d2f);
                    neurons[i,j] = f;
                }
            }
            if( nkind==1 )
            {
                s = 0;
                for(j=0; j<=nout-1; j++)
                {
                    s = s+Math.Exp(neurons[nlayers-1,j]);
                }
                for(j=0; j<=nout-1; j++)
                {
                    neurons[nlayers-1,j] = Math.Exp(neurons[nlayers-1,j])/s;
                }
            }
            for(j=0; j<=nout-1; j++)
            {
                mlpbase.mlpgetoutputscaling(network, j, ref mean, ref sigma);
                neurons[nlayers-1,j] = neurons[nlayers-1,j]*sigma+mean;
            }
            mlpbase.mlpprocess(network, x, ref y);
            for(j=0; j<=nout-1; j++)
            {
                err = err | (double)(Math.Abs(neurons[nlayers-1,j]-y[j]))>(double)(threshold);
            }
        }


        /*************************************************************************
        Processing functions test
        *************************************************************************/
        private static void testprocessing(int nkind,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int passcount,
            ref bool err)
        {
            mlpbase.multilayerperceptron network = new mlpbase.multilayerperceptron();
            mlpbase.multilayerperceptron network2 = new mlpbase.multilayerperceptron();
            int n1 = 0;
            int n2 = 0;
            int wcount = 0;
            bool zeronet = new bool();
            double a1 = 0;
            double a2 = 0;
            int pass = 0;
            int i = 0;
            bool allsame = new bool();
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[] y1 = new double[0];
            double[] y2 = new double[0];
            double v = 0;
            int i_ = 0;

            ap.assert(passcount>=2, "PassCount<2!");
            
            //
            // Prepare network
            //
            a1 = 0;
            a2 = 0;
            if( nkind==2 )
            {
                a1 = 1000*math.randomreal()-500;
                a2 = 2*math.randomreal()-1;
            }
            if( nkind==3 )
            {
                a1 = 1000*math.randomreal()-500;
                a2 = a1+(2*math.randominteger(2)-1)*(0.1+0.9*math.randomreal());
            }
            createnetwork(network, nkind, a1, a2, nin, nhid1, nhid2, nout);
            mlpbase.mlpproperties(network, ref n1, ref n2, ref wcount);
            
            //
            // Initialize arrays
            //
            x1 = new double[nin-1+1];
            x2 = new double[nin-1+1];
            y1 = new double[nout-1+1];
            y2 = new double[nout-1+1];
            
            //
            // Main cycle
            //
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Last run is made on zero network
                //
                mlpbase.mlprandomizefull(network);
                zeronet = false;
                if( pass==passcount )
                {
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        network.weights[i_] = 0*network.weights[i_];
                    }
                    zeronet = true;
                }
                
                //
                // Same inputs leads to same outputs
                //
                for(i=0; i<=nin-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                for(i=0; i<=nout-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = 2*math.randomreal()-1;
                }
                mlpbase.mlpprocess(network, x1, ref y1);
                mlpbase.mlpprocess(network, x2, ref y2);
                allsame = true;
                for(i=0; i<=nout-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | !allsame;
                
                //
                // Same inputs on original network leads to same outputs
                // on copy created using MLPCopy
                //
                unsetnetwork(network2);
                mlpbase.mlpcopy(network, network2);
                for(i=0; i<=nin-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                for(i=0; i<=nout-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = 2*math.randomreal()-1;
                }
                mlpbase.mlpprocess(network, x1, ref y1);
                mlpbase.mlpprocess(network2, x2, ref y2);
                allsame = true;
                for(i=0; i<=nout-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | !allsame;
                
                //
                // Same inputs on original network leads to same outputs
                // on copy created using MLPSerialize
                //
                unsetnetwork(network2);
                {
                    //
                    // This code passes data structure through serializers
                    // (serializes it to string and loads back)
                    //
                    serializer _local_serializer;
                    string _local_str;
                    
                    _local_serializer = new serializer();
                    _local_serializer.alloc_start();
                    mlpbase.mlpalloc(_local_serializer, network);
                    _local_serializer.sstart_str();
                    mlpbase.mlpserialize(_local_serializer, network);
                    _local_serializer.stop();
                    _local_str = _local_serializer.get_string();
                    
                    _local_serializer = new serializer();
                    _local_serializer.ustart_str(_local_str);
                    mlpbase.mlpunserialize(_local_serializer, network2);
                    _local_serializer.stop();
                }
                for(i=0; i<=nin-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                for(i=0; i<=nout-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = 2*math.randomreal()-1;
                }
                mlpbase.mlpprocess(network, x1, ref y1);
                mlpbase.mlpprocess(network2, x2, ref y2);
                allsame = true;
                for(i=0; i<=nout-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | !allsame;
                
                //
                // Different inputs leads to different outputs (non-zero network)
                //
                if( !zeronet )
                {
                    for(i=0; i<=nin-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                        x2[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=nout-1; i++)
                    {
                        y1[i] = 2*math.randomreal()-1;
                        y2[i] = y1[i];
                    }
                    mlpbase.mlpprocess(network, x1, ref y1);
                    mlpbase.mlpprocess(network, x2, ref y2);
                    allsame = true;
                    for(i=0; i<=nout-1; i++)
                    {
                        allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                    }
                    err = err | allsame;
                }
                
                //
                // Randomization changes outputs (when inputs are unchanged, non-zero network)
                //
                if( !zeronet )
                {
                    for(i=0; i<=nin-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                        x2[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=nout-1; i++)
                    {
                        y1[i] = 2*math.randomreal()-1;
                        y2[i] = y1[i];
                    }
                    mlpbase.mlpcopy(network, network2);
                    mlpbase.mlprandomize(network2);
                    mlpbase.mlpprocess(network, x1, ref y1);
                    mlpbase.mlpprocess(network2, x1, ref y2);
                    allsame = true;
                    for(i=0; i<=nout-1; i++)
                    {
                        allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                    }
                    err = err | allsame;
                }
                
                //
                // Full randomization changes outputs (when inputs are unchanged, non-zero network)
                //
                if( !zeronet )
                {
                    for(i=0; i<=nin-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                        x2[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=nout-1; i++)
                    {
                        y1[i] = 2*math.randomreal()-1;
                        y2[i] = y1[i];
                    }
                    mlpbase.mlpcopy(network, network2);
                    mlpbase.mlprandomizefull(network2);
                    mlpbase.mlpprocess(network, x1, ref y1);
                    mlpbase.mlpprocess(network2, x1, ref y2);
                    allsame = true;
                    for(i=0; i<=nout-1; i++)
                    {
                        allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                    }
                    err = err | allsame;
                }
                
                //
                // Normalization properties
                //
                if( nkind==1 )
                {
                    
                    //
                    // Classifier network outputs are normalized
                    //
                    for(i=0; i<=nin-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                    }
                    mlpbase.mlpprocess(network, x1, ref y1);
                    v = 0;
                    for(i=0; i<=nout-1; i++)
                    {
                        v = v+y1[i];
                        err = err | (double)(y1[i])<(double)(0);
                    }
                    err = err | (double)(Math.Abs(v-1))>(double)(1000*math.machineepsilon);
                }
                if( nkind==2 )
                {
                    
                    //
                    // B-type network outputs are bounded from above/below
                    //
                    for(i=0; i<=nin-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                    }
                    mlpbase.mlpprocess(network, x1, ref y1);
                    for(i=0; i<=nout-1; i++)
                    {
                        if( (double)(a2)>=(double)(0) )
                        {
                            err = err | (double)(y1[i])<(double)(a1);
                        }
                        else
                        {
                            err = err | (double)(y1[i])>(double)(a1);
                        }
                    }
                }
                if( nkind==3 )
                {
                    
                    //
                    // R-type network outputs are within [A1,A2] (or [A2,A1])
                    //
                    for(i=0; i<=nin-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                    }
                    mlpbase.mlpprocess(network, x1, ref y1);
                    for(i=0; i<=nout-1; i++)
                    {
                        err = (err | (double)(y1[i])<(double)(Math.Min(a1, a2))) | (double)(y1[i])>(double)(Math.Max(a1, a2));
                    }
                }
            }
        }


        /*************************************************************************
        Gradient functions test
        *************************************************************************/
        private static void testgradient(int nkind,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int passcount,
            ref bool err)
        {
            mlpbase.multilayerperceptron network = new mlpbase.multilayerperceptron();
            int n1 = 0;
            int n2 = 0;
            int wcount = 0;
            double h = 0;
            double etol = 0;
            double a1 = 0;
            double a2 = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int ssize = 0;
            double[,] xy = new double[0,0];
            double[] grad1 = new double[0];
            double[] grad2 = new double[0];
            double[] x = new double[0];
            double[] y = new double[0];
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[] y1 = new double[0];
            double[] y2 = new double[0];
            double v = 0;
            double e = 0;
            double e1 = 0;
            double e2 = 0;
            double v1 = 0;
            double v2 = 0;
            double v3 = 0;
            double v4 = 0;
            double wprev = 0;
            int i_ = 0;
            int i1_ = 0;

            ap.assert(passcount>=2, "PassCount<2!");
            a1 = 0;
            a2 = 0;
            if( nkind==2 )
            {
                a1 = 1000*math.randomreal()-500;
                a2 = 2*math.randomreal()-1;
            }
            if( nkind==3 )
            {
                a1 = 1000*math.randomreal()-500;
                a2 = a1+(2*math.randominteger(2)-1)*(0.1+0.9*math.randomreal());
            }
            createnetwork(network, nkind, a1, a2, nin, nhid1, nhid2, nout);
            mlpbase.mlpproperties(network, ref n1, ref n2, ref wcount);
            h = 0.0001;
            etol = 0.01;
            
            //
            // Initialize
            //
            x = new double[nin-1+1];
            x1 = new double[nin-1+1];
            x2 = new double[nin-1+1];
            y = new double[nout-1+1];
            y1 = new double[nout-1+1];
            y2 = new double[nout-1+1];
            grad1 = new double[wcount-1+1];
            grad2 = new double[wcount-1+1];
            
            //
            // Process
            //
            for(pass=1; pass<=passcount; pass++)
            {
                mlpbase.mlprandomizefull(network);
                
                //
                // Test error/gradient calculation (least squares)
                //
                xy = new double[0+1, nin+nout-1+1];
                for(i=0; i<=nin-1; i++)
                {
                    x[i] = 4*math.randomreal()-2;
                }
                for(i_=0; i_<=nin-1;i_++)
                {
                    xy[0,i_] = x[i_];
                }
                if( mlpbase.mlpissoftmax(network) )
                {
                    for(i=0; i<=nout-1; i++)
                    {
                        y[i] = 0;
                    }
                    xy[0,nin] = math.randominteger(nout);
                    y[(int)Math.Round(xy[0,nin])] = 1;
                }
                else
                {
                    for(i=0; i<=nout-1; i++)
                    {
                        y[i] = 4*math.randomreal()-2;
                    }
                    i1_ = (0) - (nin);
                    for(i_=nin; i_<=nin+nout-1;i_++)
                    {
                        xy[0,i_] = y[i_+i1_];
                    }
                }
                mlpbase.mlpgrad(network, x, y, ref e, ref grad2);
                mlpbase.mlpprocess(network, x, ref y2);
                for(i_=0; i_<=nout-1;i_++)
                {
                    y2[i_] = y2[i_] - y[i_];
                }
                v = 0.0;
                for(i_=0; i_<=nout-1;i_++)
                {
                    v += y2[i_]*y2[i_];
                }
                v = v/2;
                err = err | (double)(Math.Abs((v-e)/v))>(double)(etol);
                err = err | (double)(Math.Abs((mlpbase.mlperror(network, xy, 1)-v)/v))>(double)(etol);
                for(i=0; i<=wcount-1; i++)
                {
                    wprev = network.weights[i];
                    network.weights[i] = wprev-2*h;
                    mlpbase.mlpprocess(network, x, ref y1);
                    for(i_=0; i_<=nout-1;i_++)
                    {
                        y1[i_] = y1[i_] - y[i_];
                    }
                    v1 = 0.0;
                    for(i_=0; i_<=nout-1;i_++)
                    {
                        v1 += y1[i_]*y1[i_];
                    }
                    v1 = v1/2;
                    network.weights[i] = wprev-h;
                    mlpbase.mlpprocess(network, x, ref y1);
                    for(i_=0; i_<=nout-1;i_++)
                    {
                        y1[i_] = y1[i_] - y[i_];
                    }
                    v2 = 0.0;
                    for(i_=0; i_<=nout-1;i_++)
                    {
                        v2 += y1[i_]*y1[i_];
                    }
                    v2 = v2/2;
                    network.weights[i] = wprev+h;
                    mlpbase.mlpprocess(network, x, ref y1);
                    for(i_=0; i_<=nout-1;i_++)
                    {
                        y1[i_] = y1[i_] - y[i_];
                    }
                    v3 = 0.0;
                    for(i_=0; i_<=nout-1;i_++)
                    {
                        v3 += y1[i_]*y1[i_];
                    }
                    v3 = v3/2;
                    network.weights[i] = wprev+2*h;
                    mlpbase.mlpprocess(network, x, ref y1);
                    for(i_=0; i_<=nout-1;i_++)
                    {
                        y1[i_] = y1[i_] - y[i_];
                    }
                    v4 = 0.0;
                    for(i_=0; i_<=nout-1;i_++)
                    {
                        v4 += y1[i_]*y1[i_];
                    }
                    v4 = v4/2;
                    network.weights[i] = wprev;
                    grad1[i] = (v1-8*v2+8*v3-v4)/(12*h);
                    if( (double)(Math.Abs(grad1[i]))>(double)(1.0E-3) )
                    {
                        err = err | (double)(Math.Abs((grad2[i]-grad1[i])/grad1[i]))>(double)(etol);
                    }
                    else
                    {
                        err = err | (double)(Math.Abs(grad2[i]-grad1[i]))>(double)(etol);
                    }
                }
                
                //
                // Test error/gradient calculation (natural).
                // Testing on non-random structure networks
                // (because NKind is representative only in that case).
                //
                xy = new double[0+1, nin+nout-1+1];
                for(i=0; i<=nin-1; i++)
                {
                    x[i] = 4*math.randomreal()-2;
                }
                for(i_=0; i_<=nin-1;i_++)
                {
                    xy[0,i_] = x[i_];
                }
                if( mlpbase.mlpissoftmax(network) )
                {
                    for(i=0; i<=nout-1; i++)
                    {
                        y[i] = 0;
                    }
                    xy[0,nin] = math.randominteger(nout);
                    y[(int)Math.Round(xy[0,nin])] = 1;
                }
                else
                {
                    for(i=0; i<=nout-1; i++)
                    {
                        y[i] = 4*math.randomreal()-2;
                    }
                    i1_ = (0) - (nin);
                    for(i_=nin; i_<=nin+nout-1;i_++)
                    {
                        xy[0,i_] = y[i_+i1_];
                    }
                }
                mlpbase.mlpgradn(network, x, y, ref e, ref grad2);
                mlpbase.mlpprocess(network, x, ref y2);
                v = 0;
                if( nkind!=1 )
                {
                    for(i=0; i<=nout-1; i++)
                    {
                        v = v+0.5*math.sqr(y2[i]-y[i]);
                    }
                }
                else
                {
                    for(i=0; i<=nout-1; i++)
                    {
                        if( (double)(y[i])!=(double)(0) )
                        {
                            if( (double)(y2[i])==(double)(0) )
                            {
                                v = v+y[i]*Math.Log(math.maxrealnumber);
                            }
                            else
                            {
                                v = v+y[i]*Math.Log(y[i]/y2[i]);
                            }
                        }
                    }
                }
                err = err | (double)(Math.Abs((v-e)/v))>(double)(etol);
                err = err | (double)(Math.Abs((mlpbase.mlperrorn(network, xy, 1)-v)/v))>(double)(etol);
                for(i=0; i<=wcount-1; i++)
                {
                    wprev = network.weights[i];
                    network.weights[i] = wprev+h;
                    mlpbase.mlpprocess(network, x, ref y2);
                    network.weights[i] = wprev-h;
                    mlpbase.mlpprocess(network, x, ref y1);
                    network.weights[i] = wprev;
                    v = 0;
                    if( nkind!=1 )
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            v = v+0.5*(math.sqr(y2[j]-y[j])-math.sqr(y1[j]-y[j]))/(2*h);
                        }
                    }
                    else
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            if( (double)(y[j])!=(double)(0) )
                            {
                                if( (double)(y2[j])==(double)(0) )
                                {
                                    v = v+y[j]*Math.Log(math.maxrealnumber);
                                }
                                else
                                {
                                    v = v+y[j]*Math.Log(y[j]/y2[j]);
                                }
                                if( (double)(y1[j])==(double)(0) )
                                {
                                    v = v-y[j]*Math.Log(math.maxrealnumber);
                                }
                                else
                                {
                                    v = v-y[j]*Math.Log(y[j]/y1[j]);
                                }
                            }
                        }
                        v = v/(2*h);
                    }
                    grad1[i] = v;
                    if( (double)(Math.Abs(grad1[i]))>(double)(1.0E-3) )
                    {
                        err = err | (double)(Math.Abs((grad2[i]-grad1[i])/grad1[i]))>(double)(etol);
                    }
                    else
                    {
                        err = err | (double)(Math.Abs(grad2[i]-grad1[i]))>(double)(etol);
                    }
                }
                
                //
                // Test gradient calculation: batch (least squares)
                //
                ssize = 1+math.randominteger(10);
                xy = new double[ssize-1+1, nin+nout-1+1];
                for(i=0; i<=wcount-1; i++)
                {
                    grad1[i] = 0;
                }
                e1 = 0;
                for(i=0; i<=ssize-1; i++)
                {
                    for(j=0; j<=nin-1; j++)
                    {
                        x1[j] = 4*math.randomreal()-2;
                    }
                    for(i_=0; i_<=nin-1;i_++)
                    {
                        xy[i,i_] = x1[i_];
                    }
                    if( mlpbase.mlpissoftmax(network) )
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 0;
                        }
                        xy[i,nin] = math.randominteger(nout);
                        y1[(int)Math.Round(xy[i,nin])] = 1;
                    }
                    else
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 4*math.randomreal()-2;
                        }
                        i1_ = (0) - (nin);
                        for(i_=nin; i_<=nin+nout-1;i_++)
                        {
                            xy[i,i_] = y1[i_+i1_];
                        }
                    }
                    mlpbase.mlpgrad(network, x1, y1, ref v, ref grad2);
                    e1 = e1+v;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        grad1[i_] = grad1[i_] + grad2[i_];
                    }
                }
                mlpbase.mlpgradbatch(network, xy, ssize, ref e2, ref grad2);
                err = err | (double)(Math.Abs(e1-e2)/e1)>(double)(0.01);
                for(i=0; i<=wcount-1; i++)
                {
                    if( (double)(grad1[i])!=(double)(0) )
                    {
                        err = err | (double)(Math.Abs((grad2[i]-grad1[i])/grad1[i]))>(double)(etol);
                    }
                    else
                    {
                        err = err | (double)(grad2[i])!=(double)(grad1[i]);
                    }
                }
                
                //
                // Test gradient calculation: batch (natural error func)
                //
                ssize = 1+math.randominteger(10);
                xy = new double[ssize-1+1, nin+nout-1+1];
                for(i=0; i<=wcount-1; i++)
                {
                    grad1[i] = 0;
                }
                e1 = 0;
                for(i=0; i<=ssize-1; i++)
                {
                    for(j=0; j<=nin-1; j++)
                    {
                        x1[j] = 4*math.randomreal()-2;
                    }
                    for(i_=0; i_<=nin-1;i_++)
                    {
                        xy[i,i_] = x1[i_];
                    }
                    if( mlpbase.mlpissoftmax(network) )
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 0;
                        }
                        xy[i,nin] = math.randominteger(nout);
                        y1[(int)Math.Round(xy[i,nin])] = 1;
                    }
                    else
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 4*math.randomreal()-2;
                        }
                        i1_ = (0) - (nin);
                        for(i_=nin; i_<=nin+nout-1;i_++)
                        {
                            xy[i,i_] = y1[i_+i1_];
                        }
                    }
                    mlpbase.mlpgradn(network, x1, y1, ref v, ref grad2);
                    e1 = e1+v;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        grad1[i_] = grad1[i_] + grad2[i_];
                    }
                }
                mlpbase.mlpgradnbatch(network, xy, ssize, ref e2, ref grad2);
                err = err | (double)(Math.Abs(e1-e2)/e1)>(double)(etol);
                for(i=0; i<=wcount-1; i++)
                {
                    if( (double)(grad1[i])!=(double)(0) )
                    {
                        err = err | (double)(Math.Abs((grad2[i]-grad1[i])/grad1[i]))>(double)(etol);
                    }
                    else
                    {
                        err = err | (double)(grad2[i])!=(double)(grad1[i]);
                    }
                }
            }
        }


        /*************************************************************************
        Hessian functions test
        *************************************************************************/
        private static void testhessian(int nkind,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int passcount,
            ref bool err)
        {
            mlpbase.multilayerperceptron network = new mlpbase.multilayerperceptron();
            int hkind = 0;
            int n1 = 0;
            int n2 = 0;
            int wcount = 0;
            double h = 0;
            double etol = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int ssize = 0;
            double a1 = 0;
            double a2 = 0;
            double[,] xy = new double[0,0];
            double[,] h1 = new double[0,0];
            double[,] h2 = new double[0,0];
            double[] grad1 = new double[0];
            double[] grad2 = new double[0];
            double[] grad3 = new double[0];
            double[] x = new double[0];
            double[] y = new double[0];
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[] y1 = new double[0];
            double[] y2 = new double[0];
            double v = 0;
            double e1 = 0;
            double e2 = 0;
            double wprev = 0;
            int i_ = 0;
            int i1_ = 0;

            ap.assert(passcount>=2, "PassCount<2!");
            a1 = 0;
            a2 = 0;
            if( nkind==2 )
            {
                a1 = 1000*math.randomreal()-500;
                a2 = 2*math.randomreal()-1;
            }
            if( nkind==3 )
            {
                a1 = 1000*math.randomreal()-500;
                a2 = a1+(2*math.randominteger(2)-1)*(0.1+0.9*math.randomreal());
            }
            createnetwork(network, nkind, a1, a2, nin, nhid1, nhid2, nout);
            mlpbase.mlpproperties(network, ref n1, ref n2, ref wcount);
            h = 0.0001;
            etol = 0.05;
            
            //
            // Initialize
            //
            x = new double[nin-1+1];
            x1 = new double[nin-1+1];
            x2 = new double[nin-1+1];
            y = new double[nout-1+1];
            y1 = new double[nout-1+1];
            y2 = new double[nout-1+1];
            grad1 = new double[wcount-1+1];
            grad2 = new double[wcount-1+1];
            grad3 = new double[wcount-1+1];
            h1 = new double[wcount-1+1, wcount-1+1];
            h2 = new double[wcount-1+1, wcount-1+1];
            
            //
            // Process
            //
            for(pass=1; pass<=passcount; pass++)
            {
                mlpbase.mlprandomizefull(network);
                
                //
                // Test hessian calculation .
                // E1 contains total error (calculated using MLPGrad/MLPGradN)
                // Grad1 contains total gradient (calculated using MLPGrad/MLPGradN)
                // H1 contains Hessian calculated using differences of gradients
                //
                // E2, Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch
                //
                for(hkind=0; hkind<=1; hkind++)
                {
                    ssize = 1+math.randominteger(10);
                    xy = new double[ssize-1+1, nin+nout-1+1];
                    for(i=0; i<=wcount-1; i++)
                    {
                        grad1[i] = 0;
                    }
                    for(i=0; i<=wcount-1; i++)
                    {
                        for(j=0; j<=wcount-1; j++)
                        {
                            h1[i,j] = 0;
                        }
                    }
                    e1 = 0;
                    for(i=0; i<=ssize-1; i++)
                    {
                        
                        //
                        // X, Y
                        //
                        for(j=0; j<=nin-1; j++)
                        {
                            x1[j] = 4*math.randomreal()-2;
                        }
                        for(i_=0; i_<=nin-1;i_++)
                        {
                            xy[i,i_] = x1[i_];
                        }
                        if( mlpbase.mlpissoftmax(network) )
                        {
                            for(j=0; j<=nout-1; j++)
                            {
                                y1[j] = 0;
                            }
                            xy[i,nin] = math.randominteger(nout);
                            y1[(int)Math.Round(xy[i,nin])] = 1;
                        }
                        else
                        {
                            for(j=0; j<=nout-1; j++)
                            {
                                y1[j] = 4*math.randomreal()-2;
                            }
                            i1_ = (0) - (nin);
                            for(i_=nin; i_<=nin+nout-1;i_++)
                            {
                                xy[i,i_] = y1[i_+i1_];
                            }
                        }
                        
                        //
                        // E1, Grad1
                        //
                        if( hkind==0 )
                        {
                            mlpbase.mlpgrad(network, x1, y1, ref v, ref grad2);
                        }
                        else
                        {
                            mlpbase.mlpgradn(network, x1, y1, ref v, ref grad2);
                        }
                        e1 = e1+v;
                        for(i_=0; i_<=wcount-1;i_++)
                        {
                            grad1[i_] = grad1[i_] + grad2[i_];
                        }
                        
                        //
                        // H1
                        //
                        for(j=0; j<=wcount-1; j++)
                        {
                            wprev = network.weights[j];
                            network.weights[j] = wprev-2*h;
                            if( hkind==0 )
                            {
                                mlpbase.mlpgrad(network, x1, y1, ref v, ref grad2);
                            }
                            else
                            {
                                mlpbase.mlpgradn(network, x1, y1, ref v, ref grad2);
                            }
                            network.weights[j] = wprev-h;
                            if( hkind==0 )
                            {
                                mlpbase.mlpgrad(network, x1, y1, ref v, ref grad3);
                            }
                            else
                            {
                                mlpbase.mlpgradn(network, x1, y1, ref v, ref grad3);
                            }
                            for(i_=0; i_<=wcount-1;i_++)
                            {
                                grad2[i_] = grad2[i_] - 8*grad3[i_];
                            }
                            network.weights[j] = wprev+h;
                            if( hkind==0 )
                            {
                                mlpbase.mlpgrad(network, x1, y1, ref v, ref grad3);
                            }
                            else
                            {
                                mlpbase.mlpgradn(network, x1, y1, ref v, ref grad3);
                            }
                            for(i_=0; i_<=wcount-1;i_++)
                            {
                                grad2[i_] = grad2[i_] + 8*grad3[i_];
                            }
                            network.weights[j] = wprev+2*h;
                            if( hkind==0 )
                            {
                                mlpbase.mlpgrad(network, x1, y1, ref v, ref grad3);
                            }
                            else
                            {
                                mlpbase.mlpgradn(network, x1, y1, ref v, ref grad3);
                            }
                            for(i_=0; i_<=wcount-1;i_++)
                            {
                                grad2[i_] = grad2[i_] - grad3[i_];
                            }
                            v = 1/(12*h);
                            for(i_=0; i_<=wcount-1;i_++)
                            {
                                h1[j,i_] = h1[j,i_] + v*grad2[i_];
                            }
                            network.weights[j] = wprev;
                        }
                    }
                    if( hkind==0 )
                    {
                        mlpbase.mlphessianbatch(network, xy, ssize, ref e2, ref grad2, ref h2);
                    }
                    else
                    {
                        mlpbase.mlphessiannbatch(network, xy, ssize, ref e2, ref grad2, ref h2);
                    }
                    err = err | (double)(Math.Abs(e1-e2)/e1)>(double)(etol);
                    for(i=0; i<=wcount-1; i++)
                    {
                        if( (double)(Math.Abs(grad1[i]))>(double)(1.0E-2) )
                        {
                            err = err | (double)(Math.Abs((grad2[i]-grad1[i])/grad1[i]))>(double)(etol);
                        }
                        else
                        {
                            err = err | (double)(Math.Abs(grad2[i]-grad1[i]))>(double)(etol);
                        }
                    }
                    for(i=0; i<=wcount-1; i++)
                    {
                        for(j=0; j<=wcount-1; j++)
                        {
                            if( (double)(Math.Abs(h1[i,j]))>(double)(5.0E-2) )
                            {
                                err = err | (double)(Math.Abs((h1[i,j]-h2[i,j])/h1[i,j]))>(double)(etol);
                            }
                            else
                            {
                                err = err | (double)(Math.Abs(h2[i,j]-h1[i,j]))>(double)(etol);
                            }
                        }
                    }
                }
            }
        }


    }
    public class testmlpeunit
    {
        public static bool testmlpe(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            int passcount = 0;
            int maxn = 0;
            int maxhid = 0;
            int nf = 0;
            int nhid = 0;
            int nl = 0;
            int nhid1 = 0;
            int nhid2 = 0;
            int ec = 0;
            int nkind = 0;
            int algtype = 0;
            int tasktype = 0;
            int pass = 0;
            mlpe.mlpensemble ensemble = new mlpe.mlpensemble();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            mlptrain.mlpcvreport oobrep = new mlptrain.mlpcvreport();
            double[,] xy = new double[0,0];
            int i = 0;
            int j = 0;
            int nin = 0;
            int nout = 0;
            int npoints = 0;
            double e = 0;
            int info = 0;
            int nless = 0;
            int nall = 0;
            int nclasses = 0;
            bool inferrors = new bool();
            bool procerrors = new bool();
            bool trnerrors = new bool();

            waserrors = false;
            inferrors = false;
            procerrors = false;
            trnerrors = false;
            passcount = 10;
            maxn = 4;
            maxhid = 4;
            
            //
            // General MLP ensembles tests
            //
            for(nf=1; nf<=maxn; nf++)
            {
                for(nl=1; nl<=maxn; nl++)
                {
                    for(nhid1=0; nhid1<=maxhid; nhid1++)
                    {
                        for(nhid2=0; nhid2<=0; nhid2++)
                        {
                            for(nkind=0; nkind<=3; nkind++)
                            {
                                for(ec=1; ec<=3; ec++)
                                {
                                    
                                    //
                                    //  Skip meaningless parameters combinations
                                    //
                                    if( nkind==1 & nl<2 )
                                    {
                                        continue;
                                    }
                                    if( nhid1==0 & nhid2!=0 )
                                    {
                                        continue;
                                    }
                                    
                                    //
                                    // Tests
                                    //
                                    testinformational(nkind, nf, nhid1, nhid2, nl, ec, passcount, ref inferrors);
                                    testprocessing(nkind, nf, nhid1, nhid2, nl, ec, passcount, ref procerrors);
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // network training must reduce error
            // test on random regression task
            //
            nin = 3;
            nout = 2;
            nhid = 5;
            npoints = 100;
            nless = 0;
            nall = 0;
            for(pass=1; pass<=10; pass++)
            {
                for(algtype=0; algtype<=1; algtype++)
                {
                    for(tasktype=0; tasktype<=1; tasktype++)
                    {
                        if( tasktype==0 )
                        {
                            xy = new double[npoints-1+1, nin+nout-1+1];
                            for(i=0; i<=npoints-1; i++)
                            {
                                for(j=0; j<=nin+nout-1; j++)
                                {
                                    xy[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            mlpe.mlpecreate1(nin, nhid, nout, 1+math.randominteger(3), ensemble);
                        }
                        else
                        {
                            xy = new double[npoints-1+1, nin+1];
                            nclasses = 2+math.randominteger(2);
                            for(i=0; i<=npoints-1; i++)
                            {
                                for(j=0; j<=nin-1; j++)
                                {
                                    xy[i,j] = 2*math.randomreal()-1;
                                }
                                xy[i,nin] = math.randominteger(nclasses);
                            }
                            mlpe.mlpecreatec1(nin, nhid, nclasses, 1+math.randominteger(3), ensemble);
                        }
                        e = mlpe.mlpermserror(ensemble, xy, npoints);
                        if( algtype==0 )
                        {
                            mlpe.mlpebagginglm(ensemble, xy, npoints, 0.001, 1, ref info, rep, oobrep);
                        }
                        else
                        {
                            mlpe.mlpebagginglbfgs(ensemble, xy, npoints, 0.001, 1, 0.01, 0, ref info, rep, oobrep);
                        }
                        if( info<0 )
                        {
                            trnerrors = true;
                        }
                        else
                        {
                            if( (double)(mlpe.mlpermserror(ensemble, xy, npoints))<(double)(e) )
                            {
                                nless = nless+1;
                            }
                        }
                        nall = nall+1;
                    }
                }
            }
            trnerrors = trnerrors | (double)(nall-nless)>(double)(0.3*nall);
            
            //
            // Final report
            //
            waserrors = (inferrors | procerrors) | trnerrors;
            if( !silent )
            {
                System.Console.Write("MLP ENSEMBLE TEST");
                System.Console.WriteLine();
                System.Console.Write("INFORMATIONAL FUNCTIONS:                 ");
                if( !inferrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("BASIC PROCESSING:                        ");
                if( !procerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("TRAINING:                                ");
                if( !trnerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Network creation
        *************************************************************************/
        private static void createensemble(mlpe.mlpensemble ensemble,
            int nkind,
            double a1,
            double a2,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int ec)
        {
            ap.assert(((nin>0 & nhid1>=0) & nhid2>=0) & nout>0, "CreateNetwork error");
            ap.assert(nhid1!=0 | nhid2==0, "CreateNetwork error");
            ap.assert(nkind!=1 | nout>=2, "CreateNetwork error");
            if( nhid1==0 )
            {
                
                //
                // No hidden layers
                //
                if( nkind==0 )
                {
                    mlpe.mlpecreate0(nin, nout, ec, ensemble);
                }
                else
                {
                    if( nkind==1 )
                    {
                        mlpe.mlpecreatec0(nin, nout, ec, ensemble);
                    }
                    else
                    {
                        if( nkind==2 )
                        {
                            mlpe.mlpecreateb0(nin, nout, a1, a2, ec, ensemble);
                        }
                        else
                        {
                            if( nkind==3 )
                            {
                                mlpe.mlpecreater0(nin, nout, a1, a2, ec, ensemble);
                            }
                        }
                    }
                }
                return;
            }
            if( nhid2==0 )
            {
                
                //
                // One hidden layer
                //
                if( nkind==0 )
                {
                    mlpe.mlpecreate1(nin, nhid1, nout, ec, ensemble);
                }
                else
                {
                    if( nkind==1 )
                    {
                        mlpe.mlpecreatec1(nin, nhid1, nout, ec, ensemble);
                    }
                    else
                    {
                        if( nkind==2 )
                        {
                            mlpe.mlpecreateb1(nin, nhid1, nout, a1, a2, ec, ensemble);
                        }
                        else
                        {
                            if( nkind==3 )
                            {
                                mlpe.mlpecreater1(nin, nhid1, nout, a1, a2, ec, ensemble);
                            }
                        }
                    }
                }
                return;
            }
            
            //
            // Two hidden layers
            //
            if( nkind==0 )
            {
                mlpe.mlpecreate2(nin, nhid1, nhid2, nout, ec, ensemble);
            }
            else
            {
                if( nkind==1 )
                {
                    mlpe.mlpecreatec2(nin, nhid1, nhid2, nout, ec, ensemble);
                }
                else
                {
                    if( nkind==2 )
                    {
                        mlpe.mlpecreateb2(nin, nhid1, nhid2, nout, a1, a2, ec, ensemble);
                    }
                    else
                    {
                        if( nkind==3 )
                        {
                            mlpe.mlpecreater2(nin, nhid1, nhid2, nout, a1, a2, ec, ensemble);
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Unsets network (initialize it to smallest network possible
        *************************************************************************/
        private static void unsetensemble(mlpe.mlpensemble ensemble)
        {
            mlpe.mlpecreate0(1, 1, 1, ensemble);
        }


        /*************************************************************************
        Iformational functions test
        *************************************************************************/
        private static void testinformational(int nkind,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int ec,
            int passcount,
            ref bool err)
        {
            mlpe.mlpensemble ensemble = new mlpe.mlpensemble();
            int n1 = 0;
            int n2 = 0;

            createensemble(ensemble, nkind, -1.0, 1.0, nin, nhid1, nhid2, nout, ec);
            mlpe.mlpeproperties(ensemble, ref n1, ref n2);
            err = (err | n1!=nin) | n2!=nout;
        }


        /*************************************************************************
        Processing functions test
        *************************************************************************/
        private static void testprocessing(int nkind,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int ec,
            int passcount,
            ref bool err)
        {
            mlpe.mlpensemble ensemble = new mlpe.mlpensemble();
            mlpe.mlpensemble ensemble2 = new mlpe.mlpensemble();
            double a1 = 0;
            double a2 = 0;
            int pass = 0;
            int i = 0;
            bool allsame = new bool();
            int rlen = 0;
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[] y1 = new double[0];
            double[] y2 = new double[0];
            double[] ra = new double[0];
            double[] ra2 = new double[0];
            double v = 0;

            
            //
            // Prepare network
            //
            a1 = 0;
            a2 = 0;
            if( nkind==2 )
            {
                a1 = 1000*math.randomreal()-500;
                a2 = 2*math.randomreal()-1;
            }
            if( nkind==3 )
            {
                a1 = 1000*math.randomreal()-500;
                a2 = a1+(2*math.randominteger(2)-1)*(0.1+0.9*math.randomreal());
            }
            
            //
            // Initialize arrays
            //
            x1 = new double[nin-1+1];
            x2 = new double[nin-1+1];
            y1 = new double[nout-1+1];
            y2 = new double[nout-1+1];
            
            //
            // Main cycle
            //
            for(pass=1; pass<=passcount; pass++)
            {
                createensemble(ensemble, nkind, a1, a2, nin, nhid1, nhid2, nout, ec);
                
                //
                // Same inputs leads to same outputs
                //
                for(i=0; i<=nin-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                for(i=0; i<=nout-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = 2*math.randomreal()-1;
                }
                mlpe.mlpeprocess(ensemble, x1, ref y1);
                mlpe.mlpeprocess(ensemble, x2, ref y2);
                allsame = true;
                for(i=0; i<=nout-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | !allsame;
                
                //
                // Same inputs on original network leads to same outputs
                // on copy created using MLPCopy
                //
                unsetensemble(ensemble2);
                mlpe.mlpecopy(ensemble, ensemble2);
                for(i=0; i<=nin-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                for(i=0; i<=nout-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = 2*math.randomreal()-1;
                }
                mlpe.mlpeprocess(ensemble, x1, ref y1);
                mlpe.mlpeprocess(ensemble2, x2, ref y2);
                allsame = true;
                for(i=0; i<=nout-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | !allsame;
                
                //
                // Same inputs on original network leads to same outputs
                // on copy created using MLPSerialize
                //
                unsetensemble(ensemble2);
                mlpe.mlpeserialize(ensemble, ref ra, ref rlen);
                ra2 = new double[rlen-1+1];
                for(i=0; i<=rlen-1; i++)
                {
                    ra2[i] = ra[i];
                }
                mlpe.mlpeunserialize(ra2, ensemble2);
                for(i=0; i<=nin-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = x1[i];
                }
                for(i=0; i<=nout-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = 2*math.randomreal()-1;
                }
                mlpe.mlpeprocess(ensemble, x1, ref y1);
                mlpe.mlpeprocess(ensemble2, x2, ref y2);
                allsame = true;
                for(i=0; i<=nout-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | !allsame;
                
                //
                // Different inputs leads to different outputs (non-zero network)
                //
                for(i=0; i<=nin-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = 2*math.randomreal()-1;
                }
                for(i=0; i<=nout-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = y1[i];
                }
                mlpe.mlpeprocess(ensemble, x1, ref y1);
                mlpe.mlpeprocess(ensemble, x2, ref y2);
                allsame = true;
                for(i=0; i<=nout-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | allsame;
                
                //
                // Randomization changes outputs (when inputs are unchanged, non-zero network)
                //
                for(i=0; i<=nin-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    x2[i] = 2*math.randomreal()-1;
                }
                for(i=0; i<=nout-1; i++)
                {
                    y1[i] = 2*math.randomreal()-1;
                    y2[i] = y1[i];
                }
                mlpe.mlpecopy(ensemble, ensemble2);
                mlpe.mlperandomize(ensemble2);
                mlpe.mlpeprocess(ensemble, x1, ref y1);
                mlpe.mlpeprocess(ensemble2, x1, ref y2);
                allsame = true;
                for(i=0; i<=nout-1; i++)
                {
                    allsame = allsame & (double)(y1[i])==(double)(y2[i]);
                }
                err = err | allsame;
                
                //
                // Normalization properties
                //
                if( nkind==1 )
                {
                    
                    //
                    // Classifier network outputs are normalized
                    //
                    for(i=0; i<=nin-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                    }
                    mlpe.mlpeprocess(ensemble, x1, ref y1);
                    v = 0;
                    for(i=0; i<=nout-1; i++)
                    {
                        v = v+y1[i];
                        err = err | (double)(y1[i])<(double)(0);
                    }
                    err = err | (double)(Math.Abs(v-1))>(double)(1000*math.machineepsilon);
                }
                if( nkind==2 )
                {
                    
                    //
                    // B-type network outputs are bounded from above/below
                    //
                    for(i=0; i<=nin-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                    }
                    mlpe.mlpeprocess(ensemble, x1, ref y1);
                    for(i=0; i<=nout-1; i++)
                    {
                        if( (double)(a2)>=(double)(0) )
                        {
                            err = err | (double)(y1[i])<(double)(a1);
                        }
                        else
                        {
                            err = err | (double)(y1[i])>(double)(a1);
                        }
                    }
                }
                if( nkind==3 )
                {
                    
                    //
                    // R-type network outputs are within [A1,A2] (or [A2,A1])
                    //
                    for(i=0; i<=nin-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                    }
                    mlpe.mlpeprocess(ensemble, x1, ref y1);
                    for(i=0; i<=nout-1; i++)
                    {
                        err = (err | (double)(y1[i])<(double)(Math.Min(a1, a2))) | (double)(y1[i])>(double)(Math.Max(a1, a2));
                    }
                }
            }
        }


    }
    public class testpcaunit
    {
        public static bool testpca(bool silent)
        {
            bool result = new bool();
            int passcount = 0;
            int maxn = 0;
            int maxm = 0;
            double threshold = 0;
            int m = 0;
            int n = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int info = 0;
            double[] means = new double[0];
            double[] s = new double[0];
            double[] t2 = new double[0];
            double[] t3 = new double[0];
            double[,] v = new double[0,0];
            double[,] x = new double[0,0];
            double t = 0;
            double h = 0;
            double tmean = 0;
            double tmeans = 0;
            double tstddev = 0;
            double tstddevs = 0;
            double tmean2 = 0;
            double tmeans2 = 0;
            double tstddev2 = 0;
            double tstddevs2 = 0;
            bool pcaconverrors = new bool();
            bool pcaorterrors = new bool();
            bool pcavarerrors = new bool();
            bool pcaopterrors = new bool();
            bool waserrors = new bool();
            int i_ = 0;

            
            //
            // Primary settings
            //
            maxm = 10;
            maxn = 100;
            passcount = 1;
            threshold = 1000*math.machineepsilon;
            waserrors = false;
            pcaconverrors = false;
            pcaorterrors = false;
            pcavarerrors = false;
            pcaopterrors = false;
            
            //
            // Test 1: N random points in M-dimensional space
            //
            for(m=1; m<=maxm; m++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // Generate task
                    //
                    x = new double[n-1+1, m-1+1];
                    means = new double[m-1+1];
                    for(j=0; j<=m-1; j++)
                    {
                        means[j] = 1.5*math.randomreal()-0.75;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            x[i,j] = means[j]+(2*math.randomreal()-1);
                        }
                    }
                    
                    //
                    // Solve
                    //
                    pca.pcabuildbasis(x, n, m, ref info, ref s, ref v);
                    if( info!=1 )
                    {
                        pcaconverrors = true;
                        continue;
                    }
                    
                    //
                    // Orthogonality test
                    //
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            t = 0.0;
                            for(i_=0; i_<=m-1;i_++)
                            {
                                t += v[i_,i]*v[i_,j];
                            }
                            if( i==j )
                            {
                                t = t-1;
                            }
                            pcaorterrors = pcaorterrors | (double)(Math.Abs(t))>(double)(threshold);
                        }
                    }
                    
                    //
                    // Variance test
                    //
                    t2 = new double[n-1+1];
                    for(k=0; k<=m-1; k++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            t = 0.0;
                            for(i_=0; i_<=m-1;i_++)
                            {
                                t += x[i,i_]*v[i_,k];
                            }
                            t2[i] = t;
                        }
                        calculatemv(t2, n, ref tmean, ref tmeans, ref tstddev, ref tstddevs);
                        if( n!=1 )
                        {
                            t = math.sqr(tstddev)*n/(n-1);
                        }
                        else
                        {
                            t = 0;
                        }
                        pcavarerrors = pcavarerrors | (double)(Math.Abs(t-s[k]))>(double)(threshold);
                    }
                    for(k=0; k<=m-2; k++)
                    {
                        pcavarerrors = pcavarerrors | (double)(s[k])<(double)(s[k+1]);
                    }
                    
                    //
                    // Optimality: different perturbations in V[..,0] can't
                    // increase variance of projection - can only decrease.
                    //
                    t2 = new double[n-1+1];
                    t3 = new double[n-1+1];
                    for(i=0; i<=n-1; i++)
                    {
                        t = 0.0;
                        for(i_=0; i_<=m-1;i_++)
                        {
                            t += x[i,i_]*v[i_,0];
                        }
                        t2[i] = t;
                    }
                    calculatemv(t2, n, ref tmean, ref tmeans, ref tstddev, ref tstddevs);
                    for(k=0; k<=2*m-1; k++)
                    {
                        h = 0.001;
                        if( k%2!=0 )
                        {
                            h = -h;
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            t3[i_] = t2[i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            t3[i_] = t3[i_] + h*x[i_,k/2];
                        }
                        t = 0;
                        for(j=0; j<=m-1; j++)
                        {
                            if( j!=k/2 )
                            {
                                t = t+math.sqr(v[j,0]);
                            }
                            else
                            {
                                t = t+math.sqr(v[j,0]+h);
                            }
                        }
                        t = 1/Math.Sqrt(t);
                        for(i_=0; i_<=n-1;i_++)
                        {
                            t3[i_] = t*t3[i_];
                        }
                        calculatemv(t3, n, ref tmean2, ref tmeans2, ref tstddev2, ref tstddevs2);
                        pcaopterrors = pcaopterrors | (double)(tstddev2)>(double)(tstddev+threshold);
                    }
                }
            }
            
            //
            // Special test for N=0
            //
            for(m=1; m<=maxm; m++)
            {
                
                //
                // Solve
                //
                pca.pcabuildbasis(x, 0, m, ref info, ref s, ref v);
                if( info!=1 )
                {
                    pcaconverrors = true;
                    continue;
                }
                
                //
                // Orthogonality test
                //
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        t = 0.0;
                        for(i_=0; i_<=m-1;i_++)
                        {
                            t += v[i_,i]*v[i_,j];
                        }
                        if( i==j )
                        {
                            t = t-1;
                        }
                        pcaorterrors = pcaorterrors | (double)(Math.Abs(t))>(double)(threshold);
                    }
                }
            }
            
            //
            // Final report
            //
            waserrors = ((pcaconverrors | pcaorterrors) | pcavarerrors) | pcaopterrors;
            if( !silent )
            {
                System.Console.Write("PCA TEST");
                System.Console.WriteLine();
                System.Console.Write("TOTAL RESULTS:                           ");
                if( !waserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* CONVERGENCE                            ");
                if( !pcaconverrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* ORTOGONALITY                           ");
                if( !pcaorterrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* VARIANCE REPORT                        ");
                if( !pcavarerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* OPTIMALITY                             ");
                if( !pcaopterrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST SUMMARY: FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST SUMMARY: PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Moments estimates and their errors
        *************************************************************************/
        private static void calculatemv(double[] x,
            int n,
            ref double mean,
            ref double means,
            ref double stddev,
            ref double stddevs)
        {
            int i = 0;
            double v1 = 0;
            double v2 = 0;
            double variance = 0;

            mean = 0;
            means = 0;
            stddev = 0;
            stddevs = 0;

            mean = 0;
            means = 1;
            stddev = 0;
            stddevs = 1;
            variance = 0;
            if( n<=1 )
            {
                return;
            }
            
            //
            // Mean
            //
            for(i=0; i<=n-1; i++)
            {
                mean = mean+x[i];
            }
            mean = mean/n;
            
            //
            // Variance (using corrected two-pass algorithm)
            //
            if( n!=1 )
            {
                v1 = 0;
                for(i=0; i<=n-1; i++)
                {
                    v1 = v1+math.sqr(x[i]-mean);
                }
                v2 = 0;
                for(i=0; i<=n-1; i++)
                {
                    v2 = v2+(x[i]-mean);
                }
                v2 = math.sqr(v2)/n;
                variance = (v1-v2)/n;
                if( (double)(variance)<(double)(0) )
                {
                    variance = 0;
                }
                stddev = Math.Sqrt(variance);
            }
            
            //
            // Errors
            //
            means = stddev/Math.Sqrt(n);
            stddevs = stddev*Math.Sqrt(2)/Math.Sqrt(n-1);
        }


    }
    public class testodesolverunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testodesolver(bool silent)
        {
            bool result = new bool();
            int passcount = 0;
            bool curerrors = new bool();
            bool rkckerrors = new bool();
            bool waserrors = new bool();
            double[] xtbl = new double[0];
            double[,] ytbl = new double[0,0];
            odesolver.odesolverreport rep = new odesolver.odesolverreport();
            double[] xg = new double[0];
            double[] y = new double[0];
            double h = 0;
            double eps = 0;
            int solver = 0;
            int pass = 0;
            int mynfev = 0;
            double v = 0;
            int m = 0;
            int m2 = 0;
            int i = 0;
            double err = 0;
            odesolver.odesolverstate state = new odesolver.odesolverstate();
            int i_ = 0;

            rkckerrors = false;
            waserrors = false;
            passcount = 10;
            
            //
            // simple test: just A*sin(x)+B*cos(x)
            //
            ap.assert(passcount>=2);
            for(pass=0; pass<=passcount-1; pass++)
            {
                for(solver=0; solver<=0; solver++)
                {
                    
                    //
                    // prepare
                    //
                    h = 1.0E-2;
                    eps = 1.0E-5;
                    if( pass%2==0 )
                    {
                        eps = -eps;
                    }
                    y = new double[2];
                    for(i=0; i<=1; i++)
                    {
                        y[i] = 2*math.randomreal()-1;
                    }
                    m = 2+math.randominteger(10);
                    xg = new double[m];
                    xg[0] = (m-1)*math.randomreal();
                    for(i=1; i<=m-1; i++)
                    {
                        xg[i] = xg[i-1]+math.randomreal();
                    }
                    v = 2*Math.PI/(xg[m-1]-xg[0]);
                    for(i_=0; i_<=m-1;i_++)
                    {
                        xg[i_] = v*xg[i_];
                    }
                    if( (double)(math.randomreal())>(double)(0.5) )
                    {
                        for(i_=0; i_<=m-1;i_++)
                        {
                            xg[i_] = -1*xg[i_];
                        }
                    }
                    mynfev = 0;
                    
                    //
                    // choose solver
                    //
                    if( solver==0 )
                    {
                        odesolver.odesolverrkck(y, 2, xg, m, eps, h, state);
                    }
                    
                    //
                    // solve
                    //
                    while( odesolver.odesolveriteration(state) )
                    {
                        state.dy[0] = state.y[1];
                        state.dy[1] = -state.y[0];
                        mynfev = mynfev+1;
                    }
                    odesolver.odesolverresults(state, ref m2, ref xtbl, ref ytbl, rep);
                    
                    //
                    // check results
                    //
                    curerrors = false;
                    if( rep.terminationtype<=0 )
                    {
                        curerrors = true;
                    }
                    else
                    {
                        curerrors = curerrors | m2!=m;
                        err = 0;
                        for(i=0; i<=m-1; i++)
                        {
                            err = Math.Max(err, Math.Abs(ytbl[i,0]-(y[0]*Math.Cos(xtbl[i]-xtbl[0])+y[1]*Math.Sin(xtbl[i]-xtbl[0]))));
                            err = Math.Max(err, Math.Abs(ytbl[i,1]-(-(y[0]*Math.Sin(xtbl[i]-xtbl[0]))+y[1]*Math.Cos(xtbl[i]-xtbl[0]))));
                        }
                        curerrors = curerrors | (double)(err)>(double)(10*Math.Abs(eps));
                        curerrors = curerrors | mynfev!=rep.nfev;
                    }
                    if( solver==0 )
                    {
                        rkckerrors = rkckerrors | curerrors;
                    }
                }
            }
            
            //
            // another test:
            //
            //     y(0)   = 0
            //     dy/dx  = f(x,y)
            //     f(x,y) = 0,   x<1
            //              x-1, x>=1
            //
            // with BOTH absolute and fractional tolerances.
            // Starting from zero will be real challenge for
            // fractional tolerance.
            //
            ap.assert(passcount>=2);
            for(pass=0; pass<=passcount-1; pass++)
            {
                h = 1.0E-4;
                eps = 1.0E-4;
                if( pass%2==0 )
                {
                    eps = -eps;
                }
                y = new double[1];
                y[0] = 0;
                m = 21;
                xg = new double[m];
                for(i=0; i<=m-1; i++)
                {
                    xg[i] = (double)(2*i)/(double)(m-1);
                }
                mynfev = 0;
                odesolver.odesolverrkck(y, 1, xg, m, eps, h, state);
                while( odesolver.odesolveriteration(state) )
                {
                    state.dy[0] = Math.Max(state.x-1, 0);
                    mynfev = mynfev+1;
                }
                odesolver.odesolverresults(state, ref m2, ref xtbl, ref ytbl, rep);
                if( rep.terminationtype<=0 )
                {
                    rkckerrors = true;
                }
                else
                {
                    rkckerrors = rkckerrors | m2!=m;
                    err = 0;
                    for(i=0; i<=m-1; i++)
                    {
                        err = Math.Max(err, Math.Abs(ytbl[i,0]-math.sqr(Math.Max(xg[i]-1, 0))/2));
                    }
                    rkckerrors = rkckerrors | (double)(err)>(double)(Math.Abs(eps));
                    rkckerrors = rkckerrors | mynfev!=rep.nfev;
                }
            }
            
            //
            // end
            //
            waserrors = rkckerrors;
            if( !silent )
            {
                System.Console.Write("TESTING ODE SOLVER");
                System.Console.WriteLine();
                System.Console.Write("* RK CASH-KARP:                           ");
                if( rkckerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Unsets real matrix
        *************************************************************************/
        private static void unset2d(ref double[,] x)
        {
            x = new double[1, 1];
            x[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets real vector
        *************************************************************************/
        private static void unset1d(ref double[] x)
        {
            x = new double[1];
            x[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets report
        *************************************************************************/
        private static void unsetrep(odesolver.odesolverreport rep)
        {
            rep.nfev = 0;
        }


    }
    public class testfftunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testfft(bool silent)
        {
            bool result = new bool();
            int n = 0;
            int i = 0;
            int k = 0;
            complex[] a1 = new complex[0];
            complex[] a2 = new complex[0];
            complex[] a3 = new complex[0];
            double[] r1 = new double[0];
            double[] r2 = new double[0];
            double[] buf = new double[0];
            ftbase.ftplan plan = new ftbase.ftplan();
            int maxn = 0;
            double bidierr = 0;
            double bidirerr = 0;
            double referr = 0;
            double refrerr = 0;
            double reinterr = 0;
            double errtol = 0;
            bool referrors = new bool();
            bool bidierrors = new bool();
            bool refrerrors = new bool();
            bool bidirerrors = new bool();
            bool reinterrors = new bool();
            bool waserrors = new bool();
            int i_ = 0;

            maxn = 128;
            errtol = 100000*Math.Pow(maxn, (double)3/(double)2)*math.machineepsilon;
            bidierrors = false;
            referrors = false;
            bidirerrors = false;
            refrerrors = false;
            reinterrors = false;
            waserrors = false;
            
            //
            // Test bi-directional error: norm(x-invFFT(FFT(x)))
            //
            bidierr = 0;
            bidirerr = 0;
            for(n=1; n<=maxn; n++)
            {
                
                //
                // Complex FFT/invFFT
                //
                a1 = new complex[n];
                a2 = new complex[n];
                a3 = new complex[n];
                for(i=0; i<=n-1; i++)
                {
                    a1[i].x = 2*math.randomreal()-1;
                    a1[i].y = 2*math.randomreal()-1;
                    a2[i] = a1[i];
                    a3[i] = a1[i];
                }
                fft.fftc1d(ref a2, n);
                fft.fftc1dinv(ref a2, n);
                fft.fftc1dinv(ref a3, n);
                fft.fftc1d(ref a3, n);
                for(i=0; i<=n-1; i++)
                {
                    bidierr = Math.Max(bidierr, math.abscomplex(a1[i]-a2[i]));
                    bidierr = Math.Max(bidierr, math.abscomplex(a1[i]-a3[i]));
                }
                
                //
                // Real
                //
                r1 = new double[n];
                r2 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    r1[i] = 2*math.randomreal()-1;
                    r2[i] = r1[i];
                }
                fft.fftr1d(r2, n, ref a1);
                for(i_=0; i_<=n-1;i_++)
                {
                    r2[i_] = 0*r2[i_];
                }
                fft.fftr1dinv(a1, n, ref r2);
                for(i=0; i<=n-1; i++)
                {
                    bidirerr = Math.Max(bidirerr, math.abscomplex(r1[i]-r2[i]));
                }
            }
            bidierrors = bidierrors | (double)(bidierr)>(double)(errtol);
            bidirerrors = bidirerrors | (double)(bidirerr)>(double)(errtol);
            
            //
            // Test against reference O(N^2) implementation
            //
            referr = 0;
            refrerr = 0;
            for(n=1; n<=maxn; n++)
            {
                
                //
                // Complex FFT
                //
                a1 = new complex[n];
                a2 = new complex[n];
                for(i=0; i<=n-1; i++)
                {
                    a1[i].x = 2*math.randomreal()-1;
                    a1[i].y = 2*math.randomreal()-1;
                    a2[i] = a1[i];
                }
                fft.fftc1d(ref a1, n);
                reffftc1d(ref a2, n);
                for(i=0; i<=n-1; i++)
                {
                    referr = Math.Max(referr, math.abscomplex(a1[i]-a2[i]));
                }
                
                //
                // Complex inverse FFT
                //
                a1 = new complex[n];
                a2 = new complex[n];
                for(i=0; i<=n-1; i++)
                {
                    a1[i].x = 2*math.randomreal()-1;
                    a1[i].y = 2*math.randomreal()-1;
                    a2[i] = a1[i];
                }
                fft.fftc1dinv(ref a1, n);
                reffftc1dinv(ref a2, n);
                for(i=0; i<=n-1; i++)
                {
                    referr = Math.Max(referr, math.abscomplex(a1[i]-a2[i]));
                }
                
                //
                // Real forward/inverse FFT:
                // * calculate and check forward FFT
                // * use precalculated FFT to check backward FFT
                //   fill unused parts of frequencies array with random numbers
                //   to ensure that they are not really used
                //
                r1 = new double[n];
                r2 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    r1[i] = 2*math.randomreal()-1;
                    r2[i] = r1[i];
                }
                fft.fftr1d(r1, n, ref a1);
                refinternalrfft(r2, n, ref a2);
                for(i=0; i<=n-1; i++)
                {
                    refrerr = Math.Max(refrerr, math.abscomplex(a1[i]-a2[i]));
                }
                a3 = new complex[(int)Math.Floor((double)n/(double)2)+1];
                for(i=0; i<=(int)Math.Floor((double)n/(double)2); i++)
                {
                    a3[i] = a2[i];
                }
                a3[0].y = 2*math.randomreal()-1;
                if( n%2==0 )
                {
                    a3[(int)Math.Floor((double)n/(double)2)].y = 2*math.randomreal()-1;
                }
                for(i=0; i<=n-1; i++)
                {
                    r1[i] = 0;
                }
                fft.fftr1dinv(a3, n, ref r1);
                for(i=0; i<=n-1; i++)
                {
                    refrerr = Math.Max(refrerr, Math.Abs(r2[i]-r1[i]));
                }
            }
            referrors = referrors | (double)(referr)>(double)(errtol);
            refrerrors = refrerrors | (double)(refrerr)>(double)(errtol);
            
            //
            // test internal real even FFT
            //
            reinterr = 0;
            for(k=1; k<=maxn/2; k++)
            {
                n = 2*k;
                
                //
                // Real forward FFT
                //
                r1 = new double[n];
                r2 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    r1[i] = 2*math.randomreal()-1;
                    r2[i] = r1[i];
                }
                ftbase.ftbasegeneratecomplexfftplan(n/2, plan);
                buf = new double[n];
                fft.fftr1dinternaleven(ref r1, n, ref buf, plan);
                refinternalrfft(r2, n, ref a2);
                reinterr = Math.Max(reinterr, Math.Abs(r1[0]-a2[0].x));
                reinterr = Math.Max(reinterr, Math.Abs(r1[1]-a2[n/2].x));
                for(i=1; i<=n/2-1; i++)
                {
                    reinterr = Math.Max(reinterr, Math.Abs(r1[2*i+0]-a2[i].x));
                    reinterr = Math.Max(reinterr, Math.Abs(r1[2*i+1]-a2[i].y));
                }
                
                //
                // Real backward FFT
                //
                r1 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    r1[i] = 2*math.randomreal()-1;
                }
                a2 = new complex[(int)Math.Floor((double)n/(double)2)+1];
                a2[0] = r1[0];
                for(i=1; i<=(int)Math.Floor((double)n/(double)2)-1; i++)
                {
                    a2[i].x = r1[2*i+0];
                    a2[i].y = r1[2*i+1];
                }
                a2[(int)Math.Floor((double)n/(double)2)] = r1[1];
                ftbase.ftbasegeneratecomplexfftplan(n/2, plan);
                buf = new double[n];
                fft.fftr1dinvinternaleven(ref r1, n, ref buf, plan);
                fft.fftr1dinv(a2, n, ref r2);
                for(i=0; i<=n-1; i++)
                {
                    reinterr = Math.Max(reinterr, Math.Abs(r1[i]-r2[i]));
                }
            }
            reinterrors = reinterrors | (double)(reinterr)>(double)(errtol);
            
            //
            // end
            //
            waserrors = (((bidierrors | bidirerrors) | referrors) | refrerrors) | reinterrors;
            if( !silent )
            {
                System.Console.Write("TESTING FFT");
                System.Console.WriteLine();
                System.Console.Write("FINAL RESULT:                             ");
                if( waserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* BI-DIRECTIONAL COMPLEX TEST:            ");
                if( bidierrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* AGAINST REFERENCE COMPLEX FFT:          ");
                if( referrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* BI-DIRECTIONAL REAL TEST:               ");
                if( bidirerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* AGAINST REFERENCE REAL FFT:             ");
                if( refrerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* INTERNAL EVEN FFT:                      ");
                if( reinterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Reference FFT
        *************************************************************************/
        private static void reffftc1d(ref complex[] a,
            int n)
        {
            double[] buf = new double[0];
            int i = 0;

            ap.assert(n>0, "FFTC1D: incorrect N!");
            buf = new double[2*n];
            for(i=0; i<=n-1; i++)
            {
                buf[2*i+0] = a[i].x;
                buf[2*i+1] = a[i].y;
            }
            refinternalcfft(ref buf, n, false);
            for(i=0; i<=n-1; i++)
            {
                a[i].x = buf[2*i+0];
                a[i].y = buf[2*i+1];
            }
        }


        /*************************************************************************
        Reference inverse FFT
        *************************************************************************/
        private static void reffftc1dinv(ref complex[] a,
            int n)
        {
            double[] buf = new double[0];
            int i = 0;

            ap.assert(n>0, "FFTC1DInv: incorrect N!");
            buf = new double[2*n];
            for(i=0; i<=n-1; i++)
            {
                buf[2*i+0] = a[i].x;
                buf[2*i+1] = a[i].y;
            }
            refinternalcfft(ref buf, n, true);
            for(i=0; i<=n-1; i++)
            {
                a[i].x = buf[2*i+0];
                a[i].y = buf[2*i+1];
            }
        }


        /*************************************************************************
        Internal complex FFT stub.
        Uses straightforward formula with O(N^2) complexity.
        *************************************************************************/
        private static void refinternalcfft(ref double[] a,
            int nn,
            bool inversefft)
        {
            double[] tmp = new double[0];
            int i = 0;
            int k = 0;
            double hre = 0;
            double him = 0;
            double c = 0;
            double s = 0;
            double re = 0;
            double im = 0;

            tmp = new double[2*nn-1+1];
            if( !inversefft )
            {
                for(i=0; i<=nn-1; i++)
                {
                    hre = 0;
                    him = 0;
                    for(k=0; k<=nn-1; k++)
                    {
                        re = a[2*k];
                        im = a[2*k+1];
                        c = Math.Cos(-(2*Math.PI*k*i/nn));
                        s = Math.Sin(-(2*Math.PI*k*i/nn));
                        hre = hre+c*re-s*im;
                        him = him+c*im+s*re;
                    }
                    tmp[2*i] = hre;
                    tmp[2*i+1] = him;
                }
                for(i=0; i<=2*nn-1; i++)
                {
                    a[i] = tmp[i];
                }
            }
            else
            {
                for(k=0; k<=nn-1; k++)
                {
                    hre = 0;
                    him = 0;
                    for(i=0; i<=nn-1; i++)
                    {
                        re = a[2*i];
                        im = a[2*i+1];
                        c = Math.Cos(2*Math.PI*k*i/nn);
                        s = Math.Sin(2*Math.PI*k*i/nn);
                        hre = hre+c*re-s*im;
                        him = him+c*im+s*re;
                    }
                    tmp[2*k] = hre/nn;
                    tmp[2*k+1] = him/nn;
                }
                for(i=0; i<=2*nn-1; i++)
                {
                    a[i] = tmp[i];
                }
            }
        }


        /*************************************************************************
        Internal real FFT stub.
        Uses straightforward formula with O(N^2) complexity.
        *************************************************************************/
        private static void refinternalrfft(double[] a,
            int nn,
            ref complex[] f)
        {
            double[] tmp = new double[0];
            int i = 0;

            f = new complex[0];

            tmp = new double[2*nn-1+1];
            for(i=0; i<=nn-1; i++)
            {
                tmp[2*i] = a[i];
                tmp[2*i+1] = 0;
            }
            refinternalcfft(ref tmp, nn, false);
            f = new complex[nn];
            for(i=0; i<=nn-1; i++)
            {
                f[i].x = tmp[2*i+0];
                f[i].y = tmp[2*i+1];
            }
        }


    }
    public class testconvunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testconv(bool silent)
        {
            bool result = new bool();
            int m = 0;
            int n = 0;
            int i = 0;
            int rkind = 0;
            int circkind = 0;
            double[] ra = new double[0];
            double[] rb = new double[0];
            double[] rr1 = new double[0];
            double[] rr2 = new double[0];
            complex[] ca = new complex[0];
            complex[] cb = new complex[0];
            complex[] cr1 = new complex[0];
            complex[] cr2 = new complex[0];
            int maxn = 0;
            double referr = 0;
            double refrerr = 0;
            double inverr = 0;
            double invrerr = 0;
            double errtol = 0;
            bool referrors = new bool();
            bool refrerrors = new bool();
            bool inverrors = new bool();
            bool invrerrors = new bool();
            bool waserrors = new bool();

            maxn = 32;
            errtol = 100000*Math.Pow(maxn, (double)3/(double)2)*math.machineepsilon;
            referrors = false;
            refrerrors = false;
            inverrors = false;
            invrerrors = false;
            waserrors = false;
            
            //
            // Test against reference O(N^2) implementation.
            //
            // Automatic ConvC1D() and different algorithms of ConvC1DX() are tested.
            //
            referr = 0;
            refrerr = 0;
            for(m=1; m<=maxn; m++)
            {
                for(n=1; n<=maxn; n++)
                {
                    for(circkind=0; circkind<=1; circkind++)
                    {
                        for(rkind=-3; rkind<=1; rkind++)
                        {
                            
                            //
                            // skip impossible combinations of parameters:
                            // * circular convolution, M<N, RKind<>-3 - internal subroutine does not support M<N.
                            //
                            if( (circkind!=0 & m<n) & rkind!=-3 )
                            {
                                continue;
                            }
                            
                            //
                            // Complex convolution
                            //
                            ca = new complex[m];
                            for(i=0; i<=m-1; i++)
                            {
                                ca[i].x = 2*math.randomreal()-1;
                                ca[i].y = 2*math.randomreal()-1;
                            }
                            cb = new complex[n];
                            for(i=0; i<=n-1; i++)
                            {
                                cb[i].x = 2*math.randomreal()-1;
                                cb[i].y = 2*math.randomreal()-1;
                            }
                            cr1 = new complex[1];
                            if( rkind==-3 )
                            {
                                
                                //
                                // test wrapper subroutine:
                                // * circular/non-circular
                                //
                                if( circkind==0 )
                                {
                                    conv.convc1d(ca, m, cb, n, ref cr1);
                                }
                                else
                                {
                                    conv.convc1dcircular(ca, m, cb, n, ref cr1);
                                }
                            }
                            else
                            {
                                
                                //
                                // test internal subroutine
                                //
                                if( m>=n )
                                {
                                    
                                    //
                                    // test internal subroutine:
                                    // * circular/non-circular mode
                                    //
                                    conv.convc1dx(ca, m, cb, n, circkind!=0, rkind, 0, ref cr1);
                                }
                                else
                                {
                                    
                                    //
                                    // test internal subroutine - circular mode only
                                    //
                                    ap.assert(circkind==0, "Convolution test: internal error!");
                                    conv.convc1dx(cb, n, ca, m, false, rkind, 0, ref cr1);
                                }
                            }
                            if( circkind==0 )
                            {
                                refconvc1d(ca, m, cb, n, ref cr2);
                            }
                            else
                            {
                                refconvc1dcircular(ca, m, cb, n, ref cr2);
                            }
                            if( circkind==0 )
                            {
                                for(i=0; i<=m+n-2; i++)
                                {
                                    referr = Math.Max(referr, math.abscomplex(cr1[i]-cr2[i]));
                                }
                            }
                            else
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    referr = Math.Max(referr, math.abscomplex(cr1[i]-cr2[i]));
                                }
                            }
                            
                            //
                            // Real convolution
                            //
                            ra = new double[m];
                            for(i=0; i<=m-1; i++)
                            {
                                ra[i] = 2*math.randomreal()-1;
                            }
                            rb = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                rb[i] = 2*math.randomreal()-1;
                            }
                            rr1 = new double[1];
                            if( rkind==-3 )
                            {
                                
                                //
                                // test wrapper subroutine:
                                // * circular/non-circular
                                //
                                if( circkind==0 )
                                {
                                    conv.convr1d(ra, m, rb, n, ref rr1);
                                }
                                else
                                {
                                    conv.convr1dcircular(ra, m, rb, n, ref rr1);
                                }
                            }
                            else
                            {
                                if( m>=n )
                                {
                                    
                                    //
                                    // test internal subroutine:
                                    // * circular/non-circular mode
                                    //
                                    conv.convr1dx(ra, m, rb, n, circkind!=0, rkind, 0, ref rr1);
                                }
                                else
                                {
                                    
                                    //
                                    // test internal subroutine - non-circular mode only
                                    //
                                    conv.convr1dx(rb, n, ra, m, circkind!=0, rkind, 0, ref rr1);
                                }
                            }
                            if( circkind==0 )
                            {
                                refconvr1d(ra, m, rb, n, ref rr2);
                            }
                            else
                            {
                                refconvr1dcircular(ra, m, rb, n, ref rr2);
                            }
                            if( circkind==0 )
                            {
                                for(i=0; i<=m+n-2; i++)
                                {
                                    refrerr = Math.Max(refrerr, Math.Abs(rr1[i]-rr2[i]));
                                }
                            }
                            else
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    refrerr = Math.Max(refrerr, Math.Abs(rr1[i]-rr2[i]));
                                }
                            }
                        }
                    }
                }
            }
            referrors = referrors | (double)(referr)>(double)(errtol);
            refrerrors = refrerrors | (double)(refrerr)>(double)(errtol);
            
            //
            // Test inverse convolution
            //
            inverr = 0;
            invrerr = 0;
            for(m=1; m<=maxn; m++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // Complex circilar and non-circular
                    //
                    ca = new complex[m];
                    for(i=0; i<=m-1; i++)
                    {
                        ca[i].x = 2*math.randomreal()-1;
                        ca[i].y = 2*math.randomreal()-1;
                    }
                    cb = new complex[n];
                    for(i=0; i<=n-1; i++)
                    {
                        cb[i].x = 2*math.randomreal()-1;
                        cb[i].y = 2*math.randomreal()-1;
                    }
                    cr1 = new complex[1];
                    cr2 = new complex[1];
                    conv.convc1d(ca, m, cb, n, ref cr2);
                    conv.convc1dinv(cr2, m+n-1, cb, n, ref cr1);
                    for(i=0; i<=m-1; i++)
                    {
                        inverr = Math.Max(inverr, math.abscomplex(cr1[i]-ca[i]));
                    }
                    cr1 = new complex[1];
                    cr2 = new complex[1];
                    conv.convc1dcircular(ca, m, cb, n, ref cr2);
                    conv.convc1dcircularinv(cr2, m, cb, n, ref cr1);
                    for(i=0; i<=m-1; i++)
                    {
                        inverr = Math.Max(inverr, math.abscomplex(cr1[i]-ca[i]));
                    }
                    
                    //
                    // Real circilar and non-circular
                    //
                    ra = new double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        ra[i] = 2*math.randomreal()-1;
                    }
                    rb = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        rb[i] = 2*math.randomreal()-1;
                    }
                    rr1 = new double[1];
                    rr2 = new double[1];
                    conv.convr1d(ra, m, rb, n, ref rr2);
                    conv.convr1dinv(rr2, m+n-1, rb, n, ref rr1);
                    for(i=0; i<=m-1; i++)
                    {
                        invrerr = Math.Max(invrerr, Math.Abs(rr1[i]-ra[i]));
                    }
                    rr1 = new double[1];
                    rr2 = new double[1];
                    conv.convr1dcircular(ra, m, rb, n, ref rr2);
                    conv.convr1dcircularinv(rr2, m, rb, n, ref rr1);
                    for(i=0; i<=m-1; i++)
                    {
                        invrerr = Math.Max(invrerr, Math.Abs(rr1[i]-ra[i]));
                    }
                }
            }
            inverrors = inverrors | (double)(inverr)>(double)(errtol);
            invrerrors = invrerrors | (double)(invrerr)>(double)(errtol);
            
            //
            // end
            //
            waserrors = ((referrors | refrerrors) | inverrors) | invrerrors;
            if( !silent )
            {
                System.Console.Write("TESTING CONVOLUTION");
                System.Console.WriteLine();
                System.Console.Write("FINAL RESULT:                             ");
                if( waserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* AGAINST REFERENCE COMPLEX CONV:         ");
                if( referrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* AGAINST REFERENCE REAL CONV:            ");
                if( refrerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* COMPLEX INVERSE:                        ");
                if( inverrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* REAL INVERSE:                           ");
                if( invrerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refconvc1d(complex[] a,
            int m,
            complex[] b,
            int n,
            ref complex[] r)
        {
            int i = 0;
            complex v = 0;
            int i_ = 0;
            int i1_ = 0;

            r = new complex[0];

            r = new complex[m+n-1];
            for(i=0; i<=m+n-2; i++)
            {
                r[i] = 0;
            }
            for(i=0; i<=m-1; i++)
            {
                v = a[i];
                i1_ = (0) - (i);
                for(i_=i; i_<=i+n-1;i_++)
                {
                    r[i_] = r[i_] + v*b[i_+i1_];
                }
            }
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refconvc1dcircular(complex[] a,
            int m,
            complex[] b,
            int n,
            ref complex[] r)
        {
            int i1 = 0;
            int i2 = 0;
            int j2 = 0;
            complex[] buf = new complex[0];
            int i_ = 0;
            int i1_ = 0;

            r = new complex[0];

            refconvc1d(a, m, b, n, ref buf);
            r = new complex[m];
            for(i_=0; i_<=m-1;i_++)
            {
                r[i_] = buf[i_];
            }
            i1 = m;
            while( i1<=m+n-2 )
            {
                i2 = Math.Min(i1+m-1, m+n-2);
                j2 = i2-i1;
                i1_ = (i1) - (0);
                for(i_=0; i_<=j2;i_++)
                {
                    r[i_] = r[i_] + buf[i_+i1_];
                }
                i1 = i1+m;
            }
        }


        /*************************************************************************
        Reference FFT
        *************************************************************************/
        private static void refconvr1d(double[] a,
            int m,
            double[] b,
            int n,
            ref double[] r)
        {
            int i = 0;
            double v = 0;
            int i_ = 0;
            int i1_ = 0;

            r = new double[0];

            r = new double[m+n-1];
            for(i=0; i<=m+n-2; i++)
            {
                r[i] = 0;
            }
            for(i=0; i<=m-1; i++)
            {
                v = a[i];
                i1_ = (0) - (i);
                for(i_=i; i_<=i+n-1;i_++)
                {
                    r[i_] = r[i_] + v*b[i_+i1_];
                }
            }
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refconvr1dcircular(double[] a,
            int m,
            double[] b,
            int n,
            ref double[] r)
        {
            int i1 = 0;
            int i2 = 0;
            int j2 = 0;
            double[] buf = new double[0];
            int i_ = 0;
            int i1_ = 0;

            r = new double[0];

            refconvr1d(a, m, b, n, ref buf);
            r = new double[m];
            for(i_=0; i_<=m-1;i_++)
            {
                r[i_] = buf[i_];
            }
            i1 = m;
            while( i1<=m+n-2 )
            {
                i2 = Math.Min(i1+m-1, m+n-2);
                j2 = i2-i1;
                i1_ = (i1) - (0);
                for(i_=0; i_<=j2;i_++)
                {
                    r[i_] = r[i_] + buf[i_+i1_];
                }
                i1 = i1+m;
            }
        }


    }
    public class testcorrunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testcorr(bool silent)
        {
            bool result = new bool();
            int m = 0;
            int n = 0;
            int i = 0;
            double[] ra = new double[0];
            double[] rb = new double[0];
            double[] rr1 = new double[0];
            double[] rr2 = new double[0];
            complex[] ca = new complex[0];
            complex[] cb = new complex[0];
            complex[] cr1 = new complex[0];
            complex[] cr2 = new complex[0];
            int maxn = 0;
            double referr = 0;
            double refrerr = 0;
            double errtol = 0;
            bool referrors = new bool();
            bool refrerrors = new bool();
            bool inverrors = new bool();
            bool invrerrors = new bool();
            bool waserrors = new bool();

            maxn = 32;
            errtol = 100000*Math.Pow(maxn, (double)3/(double)2)*math.machineepsilon;
            referrors = false;
            refrerrors = false;
            inverrors = false;
            invrerrors = false;
            waserrors = false;
            
            //
            // Test against reference O(N^2) implementation.
            //
            referr = 0;
            refrerr = 0;
            for(m=1; m<=maxn; m++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // Complex correlation
                    //
                    ca = new complex[m];
                    for(i=0; i<=m-1; i++)
                    {
                        ca[i].x = 2*math.randomreal()-1;
                        ca[i].y = 2*math.randomreal()-1;
                    }
                    cb = new complex[n];
                    for(i=0; i<=n-1; i++)
                    {
                        cb[i].x = 2*math.randomreal()-1;
                        cb[i].y = 2*math.randomreal()-1;
                    }
                    cr1 = new complex[1];
                    corr.corrc1d(ca, m, cb, n, ref cr1);
                    refcorrc1d(ca, m, cb, n, ref cr2);
                    for(i=0; i<=m+n-2; i++)
                    {
                        referr = Math.Max(referr, math.abscomplex(cr1[i]-cr2[i]));
                    }
                    cr1 = new complex[1];
                    corr.corrc1dcircular(ca, m, cb, n, ref cr1);
                    refcorrc1dcircular(ca, m, cb, n, ref cr2);
                    for(i=0; i<=m-1; i++)
                    {
                        referr = Math.Max(referr, math.abscomplex(cr1[i]-cr2[i]));
                    }
                    
                    //
                    // Real correlation
                    //
                    ra = new double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        ra[i] = 2*math.randomreal()-1;
                    }
                    rb = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        rb[i] = 2*math.randomreal()-1;
                    }
                    rr1 = new double[1];
                    corr.corrr1d(ra, m, rb, n, ref rr1);
                    refcorrr1d(ra, m, rb, n, ref rr2);
                    for(i=0; i<=m+n-2; i++)
                    {
                        refrerr = Math.Max(refrerr, Math.Abs(rr1[i]-rr2[i]));
                    }
                    rr1 = new double[1];
                    corr.corrr1dcircular(ra, m, rb, n, ref rr1);
                    refcorrr1dcircular(ra, m, rb, n, ref rr2);
                    for(i=0; i<=m-1; i++)
                    {
                        refrerr = Math.Max(refrerr, Math.Abs(rr1[i]-rr2[i]));
                    }
                }
            }
            referrors = referrors | (double)(referr)>(double)(errtol);
            refrerrors = refrerrors | (double)(refrerr)>(double)(errtol);
            
            //
            // end
            //
            waserrors = referrors | refrerrors;
            if( !silent )
            {
                System.Console.Write("TESTING CORRELATION");
                System.Console.WriteLine();
                System.Console.Write("FINAL RESULT:                             ");
                if( waserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* AGAINST REFERENCE COMPLEX CORR:         ");
                if( referrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* AGAINST REFERENCE REAL CORR:            ");
                if( refrerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refcorrc1d(complex[] signal,
            int n,
            complex[] pattern,
            int m,
            ref complex[] r)
        {
            int i = 0;
            int j = 0;
            complex v = 0;
            complex[] s = new complex[0];
            int i_ = 0;

            r = new complex[0];

            s = new complex[m+n-1];
            for(i_=0; i_<=n-1;i_++)
            {
                s[i_] = signal[i_];
            }
            for(i=n; i<=m+n-2; i++)
            {
                s[i] = 0;
            }
            r = new complex[m+n-1];
            for(i=0; i<=n-1; i++)
            {
                v = 0;
                for(j=0; j<=m-1; j++)
                {
                    if( i+j>=n )
                    {
                        break;
                    }
                    v = v+math.conj(pattern[j])*s[i+j];
                }
                r[i] = v;
            }
            for(i=1; i<=m-1; i++)
            {
                v = 0;
                for(j=i; j<=m-1; j++)
                {
                    v = v+math.conj(pattern[j])*s[j-i];
                }
                r[m+n-1-i] = v;
            }
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refcorrc1dcircular(complex[] signal,
            int n,
            complex[] pattern,
            int m,
            ref complex[] r)
        {
            int i = 0;
            int j = 0;
            complex v = 0;

            r = new complex[0];

            r = new complex[n];
            for(i=0; i<=n-1; i++)
            {
                v = 0;
                for(j=0; j<=m-1; j++)
                {
                    v = v+math.conj(pattern[j])*signal[(i+j)%n];
                }
                r[i] = v;
            }
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refcorrr1d(double[] signal,
            int n,
            double[] pattern,
            int m,
            ref double[] r)
        {
            int i = 0;
            int j = 0;
            double v = 0;
            double[] s = new double[0];
            int i_ = 0;

            r = new double[0];

            s = new double[m+n-1];
            for(i_=0; i_<=n-1;i_++)
            {
                s[i_] = signal[i_];
            }
            for(i=n; i<=m+n-2; i++)
            {
                s[i] = 0;
            }
            r = new double[m+n-1];
            for(i=0; i<=n-1; i++)
            {
                v = 0;
                for(j=0; j<=m-1; j++)
                {
                    if( i+j>=n )
                    {
                        break;
                    }
                    v = v+pattern[j]*s[i+j];
                }
                r[i] = v;
            }
            for(i=1; i<=m-1; i++)
            {
                v = 0;
                for(j=i; j<=m-1; j++)
                {
                    v = v+pattern[j]*s[-i+j];
                }
                r[m+n-1-i] = v;
            }
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refcorrr1dcircular(double[] signal,
            int n,
            double[] pattern,
            int m,
            ref double[] r)
        {
            int i = 0;
            int j = 0;
            double v = 0;

            r = new double[0];

            r = new double[n];
            for(i=0; i<=n-1; i++)
            {
                v = 0;
                for(j=0; j<=m-1; j++)
                {
                    v = v+pattern[j]*signal[(i+j)%n];
                }
                r[i] = v;
            }
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refconvc1d(complex[] a,
            int m,
            complex[] b,
            int n,
            ref complex[] r)
        {
            int i = 0;
            complex v = 0;
            int i_ = 0;
            int i1_ = 0;

            r = new complex[0];

            r = new complex[m+n-1];
            for(i=0; i<=m+n-2; i++)
            {
                r[i] = 0;
            }
            for(i=0; i<=m-1; i++)
            {
                v = a[i];
                i1_ = (0) - (i);
                for(i_=i; i_<=i+n-1;i_++)
                {
                    r[i_] = r[i_] + v*b[i_+i1_];
                }
            }
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refconvc1dcircular(complex[] a,
            int m,
            complex[] b,
            int n,
            ref complex[] r)
        {
            int i1 = 0;
            int i2 = 0;
            int j2 = 0;
            complex[] buf = new complex[0];
            int i_ = 0;
            int i1_ = 0;

            r = new complex[0];

            refconvc1d(a, m, b, n, ref buf);
            r = new complex[m];
            for(i_=0; i_<=m-1;i_++)
            {
                r[i_] = buf[i_];
            }
            i1 = m;
            while( i1<=m+n-2 )
            {
                i2 = Math.Min(i1+m-1, m+n-2);
                j2 = i2-i1;
                i1_ = (i1) - (0);
                for(i_=0; i_<=j2;i_++)
                {
                    r[i_] = r[i_] + buf[i_+i1_];
                }
                i1 = i1+m;
            }
        }


        /*************************************************************************
        Reference FFT
        *************************************************************************/
        private static void refconvr1d(double[] a,
            int m,
            double[] b,
            int n,
            ref double[] r)
        {
            int i = 0;
            double v = 0;
            int i_ = 0;
            int i1_ = 0;

            r = new double[0];

            r = new double[m+n-1];
            for(i=0; i<=m+n-2; i++)
            {
                r[i] = 0;
            }
            for(i=0; i<=m-1; i++)
            {
                v = a[i];
                i1_ = (0) - (i);
                for(i_=i; i_<=i+n-1;i_++)
                {
                    r[i_] = r[i_] + v*b[i_+i1_];
                }
            }
        }


        /*************************************************************************
        Reference implementation
        *************************************************************************/
        private static void refconvr1dcircular(double[] a,
            int m,
            double[] b,
            int n,
            ref double[] r)
        {
            int i1 = 0;
            int i2 = 0;
            int j2 = 0;
            double[] buf = new double[0];
            int i_ = 0;
            int i1_ = 0;

            r = new double[0];

            refconvr1d(a, m, b, n, ref buf);
            r = new double[m];
            for(i_=0; i_<=m-1;i_++)
            {
                r[i_] = buf[i_];
            }
            i1 = m;
            while( i1<=m+n-2 )
            {
                i2 = Math.Min(i1+m-1, m+n-2);
                j2 = i2-i1;
                i1_ = (i1) - (0);
                for(i_=0; i_<=j2;i_++)
                {
                    r[i_] = r[i_] + buf[i_+i1_];
                }
                i1 = i1+m;
            }
        }


    }
    public class testfhtunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testfht(bool silent)
        {
            bool result = new bool();
            int n = 0;
            int i = 0;
            double[] r1 = new double[0];
            double[] r2 = new double[0];
            double[] r3 = new double[0];
            int maxn = 0;
            double bidierr = 0;
            double referr = 0;
            double errtol = 0;
            bool referrors = new bool();
            bool bidierrors = new bool();
            bool waserrors = new bool();

            maxn = 128;
            errtol = 100000*Math.Pow(maxn, (double)3/(double)2)*math.machineepsilon;
            bidierrors = false;
            referrors = false;
            waserrors = false;
            
            //
            // Test bi-directional error: norm(x-invFHT(FHT(x)))
            //
            bidierr = 0;
            for(n=1; n<=maxn; n++)
            {
                
                //
                // FHT/invFHT
                //
                r1 = new double[n];
                r2 = new double[n];
                r3 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    r1[i] = 2*math.randomreal()-1;
                    r2[i] = r1[i];
                    r3[i] = r1[i];
                }
                fht.fhtr1d(ref r2, n);
                fht.fhtr1dinv(ref r2, n);
                fht.fhtr1dinv(ref r3, n);
                fht.fhtr1d(ref r3, n);
                for(i=0; i<=n-1; i++)
                {
                    bidierr = Math.Max(bidierr, Math.Abs(r1[i]-r2[i]));
                    bidierr = Math.Max(bidierr, Math.Abs(r1[i]-r3[i]));
                }
            }
            bidierrors = bidierrors | (double)(bidierr)>(double)(errtol);
            
            //
            // Test against reference O(N^2) implementation
            //
            referr = 0;
            for(n=1; n<=maxn; n++)
            {
                
                //
                // FHT
                //
                r1 = new double[n];
                r2 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    r1[i] = 2*math.randomreal()-1;
                    r2[i] = r1[i];
                }
                fht.fhtr1d(ref r1, n);
                reffhtr1d(ref r2, n);
                for(i=0; i<=n-1; i++)
                {
                    referr = Math.Max(referr, Math.Abs(r1[i]-r2[i]));
                }
                
                //
                // inverse FHT
                //
                r1 = new double[n];
                r2 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    r1[i] = 2*math.randomreal()-1;
                    r2[i] = r1[i];
                }
                fht.fhtr1dinv(ref r1, n);
                reffhtr1dinv(ref r2, n);
                for(i=0; i<=n-1; i++)
                {
                    referr = Math.Max(referr, Math.Abs(r1[i]-r2[i]));
                }
            }
            referrors = referrors | (double)(referr)>(double)(errtol);
            
            //
            // end
            //
            waserrors = bidierrors | referrors;
            if( !silent )
            {
                System.Console.Write("TESTING FHT");
                System.Console.WriteLine();
                System.Console.Write("FINAL RESULT:                             ");
                if( waserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* BI-DIRECTIONAL TEST:                    ");
                if( bidierrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* AGAINST REFERENCE FHT:                  ");
                if( referrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Reference FHT
        *************************************************************************/
        private static void reffhtr1d(ref double[] a,
            int n)
        {
            double[] buf = new double[0];
            int i = 0;
            int j = 0;
            double v = 0;

            ap.assert(n>0, "RefFHTR1D: incorrect N!");
            buf = new double[n];
            for(i=0; i<=n-1; i++)
            {
                v = 0;
                for(j=0; j<=n-1; j++)
                {
                    v = v+a[j]*(Math.Cos(2*Math.PI*i*j/n)+Math.Sin(2*Math.PI*i*j/n));
                }
                buf[i] = v;
            }
            for(i=0; i<=n-1; i++)
            {
                a[i] = buf[i];
            }
        }


        /*************************************************************************
        Reference inverse FHT
        *************************************************************************/
        private static void reffhtr1dinv(ref double[] a,
            int n)
        {
            int i = 0;

            ap.assert(n>0, "RefFHTR1DInv: incorrect N!");
            reffhtr1d(ref a, n);
            for(i=0; i<=n-1; i++)
            {
                a[i] = a[i]/n;
            }
        }


    }
    public class testgqunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testgq(bool silent)
        {
            bool result = new bool();
            double[] alpha = new double[0];
            double[] beta = new double[0];
            double[] x = new double[0];
            double[] w = new double[0];
            double[] x2 = new double[0];
            double[] w2 = new double[0];
            double err = 0;
            int n = 0;
            int i = 0;
            int info = 0;
            int akind = 0;
            int bkind = 0;
            double alphac = 0;
            double betac = 0;
            double errtol = 0;
            double nonstricterrtol = 0;
            double stricterrtol = 0;
            bool recerrors = new bool();
            bool specerrors = new bool();
            bool waserrors = new bool();

            recerrors = false;
            specerrors = false;
            waserrors = false;
            errtol = 1.0E-12;
            nonstricterrtol = 1.0E-6;
            stricterrtol = 1000*math.machineepsilon;
            
            //
            // Three tests for rec-based Gauss quadratures with known weights/nodes:
            // 1. Gauss-Legendre with N=2
            // 2. Gauss-Legendre with N=5
            // 3. Gauss-Chebyshev with N=1, 2, 4, 8, ..., 512
            //
            err = 0;
            alpha = new double[2];
            beta = new double[2];
            alpha[0] = 0;
            alpha[1] = 0;
            beta[1] = (double)1/(double)(4*1*1-1);
            gq.gqgeneraterec(alpha, beta, 2.0, 2, ref info, ref x, ref w);
            if( info>0 )
            {
                err = Math.Max(err, Math.Abs(x[0]+Math.Sqrt(3)/3));
                err = Math.Max(err, Math.Abs(x[1]-Math.Sqrt(3)/3));
                err = Math.Max(err, Math.Abs(w[0]-1));
                err = Math.Max(err, Math.Abs(w[1]-1));
                for(i=0; i<=0; i++)
                {
                    recerrors = recerrors | (double)(x[i])>=(double)(x[i+1]);
                }
            }
            else
            {
                recerrors = true;
            }
            alpha = new double[5];
            beta = new double[5];
            alpha[0] = 0;
            for(i=1; i<=4; i++)
            {
                alpha[i] = 0;
                beta[i] = math.sqr(i)/(4*math.sqr(i)-1);
            }
            gq.gqgeneraterec(alpha, beta, 2.0, 5, ref info, ref x, ref w);
            if( info>0 )
            {
                err = Math.Max(err, Math.Abs(x[0]+Math.Sqrt(245+14*Math.Sqrt(70))/21));
                err = Math.Max(err, Math.Abs(x[0]+x[4]));
                err = Math.Max(err, Math.Abs(x[1]+Math.Sqrt(245-14*Math.Sqrt(70))/21));
                err = Math.Max(err, Math.Abs(x[1]+x[3]));
                err = Math.Max(err, Math.Abs(x[2]));
                err = Math.Max(err, Math.Abs(w[0]-(322-13*Math.Sqrt(70))/900));
                err = Math.Max(err, Math.Abs(w[0]-w[4]));
                err = Math.Max(err, Math.Abs(w[1]-(322+13*Math.Sqrt(70))/900));
                err = Math.Max(err, Math.Abs(w[1]-w[3]));
                err = Math.Max(err, Math.Abs(w[2]-(double)128/(double)225));
                for(i=0; i<=3; i++)
                {
                    recerrors = recerrors | (double)(x[i])>=(double)(x[i+1]);
                }
            }
            else
            {
                recerrors = true;
            }
            n = 1;
            while( n<=512 )
            {
                alpha = new double[n];
                beta = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    alpha[i] = 0;
                    if( i==0 )
                    {
                        beta[i] = 0;
                    }
                    if( i==1 )
                    {
                        beta[i] = (double)1/(double)2;
                    }
                    if( i>1 )
                    {
                        beta[i] = (double)1/(double)4;
                    }
                }
                gq.gqgeneraterec(alpha, beta, Math.PI, n, ref info, ref x, ref w);
                if( info>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        err = Math.Max(err, Math.Abs(x[i]-Math.Cos(Math.PI*(n-i-0.5)/n)));
                        err = Math.Max(err, Math.Abs(w[i]-Math.PI/n));
                    }
                    for(i=0; i<=n-2; i++)
                    {
                        recerrors = recerrors | (double)(x[i])>=(double)(x[i+1]);
                    }
                }
                else
                {
                    recerrors = true;
                }
                n = n*2;
            }
            recerrors = recerrors | (double)(err)>(double)(errtol);
            
            //
            // Three tests for rec-based Gauss-Lobatto quadratures with known weights/nodes:
            // 1. Gauss-Lobatto with N=3
            // 2. Gauss-Lobatto with N=4
            // 3. Gauss-Lobatto with N=6
            //
            err = 0;
            alpha = new double[2];
            beta = new double[2];
            alpha[0] = 0;
            alpha[1] = 0;
            beta[0] = 0;
            beta[1] = (double)(1*1)/(double)(4*1*1-1);
            gq.gqgenerategausslobattorec(alpha, beta, 2.0, -1, 1, 3, ref info, ref x, ref w);
            if( info>0 )
            {
                err = Math.Max(err, Math.Abs(x[0]+1));
                err = Math.Max(err, Math.Abs(x[1]));
                err = Math.Max(err, Math.Abs(x[2]-1));
                err = Math.Max(err, Math.Abs(w[0]-(double)1/(double)3));
                err = Math.Max(err, Math.Abs(w[1]-(double)4/(double)3));
                err = Math.Max(err, Math.Abs(w[2]-(double)1/(double)3));
                for(i=0; i<=1; i++)
                {
                    recerrors = recerrors | (double)(x[i])>=(double)(x[i+1]);
                }
            }
            else
            {
                recerrors = true;
            }
            alpha = new double[3];
            beta = new double[3];
            alpha[0] = 0;
            alpha[1] = 0;
            alpha[2] = 0;
            beta[0] = 0;
            beta[1] = (double)(1*1)/(double)(4*1*1-1);
            beta[2] = (double)(2*2)/(double)(4*2*2-1);
            gq.gqgenerategausslobattorec(alpha, beta, 2.0, -1, 1, 4, ref info, ref x, ref w);
            if( info>0 )
            {
                err = Math.Max(err, Math.Abs(x[0]+1));
                err = Math.Max(err, Math.Abs(x[1]+Math.Sqrt(5)/5));
                err = Math.Max(err, Math.Abs(x[2]-Math.Sqrt(5)/5));
                err = Math.Max(err, Math.Abs(x[3]-1));
                err = Math.Max(err, Math.Abs(w[0]-(double)1/(double)6));
                err = Math.Max(err, Math.Abs(w[1]-(double)5/(double)6));
                err = Math.Max(err, Math.Abs(w[2]-(double)5/(double)6));
                err = Math.Max(err, Math.Abs(w[3]-(double)1/(double)6));
                for(i=0; i<=2; i++)
                {
                    recerrors = recerrors | (double)(x[i])>=(double)(x[i+1]);
                }
            }
            else
            {
                recerrors = true;
            }
            alpha = new double[5];
            beta = new double[5];
            alpha[0] = 0;
            alpha[1] = 0;
            alpha[2] = 0;
            alpha[3] = 0;
            alpha[4] = 0;
            beta[0] = 0;
            beta[1] = (double)(1*1)/(double)(4*1*1-1);
            beta[2] = (double)(2*2)/(double)(4*2*2-1);
            beta[3] = (double)(3*3)/(double)(4*3*3-1);
            beta[4] = (double)(4*4)/(double)(4*4*4-1);
            gq.gqgenerategausslobattorec(alpha, beta, 2.0, -1, 1, 6, ref info, ref x, ref w);
            if( info>0 )
            {
                err = Math.Max(err, Math.Abs(x[0]+1));
                err = Math.Max(err, Math.Abs(x[1]+Math.Sqrt((7+2*Math.Sqrt(7))/21)));
                err = Math.Max(err, Math.Abs(x[2]+Math.Sqrt((7-2*Math.Sqrt(7))/21)));
                err = Math.Max(err, Math.Abs(x[3]-Math.Sqrt((7-2*Math.Sqrt(7))/21)));
                err = Math.Max(err, Math.Abs(x[4]-Math.Sqrt((7+2*Math.Sqrt(7))/21)));
                err = Math.Max(err, Math.Abs(x[5]-1));
                err = Math.Max(err, Math.Abs(w[0]-(double)1/(double)15));
                err = Math.Max(err, Math.Abs(w[1]-(14-Math.Sqrt(7))/30));
                err = Math.Max(err, Math.Abs(w[2]-(14+Math.Sqrt(7))/30));
                err = Math.Max(err, Math.Abs(w[3]-(14+Math.Sqrt(7))/30));
                err = Math.Max(err, Math.Abs(w[4]-(14-Math.Sqrt(7))/30));
                err = Math.Max(err, Math.Abs(w[5]-(double)1/(double)15));
                for(i=0; i<=4; i++)
                {
                    recerrors = recerrors | (double)(x[i])>=(double)(x[i+1]);
                }
            }
            else
            {
                recerrors = true;
            }
            recerrors = recerrors | (double)(err)>(double)(errtol);
            
            //
            // Three tests for rec-based Gauss-Radau quadratures with known weights/nodes:
            // 1. Gauss-Radau with N=2
            // 2. Gauss-Radau with N=3
            // 3. Gauss-Radau with N=3 (another case)
            //
            err = 0;
            alpha = new double[1];
            beta = new double[2];
            alpha[0] = 0;
            beta[0] = 0;
            beta[1] = (double)(1*1)/(double)(4*1*1-1);
            gq.gqgenerategaussradaurec(alpha, beta, 2.0, -1, 2, ref info, ref x, ref w);
            if( info>0 )
            {
                err = Math.Max(err, Math.Abs(x[0]+1));
                err = Math.Max(err, Math.Abs(x[1]-(double)1/(double)3));
                err = Math.Max(err, Math.Abs(w[0]-0.5));
                err = Math.Max(err, Math.Abs(w[1]-1.5));
                for(i=0; i<=0; i++)
                {
                    recerrors = recerrors | (double)(x[i])>=(double)(x[i+1]);
                }
            }
            else
            {
                recerrors = true;
            }
            alpha = new double[2];
            beta = new double[3];
            alpha[0] = 0;
            alpha[1] = 0;
            for(i=0; i<=2; i++)
            {
                beta[i] = math.sqr(i)/(4*math.sqr(i)-1);
            }
            gq.gqgenerategaussradaurec(alpha, beta, 2.0, -1, 3, ref info, ref x, ref w);
            if( info>0 )
            {
                err = Math.Max(err, Math.Abs(x[0]+1));
                err = Math.Max(err, Math.Abs(x[1]-(1-Math.Sqrt(6))/5));
                err = Math.Max(err, Math.Abs(x[2]-(1+Math.Sqrt(6))/5));
                err = Math.Max(err, Math.Abs(w[0]-(double)2/(double)9));
                err = Math.Max(err, Math.Abs(w[1]-(16+Math.Sqrt(6))/18));
                err = Math.Max(err, Math.Abs(w[2]-(16-Math.Sqrt(6))/18));
                for(i=0; i<=1; i++)
                {
                    recerrors = recerrors | (double)(x[i])>=(double)(x[i+1]);
                }
            }
            else
            {
                recerrors = true;
            }
            alpha = new double[2];
            beta = new double[3];
            alpha[0] = 0;
            alpha[1] = 0;
            for(i=0; i<=2; i++)
            {
                beta[i] = math.sqr(i)/(4*math.sqr(i)-1);
            }
            gq.gqgenerategaussradaurec(alpha, beta, 2.0, 1, 3, ref info, ref x, ref w);
            if( info>0 )
            {
                err = Math.Max(err, Math.Abs(x[2]-1));
                err = Math.Max(err, Math.Abs(x[1]+(1-Math.Sqrt(6))/5));
                err = Math.Max(err, Math.Abs(x[0]+(1+Math.Sqrt(6))/5));
                err = Math.Max(err, Math.Abs(w[2]-(double)2/(double)9));
                err = Math.Max(err, Math.Abs(w[1]-(16+Math.Sqrt(6))/18));
                err = Math.Max(err, Math.Abs(w[0]-(16-Math.Sqrt(6))/18));
                for(i=0; i<=1; i++)
                {
                    recerrors = recerrors | (double)(x[i])>=(double)(x[i+1]);
                }
            }
            else
            {
                recerrors = true;
            }
            recerrors = recerrors | (double)(err)>(double)(errtol);
            
            //
            // test recurrence-based special cases (Legendre, Jacobi, Hermite, ...)
            // against another implementation (polynomial root-finder)
            //
            for(n=1; n<=20; n++)
            {
                
                //
                // test gauss-legendre
                //
                err = 0;
                gq.gqgenerategausslegendre(n, ref info, ref x, ref w);
                if( info>0 )
                {
                    buildgausslegendrequadrature(n, ref x2, ref w2);
                    for(i=0; i<=n-1; i++)
                    {
                        err = Math.Max(err, Math.Abs(x[i]-x2[i]));
                        err = Math.Max(err, Math.Abs(w[i]-w2[i]));
                    }
                }
                else
                {
                    specerrors = true;
                }
                specerrors = specerrors | (double)(err)>(double)(errtol);
                
                //
                // Test Gauss-Jacobi.
                // Since task is much more difficult we will use less strict
                // threshold.
                //
                err = 0;
                for(akind=0; akind<=9; akind++)
                {
                    for(bkind=0; bkind<=9; bkind++)
                    {
                        alphac = mapkind(akind);
                        betac = mapkind(bkind);
                        gq.gqgenerategaussjacobi(n, alphac, betac, ref info, ref x, ref w);
                        if( info>0 )
                        {
                            buildgaussjacobiquadrature(n, alphac, betac, ref x2, ref w2);
                            for(i=0; i<=n-1; i++)
                            {
                                err = Math.Max(err, Math.Abs(x[i]-x2[i]));
                                err = Math.Max(err, Math.Abs(w[i]-w2[i]));
                            }
                        }
                        else
                        {
                            specerrors = true;
                        }
                    }
                }
                specerrors = specerrors | (double)(err)>(double)(nonstricterrtol);
                
                //
                // special test for Gauss-Jacobi (Chebyshev weight
                // function with analytically known nodes/weights)
                //
                err = 0;
                gq.gqgenerategaussjacobi(n, -0.5, -0.5, ref info, ref x, ref w);
                if( info>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        err = Math.Max(err, Math.Abs(x[i]+Math.Cos(Math.PI*(i+0.5)/n)));
                        err = Math.Max(err, Math.Abs(w[i]-Math.PI/n));
                    }
                }
                else
                {
                    specerrors = true;
                }
                specerrors = specerrors | (double)(err)>(double)(stricterrtol);
                
                //
                // Test Gauss-Laguerre
                //
                err = 0;
                for(akind=0; akind<=9; akind++)
                {
                    alphac = mapkind(akind);
                    gq.gqgenerategausslaguerre(n, alphac, ref info, ref x, ref w);
                    if( info>0 )
                    {
                        buildgausslaguerrequadrature(n, alphac, ref x2, ref w2);
                        for(i=0; i<=n-1; i++)
                        {
                            err = Math.Max(err, Math.Abs(x[i]-x2[i]));
                            err = Math.Max(err, Math.Abs(w[i]-w2[i]));
                        }
                    }
                    else
                    {
                        specerrors = true;
                    }
                }
                specerrors = specerrors | (double)(err)>(double)(nonstricterrtol);
                
                //
                // Test Gauss-Hermite
                //
                err = 0;
                gq.gqgenerategausshermite(n, ref info, ref x, ref w);
                if( info>0 )
                {
                    buildgausshermitequadrature(n, ref x2, ref w2);
                    for(i=0; i<=n-1; i++)
                    {
                        err = Math.Max(err, Math.Abs(x[i]-x2[i]));
                        err = Math.Max(err, Math.Abs(w[i]-w2[i]));
                    }
                }
                else
                {
                    specerrors = true;
                }
                specerrors = specerrors | (double)(err)>(double)(nonstricterrtol);
            }
            
            //
            // end
            //
            waserrors = recerrors | specerrors;
            if( !silent )
            {
                System.Console.Write("TESTING GAUSS QUADRATURES");
                System.Console.WriteLine();
                System.Console.Write("FINAL RESULT:                             ");
                if( waserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SPECIAL CASES (LEGENDRE/JACOBI/..)      ");
                if( specerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* RECURRENCE-BASED:                       ");
                if( recerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Maps:
            0   =>  -0.9
            1   =>  -0.5
            2   =>  -0.1
            3   =>   0.0
            4   =>  +0.1
            5   =>  +0.5
            6   =>  +0.9
            7   =>  +1.0
            8   =>  +1.5
            9   =>  +2.0
        *************************************************************************/
        private static double mapkind(int k)
        {
            double result = 0;

            result = 0;
            if( k==0 )
            {
                result = -0.9;
            }
            if( k==1 )
            {
                result = -0.5;
            }
            if( k==2 )
            {
                result = -0.1;
            }
            if( k==3 )
            {
                result = 0.0;
            }
            if( k==4 )
            {
                result = 0.1;
            }
            if( k==5 )
            {
                result = 0.5;
            }
            if( k==6 )
            {
                result = 0.9;
            }
            if( k==7 )
            {
                result = 1.0;
            }
            if( k==8 )
            {
                result = 1.5;
            }
            if( k==9 )
            {
                result = 2.0;
            }
            return result;
        }


        /*************************************************************************
        Gauss-Legendre, another variant
        *************************************************************************/
        private static void buildgausslegendrequadrature(int n,
            ref double[] x,
            ref double[] w)
        {
            int i = 0;
            int j = 0;
            double r = 0;
            double r1 = 0;
            double p1 = 0;
            double p2 = 0;
            double p3 = 0;
            double dp3 = 0;
            double tmp = 0;

            x = new double[0];
            w = new double[0];

            x = new double[n-1+1];
            w = new double[n-1+1];
            for(i=0; i<=(n+1)/2-1; i++)
            {
                r = Math.Cos(Math.PI*(4*i+3)/(4*n+2));
                do
                {
                    p2 = 0;
                    p3 = 1;
                    for(j=0; j<=n-1; j++)
                    {
                        p1 = p2;
                        p2 = p3;
                        p3 = ((2*j+1)*r*p2-j*p1)/(j+1);
                    }
                    dp3 = n*(r*p3-p2)/(r*r-1);
                    r1 = r;
                    r = r-p3/dp3;
                }
                while( (double)(Math.Abs(r-r1))>=(double)(math.machineepsilon*(1+Math.Abs(r))*100) );
                x[i] = r;
                x[n-1-i] = -r;
                w[i] = 2/((1-r*r)*dp3*dp3);
                w[n-1-i] = 2/((1-r*r)*dp3*dp3);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-2-i; j++)
                {
                    if( (double)(x[j])>=(double)(x[j+1]) )
                    {
                        tmp = x[j];
                        x[j] = x[j+1];
                        x[j+1] = tmp;
                        tmp = w[j];
                        w[j] = w[j+1];
                        w[j+1] = tmp;
                    }
                }
            }
        }


        /*************************************************************************
        Gauss-Jacobi, another variant
        *************************************************************************/
        private static void buildgaussjacobiquadrature(int n,
            double alpha,
            double beta,
            ref double[] x,
            ref double[] w)
        {
            int i = 0;
            int j = 0;
            double r = 0;
            double r1 = 0;
            double t1 = 0;
            double t2 = 0;
            double t3 = 0;
            double p1 = 0;
            double p2 = 0;
            double p3 = 0;
            double pp = 0;
            double an = 0;
            double bn = 0;
            double a = 0;
            double b = 0;
            double c = 0;
            double tmpsgn = 0;
            double tmp = 0;
            double alfbet = 0;
            double temp = 0;

            x = new double[0];
            w = new double[0];

            x = new double[n-1+1];
            w = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                if( i==0 )
                {
                    an = alpha/n;
                    bn = beta/n;
                    t1 = (1+alpha)*(2.78/(4+n*n)+0.768*an/n);
                    t2 = 1+1.48*an+0.96*bn+0.452*an*an+0.83*an*bn;
                    r = (t2-t1)/t2;
                }
                else
                {
                    if( i==1 )
                    {
                        t1 = (4.1+alpha)/((1+alpha)*(1+0.156*alpha));
                        t2 = 1+0.06*(n-8)*(1+0.12*alpha)/n;
                        t3 = 1+0.012*beta*(1+0.25*Math.Abs(alpha))/n;
                        r = r-t1*t2*t3*(1-r);
                    }
                    else
                    {
                        if( i==2 )
                        {
                            t1 = (1.67+0.28*alpha)/(1+0.37*alpha);
                            t2 = 1+0.22*(n-8)/n;
                            t3 = 1+8*beta/((6.28+beta)*n*n);
                            r = r-t1*t2*t3*(x[0]-r);
                        }
                        else
                        {
                            if( i<n-2 )
                            {
                                r = 3*x[i-1]-3*x[i-2]+x[i-3];
                            }
                            else
                            {
                                if( i==n-2 )
                                {
                                    t1 = (1+0.235*beta)/(0.766+0.119*beta);
                                    t2 = 1/(1+0.639*(n-4)/(1+0.71*(n-4)));
                                    t3 = 1/(1+20*alpha/((7.5+alpha)*n*n));
                                    r = r+t1*t2*t3*(r-x[i-2]);
                                }
                                else
                                {
                                    if( i==n-1 )
                                    {
                                        t1 = (1+0.37*beta)/(1.67+0.28*beta);
                                        t2 = 1/(1+0.22*(n-8)/n);
                                        t3 = 1/(1+8*alpha/((6.28+alpha)*n*n));
                                        r = r+t1*t2*t3*(r-x[i-2]);
                                    }
                                }
                            }
                        }
                    }
                }
                alfbet = alpha+beta;
                do
                {
                    temp = 2+alfbet;
                    p1 = (alpha-beta+temp*r)*0.5;
                    p2 = 1;
                    for(j=2; j<=n; j++)
                    {
                        p3 = p2;
                        p2 = p1;
                        temp = 2*j+alfbet;
                        a = 2*j*(j+alfbet)*(temp-2);
                        b = (temp-1)*(alpha*alpha-beta*beta+temp*(temp-2)*r);
                        c = 2*(j-1+alpha)*(j-1+beta)*temp;
                        p1 = (b*p2-c*p3)/a;
                    }
                    pp = (n*(alpha-beta-temp*r)*p1+2*(n+alpha)*(n+beta)*p2)/(temp*(1-r*r));
                    r1 = r;
                    r = r1-p1/pp;
                }
                while( (double)(Math.Abs(r-r1))>=(double)(math.machineepsilon*(1+Math.Abs(r))*100) );
                x[i] = r;
                w[i] = Math.Exp(gammafunc.lngamma(alpha+n, ref tmpsgn)+gammafunc.lngamma(beta+n, ref tmpsgn)-gammafunc.lngamma(n+1, ref tmpsgn)-gammafunc.lngamma(n+alfbet+1, ref tmpsgn))*temp*Math.Pow(2, alfbet)/(pp*p2);
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-2-i; j++)
                {
                    if( (double)(x[j])>=(double)(x[j+1]) )
                    {
                        tmp = x[j];
                        x[j] = x[j+1];
                        x[j+1] = tmp;
                        tmp = w[j];
                        w[j] = w[j+1];
                        w[j+1] = tmp;
                    }
                }
            }
        }


        /*************************************************************************
        Gauss-Laguerre, another variant
        *************************************************************************/
        private static void buildgausslaguerrequadrature(int n,
            double alpha,
            ref double[] x,
            ref double[] w)
        {
            int i = 0;
            int j = 0;
            double r = 0;
            double r1 = 0;
            double p1 = 0;
            double p2 = 0;
            double p3 = 0;
            double dp3 = 0;
            double tsg = 0;
            double tmp = 0;

            x = new double[0];
            w = new double[0];

            x = new double[n-1+1];
            w = new double[n-1+1];
            for(i=0; i<=n-1; i++)
            {
                if( i==0 )
                {
                    r = (1+alpha)*(3+0.92*alpha)/(1+2.4*n+1.8*alpha);
                }
                else
                {
                    if( i==1 )
                    {
                        r = r+(15+6.25*alpha)/(1+0.9*alpha+2.5*n);
                    }
                    else
                    {
                        r = r+((1+2.55*(i-1))/(1.9*(i-1))+1.26*(i-1)*alpha/(1+3.5*(i-1)))/(1+0.3*alpha)*(r-x[i-2]);
                    }
                }
                do
                {
                    p2 = 0;
                    p3 = 1;
                    for(j=0; j<=n-1; j++)
                    {
                        p1 = p2;
                        p2 = p3;
                        p3 = ((-r+2*j+alpha+1)*p2-(j+alpha)*p1)/(j+1);
                    }
                    dp3 = (n*p3-(n+alpha)*p2)/r;
                    r1 = r;
                    r = r-p3/dp3;
                }
                while( (double)(Math.Abs(r-r1))>=(double)(math.machineepsilon*(1+Math.Abs(r))*100) );
                x[i] = r;
                w[i] = -(Math.Exp(gammafunc.lngamma(alpha+n, ref tsg)-gammafunc.lngamma(n, ref tsg))/(dp3*n*p2));
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-2-i; j++)
                {
                    if( (double)(x[j])>=(double)(x[j+1]) )
                    {
                        tmp = x[j];
                        x[j] = x[j+1];
                        x[j+1] = tmp;
                        tmp = w[j];
                        w[j] = w[j+1];
                        w[j+1] = tmp;
                    }
                }
            }
        }


        /*************************************************************************
        Gauss-Hermite, another variant
        *************************************************************************/
        private static void buildgausshermitequadrature(int n,
            ref double[] x,
            ref double[] w)
        {
            int i = 0;
            int j = 0;
            double r = 0;
            double r1 = 0;
            double p1 = 0;
            double p2 = 0;
            double p3 = 0;
            double dp3 = 0;
            double pipm4 = 0;
            double tmp = 0;

            x = new double[0];
            w = new double[0];

            x = new double[n-1+1];
            w = new double[n-1+1];
            pipm4 = Math.Pow(Math.PI, -0.25);
            for(i=0; i<=(n+1)/2-1; i++)
            {
                if( i==0 )
                {
                    r = Math.Sqrt(2*n+1)-1.85575*Math.Pow(2*n+1, -((double)1/(double)6));
                }
                else
                {
                    if( i==1 )
                    {
                        r = r-1.14*Math.Pow(n, 0.426)/r;
                    }
                    else
                    {
                        if( i==2 )
                        {
                            r = 1.86*r-0.86*x[0];
                        }
                        else
                        {
                            if( i==3 )
                            {
                                r = 1.91*r-0.91*x[1];
                            }
                            else
                            {
                                r = 2*r-x[i-2];
                            }
                        }
                    }
                }
                do
                {
                    p2 = 0;
                    p3 = pipm4;
                    for(j=0; j<=n-1; j++)
                    {
                        p1 = p2;
                        p2 = p3;
                        p3 = p2*r*Math.Sqrt((double)2/(double)(j+1))-p1*Math.Sqrt((double)j/(double)(j+1));
                    }
                    dp3 = Math.Sqrt(2*j)*p2;
                    r1 = r;
                    r = r-p3/dp3;
                }
                while( (double)(Math.Abs(r-r1))>=(double)(math.machineepsilon*(1+Math.Abs(r))*100) );
                x[i] = r;
                w[i] = 2/(dp3*dp3);
                x[n-1-i] = -x[i];
                w[n-1-i] = w[i];
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-2-i; j++)
                {
                    if( (double)(x[j])>=(double)(x[j+1]) )
                    {
                        tmp = x[j];
                        x[j] = x[j+1];
                        x[j+1] = tmp;
                        tmp = w[j];
                        w[j] = w[j+1];
                        w[j+1] = tmp;
                    }
                }
            }
        }


    }
    public class testgkqunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testgkq(bool silent)
        {
            bool result = new bool();
            int pkind = 0;
            double errtol = 0;
            double eps = 0;
            double nonstricterrtol = 0;
            int n = 0;
            int i = 0;
            int k = 0;
            int info = 0;
            double err = 0;
            int akind = 0;
            int bkind = 0;
            double alphac = 0;
            double betac = 0;
            double[] x1 = new double[0];
            double[] wg1 = new double[0];
            double[] wk1 = new double[0];
            double[] x2 = new double[0];
            double[] wg2 = new double[0];
            double[] wk2 = new double[0];
            int info1 = 0;
            int info2 = 0;
            bool successatleastonce = new bool();
            bool intblerrors = new bool();
            bool vstblerrors = new bool();
            bool generrors = new bool();
            bool waserrors = new bool();

            intblerrors = false;
            vstblerrors = false;
            generrors = false;
            waserrors = false;
            errtol = 10000*math.machineepsilon;
            nonstricterrtol = 1000*errtol;
            
            //
            // test recurrence-based Legendre nodes against the precalculated table
            //
            for(pkind=0; pkind<=5; pkind++)
            {
                n = 0;
                if( pkind==0 )
                {
                    n = 15;
                }
                if( pkind==1 )
                {
                    n = 21;
                }
                if( pkind==2 )
                {
                    n = 31;
                }
                if( pkind==3 )
                {
                    n = 41;
                }
                if( pkind==4 )
                {
                    n = 51;
                }
                if( pkind==5 )
                {
                    n = 61;
                }
                gkq.gkqlegendrecalc(n, ref info, ref x1, ref wk1, ref wg1);
                gkq.gkqlegendretbl(n, ref x2, ref wk2, ref wg2, ref eps);
                if( info<=0 )
                {
                    generrors = true;
                    break;
                }
                for(i=0; i<=n-1; i++)
                {
                    vstblerrors = vstblerrors | (double)(Math.Abs(x1[i]-x2[i]))>(double)(errtol);
                    vstblerrors = vstblerrors | (double)(Math.Abs(wk1[i]-wk2[i]))>(double)(errtol);
                    vstblerrors = vstblerrors | (double)(Math.Abs(wg1[i]-wg2[i]))>(double)(errtol);
                }
            }
            
            //
            // Test recurrence-baced Gauss-Kronrod nodes against Gauss-only nodes
            // calculated with subroutines from GQ unit.
            //
            for(k=1; k<=30; k++)
            {
                n = 2*k+1;
                
                //
                // Gauss-Legendre
                //
                err = 0;
                gkq.gkqgenerategausslegendre(n, ref info1, ref x1, ref wk1, ref wg1);
                gq.gqgenerategausslegendre(k, ref info2, ref x2, ref wg2);
                if( info1>0 & info2>0 )
                {
                    for(i=0; i<=k-1; i++)
                    {
                        err = Math.Max(err, Math.Abs(x1[2*i+1]-x2[i]));
                        err = Math.Max(err, Math.Abs(wg1[2*i+1]-wg2[i]));
                    }
                }
                else
                {
                    generrors = true;
                }
                generrors = generrors | (double)(err)>(double)(errtol);
            }
            for(k=1; k<=15; k++)
            {
                n = 2*k+1;
                
                //
                // Gauss-Jacobi
                //
                successatleastonce = false;
                err = 0;
                for(akind=0; akind<=9; akind++)
                {
                    for(bkind=0; bkind<=9; bkind++)
                    {
                        alphac = mapkind(akind);
                        betac = mapkind(bkind);
                        gkq.gkqgenerategaussjacobi(n, alphac, betac, ref info1, ref x1, ref wk1, ref wg1);
                        gq.gqgenerategaussjacobi(k, alphac, betac, ref info2, ref x2, ref wg2);
                        if( info1>0 & info2>0 )
                        {
                            successatleastonce = true;
                            for(i=0; i<=k-1; i++)
                            {
                                err = Math.Max(err, Math.Abs(x1[2*i+1]-x2[i]));
                                err = Math.Max(err, Math.Abs(wg1[2*i+1]-wg2[i]));
                            }
                        }
                        else
                        {
                            generrors = generrors | info1!=-5;
                        }
                    }
                }
                generrors = (generrors | (double)(err)>(double)(errtol)) | !successatleastonce;
            }
            
            //
            // end
            //
            waserrors = (intblerrors | vstblerrors) | generrors;
            if( !silent )
            {
                System.Console.Write("TESTING GAUSS-KRONROD QUADRATURES");
                System.Console.WriteLine();
                System.Console.Write("FINAL RESULT:                             ");
                if( waserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* PRE-CALCULATED TABLE:                   ");
                if( intblerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* CALCULATED AGAINST THE TABLE:           ");
                if( vstblerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* GENERAL PROPERTIES:                     ");
                if( generrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Maps:
            0   =>  -0.9
            1   =>  -0.5
            2   =>  -0.1
            3   =>   0.0
            4   =>  +0.1
            5   =>  +0.5
            6   =>  +0.9
            7   =>  +1.0
            8   =>  +1.5
            9   =>  +2.0
        *************************************************************************/
        private static double mapkind(int k)
        {
            double result = 0;

            result = 0;
            if( k==0 )
            {
                result = -0.9;
            }
            if( k==1 )
            {
                result = -0.5;
            }
            if( k==2 )
            {
                result = -0.1;
            }
            if( k==3 )
            {
                result = 0.0;
            }
            if( k==4 )
            {
                result = 0.1;
            }
            if( k==5 )
            {
                result = 0.5;
            }
            if( k==6 )
            {
                result = 0.9;
            }
            if( k==7 )
            {
                result = 1.0;
            }
            if( k==8 )
            {
                result = 1.5;
            }
            if( k==9 )
            {
                result = 2.0;
            }
            return result;
        }


    }
    public class testautogkunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testautogk(bool silent)
        {
            bool result = new bool();
            double a = 0;
            double b = 0;
            autogk.autogkstate state = new autogk.autogkstate();
            autogk.autogkreport rep = new autogk.autogkreport();
            double v = 0;
            double exact = 0;
            double eabs = 0;
            double alpha = 0;
            int pkind = 0;
            double errtol = 0;
            bool simpleerrors = new bool();
            bool sngenderrors = new bool();
            bool waserrors = new bool();

            simpleerrors = false;
            sngenderrors = false;
            waserrors = false;
            errtol = 10000*math.machineepsilon;
            
            //
            // Simple test: integral(exp(x),+-1,+-2), no maximum width requirements
            //
            a = (2*math.randominteger(2)-1)*1.0;
            b = (2*math.randominteger(2)-1)*2.0;
            autogk.autogksmooth(a, b, state);
            while( autogk.autogkiteration(state) )
            {
                state.f = Math.Exp(state.x);
            }
            autogk.autogkresults(state, ref v, rep);
            exact = Math.Exp(b)-Math.Exp(a);
            eabs = Math.Abs(Math.Exp(b)-Math.Exp(a));
            if( rep.terminationtype<=0 )
            {
                simpleerrors = true;
            }
            else
            {
                simpleerrors = simpleerrors | (double)(Math.Abs(exact-v))>(double)(errtol*eabs);
            }
            
            //
            // Simple test: integral(exp(x),+-1,+-2), XWidth=0.1
            //
            a = (2*math.randominteger(2)-1)*1.0;
            b = (2*math.randominteger(2)-1)*2.0;
            autogk.autogksmoothw(a, b, 0.1, state);
            while( autogk.autogkiteration(state) )
            {
                state.f = Math.Exp(state.x);
            }
            autogk.autogkresults(state, ref v, rep);
            exact = Math.Exp(b)-Math.Exp(a);
            eabs = Math.Abs(Math.Exp(b)-Math.Exp(a));
            if( rep.terminationtype<=0 )
            {
                simpleerrors = true;
            }
            else
            {
                simpleerrors = simpleerrors | (double)(Math.Abs(exact-v))>(double)(errtol*eabs);
            }
            
            //
            // Simple test: integral(cos(100*x),0,2*pi), no maximum width requirements
            //
            a = 0;
            b = 2*Math.PI;
            autogk.autogksmooth(a, b, state);
            while( autogk.autogkiteration(state) )
            {
                state.f = Math.Cos(100*state.x);
            }
            autogk.autogkresults(state, ref v, rep);
            exact = 0;
            eabs = 4;
            if( rep.terminationtype<=0 )
            {
                simpleerrors = true;
            }
            else
            {
                simpleerrors = simpleerrors | (double)(Math.Abs(exact-v))>(double)(errtol*eabs);
            }
            
            //
            // Simple test: integral(cos(100*x),0,2*pi), XWidth=0.3
            //
            a = 0;
            b = 2*Math.PI;
            autogk.autogksmoothw(a, b, 0.3, state);
            while( autogk.autogkiteration(state) )
            {
                state.f = Math.Cos(100*state.x);
            }
            autogk.autogkresults(state, ref v, rep);
            exact = 0;
            eabs = 4;
            if( rep.terminationtype<=0 )
            {
                simpleerrors = true;
            }
            else
            {
                simpleerrors = simpleerrors | (double)(Math.Abs(exact-v))>(double)(errtol*eabs);
            }
            
            //
            // singular problem on [a,b] = [0.1, 0.5]
            //     f2(x) = (1+x)*(b-x)^alpha, -1 < alpha < 1
            //
            for(pkind=0; pkind<=6; pkind++)
            {
                a = 0.1;
                b = 0.5;
                if( pkind==0 )
                {
                    alpha = -0.9;
                }
                if( pkind==1 )
                {
                    alpha = -0.5;
                }
                if( pkind==2 )
                {
                    alpha = -0.1;
                }
                if( pkind==3 )
                {
                    alpha = 0.0;
                }
                if( pkind==4 )
                {
                    alpha = 0.1;
                }
                if( pkind==5 )
                {
                    alpha = 0.5;
                }
                if( pkind==6 )
                {
                    alpha = 0.9;
                }
                
                //
                // f1(x) = (1+x)*(x-a)^alpha, -1 < alpha < 1
                // 1. use singular integrator for [a,b]
                // 2. use singular integrator for [b,a]
                //
                exact = Math.Pow(b-a, alpha+2)/(alpha+2)+(1+a)*Math.Pow(b-a, alpha+1)/(alpha+1);
                eabs = Math.Abs(exact);
                autogk.autogksingular(a, b, alpha, 0.0, state);
                while( autogk.autogkiteration(state) )
                {
                    if( (double)(state.xminusa)<(double)(0.01) )
                    {
                        state.f = Math.Pow(state.xminusa, alpha)*(1+state.x);
                    }
                    else
                    {
                        state.f = Math.Pow(state.x-a, alpha)*(1+state.x);
                    }
                }
                autogk.autogkresults(state, ref v, rep);
                if( rep.terminationtype<=0 )
                {
                    sngenderrors = true;
                }
                else
                {
                    sngenderrors = sngenderrors | (double)(Math.Abs(v-exact))>(double)(errtol*eabs);
                }
                autogk.autogksingular(b, a, 0.0, alpha, state);
                while( autogk.autogkiteration(state) )
                {
                    if( (double)(state.bminusx)>(double)(-0.01) )
                    {
                        state.f = Math.Pow(-state.bminusx, alpha)*(1+state.x);
                    }
                    else
                    {
                        state.f = Math.Pow(state.x-a, alpha)*(1+state.x);
                    }
                }
                autogk.autogkresults(state, ref v, rep);
                if( rep.terminationtype<=0 )
                {
                    sngenderrors = true;
                }
                else
                {
                    sngenderrors = sngenderrors | (double)(Math.Abs(-v-exact))>(double)(errtol*eabs);
                }
                
                //
                // f1(x) = (1+x)*(b-x)^alpha, -1 < alpha < 1
                // 1. use singular integrator for [a,b]
                // 2. use singular integrator for [b,a]
                //
                exact = (1+b)*Math.Pow(b-a, alpha+1)/(alpha+1)-Math.Pow(b-a, alpha+2)/(alpha+2);
                eabs = Math.Abs(exact);
                autogk.autogksingular(a, b, 0.0, alpha, state);
                while( autogk.autogkiteration(state) )
                {
                    if( (double)(state.bminusx)<(double)(0.01) )
                    {
                        state.f = Math.Pow(state.bminusx, alpha)*(1+state.x);
                    }
                    else
                    {
                        state.f = Math.Pow(b-state.x, alpha)*(1+state.x);
                    }
                }
                autogk.autogkresults(state, ref v, rep);
                if( rep.terminationtype<=0 )
                {
                    sngenderrors = true;
                }
                else
                {
                    sngenderrors = sngenderrors | (double)(Math.Abs(v-exact))>(double)(errtol*eabs);
                }
                autogk.autogksingular(b, a, alpha, 0.0, state);
                while( autogk.autogkiteration(state) )
                {
                    if( (double)(state.xminusa)>(double)(-0.01) )
                    {
                        state.f = Math.Pow(-state.xminusa, alpha)*(1+state.x);
                    }
                    else
                    {
                        state.f = Math.Pow(b-state.x, alpha)*(1+state.x);
                    }
                }
                autogk.autogkresults(state, ref v, rep);
                if( rep.terminationtype<=0 )
                {
                    sngenderrors = true;
                }
                else
                {
                    sngenderrors = sngenderrors | (double)(Math.Abs(-v-exact))>(double)(errtol*eabs);
                }
            }
            
            //
            // end
            //
            waserrors = simpleerrors | sngenderrors;
            if( !silent )
            {
                System.Console.Write("TESTING AUTOGK");
                System.Console.WriteLine();
                System.Console.Write("INTEGRATION WITH GIVEN ACCURACY:          ");
                if( simpleerrors | sngenderrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SIMPLE PROBLEMS:                        ");
                if( simpleerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SINGULAR PROBLEMS (ENDS OF INTERVAL):   ");
                if( sngenderrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


    }
    public class testidwintunit
    {
        /*************************************************************************
        Testing IDW interpolation
        *************************************************************************/
        public static bool testidwint(bool silent)
        {
            bool result = new bool();
            double[,] xy = new double[0,0];
            int i = 0;
            int j = 0;
            double vx = 0;
            double vy = 0;
            double vz = 0;
            int d = 0;
            int dtask = 0;
            int nx = 0;
            int nq = 0;
            int nw = 0;
            int smalln = 0;
            int largen = 0;
            bool waserrors = new bool();
            bool idwerrors = new bool();

            idwerrors = false;
            smalln = 256;
            largen = 1024;
            nq = 10;
            nw = 18;
            
            //
            // Simple test:
            // * F = x^3 + sin(pi*y)*z^2 - (x+y)^2
            // * space is either R1=[-1,+1] (other dimensions are
            //   fixed at 0), R1^2 or R1^3.
            //* D = -1, 0, 1, 2
            //
            for(nx=1; nx<=2; nx++)
            {
                xy = new double[largen, nx+1];
                for(i=0; i<=largen-1; i++)
                {
                    for(j=0; j<=nx-1; j++)
                    {
                        xy[i,j] = 2*math.randomreal()-1;
                    }
                    if( nx>=1 )
                    {
                        vx = xy[i,0];
                    }
                    else
                    {
                        vx = 0;
                    }
                    if( nx>=2 )
                    {
                        vy = xy[i,1];
                    }
                    else
                    {
                        vy = 0;
                    }
                    if( nx>=3 )
                    {
                        vz = xy[i,2];
                    }
                    else
                    {
                        vz = 0;
                    }
                    xy[i,nx] = vx*vx*vx+Math.Sin(Math.PI*vy)*math.sqr(vz)-math.sqr(vx+vy);
                }
                for(d=-1; d<=2; d++)
                {
                    testxy(xy, largen, nx, d, nq, nw, ref idwerrors);
                }
            }
            
            //
            // Another simple test:
            // * five points in 2D - (0,0), (0,1), (1,0), (-1,0) (0,-1)
            // * F is random
            // * D = -1, 0, 1, 2
            //
            nx = 2;
            xy = new double[5, nx+1];
            xy[0,0] = 0;
            xy[0,1] = 0;
            xy[0,2] = 2*math.randomreal()-1;
            xy[1,0] = 1;
            xy[1,1] = 0;
            xy[1,2] = 2*math.randomreal()-1;
            xy[2,0] = 0;
            xy[2,1] = 1;
            xy[2,2] = 2*math.randomreal()-1;
            xy[3,0] = -1;
            xy[3,1] = 0;
            xy[3,2] = 2*math.randomreal()-1;
            xy[4,0] = 0;
            xy[4,1] = -1;
            xy[4,2] = 2*math.randomreal()-1;
            for(d=-1; d<=2; d++)
            {
                testxy(xy, 5, nx, d, nq, nw, ref idwerrors);
            }
            
            //
            // Degree test.
            //
            // F is either:
            // * constant (DTask=0)
            // * linear (DTask=1)
            // * quadratic (DTask=2)
            //
            // Nodal functions are either
            // * constant (D=0)
            // * linear (D=1)
            // * quadratic (D=2)
            //
            // When DTask<=D, we can interpolate without errors.
            // When DTask>D, we MUST have errors.
            //
            for(nx=1; nx<=3; nx++)
            {
                for(d=0; d<=2; d++)
                {
                    for(dtask=0; dtask<=2; dtask++)
                    {
                        testdegree(smalln, nx, d, dtask, ref idwerrors);
                    }
                }
            }
            
            //
            // Noisy test
            //
            testnoisy(ref idwerrors);
            
            //
            // report
            //
            waserrors = idwerrors;
            if( !silent )
            {
                System.Console.Write("TESTING INVERSE DISTANCE WEIGHTING");
                System.Console.WriteLine();
                System.Console.Write("* IDW:                                   ");
                if( !idwerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Unsets 2D array.
        *************************************************************************/
        private static void unset2d(ref complex[,] a)
        {
            a = new complex[0+1, 0+1];
            a[0,0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Unsets 1D array.
        *************************************************************************/
        private static void unset1d(ref double[] a)
        {
            a = new double[0+1];
            a[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Testing IDW:
        * generate model using N/NX/D/NQ/NW
        * test basic properties
        *************************************************************************/
        private static void testxy(double[,] xy,
            int n,
            int nx,
            int d,
            int nq,
            int nw,
            ref bool idwerrors)
        {
            double threshold = 0;
            double lipschitzstep = 0;
            int i = 0;
            int i1 = 0;
            int i2 = 0;
            double v = 0;
            double v1 = 0;
            double v2 = 0;
            double t = 0;
            double l1 = 0;
            double l2 = 0;
            idwint.idwinterpolant z1 = new idwint.idwinterpolant();
            double[] x = new double[0];
            int i_ = 0;

            threshold = 1000*math.machineepsilon;
            lipschitzstep = 0.001;
            x = new double[nx];
            
            //
            // build
            //
            idwint.idwbuildmodifiedshepard(xy, n, nx, d, nq, nw, z1);
            
            //
            // first, test interpolation properties at nodes
            //
            for(i=0; i<=n-1; i++)
            {
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = xy[i,i_];
                }
                idwerrors = idwerrors | (double)(idwint.idwcalc(z1, x))!=(double)(xy[i,nx]);
            }
            
            //
            // test Lipschitz continuity
            //
            i1 = math.randominteger(n);
            do
            {
                i2 = math.randominteger(n);
            }
            while( i2==i1 );
            l1 = 0;
            t = 0;
            while( (double)(t)<(double)(1) )
            {
                v = 1-t;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = v*xy[i1,i_];
                }
                v = t;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = x[i_] + v*xy[i2,i_];
                }
                v1 = idwint.idwcalc(z1, x);
                v = 1-(t+lipschitzstep);
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = v*xy[i1,i_];
                }
                v = t+lipschitzstep;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = x[i_] + v*xy[i2,i_];
                }
                v2 = idwint.idwcalc(z1, x);
                l1 = Math.Max(l1, Math.Abs(v2-v1)/lipschitzstep);
                t = t+lipschitzstep;
            }
            l2 = 0;
            t = 0;
            while( (double)(t)<(double)(1) )
            {
                v = 1-t;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = v*xy[i1,i_];
                }
                v = t;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = x[i_] + v*xy[i2,i_];
                }
                v1 = idwint.idwcalc(z1, x);
                v = 1-(t+lipschitzstep/3);
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = v*xy[i1,i_];
                }
                v = t+lipschitzstep/3;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = x[i_] + v*xy[i2,i_];
                }
                v2 = idwint.idwcalc(z1, x);
                l2 = Math.Max(l2, Math.Abs(v2-v1)/(lipschitzstep/3));
                t = t+lipschitzstep/3;
            }
            idwerrors = idwerrors | (double)(l2)>(double)(2.0*l1);
        }


        /*************************************************************************
        Testing IDW:
        * generate model using R-based model
        * test basic properties
        *************************************************************************/
        private static void testrxy(double[,] xy,
            int n,
            int nx,
            double r,
            ref bool idwerrors)
        {
            double threshold = 0;
            double lipschitzstep = 0;
            int i = 0;
            int i1 = 0;
            int i2 = 0;
            double v = 0;
            double v1 = 0;
            double v2 = 0;
            double t = 0;
            double l1 = 0;
            double l2 = 0;
            idwint.idwinterpolant z1 = new idwint.idwinterpolant();
            double[] x = new double[0];
            int i_ = 0;

            threshold = 1000*math.machineepsilon;
            lipschitzstep = 0.001;
            x = new double[nx];
            
            //
            // build
            //
            idwint.idwbuildmodifiedshepardr(xy, n, nx, r, z1);
            
            //
            // first, test interpolation properties at nodes
            //
            for(i=0; i<=n-1; i++)
            {
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = xy[i,i_];
                }
                idwerrors = idwerrors | (double)(idwint.idwcalc(z1, x))!=(double)(xy[i,nx]);
            }
            
            //
            // test Lipschitz continuity
            //
            i1 = math.randominteger(n);
            do
            {
                i2 = math.randominteger(n);
            }
            while( i2==i1 );
            l1 = 0;
            t = 0;
            while( (double)(t)<(double)(1) )
            {
                v = 1-t;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = v*xy[i1,i_];
                }
                v = t;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = x[i_] + v*xy[i2,i_];
                }
                v1 = idwint.idwcalc(z1, x);
                v = 1-(t+lipschitzstep);
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = v*xy[i1,i_];
                }
                v = t+lipschitzstep;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = x[i_] + v*xy[i2,i_];
                }
                v2 = idwint.idwcalc(z1, x);
                l1 = Math.Max(l1, Math.Abs(v2-v1)/lipschitzstep);
                t = t+lipschitzstep;
            }
            l2 = 0;
            t = 0;
            while( (double)(t)<(double)(1) )
            {
                v = 1-t;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = v*xy[i1,i_];
                }
                v = t;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = x[i_] + v*xy[i2,i_];
                }
                v1 = idwint.idwcalc(z1, x);
                v = 1-(t+lipschitzstep/3);
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = v*xy[i1,i_];
                }
                v = t+lipschitzstep/3;
                for(i_=0; i_<=nx-1;i_++)
                {
                    x[i_] = x[i_] + v*xy[i2,i_];
                }
                v2 = idwint.idwcalc(z1, x);
                l2 = Math.Max(l2, Math.Abs(v2-v1)/(lipschitzstep/3));
                t = t+lipschitzstep/3;
            }
            idwerrors = idwerrors | (double)(l2)>(double)(2.0*l1);
        }


        /*************************************************************************
        Testing degree properties

        F is either:
        * constant (DTask=0)
        * linear (DTask=1)
        * quadratic (DTask=2)

        Nodal functions are either
        * constant (D=0)
        * linear (D=1)
        * quadratic (D=2)

        When DTask<=D, we can interpolate without errors.
        When DTask>D, we MUST have errors.
        *************************************************************************/
        private static void testdegree(int n,
            int nx,
            int d,
            int dtask,
            ref bool idwerrors)
        {
            double threshold = 0;
            int nq = 0;
            int nw = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            double c0 = 0;
            double[] c1 = new double[0];
            double[,] c2 = new double[0,0];
            double[] x = new double[0];
            double[,] xy = new double[0,0];
            idwint.idwinterpolant z1 = new idwint.idwinterpolant();
            double v1 = 0;
            double v2 = 0;
            int i_ = 0;

            threshold = 1.0E6*math.machineepsilon;
            nq = 2*(nx*nx+nx+1);
            nw = 10;
            ap.assert(nq<=n, "TestDegree: internal error");
            
            //
            // prepare model
            //
            c0 = 2*math.randomreal()-1;
            c1 = new double[nx];
            for(i=0; i<=nx-1; i++)
            {
                c1[i] = 2*math.randomreal()-1;
            }
            c2 = new double[nx, nx];
            for(i=0; i<=nx-1; i++)
            {
                for(j=i+1; j<=nx-1; j++)
                {
                    c2[i,j] = 2*math.randomreal()-1;
                    c2[j,i] = c2[i,j];
                }
                do
                {
                    c2[i,i] = 2*math.randomreal()-1;
                }
                while( (double)(Math.Abs(c2[i,i]))<=(double)(0.3) );
            }
            
            //
            // prepare points
            //
            xy = new double[n, nx+1];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx-1; j++)
                {
                    xy[i,j] = 4*math.randomreal()-2;
                }
                xy[i,nx] = c0;
                if( dtask>=1 )
                {
                    v = 0.0;
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        v += c1[i_]*xy[i,i_];
                    }
                    xy[i,nx] = xy[i,nx]+v;
                }
                if( dtask==2 )
                {
                    for(j=0; j<=nx-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=nx-1;i_++)
                        {
                            v += c2[j,i_]*xy[i,i_];
                        }
                        xy[i,nx] = xy[i,nx]+xy[i,j]*v;
                    }
                }
            }
            
            //
            // build interpolant, calculate value at random point
            //
            idwint.idwbuildmodifiedshepard(xy, n, nx, d, nq, nw, z1);
            x = new double[nx];
            for(i=0; i<=nx-1; i++)
            {
                x[i] = 4*math.randomreal()-2;
            }
            v1 = idwint.idwcalc(z1, x);
            
            //
            // calculate model value at the same point
            //
            v2 = c0;
            if( dtask>=1 )
            {
                v = 0.0;
                for(i_=0; i_<=nx-1;i_++)
                {
                    v += c1[i_]*x[i_];
                }
                v2 = v2+v;
            }
            if( dtask==2 )
            {
                for(j=0; j<=nx-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=nx-1;i_++)
                    {
                        v += c2[j,i_]*x[i_];
                    }
                    v2 = v2+x[j]*v;
                }
            }
            
            //
            // Compare
            //
            if( dtask<=d )
            {
                idwerrors = idwerrors | (double)(Math.Abs(v2-v1))>(double)(threshold);
            }
            else
            {
                idwerrors = idwerrors | (double)(Math.Abs(v2-v1))<(double)(threshold);
            }
        }


        /*************************************************************************
        Noisy test:
         * F = x^2 + y^2 + z^2 + noise on [-1,+1]^3
         * space is either R1=[-1,+1] (other dimensions are
           fixed at 0), R1^2 or R1^3.
         * D = 1, 2
         * 4096 points is used for function generation,
           4096 points - for testing
         * RMS error of "noisy" model on test set must be
           lower than RMS error of interpolation model.
        *************************************************************************/
        private static void testnoisy(ref bool idwerrors)
        {
            double noiselevel = 0;
            int nq = 0;
            int nw = 0;
            int d = 0;
            int nx = 0;
            int ntrn = 0;
            int ntst = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            double t = 0;
            double v1 = 0;
            double v2 = 0;
            double ve = 0;
            double[,] xy = new double[0,0];
            double[] x = new double[0];
            idwint.idwinterpolant z1 = new idwint.idwinterpolant();
            idwint.idwinterpolant z2 = new idwint.idwinterpolant();
            double rms1 = 0;
            double rms2 = 0;

            nq = 20;
            nw = 40;
            noiselevel = 0.2;
            ntrn = 256;
            ntst = 1024;
            for(d=1; d<=2; d++)
            {
                for(nx=1; nx<=2; nx++)
                {
                    
                    //
                    // prepare dataset
                    //
                    xy = new double[ntrn, nx+1];
                    for(i=0; i<=ntrn-1; i++)
                    {
                        v = noiselevel*(2*math.randomreal()-1);
                        for(j=0; j<=nx-1; j++)
                        {
                            t = 2*math.randomreal()-1;
                            v = v+math.sqr(t);
                            xy[i,j] = t;
                        }
                        xy[i,nx] = v;
                    }
                    
                    //
                    // build interpolants
                    //
                    idwint.idwbuildmodifiedshepard(xy, ntrn, nx, d, nq, nw, z1);
                    idwint.idwbuildnoisy(xy, ntrn, nx, d, nq, nw, z2);
                    
                    //
                    // calculate RMS errors
                    //
                    x = new double[nx];
                    rms1 = 0;
                    rms2 = 0;
                    for(i=0; i<=ntst-1; i++)
                    {
                        ve = 0;
                        for(j=0; j<=nx-1; j++)
                        {
                            t = 2*math.randomreal()-1;
                            x[j] = t;
                            ve = ve+math.sqr(t);
                        }
                        v1 = idwint.idwcalc(z1, x);
                        v2 = idwint.idwcalc(z2, x);
                        rms1 = rms1+math.sqr(v1-ve);
                        rms2 = rms2+math.sqr(v2-ve);
                    }
                    idwerrors = idwerrors | (double)(rms2)>(double)(rms1);
                }
            }
        }


    }
    public class testratintunit
    {
        public static bool testratint(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool bcerrors = new bool();
            bool nperrors = new bool();
            double threshold = 0;
            double lipschitztol = 0;
            int maxn = 0;
            int passcount = 0;
            ratint.barycentricinterpolant b1 = new ratint.barycentricinterpolant();
            ratint.barycentricinterpolant b2 = new ratint.barycentricinterpolant();
            double[] x = new double[0];
            double[] x2 = new double[0];
            double[] y = new double[0];
            double[] y2 = new double[0];
            double[] w = new double[0];
            double[] w2 = new double[0];
            double[] xc = new double[0];
            double[] yc = new double[0];
            int[] dc = new int[0];
            double h = 0;
            double s1 = 0;
            double s2 = 0;
            int n = 0;
            int n2 = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int d = 0;
            int pass = 0;
            double maxerr = 0;
            double t = 0;
            double a = 0;
            double b = 0;
            double s = 0;
            double v0 = 0;
            double v1 = 0;
            double v2 = 0;
            double v3 = 0;
            double d0 = 0;
            double d1 = 0;
            double d2 = 0;

            nperrors = false;
            bcerrors = false;
            waserrors = false;
            
            //
            // PassCount        number of repeated passes
            // Threshold        error tolerance
            // LipschitzTol     Lipschitz constant increase allowed
            //                  when calculating constant on a twice denser grid
            //
            passcount = 5;
            maxn = 15;
            threshold = 1000000*math.machineepsilon;
            lipschitztol = 1.3;
            
            //
            // Basic barycentric functions
            //
            for(n=1; n<=10; n++)
            {
                
                //
                // randomized tests
                //
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // generate weights from polynomial interpolation
                    //
                    v0 = 1+0.4*math.randomreal()-0.2;
                    v1 = 2*math.randomreal()-1;
                    v2 = 2*math.randomreal()-1;
                    v3 = 2*math.randomreal()-1;
                    x = new double[n];
                    y = new double[n];
                    w = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        if( n==1 )
                        {
                            x[i] = 0;
                        }
                        else
                        {
                            x[i] = v0*Math.Cos(i*Math.PI/(n-1));
                        }
                        y[i] = Math.Sin(v1*x[i])+Math.Cos(v2*x[i])+Math.Exp(v3*x[i]);
                    }
                    for(j=0; j<=n-1; j++)
                    {
                        w[j] = 1;
                        for(k=0; k<=n-1; k++)
                        {
                            if( k!=j )
                            {
                                w[j] = w[j]/(x[j]-x[k]);
                            }
                        }
                    }
                    ratint.barycentricbuildxyw(x, y, w, n, b1);
                    
                    //
                    // unpack, then pack again and compare
                    //
                    brcunset(b2);
                    ratint.barycentricunpack(b1, ref n2, ref x2, ref y2, ref w2);
                    bcerrors = bcerrors | n2!=n;
                    ratint.barycentricbuildxyw(x2, y2, w2, n2, b2);
                    t = 2*math.randomreal()-1;
                    bcerrors = bcerrors | (double)(Math.Abs(ratint.barycentriccalc(b1, t)-ratint.barycentriccalc(b2, t)))>(double)(threshold);
                    
                    //
                    // copy, compare
                    //
                    brcunset(b2);
                    ratint.barycentriccopy(b1, b2);
                    t = 2*math.randomreal()-1;
                    bcerrors = bcerrors | (double)(Math.Abs(ratint.barycentriccalc(b1, t)-ratint.barycentriccalc(b2, t)))>(double)(threshold);
                    
                    //
                    // test interpolation properties
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        
                        //
                        // test interpolation at nodes
                        //
                        bcerrors = bcerrors | (double)(Math.Abs(ratint.barycentriccalc(b1, x[i])-y[i]))>(double)(threshold*Math.Abs(y[i]));
                        
                        //
                        // compare with polynomial interpolation
                        //
                        t = 2*math.randomreal()-1;
                        poldiff2(x, y, n, t, ref v0, ref v1, ref v2);
                        bcerrors = bcerrors | (double)(Math.Abs(ratint.barycentriccalc(b1, t)-v0))>(double)(threshold*Math.Max(Math.Abs(v0), 1));
                        
                        //
                        // test continuity between nodes
                        // calculate Lipschitz constant on two grids -
                        // dense and even more dense. If Lipschitz constant
                        // on a denser grid is significantly increased,
                        // continuity test is failed
                        //
                        t = 3.0;
                        k = 100;
                        s1 = 0;
                        for(j=0; j<=k-1; j++)
                        {
                            v1 = x[i]+(t-x[i])*j/k;
                            v2 = x[i]+(t-x[i])*(j+1)/k;
                            s1 = Math.Max(s1, Math.Abs(ratint.barycentriccalc(b1, v2)-ratint.barycentriccalc(b1, v1))/Math.Abs(v2-v1));
                        }
                        k = 2*k;
                        s2 = 0;
                        for(j=0; j<=k-1; j++)
                        {
                            v1 = x[i]+(t-x[i])*j/k;
                            v2 = x[i]+(t-x[i])*(j+1)/k;
                            s2 = Math.Max(s2, Math.Abs(ratint.barycentriccalc(b1, v2)-ratint.barycentriccalc(b1, v1))/Math.Abs(v2-v1));
                        }
                        bcerrors = bcerrors | ((double)(s2)>(double)(lipschitztol*s1) & (double)(s1)>(double)(threshold*k));
                    }
                    
                    //
                    // test differentiation properties
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        t = 2*math.randomreal()-1;
                        poldiff2(x, y, n, t, ref v0, ref v1, ref v2);
                        d0 = 0;
                        d1 = 0;
                        d2 = 0;
                        ratint.barycentricdiff1(b1, t, ref d0, ref d1);
                        bcerrors = bcerrors | (double)(Math.Abs(v0-d0))>(double)(threshold*Math.Max(Math.Abs(v0), 1));
                        bcerrors = bcerrors | (double)(Math.Abs(v1-d1))>(double)(threshold*Math.Max(Math.Abs(v1), 1));
                        d0 = 0;
                        d1 = 0;
                        d2 = 0;
                        ratint.barycentricdiff2(b1, t, ref d0, ref d1, ref d2);
                        bcerrors = bcerrors | (double)(Math.Abs(v0-d0))>(double)(threshold*Math.Max(Math.Abs(v0), 1));
                        bcerrors = bcerrors | (double)(Math.Abs(v1-d1))>(double)(threshold*Math.Max(Math.Abs(v1), 1));
                        bcerrors = bcerrors | (double)(Math.Abs(v2-d2))>(double)(Math.Sqrt(threshold)*Math.Max(Math.Abs(v2), 1));
                    }
                    
                    //
                    // test linear translation
                    //
                    t = 2*math.randomreal()-1;
                    a = 2*math.randomreal()-1;
                    b = 2*math.randomreal()-1;
                    brcunset(b2);
                    ratint.barycentriccopy(b1, b2);
                    ratint.barycentriclintransx(b2, a, b);
                    bcerrors = bcerrors | (double)(Math.Abs(ratint.barycentriccalc(b1, a*t+b)-ratint.barycentriccalc(b2, t)))>(double)(threshold);
                    a = 0;
                    b = 2*math.randomreal()-1;
                    brcunset(b2);
                    ratint.barycentriccopy(b1, b2);
                    ratint.barycentriclintransx(b2, a, b);
                    bcerrors = bcerrors | (double)(Math.Abs(ratint.barycentriccalc(b1, a*t+b)-ratint.barycentriccalc(b2, t)))>(double)(threshold);
                    a = 2*math.randomreal()-1;
                    b = 2*math.randomreal()-1;
                    brcunset(b2);
                    ratint.barycentriccopy(b1, b2);
                    ratint.barycentriclintransy(b2, a, b);
                    bcerrors = bcerrors | (double)(Math.Abs(a*ratint.barycentriccalc(b1, t)+b-ratint.barycentriccalc(b2, t)))>(double)(threshold);
                }
            }
            for(pass=0; pass<=3; pass++)
            {
                
                //
                // Crash-test: small numbers, large numbers
                //
                x = new double[4];
                y = new double[4];
                w = new double[4];
                h = 1;
                if( pass%2==0 )
                {
                    h = 100*math.minrealnumber;
                }
                if( pass%2==1 )
                {
                    h = 0.01*math.maxrealnumber;
                }
                x[0] = 0*h;
                x[1] = 1*h;
                x[2] = 2*h;
                x[3] = 3*h;
                y[0] = 0*h;
                y[1] = 1*h;
                y[2] = 2*h;
                y[3] = 3*h;
                w[0] = -(1/(x[1]-x[0]));
                w[1] = 1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
                w[2] = -(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
                w[3] = 1/(x[3]-x[2]);
                if( pass/2==0 )
                {
                    v0 = 0;
                }
                if( pass/2==1 )
                {
                    v0 = 0.6*h;
                }
                ratint.barycentricbuildxyw(x, y, w, 4, b1);
                t = ratint.barycentriccalc(b1, v0);
                d0 = 0;
                d1 = 0;
                d2 = 0;
                ratint.barycentricdiff1(b1, v0, ref d0, ref d1);
                bcerrors = bcerrors | (double)(Math.Abs(t-v0))>(double)(threshold*v0);
                bcerrors = bcerrors | (double)(Math.Abs(d0-v0))>(double)(threshold*v0);
                bcerrors = bcerrors | (double)(Math.Abs(d1-1))>(double)(1000*threshold);
            }
            
            //
            // crash test: large abscissas, small argument
            //
            // test for errors in D0 is not very strict
            // because renormalization used in Diff1()
            // destroys part of precision.
            //
            x = new double[4];
            y = new double[4];
            w = new double[4];
            h = 0.01*math.maxrealnumber;
            x[0] = 0*h;
            x[1] = 1*h;
            x[2] = 2*h;
            x[3] = 3*h;
            y[0] = 0*h;
            y[1] = 1*h;
            y[2] = 2*h;
            y[3] = 3*h;
            w[0] = -(1/(x[1]-x[0]));
            w[1] = 1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
            w[2] = -(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
            w[3] = 1/(x[3]-x[2]);
            v0 = 100*math.minrealnumber;
            ratint.barycentricbuildxyw(x, y, w, 4, b1);
            t = ratint.barycentriccalc(b1, v0);
            d0 = 0;
            d1 = 0;
            d2 = 0;
            ratint.barycentricdiff1(b1, v0, ref d0, ref d1);
            bcerrors = bcerrors | (double)(Math.Abs(t))>(double)(v0*(1+threshold));
            bcerrors = bcerrors | (double)(Math.Abs(d0))>(double)(v0*(1+threshold));
            bcerrors = bcerrors | (double)(Math.Abs(d1-1))>(double)(1000*threshold);
            
            //
            // crash test: test safe barycentric formula
            //
            x = new double[4];
            y = new double[4];
            w = new double[4];
            h = 2*math.minrealnumber;
            x[0] = 0*h;
            x[1] = 1*h;
            x[2] = 2*h;
            x[3] = 3*h;
            y[0] = 0*h;
            y[1] = 1*h;
            y[2] = 2*h;
            y[3] = 3*h;
            w[0] = -(1/(x[1]-x[0]));
            w[1] = 1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
            w[2] = -(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
            w[3] = 1/(x[3]-x[2]);
            v0 = math.minrealnumber;
            ratint.barycentricbuildxyw(x, y, w, 4, b1);
            t = ratint.barycentriccalc(b1, v0);
            bcerrors = bcerrors | (double)(Math.Abs(t-v0)/v0)>(double)(threshold);
            
            //
            // Testing "No Poles" interpolation
            //
            maxerr = 0;
            for(pass=1; pass<=passcount-1; pass++)
            {
                x = new double[1];
                y = new double[1];
                x[0] = 2*math.randomreal()-1;
                y[0] = 2*math.randomreal()-1;
                ratint.barycentricbuildfloaterhormann(x, y, 1, 1, b1);
                maxerr = Math.Max(maxerr, Math.Abs(ratint.barycentriccalc(b1, 2*math.randomreal()-1)-y[0]));
            }
            for(n=2; n<=10; n++)
            {
                
                //
                // compare interpolant built by subroutine
                // with interpolant built by hands
                //
                x = new double[n];
                y = new double[n];
                w = new double[n];
                w2 = new double[n];
                
                //
                // D=1, non-equidistant nodes
                //
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // Initialize X, Y, W
                    //
                    a = -1-1*math.randomreal();
                    b = 1+1*math.randomreal();
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = Math.Atan((b-a)*i/(n-1)+a);
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        y[i] = 2*math.randomreal()-1;
                    }
                    w[0] = -(1/(x[1]-x[0]));
                    s = 1;
                    for(i=1; i<=n-2; i++)
                    {
                        w[i] = s*(1/(x[i]-x[i-1])+1/(x[i+1]-x[i]));
                        s = -s;
                    }
                    w[n-1] = s/(x[n-1]-x[n-2]);
                    for(i=0; i<=n-1; i++)
                    {
                        k = math.randominteger(n);
                        if( k!=i )
                        {
                            t = x[i];
                            x[i] = x[k];
                            x[k] = t;
                            t = y[i];
                            y[i] = y[k];
                            y[k] = t;
                            t = w[i];
                            w[i] = w[k];
                            w[k] = t;
                        }
                    }
                    
                    //
                    // Build and test
                    //
                    ratint.barycentricbuildfloaterhormann(x, y, n, 1, b1);
                    ratint.barycentricbuildxyw(x, y, w, n, b2);
                    for(i=1; i<=2*n; i++)
                    {
                        t = a+(b-a)*math.randomreal();
                        maxerr = Math.Max(maxerr, Math.Abs(ratint.barycentriccalc(b1, t)-ratint.barycentriccalc(b2, t)));
                    }
                }
                
                //
                // D = 0, 1, 2. Equidistant nodes.
                //
                for(d=0; d<=2; d++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        
                        //
                        // Skip incorrect (N,D) pairs
                        //
                        if( n<2*d )
                        {
                            continue;
                        }
                        
                        //
                        // Initialize X, Y, W
                        //
                        a = -1-1*math.randomreal();
                        b = 1+1*math.randomreal();
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = (b-a)*i/(n-1)+a;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            y[i] = 2*math.randomreal()-1;
                        }
                        s = 1;
                        if( d==0 )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                w[i] = s;
                                s = -s;
                            }
                        }
                        if( d==1 )
                        {
                            w[0] = -s;
                            for(i=1; i<=n-2; i++)
                            {
                                w[i] = 2*s;
                                s = -s;
                            }
                            w[n-1] = s;
                        }
                        if( d==2 )
                        {
                            w[0] = s;
                            w[1] = -(3*s);
                            for(i=2; i<=n-3; i++)
                            {
                                w[i] = 4*s;
                                s = -s;
                            }
                            w[n-2] = 3*s;
                            w[n-1] = -s;
                        }
                        
                        //
                        // Mix
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            k = math.randominteger(n);
                            if( k!=i )
                            {
                                t = x[i];
                                x[i] = x[k];
                                x[k] = t;
                                t = y[i];
                                y[i] = y[k];
                                y[k] = t;
                                t = w[i];
                                w[i] = w[k];
                                w[k] = t;
                            }
                        }
                        
                        //
                        // Build and test
                        //
                        ratint.barycentricbuildfloaterhormann(x, y, n, d, b1);
                        ratint.barycentricbuildxyw(x, y, w, n, b2);
                        for(i=1; i<=2*n; i++)
                        {
                            t = a+(b-a)*math.randomreal();
                            maxerr = Math.Max(maxerr, Math.Abs(ratint.barycentriccalc(b1, t)-ratint.barycentriccalc(b2, t)));
                        }
                    }
                }
            }
            if( (double)(maxerr)>(double)(threshold) )
            {
                nperrors = true;
            }
            
            //
            // report
            //
            waserrors = bcerrors | nperrors;
            if( !silent )
            {
                System.Console.Write("TESTING RATIONAL INTERPOLATION");
                System.Console.WriteLine();
                System.Console.Write("BASIC BARYCENTRIC FUNCTIONS:             ");
                if( bcerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("FLOATER-HORMANN:                         ");
                if( nperrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            
            //
            // end
            //
            result = !waserrors;
            return result;
        }


        private static void poldiff2(double[] x,
            double[] f,
            int n,
            double t,
            ref double p,
            ref double dp,
            ref double d2p)
        {
            int m = 0;
            int i = 0;
            double[] df = new double[0];
            double[] d2f = new double[0];

            f = (double[])f.Clone();
            p = 0;
            dp = 0;
            d2p = 0;

            n = n-1;
            df = new double[n+1];
            d2f = new double[n+1];
            for(i=0; i<=n; i++)
            {
                d2f[i] = 0;
                df[i] = 0;
            }
            for(m=1; m<=n; m++)
            {
                for(i=0; i<=n-m; i++)
                {
                    d2f[i] = ((t-x[i+m])*d2f[i]+(x[i]-t)*d2f[i+1]+2*df[i]-2*df[i+1])/(x[i]-x[i+m]);
                    df[i] = ((t-x[i+m])*df[i]+f[i]+(x[i]-t)*df[i+1]-f[i+1])/(x[i]-x[i+m]);
                    f[i] = ((t-x[i+m])*f[i]+(x[i]-t)*f[i+1])/(x[i]-x[i+m]);
                }
            }
            p = f[0];
            dp = df[0];
            d2p = d2f[0];
        }


        private static void brcunset(ratint.barycentricinterpolant b)
        {
            double[] x = new double[0];
            double[] y = new double[0];
            double[] w = new double[0];

            x = new double[1];
            y = new double[1];
            w = new double[1];
            x[0] = 0;
            y[0] = 0;
            w[0] = 1;
            ratint.barycentricbuildxyw(x, y, w, 1, b);
        }


        /*************************************************************************
        Tests whether constant C is solution of 1D LLS problem
        *************************************************************************/
        private static bool is1dsolution(int n,
            double[] y,
            double[] w,
            double c)
        {
            bool result = new bool();
            int i = 0;
            double s1 = 0;
            double s2 = 0;
            double s3 = 0;
            double delta = 0;

            delta = 0.001;
            
            //
            // Test result
            //
            s1 = 0;
            for(i=0; i<=n-1; i++)
            {
                s1 = s1+math.sqr(w[i]*(c-y[i]));
            }
            s2 = 0;
            s3 = 0;
            for(i=0; i<=n-1; i++)
            {
                s2 = s2+math.sqr(w[i]*(c+delta-y[i]));
                s3 = s3+math.sqr(w[i]*(c-delta-y[i]));
            }
            result = (double)(s2)>=(double)(s1) & (double)(s3)>=(double)(s1);
            return result;
        }


    }
    public class testpolintunit
    {
        /*************************************************************************
        Unit test
        *************************************************************************/
        public static bool testpolint(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool interrors = new bool();
            double threshold = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] w = new double[0];
            double[] c = new double[0];
            double[] x2 = new double[0];
            double[] y2 = new double[0];
            double[] w2 = new double[0];
            double[] xfull = new double[0];
            double[] yfull = new double[0];
            double a = 0;
            double b = 0;
            double t = 0;
            int i = 0;
            int k = 0;
            double[] xc = new double[0];
            double[] yc = new double[0];
            int[] dc = new int[0];
            double v = 0;
            double v0 = 0;
            double v1 = 0;
            double v2 = 0;
            double v3 = 0;
            double v4 = 0;
            double pscale = 0;
            double poffset = 0;
            ratint.barycentricinterpolant p = new ratint.barycentricinterpolant();
            ratint.barycentricinterpolant p1 = new ratint.barycentricinterpolant();
            ratint.barycentricinterpolant p2 = new ratint.barycentricinterpolant();
            int n = 0;
            int maxn = 0;
            int pass = 0;
            int passcount = 0;

            waserrors = false;
            interrors = false;
            maxn = 5;
            passcount = 20;
            threshold = 1.0E8*math.machineepsilon;
            
            //
            // Test equidistant interpolation
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // prepare task:
                    // * equidistant points
                    // * random Y
                    // * T in [A,B] or near (within 10% of its width)
                    //
                    do
                    {
                        a = 2*math.randomreal()-1;
                        b = 2*math.randomreal()-1;
                    }
                    while( (double)(Math.Abs(a-b))<=(double)(0.2) );
                    t = a+(1.2*math.randomreal()-0.1)*(b-a);
                    apserv.taskgenint1dequidist(a, b, n, ref x, ref y);
                    
                    //
                    // test "fast" equidistant interpolation (no barycentric model)
                    //
                    interrors = interrors | (double)(Math.Abs(polint.polynomialcalceqdist(a, b, y, n, t)-internalpolint(x, y, n, t)))>(double)(threshold);
                    
                    //
                    // test "slow" equidistant interpolation (create barycentric model)
                    //
                    brcunset(p);
                    polint.polynomialbuild(x, y, n, p);
                    interrors = interrors | (double)(Math.Abs(ratint.barycentriccalc(p, t)-internalpolint(x, y, n, t)))>(double)(threshold);
                    
                    //
                    // test "fast" interpolation (create "fast" barycentric model)
                    //
                    brcunset(p);
                    polint.polynomialbuildeqdist(a, b, y, n, p);
                    interrors = interrors | (double)(Math.Abs(ratint.barycentriccalc(p, t)-internalpolint(x, y, n, t)))>(double)(threshold);
                }
            }
            
            //
            // Test Chebyshev-1 interpolation
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // prepare task:
                    // * equidistant points
                    // * random Y
                    // * T in [A,B] or near (within 10% of its width)
                    //
                    do
                    {
                        a = 2*math.randomreal()-1;
                        b = 2*math.randomreal()-1;
                    }
                    while( (double)(Math.Abs(a-b))<=(double)(0.2) );
                    t = a+(1.2*math.randomreal()-0.1)*(b-a);
                    apserv.taskgenint1dcheb1(a, b, n, ref x, ref y);
                    
                    //
                    // test "fast" interpolation (no barycentric model)
                    //
                    interrors = interrors | (double)(Math.Abs(polint.polynomialcalccheb1(a, b, y, n, t)-internalpolint(x, y, n, t)))>(double)(threshold);
                    
                    //
                    // test "slow" interpolation (create barycentric model)
                    //
                    brcunset(p);
                    polint.polynomialbuild(x, y, n, p);
                    interrors = interrors | (double)(Math.Abs(ratint.barycentriccalc(p, t)-internalpolint(x, y, n, t)))>(double)(threshold);
                    
                    //
                    // test "fast" interpolation (create "fast" barycentric model)
                    //
                    brcunset(p);
                    polint.polynomialbuildcheb1(a, b, y, n, p);
                    interrors = interrors | (double)(Math.Abs(ratint.barycentriccalc(p, t)-internalpolint(x, y, n, t)))>(double)(threshold);
                }
            }
            
            //
            // Test Chebyshev-2 interpolation
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // prepare task:
                    // * equidistant points
                    // * random Y
                    // * T in [A,B] or near (within 10% of its width)
                    //
                    do
                    {
                        a = 2*math.randomreal()-1;
                        b = 2*math.randomreal()-1;
                    }
                    while( (double)(Math.Abs(a-b))<=(double)(0.2) );
                    t = a+(1.2*math.randomreal()-0.1)*(b-a);
                    apserv.taskgenint1dcheb2(a, b, n, ref x, ref y);
                    
                    //
                    // test "fast" interpolation (no barycentric model)
                    //
                    interrors = interrors | (double)(Math.Abs(polint.polynomialcalccheb2(a, b, y, n, t)-internalpolint(x, y, n, t)))>(double)(threshold);
                    
                    //
                    // test "slow" interpolation (create barycentric model)
                    //
                    brcunset(p);
                    polint.polynomialbuild(x, y, n, p);
                    interrors = interrors | (double)(Math.Abs(ratint.barycentriccalc(p, t)-internalpolint(x, y, n, t)))>(double)(threshold);
                    
                    //
                    // test "fast" interpolation (create "fast" barycentric model)
                    //
                    brcunset(p);
                    polint.polynomialbuildcheb2(a, b, y, n, p);
                    interrors = interrors | (double)(Math.Abs(ratint.barycentriccalc(p, t)-internalpolint(x, y, n, t)))>(double)(threshold);
                }
            }
            
            //
            // Testing conversion Barycentric<->Chebyshev
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(k=1; k<=3; k++)
                {
                    
                    //
                    // Allocate
                    //
                    x = new double[k];
                    y = new double[k];
                    
                    //
                    // Generate problem
                    //
                    a = 2*math.randomreal()-1;
                    b = a+(0.1+math.randomreal())*(2*math.randominteger(2)-1);
                    v0 = 2*math.randomreal()-1;
                    v1 = 2*math.randomreal()-1;
                    v2 = 2*math.randomreal()-1;
                    if( k==1 )
                    {
                        x[0] = 0.5*(a+b);
                        y[0] = v0;
                    }
                    if( k==2 )
                    {
                        x[0] = a;
                        y[0] = v0-v1;
                        x[1] = b;
                        y[1] = v0+v1;
                    }
                    if( k==3 )
                    {
                        x[0] = a;
                        y[0] = v0-v1+v2;
                        x[1] = 0.5*(a+b);
                        y[1] = v0-v2;
                        x[2] = b;
                        y[2] = v0+v1+v2;
                    }
                    
                    //
                    // Test forward conversion
                    //
                    polint.polynomialbuild(x, y, k, p);
                    c = new double[1];
                    polint.polynomialbar2cheb(p, a, b, ref c);
                    interrors = interrors | ap.len(c)!=k;
                    if( k>=1 )
                    {
                        interrors = interrors | (double)(Math.Abs(c[0]-v0))>(double)(threshold);
                    }
                    if( k>=2 )
                    {
                        interrors = interrors | (double)(Math.Abs(c[1]-v1))>(double)(threshold);
                    }
                    if( k>=3 )
                    {
                        interrors = interrors | (double)(Math.Abs(c[2]-v2))>(double)(threshold);
                    }
                    
                    //
                    // Test backward conversion
                    //
                    polint.polynomialcheb2bar(c, k, a, b, p2);
                    v = a+math.randomreal()*(b-a);
                    interrors = interrors | (double)(Math.Abs(ratint.barycentriccalc(p, v)-ratint.barycentriccalc(p2, v)))>(double)(threshold);
                }
            }
            
            //
            // Testing conversion Barycentric<->Power
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(k=1; k<=5; k++)
                {
                    
                    //
                    // Allocate
                    //
                    x = new double[k];
                    y = new double[k];
                    
                    //
                    // Generate problem
                    //
                    poffset = 2*math.randomreal()-1;
                    pscale = (0.1+math.randomreal())*(2*math.randominteger(2)-1);
                    v0 = 2*math.randomreal()-1;
                    v1 = 2*math.randomreal()-1;
                    v2 = 2*math.randomreal()-1;
                    v3 = 2*math.randomreal()-1;
                    v4 = 2*math.randomreal()-1;
                    if( k==1 )
                    {
                        x[0] = poffset;
                        y[0] = v0;
                    }
                    if( k==2 )
                    {
                        x[0] = poffset-pscale;
                        y[0] = v0-v1;
                        x[1] = poffset+pscale;
                        y[1] = v0+v1;
                    }
                    if( k==3 )
                    {
                        x[0] = poffset-pscale;
                        y[0] = v0-v1+v2;
                        x[1] = poffset;
                        y[1] = v0;
                        x[2] = poffset+pscale;
                        y[2] = v0+v1+v2;
                    }
                    if( k==4 )
                    {
                        x[0] = poffset-pscale;
                        y[0] = v0-v1+v2-v3;
                        x[1] = poffset-0.5*pscale;
                        y[1] = v0-0.5*v1+0.25*v2-0.125*v3;
                        x[2] = poffset+0.5*pscale;
                        y[2] = v0+0.5*v1+0.25*v2+0.125*v3;
                        x[3] = poffset+pscale;
                        y[3] = v0+v1+v2+v3;
                    }
                    if( k==5 )
                    {
                        x[0] = poffset-pscale;
                        y[0] = v0-v1+v2-v3+v4;
                        x[1] = poffset-0.5*pscale;
                        y[1] = v0-0.5*v1+0.25*v2-0.125*v3+0.0625*v4;
                        x[2] = poffset;
                        y[2] = v0;
                        x[3] = poffset+0.5*pscale;
                        y[3] = v0+0.5*v1+0.25*v2+0.125*v3+0.0625*v4;
                        x[4] = poffset+pscale;
                        y[4] = v0+v1+v2+v3+v4;
                    }
                    
                    //
                    // Test forward conversion
                    //
                    polint.polynomialbuild(x, y, k, p);
                    c = new double[1];
                    polint.polynomialbar2pow(p, poffset, pscale, ref c);
                    interrors = interrors | ap.len(c)!=k;
                    if( k>=1 )
                    {
                        interrors = interrors | (double)(Math.Abs(c[0]-v0))>(double)(threshold);
                    }
                    if( k>=2 )
                    {
                        interrors = interrors | (double)(Math.Abs(c[1]-v1))>(double)(threshold);
                    }
                    if( k>=3 )
                    {
                        interrors = interrors | (double)(Math.Abs(c[2]-v2))>(double)(threshold);
                    }
                    if( k>=4 )
                    {
                        interrors = interrors | (double)(Math.Abs(c[3]-v3))>(double)(threshold);
                    }
                    if( k>=5 )
                    {
                        interrors = interrors | (double)(Math.Abs(c[4]-v4))>(double)(threshold);
                    }
                    
                    //
                    // Test backward conversion
                    //
                    polint.polynomialpow2bar(c, k, poffset, pscale, p2);
                    v = poffset+(2*math.randomreal()-1)*pscale;
                    interrors = interrors | (double)(Math.Abs(ratint.barycentriccalc(p, v)-ratint.barycentriccalc(p2, v)))>(double)(threshold);
                }
            }
            
            //
            // crash-test: ability to solve tasks which will overflow/underflow
            // weights with straightforward implementation
            //
            for(n=1; n<=20; n++)
            {
                a = -(0.1*math.maxrealnumber);
                b = 0.1*math.maxrealnumber;
                apserv.taskgenint1dequidist(a, b, n, ref x, ref y);
                polint.polynomialbuild(x, y, n, p);
                for(i=0; i<=n-1; i++)
                {
                    interrors = interrors | (double)(p.w[i])==(double)(0);
                }
            }
            
            //
            // report
            //
            waserrors = interrors;
            if( !silent )
            {
                System.Console.Write("TESTING POLYNOMIAL INTERPOLATION");
                System.Console.WriteLine();
                
                //
                // Normal tests
                //
                System.Console.Write("INTERPOLATION TEST:                      ");
                if( interrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            
            //
            // end
            //
            result = !waserrors;
            return result;
        }


        private static double internalpolint(double[] x,
            double[] f,
            int n,
            double t)
        {
            double result = 0;
            int i = 0;
            int j = 0;

            f = (double[])f.Clone();

            n = n-1;
            for(j=0; j<=n-1; j++)
            {
                for(i=j+1; i<=n; i++)
                {
                    f[i] = ((t-x[j])*f[i]-(t-x[i])*f[j])/(x[i]-x[j]);
                }
            }
            result = f[n];
            return result;
        }


        private static void brcunset(ratint.barycentricinterpolant b)
        {
            double[] x = new double[0];
            double[] y = new double[0];
            double[] w = new double[0];

            x = new double[1];
            y = new double[1];
            w = new double[1];
            x[0] = 0;
            y[0] = 0;
            w[0] = 1;
            ratint.barycentricbuildxyw(x, y, w, 1, b);
        }


    }
    public class testspline1dunit
    {
        public static bool testspline1d(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool crserrors = new bool();
            bool cserrors = new bool();
            bool hserrors = new bool();
            bool aserrors = new bool();
            bool lserrors = new bool();
            bool dserrors = new bool();
            bool uperrors = new bool();
            bool cperrors = new bool();
            bool lterrors = new bool();
            bool ierrors = new bool();
            double nonstrictthreshold = 0;
            double threshold = 0;
            int passcount = 0;
            double lstep = 0;
            double h = 0;
            int maxn = 0;
            int bltype = 0;
            int brtype = 0;
            bool periodiccond = new bool();
            int n = 0;
            int i = 0;
            int k = 0;
            int pass = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] yp = new double[0];
            double[] w = new double[0];
            double[] w2 = new double[0];
            double[] y2 = new double[0];
            double[] d = new double[0];
            double[] xc = new double[0];
            double[] yc = new double[0];
            int n2 = 0;
            double[] tmp0 = new double[0];
            double[] tmp1 = new double[0];
            double[] tmp2 = new double[0];
            double[] tmpx = new double[0];
            int[] dc = new int[0];
            spline1d.spline1dinterpolant c = new spline1d.spline1dinterpolant();
            spline1d.spline1dinterpolant c2 = new spline1d.spline1dinterpolant();
            double a = 0;
            double b = 0;
            double bl = 0;
            double br = 0;
            double t = 0;
            double sa = 0;
            double sb = 0;
            double v = 0;
            double l10 = 0;
            double l11 = 0;
            double l12 = 0;
            double l20 = 0;
            double l21 = 0;
            double l22 = 0;
            double p0 = 0;
            double p1 = 0;
            double p2 = 0;
            double s = 0;
            double ds = 0;
            double d2s = 0;
            double s2 = 0;
            double ds2 = 0;
            double d2s2 = 0;
            double vl = 0;
            double vm = 0;
            double vr = 0;
            double err = 0;
            double tension = 0;
            double intab = 0;
            int i_ = 0;

            waserrors = false;
            passcount = 20;
            lstep = 0.005;
            h = 0.00001;
            maxn = 10;
            threshold = 10000*math.machineepsilon;
            nonstrictthreshold = 0.00001;
            lserrors = false;
            cserrors = false;
            crserrors = false;
            hserrors = false;
            aserrors = false;
            dserrors = false;
            cperrors = false;
            uperrors = false;
            lterrors = false;
            ierrors = false;
            
            //
            // General test: linear, cubic, Hermite, Akima
            //
            for(n=2; n<=maxn; n++)
            {
                x = new double[n-1+1];
                y = new double[n-1+1];
                yp = new double[n-1+1];
                d = new double[n-1+1];
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // Prepare task:
                    // * X contains abscissas from [A,B]
                    // * Y contains function values
                    // * YP contains periodic function values
                    //
                    a = -1-math.randomreal();
                    b = 1+math.randomreal();
                    bl = 2*math.randomreal()-1;
                    br = 2*math.randomreal()-1;
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 0.5*(b+a)+0.5*(b-a)*Math.Cos(Math.PI*(2*i+1)/(2*n));
                        if( i==0 )
                        {
                            x[i] = a;
                        }
                        if( i==n-1 )
                        {
                            x[i] = b;
                        }
                        y[i] = Math.Cos(1.3*Math.PI*x[i]+0.4);
                        yp[i] = y[i];
                        d[i] = -(1.3*Math.PI*Math.Sin(1.3*Math.PI*x[i]+0.4));
                    }
                    yp[n-1] = yp[0];
                    for(i=0; i<=n-1; i++)
                    {
                        k = math.randominteger(n);
                        if( k!=i )
                        {
                            t = x[i];
                            x[i] = x[k];
                            x[k] = t;
                            t = y[i];
                            y[i] = y[k];
                            y[k] = t;
                            t = yp[i];
                            yp[i] = yp[k];
                            yp[k] = t;
                            t = d[i];
                            d[i] = d[k];
                            d[k] = t;
                        }
                    }
                    
                    //
                    // Build linear spline
                    // Test for general interpolation scheme properties:
                    // * values at nodes
                    // * continuous function
                    // Test for specific properties is implemented below.
                    //
                    spline1d.spline1dbuildlinear(x, y, n, c);
                    err = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(c, x[i])));
                    }
                    lserrors = lserrors | (double)(err)>(double)(threshold);
                    lconst(a, b, c, lstep, ref l10, ref l11, ref l12);
                    lconst(a, b, c, lstep/3, ref l20, ref l21, ref l22);
                    lserrors = lserrors | (double)(l20/l10)>(double)(1.2);
                    
                    //
                    // Build cubic spline.
                    // Test for interpolation scheme properties:
                    // * values at nodes
                    // * boundary conditions
                    // * continuous function
                    // * continuous first derivative
                    // * continuous second derivative
                    // * periodicity properties
                    // * Spline1DGridDiff(), Spline1DGridDiff2() and Spline1DDiff()
                    //   calls must return same results
                    //
                    for(bltype=-1; bltype<=2; bltype++)
                    {
                        for(brtype=-1; brtype<=2; brtype++)
                        {
                            
                            //
                            // skip meaningless combination of boundary conditions
                            // (one condition is periodic, another is not)
                            //
                            periodiccond = bltype==-1 | brtype==-1;
                            if( periodiccond & bltype!=brtype )
                            {
                                continue;
                            }
                            
                            //
                            // build
                            //
                            if( periodiccond )
                            {
                                spline1d.spline1dbuildcubic(x, yp, n, bltype, bl, brtype, br, c);
                            }
                            else
                            {
                                spline1d.spline1dbuildcubic(x, y, n, bltype, bl, brtype, br, c);
                            }
                            
                            //
                            // interpolation properties
                            //
                            err = 0;
                            if( periodiccond )
                            {
                                
                                //
                                // * check values at nodes; spline is periodic so
                                //   we add random number of periods to nodes
                                // * we also test for periodicity of derivatives
                                //
                                for(i=0; i<=n-1; i++)
                                {
                                    v = x[i];
                                    vm = v+(b-a)*(math.randominteger(5)-2);
                                    t = yp[i]-spline1d.spline1dcalc(c, vm);
                                    err = Math.Max(err, Math.Abs(t));
                                    spline1d.spline1ddiff(c, v, ref s, ref ds, ref d2s);
                                    spline1d.spline1ddiff(c, vm, ref s2, ref ds2, ref d2s2);
                                    err = Math.Max(err, Math.Abs(s-s2));
                                    err = Math.Max(err, Math.Abs(ds-ds2));
                                    err = Math.Max(err, Math.Abs(d2s-d2s2));
                                }
                                
                                //
                                // periodicity between nodes
                                //
                                v = a+(b-a)*math.randomreal();
                                vm = v+(b-a)*(math.randominteger(5)-2);
                                err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(c, v)-spline1d.spline1dcalc(c, vm)));
                                spline1d.spline1ddiff(c, v, ref s, ref ds, ref d2s);
                                spline1d.spline1ddiff(c, vm, ref s2, ref ds2, ref d2s2);
                                err = Math.Max(err, Math.Abs(s-s2));
                                err = Math.Max(err, Math.Abs(ds-ds2));
                                err = Math.Max(err, Math.Abs(d2s-d2s2));
                            }
                            else
                            {
                                
                                //
                                // * check values at nodes
                                //
                                for(i=0; i<=n-1; i++)
                                {
                                    err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(c, x[i])));
                                }
                            }
                            cserrors = cserrors | (double)(err)>(double)(threshold);
                            
                            //
                            // check boundary conditions
                            //
                            err = 0;
                            if( bltype==0 )
                            {
                                spline1d.spline1ddiff(c, a-h, ref s, ref ds, ref d2s);
                                spline1d.spline1ddiff(c, a+h, ref s2, ref ds2, ref d2s2);
                                t = (d2s2-d2s)/(2*h);
                                err = Math.Max(err, Math.Abs(t));
                            }
                            if( bltype==1 )
                            {
                                t = (spline1d.spline1dcalc(c, a+h)-spline1d.spline1dcalc(c, a-h))/(2*h);
                                err = Math.Max(err, Math.Abs(bl-t));
                            }
                            if( bltype==2 )
                            {
                                t = (spline1d.spline1dcalc(c, a+h)-2*spline1d.spline1dcalc(c, a)+spline1d.spline1dcalc(c, a-h))/math.sqr(h);
                                err = Math.Max(err, Math.Abs(bl-t));
                            }
                            if( brtype==0 )
                            {
                                spline1d.spline1ddiff(c, b-h, ref s, ref ds, ref d2s);
                                spline1d.spline1ddiff(c, b+h, ref s2, ref ds2, ref d2s2);
                                t = (d2s2-d2s)/(2*h);
                                err = Math.Max(err, Math.Abs(t));
                            }
                            if( brtype==1 )
                            {
                                t = (spline1d.spline1dcalc(c, b+h)-spline1d.spline1dcalc(c, b-h))/(2*h);
                                err = Math.Max(err, Math.Abs(br-t));
                            }
                            if( brtype==2 )
                            {
                                t = (spline1d.spline1dcalc(c, b+h)-2*spline1d.spline1dcalc(c, b)+spline1d.spline1dcalc(c, b-h))/math.sqr(h);
                                err = Math.Max(err, Math.Abs(br-t));
                            }
                            if( bltype==-1 | brtype==-1 )
                            {
                                spline1d.spline1ddiff(c, a+100*math.machineepsilon, ref s, ref ds, ref d2s);
                                spline1d.spline1ddiff(c, b-100*math.machineepsilon, ref s2, ref ds2, ref d2s2);
                                err = Math.Max(err, Math.Abs(s-s2));
                                err = Math.Max(err, Math.Abs(ds-ds2));
                                err = Math.Max(err, Math.Abs(d2s-d2s2));
                            }
                            cserrors = cserrors | (double)(err)>(double)(1.0E-3);
                            
                            //
                            // Check Lipschitz continuity
                            //
                            lconst(a, b, c, lstep, ref l10, ref l11, ref l12);
                            lconst(a, b, c, lstep/3, ref l20, ref l21, ref l22);
                            if( (double)(l10)>(double)(1.0E-6) )
                            {
                                cserrors = cserrors | (double)(l20/l10)>(double)(1.2);
                            }
                            if( (double)(l11)>(double)(1.0E-6) )
                            {
                                cserrors = cserrors | (double)(l21/l11)>(double)(1.2);
                            }
                            if( (double)(l12)>(double)(1.0E-6) )
                            {
                                cserrors = cserrors | (double)(l22/l12)>(double)(1.2);
                            }
                            
                            //
                            // compare spline1dgriddiff() and spline1ddiff() results
                            //
                            err = 0;
                            if( periodiccond )
                            {
                                spline1d.spline1dgriddiffcubic(x, yp, n, bltype, bl, brtype, br, ref tmp1);
                            }
                            else
                            {
                                spline1d.spline1dgriddiffcubic(x, y, n, bltype, bl, brtype, br, ref tmp1);
                            }
                            ap.assert(ap.len(tmp1)>=n);
                            for(i=0; i<=n-1; i++)
                            {
                                spline1d.spline1ddiff(c, x[i], ref s, ref ds, ref d2s);
                                err = Math.Max(err, Math.Abs(ds-tmp1[i]));
                            }
                            if( periodiccond )
                            {
                                spline1d.spline1dgriddiff2cubic(x, yp, n, bltype, bl, brtype, br, ref tmp1, ref tmp2);
                            }
                            else
                            {
                                spline1d.spline1dgriddiff2cubic(x, y, n, bltype, bl, brtype, br, ref tmp1, ref tmp2);
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                spline1d.spline1ddiff(c, x[i], ref s, ref ds, ref d2s);
                                err = Math.Max(err, Math.Abs(ds-tmp1[i]));
                                err = Math.Max(err, Math.Abs(d2s-tmp2[i]));
                            }
                            cserrors = cserrors | (double)(err)>(double)(threshold);
                            
                            //
                            // compare spline1dconv()/convdiff()/convdiff2() and spline1ddiff() results
                            //
                            n2 = 2+math.randominteger(2*n);
                            tmpx = new double[n2];
                            for(i=0; i<=n2-1; i++)
                            {
                                tmpx[i] = 0.5*(a+b)+(a-b)*(2*math.randomreal()-1);
                            }
                            err = 0;
                            if( periodiccond )
                            {
                                spline1d.spline1dconvcubic(x, yp, n, bltype, bl, brtype, br, tmpx, n2, ref tmp0);
                            }
                            else
                            {
                                spline1d.spline1dconvcubic(x, y, n, bltype, bl, brtype, br, tmpx, n2, ref tmp0);
                            }
                            for(i=0; i<=n2-1; i++)
                            {
                                spline1d.spline1ddiff(c, tmpx[i], ref s, ref ds, ref d2s);
                                err = Math.Max(err, Math.Abs(s-tmp0[i]));
                            }
                            if( periodiccond )
                            {
                                spline1d.spline1dconvdiffcubic(x, yp, n, bltype, bl, brtype, br, tmpx, n2, ref tmp0, ref tmp1);
                            }
                            else
                            {
                                spline1d.spline1dconvdiffcubic(x, y, n, bltype, bl, brtype, br, tmpx, n2, ref tmp0, ref tmp1);
                            }
                            for(i=0; i<=n2-1; i++)
                            {
                                spline1d.spline1ddiff(c, tmpx[i], ref s, ref ds, ref d2s);
                                err = Math.Max(err, Math.Abs(s-tmp0[i]));
                                err = Math.Max(err, Math.Abs(ds-tmp1[i]));
                            }
                            if( periodiccond )
                            {
                                spline1d.spline1dconvdiff2cubic(x, yp, n, bltype, bl, brtype, br, tmpx, n2, ref tmp0, ref tmp1, ref tmp2);
                            }
                            else
                            {
                                spline1d.spline1dconvdiff2cubic(x, y, n, bltype, bl, brtype, br, tmpx, n2, ref tmp0, ref tmp1, ref tmp2);
                            }
                            for(i=0; i<=n2-1; i++)
                            {
                                spline1d.spline1ddiff(c, tmpx[i], ref s, ref ds, ref d2s);
                                err = Math.Max(err, Math.Abs(s-tmp0[i]));
                                err = Math.Max(err, Math.Abs(ds-tmp1[i]));
                                err = Math.Max(err, Math.Abs(d2s-tmp2[i]));
                            }
                            cserrors = cserrors | (double)(err)>(double)(threshold);
                        }
                    }
                    
                    //
                    // Build Catmull-Rom spline.
                    // Test for interpolation scheme properties:
                    // * values at nodes
                    // * boundary conditions
                    // * continuous function
                    // * continuous first derivative
                    // * periodicity properties
                    //
                    for(bltype=-1; bltype<=0; bltype++)
                    {
                        periodiccond = bltype==-1;
                        
                        //
                        // select random tension value, then build
                        //
                        if( (double)(math.randomreal())>(double)(0.5) )
                        {
                            if( (double)(math.randomreal())>(double)(0.5) )
                            {
                                tension = 0;
                            }
                            else
                            {
                                tension = 1;
                            }
                        }
                        else
                        {
                            tension = math.randomreal();
                        }
                        if( periodiccond )
                        {
                            spline1d.spline1dbuildcatmullrom(x, yp, n, bltype, tension, c);
                        }
                        else
                        {
                            spline1d.spline1dbuildcatmullrom(x, y, n, bltype, tension, c);
                        }
                        
                        //
                        // interpolation properties
                        //
                        err = 0;
                        if( periodiccond )
                        {
                            
                            //
                            // * check values at nodes; spline is periodic so
                            //   we add random number of periods to nodes
                            // * we also test for periodicity of first derivative
                            //
                            for(i=0; i<=n-1; i++)
                            {
                                v = x[i];
                                vm = v+(b-a)*(math.randominteger(5)-2);
                                t = yp[i]-spline1d.spline1dcalc(c, vm);
                                err = Math.Max(err, Math.Abs(t));
                                spline1d.spline1ddiff(c, v, ref s, ref ds, ref d2s);
                                spline1d.spline1ddiff(c, vm, ref s2, ref ds2, ref d2s2);
                                err = Math.Max(err, Math.Abs(s-s2));
                                err = Math.Max(err, Math.Abs(ds-ds2));
                            }
                            
                            //
                            // periodicity between nodes
                            //
                            v = a+(b-a)*math.randomreal();
                            vm = v+(b-a)*(math.randominteger(5)-2);
                            err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(c, v)-spline1d.spline1dcalc(c, vm)));
                            spline1d.spline1ddiff(c, v, ref s, ref ds, ref d2s);
                            spline1d.spline1ddiff(c, vm, ref s2, ref ds2, ref d2s2);
                            err = Math.Max(err, Math.Abs(s-s2));
                            err = Math.Max(err, Math.Abs(ds-ds2));
                        }
                        else
                        {
                            
                            //
                            // * check values at nodes
                            //
                            for(i=0; i<=n-1; i++)
                            {
                                err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(c, x[i])));
                            }
                        }
                        crserrors = crserrors | (double)(err)>(double)(threshold);
                        
                        //
                        // check boundary conditions
                        //
                        err = 0;
                        if( bltype==0 )
                        {
                            spline1d.spline1ddiff(c, a-h, ref s, ref ds, ref d2s);
                            spline1d.spline1ddiff(c, a+h, ref s2, ref ds2, ref d2s2);
                            t = (d2s2-d2s)/(2*h);
                            err = Math.Max(err, Math.Abs(t));
                            spline1d.spline1ddiff(c, b-h, ref s, ref ds, ref d2s);
                            spline1d.spline1ddiff(c, b+h, ref s2, ref ds2, ref d2s2);
                            t = (d2s2-d2s)/(2*h);
                            err = Math.Max(err, Math.Abs(t));
                        }
                        if( bltype==-1 )
                        {
                            spline1d.spline1ddiff(c, a+100*math.machineepsilon, ref s, ref ds, ref d2s);
                            spline1d.spline1ddiff(c, b-100*math.machineepsilon, ref s2, ref ds2, ref d2s2);
                            err = Math.Max(err, Math.Abs(s-s2));
                            err = Math.Max(err, Math.Abs(ds-ds2));
                        }
                        crserrors = crserrors | (double)(err)>(double)(1.0E-3);
                        
                        //
                        // Check Lipschitz continuity
                        //
                        lconst(a, b, c, lstep, ref l10, ref l11, ref l12);
                        lconst(a, b, c, lstep/3, ref l20, ref l21, ref l22);
                        if( (double)(l10)>(double)(1.0E-6) )
                        {
                            crserrors = crserrors | (double)(l20/l10)>(double)(1.2);
                        }
                        if( (double)(l11)>(double)(1.0E-6) )
                        {
                            crserrors = crserrors | (double)(l21/l11)>(double)(1.2);
                        }
                    }
                    
                    //
                    // Build Hermite spline.
                    // Test for interpolation scheme properties:
                    // * values and derivatives at nodes
                    // * continuous function
                    // * continuous first derivative
                    //
                    spline1d.spline1dbuildhermite(x, y, d, n, c);
                    err = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(c, x[i])));
                    }
                    hserrors = hserrors | (double)(err)>(double)(threshold);
                    err = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        t = (spline1d.spline1dcalc(c, x[i]+h)-spline1d.spline1dcalc(c, x[i]-h))/(2*h);
                        err = Math.Max(err, Math.Abs(d[i]-t));
                    }
                    hserrors = hserrors | (double)(err)>(double)(1.0E-3);
                    lconst(a, b, c, lstep, ref l10, ref l11, ref l12);
                    lconst(a, b, c, lstep/3, ref l20, ref l21, ref l22);
                    hserrors = hserrors | (double)(l20/l10)>(double)(1.2);
                    hserrors = hserrors | (double)(l21/l11)>(double)(1.2);
                    
                    //
                    // Build Akima spline
                    // Test for general interpolation scheme properties:
                    // * values at nodes
                    // * continuous function
                    // * continuous first derivative
                    // Test for specific properties is implemented below.
                    //
                    if( n>=5 )
                    {
                        spline1d.spline1dbuildakima(x, y, n, c);
                        err = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            err = Math.Max(err, Math.Abs(y[i]-spline1d.spline1dcalc(c, x[i])));
                        }
                        aserrors = aserrors | (double)(err)>(double)(threshold);
                        lconst(a, b, c, lstep, ref l10, ref l11, ref l12);
                        lconst(a, b, c, lstep/3, ref l20, ref l21, ref l22);
                        hserrors = hserrors | (double)(l20/l10)>(double)(1.2);
                        hserrors = hserrors | (double)(l21/l11)>(double)(1.2);
                    }
                }
            }
            
            //
            // Special linear spline test:
            // test for linearity between x[i] and x[i+1]
            //
            for(n=2; n<=maxn; n++)
            {
                x = new double[n-1+1];
                y = new double[n-1+1];
                
                //
                // Prepare task
                //
                a = -1;
                b = 1;
                for(i=0; i<=n-1; i++)
                {
                    x[i] = a+(b-a)*i/(n-1);
                    y[i] = 2*math.randomreal()-1;
                }
                spline1d.spline1dbuildlinear(x, y, n, c);
                
                //
                // Test
                //
                err = 0;
                for(k=0; k<=n-2; k++)
                {
                    a = x[k];
                    b = x[k+1];
                    for(pass=1; pass<=passcount; pass++)
                    {
                        t = a+(b-a)*math.randomreal();
                        v = y[k]+(t-a)/(b-a)*(y[k+1]-y[k]);
                        err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(c, t)-v));
                    }
                }
                lserrors = lserrors | (double)(err)>(double)(threshold);
            }
            
            //
            // Special Akima test: test outlier sensitivity
            // Spline value at (x[i], x[i+1]) should depend from
            // f[i-2], f[i-1], f[i], f[i+1], f[i+2], f[i+3] only.
            //
            for(n=5; n<=maxn; n++)
            {
                x = new double[n-1+1];
                y = new double[n-1+1];
                y2 = new double[n-1+1];
                
                //
                // Prepare unperturbed Akima spline
                //
                a = -1;
                b = 1;
                for(i=0; i<=n-1; i++)
                {
                    x[i] = a+(b-a)*i/(n-1);
                    y[i] = Math.Cos(1.3*Math.PI*x[i]+0.4);
                }
                spline1d.spline1dbuildakima(x, y, n, c);
                
                //
                // Process perturbed tasks
                //
                err = 0;
                for(k=0; k<=n-1; k++)
                {
                    for(i_=0; i_<=n-1;i_++)
                    {
                        y2[i_] = y[i_];
                    }
                    y2[k] = 5;
                    spline1d.spline1dbuildakima(x, y2, n, c2);
                    
                    //
                    // Test left part independence
                    //
                    if( k-3>=1 )
                    {
                        a = -1;
                        b = x[k-3];
                        for(pass=1; pass<=passcount; pass++)
                        {
                            t = a+(b-a)*math.randomreal();
                            err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(c, t)-spline1d.spline1dcalc(c2, t)));
                        }
                    }
                    
                    //
                    // Test right part independence
                    //
                    if( k+3<=n-2 )
                    {
                        a = x[k+3];
                        b = 1;
                        for(pass=1; pass<=passcount; pass++)
                        {
                            t = a+(b-a)*math.randomreal();
                            err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(c, t)-spline1d.spline1dcalc(c2, t)));
                        }
                    }
                }
                aserrors = aserrors | (double)(err)>(double)(threshold);
            }
            
            //
            // Differentiation, copy/unpack test
            //
            for(n=2; n<=maxn; n++)
            {
                x = new double[n-1+1];
                y = new double[n-1+1];
                
                //
                // Prepare cubic spline
                //
                a = -1-math.randomreal();
                b = 1+math.randomreal();
                for(i=0; i<=n-1; i++)
                {
                    x[i] = a+(b-a)*i/(n-1);
                    y[i] = Math.Cos(1.3*Math.PI*x[i]+0.4);
                }
                spline1d.spline1dbuildcubic(x, y, n, 2, 0.0, 2, 0.0, c);
                
                //
                // Test diff
                //
                err = 0;
                for(pass=1; pass<=passcount; pass++)
                {
                    t = a+(b-a)*math.randomreal();
                    spline1d.spline1ddiff(c, t, ref s, ref ds, ref d2s);
                    vl = spline1d.spline1dcalc(c, t-h);
                    vm = spline1d.spline1dcalc(c, t);
                    vr = spline1d.spline1dcalc(c, t+h);
                    err = Math.Max(err, Math.Abs(s-vm));
                    err = Math.Max(err, Math.Abs(ds-(vr-vl)/(2*h)));
                    err = Math.Max(err, Math.Abs(d2s-(vr-2*vm+vl)/math.sqr(h)));
                }
                dserrors = dserrors | (double)(err)>(double)(0.001);
                
                //
                // Test copy
                //
                unsetspline1d(c2);
                spline1d.spline1dcopy(c, c2);
                err = 0;
                for(pass=1; pass<=passcount; pass++)
                {
                    t = a+(b-a)*math.randomreal();
                    err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(c, t)-spline1d.spline1dcalc(c2, t)));
                }
                cperrors = cperrors | (double)(err)>(double)(threshold);
                
                //
                // Test unpack
                //
                uperrors = uperrors | !testunpack(c, x);
                
                //
                // Test lin.trans.
                //
                err = 0;
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // LinTransX, general A
                    //
                    sa = 4*math.randomreal()-2;
                    sb = 2*math.randomreal()-1;
                    t = a+(b-a)*math.randomreal();
                    spline1d.spline1dcopy(c, c2);
                    spline1d.spline1dlintransx(c2, sa, sb);
                    err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(c, t)-spline1d.spline1dcalc(c2, (t-sb)/sa)));
                    
                    //
                    // LinTransX, special case: A=0
                    //
                    sb = 2*math.randomreal()-1;
                    t = a+(b-a)*math.randomreal();
                    spline1d.spline1dcopy(c, c2);
                    spline1d.spline1dlintransx(c2, 0, sb);
                    err = Math.Max(err, Math.Abs(spline1d.spline1dcalc(c, sb)-spline1d.spline1dcalc(c2, t)));
                    
                    //
                    // LinTransY
                    //
                    sa = 2*math.randomreal()-1;
                    sb = 2*math.randomreal()-1;
                    t = a+(b-a)*math.randomreal();
                    spline1d.spline1dcopy(c, c2);
                    spline1d.spline1dlintransy(c2, sa, sb);
                    err = Math.Max(err, Math.Abs(sa*spline1d.spline1dcalc(c, t)+sb-spline1d.spline1dcalc(c2, t)));
                }
                lterrors = lterrors | (double)(err)>(double)(threshold);
            }
            
            //
            // Testing integration.
            // Three tests are performed:
            //
            // * approximate test (well behaved smooth function, many points,
            //   integration inside [a,b]), non-periodic spline
            //
            // * exact test (integration of parabola, outside of [a,b], non-periodic spline
            //
            // * approximate test for periodic splines. F(x)=cos(2*pi*x)+1.
            //   Period length is equals to 1.0, so all operations with
            //   multiples of period are done exactly. For each value of PERIOD
            //   we calculate and test integral at four points:
            //   -   0 < t0 < PERIOD
            //   -   t1 = PERIOD-eps
            //   -   t2 = PERIOD
            //   -   t3 = PERIOD+eps
            //
            err = 0;
            for(n=20; n<=35; n++)
            {
                x = new double[n-1+1];
                y = new double[n-1+1];
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // Prepare cubic spline
                    //
                    a = -1-0.2*math.randomreal();
                    b = 1+0.2*math.randomreal();
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = a+(b-a)*i/(n-1);
                        y[i] = Math.Sin(Math.PI*x[i]+0.4)+Math.Exp(x[i]);
                    }
                    bl = Math.PI*Math.Cos(Math.PI*a+0.4)+Math.Exp(a);
                    br = Math.PI*Math.Cos(Math.PI*b+0.4)+Math.Exp(b);
                    spline1d.spline1dbuildcubic(x, y, n, 1, bl, 1, br, c);
                    
                    //
                    // Test
                    //
                    t = a+(b-a)*math.randomreal();
                    v = -(Math.Cos(Math.PI*a+0.4)/Math.PI)+Math.Exp(a);
                    v = -(Math.Cos(Math.PI*t+0.4)/Math.PI)+Math.Exp(t)-v;
                    v = v-spline1d.spline1dintegrate(c, t);
                    err = Math.Max(err, Math.Abs(v));
                }
            }
            ierrors = ierrors | (double)(err)>(double)(0.001);
            p0 = 2*math.randomreal()-1;
            p1 = 2*math.randomreal()-1;
            p2 = 2*math.randomreal()-1;
            a = -math.randomreal()-0.5;
            b = math.randomreal()+0.5;
            n = 2;
            x = new double[n];
            y = new double[n];
            d = new double[n];
            x[0] = a;
            y[0] = p0+p1*a+p2*math.sqr(a);
            d[0] = p1+2*p2*a;
            x[1] = b;
            y[1] = p0+p1*b+p2*math.sqr(b);
            d[1] = p1+2*p2*b;
            spline1d.spline1dbuildhermite(x, y, d, n, c);
            bl = Math.Min(a, b)-Math.Abs(b-a);
            br = Math.Min(a, b)+Math.Abs(b-a);
            err = 0;
            for(pass=1; pass<=100; pass++)
            {
                t = bl+(br-bl)*math.randomreal();
                v = p0*t+p1*math.sqr(t)/2+p2*math.sqr(t)*t/3-(p0*a+p1*math.sqr(a)/2+p2*math.sqr(a)*a/3);
                v = v-spline1d.spline1dintegrate(c, t);
                err = Math.Max(err, Math.Abs(v));
            }
            ierrors = ierrors | (double)(err)>(double)(threshold);
            n = 100;
            x = new double[n];
            y = new double[n];
            for(i=0; i<=n-1; i++)
            {
                x[i] = (double)i/(double)(n-1);
                y[i] = Math.Cos(2*Math.PI*x[i])+1;
            }
            y[0] = 2;
            y[n-1] = 2;
            spline1d.spline1dbuildcubic(x, y, n, -1, 0.0, -1, 0.0, c);
            intab = spline1d.spline1dintegrate(c, 1.0);
            v = math.randomreal();
            vr = spline1d.spline1dintegrate(c, v);
            ierrors = ierrors | (double)(Math.Abs(intab-1))>(double)(0.001);
            for(i=-10; i<=10; i++)
            {
                ierrors = ierrors | (double)(Math.Abs(spline1d.spline1dintegrate(c, i+v)-(i*intab+vr)))>(double)(0.001);
                ierrors = ierrors | (double)(Math.Abs(spline1d.spline1dintegrate(c, i-1000*math.machineepsilon)-i*intab))>(double)(0.001);
                ierrors = ierrors | (double)(Math.Abs(spline1d.spline1dintegrate(c, i)-i*intab))>(double)(0.001);
                ierrors = ierrors | (double)(Math.Abs(spline1d.spline1dintegrate(c, i+1000*math.machineepsilon)-i*intab))>(double)(0.001);
            }
            
            //
            // report
            //
            waserrors = ((((((((lserrors | cserrors) | crserrors) | hserrors) | aserrors) | dserrors) | cperrors) | uperrors) | lterrors) | ierrors;
            if( !silent )
            {
                System.Console.Write("TESTING SPLINE INTERPOLATION");
                System.Console.WriteLine();
                
                //
                // Normal tests
                //
                System.Console.Write("LINEAR SPLINE TEST:                      ");
                if( lserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("CUBIC SPLINE TEST:                       ");
                if( cserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("CATMULL-ROM SPLINE TEST:                 ");
                if( crserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("HERMITE SPLINE TEST:                     ");
                if( hserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("AKIMA SPLINE TEST:                       ");
                if( aserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("DIFFERENTIATION TEST:                    ");
                if( dserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("COPY/SERIALIZATION TEST:                 ");
                if( cperrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("UNPACK TEST:                             ");
                if( uperrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LIN.TRANS. TEST:                         ");
                if( lterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("INTEGRATION TEST:                        ");
                if( ierrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            
            //
            // end
            //
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Lipschitz constants for spline inself, first and second derivatives.
        *************************************************************************/
        private static void lconst(double a,
            double b,
            spline1d.spline1dinterpolant c,
            double lstep,
            ref double l0,
            ref double l1,
            ref double l2)
        {
            double t = 0;
            double vl = 0;
            double vm = 0;
            double vr = 0;
            double prevf = 0;
            double prevd = 0;
            double prevd2 = 0;
            double f = 0;
            double d = 0;
            double d2 = 0;

            l0 = 0;
            l1 = 0;
            l2 = 0;

            l0 = 0;
            l1 = 0;
            l2 = 0;
            t = a-0.1;
            vl = spline1d.spline1dcalc(c, t-2*lstep);
            vm = spline1d.spline1dcalc(c, t-lstep);
            vr = spline1d.spline1dcalc(c, t);
            f = vm;
            d = (vr-vl)/(2*lstep);
            d2 = (vr-2*vm+vl)/math.sqr(lstep);
            while( (double)(t)<=(double)(b+0.1) )
            {
                prevf = f;
                prevd = d;
                prevd2 = d2;
                vl = vm;
                vm = vr;
                vr = spline1d.spline1dcalc(c, t+lstep);
                f = vm;
                d = (vr-vl)/(2*lstep);
                d2 = (vr-2*vm+vl)/math.sqr(lstep);
                l0 = Math.Max(l0, Math.Abs((f-prevf)/lstep));
                l1 = Math.Max(l1, Math.Abs((d-prevd)/lstep));
                l2 = Math.Max(l2, Math.Abs((d2-prevd2)/lstep));
                t = t+lstep;
            }
        }


        /*************************************************************************
        Unpack testing
        *************************************************************************/
        private static bool testunpack(spline1d.spline1dinterpolant c,
            double[] x)
        {
            bool result = new bool();
            int i = 0;
            int n = 0;
            double err = 0;
            double t = 0;
            double v1 = 0;
            double v2 = 0;
            int pass = 0;
            int passcount = 0;
            double[,] tbl = new double[0,0];

            passcount = 20;
            err = 0;
            spline1d.spline1dunpack(c, ref n, ref tbl);
            for(i=0; i<=n-2; i++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    t = math.randomreal()*(tbl[i,1]-tbl[i,0]);
                    v1 = tbl[i,2]+t*tbl[i,3]+math.sqr(t)*tbl[i,4]+t*math.sqr(t)*tbl[i,5];
                    v2 = spline1d.spline1dcalc(c, tbl[i,0]+t);
                    err = Math.Max(err, Math.Abs(v1-v2));
                }
            }
            for(i=0; i<=n-2; i++)
            {
                err = Math.Max(err, Math.Abs(x[i]-tbl[i,0]));
            }
            for(i=0; i<=n-2; i++)
            {
                err = Math.Max(err, Math.Abs(x[i+1]-tbl[i,1]));
            }
            result = (double)(err)<(double)(100*math.machineepsilon);
            return result;
        }


        /*************************************************************************
        Unset spline, i.e. initialize it with random garbage
        *************************************************************************/
        private static void unsetspline1d(spline1d.spline1dinterpolant c)
        {
            double[] x = new double[0];
            double[] y = new double[0];
            double[] d = new double[0];

            x = new double[2];
            y = new double[2];
            d = new double[2];
            x[0] = -1;
            y[0] = math.randomreal();
            d[0] = math.randomreal();
            x[1] = 1;
            y[1] = math.randomreal();
            d[1] = math.randomreal();
            spline1d.spline1dbuildhermite(x, y, d, 2, c);
        }


        /*************************************************************************
        Unsets real vector
        *************************************************************************/
        private static void unset1d(ref double[] x)
        {
            x = new double[1];
            x[0] = 2*math.randomreal()-1;
        }


        /*************************************************************************
        Tests whether constant C is solution of 1D LLS problem
        *************************************************************************/
        private static bool is1dsolution(int n,
            double[] y,
            double[] w,
            double c)
        {
            bool result = new bool();
            int i = 0;
            double s1 = 0;
            double s2 = 0;
            double s3 = 0;
            double delta = 0;

            delta = 0.001;
            
            //
            // Test result
            //
            s1 = 0;
            for(i=0; i<=n-1; i++)
            {
                s1 = s1+math.sqr(w[i]*(c-y[i]));
            }
            s2 = 0;
            s3 = 0;
            for(i=0; i<=n-1; i++)
            {
                s2 = s2+math.sqr(w[i]*(c+delta-y[i]));
                s3 = s3+math.sqr(w[i]*(c-delta-y[i]));
            }
            result = (double)(s2)>=(double)(s1) & (double)(s3)>=(double)(s1);
            return result;
        }


    }
    public class testminlmunit
    {
        public static bool testminlm(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool referror = new bool();
            bool lin1error = new bool();
            bool lin2error = new bool();
            bool eqerror = new bool();
            bool converror = new bool();
            bool scerror = new bool();
            bool restartserror = new bool();
            bool othererrors = new bool();
            int rkind = 0;
            int ckind = 0;
            int tmpkind = 0;
            double epsf = 0;
            double epsx = 0;
            double epsg = 0;
            int maxits = 0;
            int n = 0;
            int m = 0;
            double[] x = new double[0];
            double[] xe = new double[0];
            double[] b = new double[0];
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] xlast = new double[0];
            int i = 0;
            int j = 0;
            double v = 0;
            double s = 0;
            double stpmax = 0;
            double h = 0;
            double[,] a = new double[0,0];
            double fprev = 0;
            double xprev = 0;
            minlm.minlmstate state = new minlm.minlmstate();
            minlm.minlmreport rep = new minlm.minlmreport();
            int i_ = 0;

            waserrors = false;
            referror = false;
            lin1error = false;
            lin2error = false;
            eqerror = false;
            converror = false;
            scerror = false;
            othererrors = false;
            restartserror = false;
            
            //
            // Reference problem.
            // See comments for RKindVsStateCheck() for more info about RKind.
            //
            // NOTES: we also test negative RKind's corresponding to "inexact" schemes
            // which use approximate finite difference Jacobian.
            //
            x = new double[3];
            n = 3;
            m = 3;
            h = 0.0001;
            for(rkind=-2; rkind<=5; rkind++)
            {
                x[0] = 100*math.randomreal()-50;
                x[1] = 100*math.randomreal()-50;
                x[2] = 100*math.randomreal()-50;
                if( rkind==-2 )
                {
                    minlm.minlmcreatev(n, m, x, h, state);
                    minlm.minlmsetacctype(state, 1);
                }
                if( rkind==-1 )
                {
                    minlm.minlmcreatev(n, m, x, h, state);
                    minlm.minlmsetacctype(state, 0);
                }
                if( rkind==0 )
                {
                    minlm.minlmcreatefj(n, m, x, state);
                }
                if( rkind==1 )
                {
                    minlm.minlmcreatefgj(n, m, x, state);
                }
                if( rkind==2 )
                {
                    minlm.minlmcreatefgh(n, x, state);
                }
                if( rkind==3 )
                {
                    minlm.minlmcreatevj(n, m, x, state);
                    minlm.minlmsetacctype(state, 0);
                }
                if( rkind==4 )
                {
                    minlm.minlmcreatevj(n, m, x, state);
                    minlm.minlmsetacctype(state, 1);
                }
                if( rkind==5 )
                {
                    minlm.minlmcreatevj(n, m, x, state);
                    minlm.minlmsetacctype(state, 2);
                }
                while( minlm.minlmiteration(state) )
                {
                    
                    //
                    // (x-2)^2 + y^2 + (z-x)^2
                    //
                    if( state.needfi )
                    {
                        state.fi[0] = state.x[0]-2;
                        state.fi[1] = state.x[1];
                        state.fi[2] = state.x[2]-state.x[0];
                    }
                    if( state.needfij )
                    {
                        state.fi[0] = state.x[0]-2;
                        state.fi[1] = state.x[1];
                        state.fi[2] = state.x[2]-state.x[0];
                        state.j[0,0] = 1;
                        state.j[0,1] = 0;
                        state.j[0,2] = 0;
                        state.j[1,0] = 0;
                        state.j[1,1] = 1;
                        state.j[1,2] = 0;
                        state.j[2,0] = -1;
                        state.j[2,1] = 0;
                        state.j[2,2] = 1;
                    }
                    if( (state.needf | state.needfg) | state.needfgh )
                    {
                        state.f = math.sqr(state.x[0]-2)+math.sqr(state.x[1])+math.sqr(state.x[2]-state.x[0]);
                    }
                    if( state.needfg | state.needfgh )
                    {
                        state.g[0] = 2*(state.x[0]-2)+2*(state.x[0]-state.x[2]);
                        state.g[1] = 2*state.x[1];
                        state.g[2] = 2*(state.x[2]-state.x[0]);
                    }
                    if( state.needfgh )
                    {
                        state.h[0,0] = 4;
                        state.h[0,1] = 0;
                        state.h[0,2] = -2;
                        state.h[1,0] = 0;
                        state.h[1,1] = 2;
                        state.h[1,2] = 0;
                        state.h[2,0] = -2;
                        state.h[2,1] = 0;
                        state.h[2,2] = 2;
                    }
                    scerror = scerror | !rkindvsstatecheck(rkind, state);
                }
                minlm.minlmresults(state, ref x, rep);
                referror = (((referror | rep.terminationtype<=0) | (double)(Math.Abs(x[0]-2))>(double)(0.001)) | (double)(Math.Abs(x[1]))>(double)(0.001)) | (double)(Math.Abs(x[2]-2))>(double)(0.001);
            }
            
            //
            // Reference bound constrained problem:
            //
            //     min sum((x[i]-xe[i])^4) subject to 0<=x[i]<=1
            //
            // NOTES:
            // 1. we test only two optimization modes - V and FGH,
            //    because from algorithm internals we can assume that actual
            //    mode being used doesn't matter for bound constrained optimization
            //    process.
            //
            for(tmpkind=0; tmpkind<=1; tmpkind++)
            {
                for(n=1; n<=5; n++)
                {
                    bl = new double[n];
                    bu = new double[n];
                    xe = new double[n];
                    x = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        bl[i] = 0;
                        bu[i] = 1;
                        xe[i] = 3*math.randomreal()-1;
                        x[i] = math.randomreal();
                    }
                    if( tmpkind==0 )
                    {
                        minlm.minlmcreatefgh(n, x, state);
                    }
                    if( tmpkind==1 )
                    {
                        minlm.minlmcreatev(n, n, x, 1.0E-3, state);
                    }
                    minlm.minlmsetcond(state, 1.0E-6, 0, 0, 0);
                    minlm.minlmsetbc(state, bl, bu);
                    while( minlm.minlmiteration(state) )
                    {
                        if( state.needfi )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[i] = Math.Pow(state.x[i]-xe[i], 2);
                            }
                        }
                        if( (state.needf | state.needfg) | state.needfgh )
                        {
                            state.f = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+Math.Pow(state.x[i]-xe[i], 4);
                            }
                        }
                        if( state.needfg | state.needfgh )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                state.g[i] = 4*Math.Pow(state.x[i]-xe[i], 3);
                            }
                        }
                        if( state.needfgh )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    state.h[i,j] = 0;
                                }
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                state.h[i,i] = 12*Math.Pow(state.x[i]-xe[i], 2);
                            }
                        }
                    }
                    minlm.minlmresults(state, ref x, rep);
                    if( rep.terminationtype==4 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            referror = referror | (double)(Math.Abs(x[i]-apserv.boundval(xe[i], bl[i], bu[i])))>(double)(5.0E-2);
                        }
                    }
                    else
                    {
                        referror = true;
                    }
                }
            }
            
            //
            // 1D problem #1
            //
            // NOTES: we also test negative RKind's corresponding to "inexact" schemes
            // which use approximate finite difference Jacobian.
            //
            for(rkind=-2; rkind<=5; rkind++)
            {
                x = new double[1];
                n = 1;
                m = 1;
                h = 0.00001;
                x[0] = 100*math.randomreal()-50;
                if( rkind==-2 )
                {
                    minlm.minlmcreatev(n, m, x, h, state);
                    minlm.minlmsetacctype(state, 1);
                }
                if( rkind==-1 )
                {
                    minlm.minlmcreatev(n, m, x, h, state);
                    minlm.minlmsetacctype(state, 0);
                }
                if( rkind==0 )
                {
                    minlm.minlmcreatefj(n, m, x, state);
                }
                if( rkind==1 )
                {
                    minlm.minlmcreatefgj(n, m, x, state);
                }
                if( rkind==2 )
                {
                    minlm.minlmcreatefgh(n, x, state);
                }
                if( rkind==3 )
                {
                    minlm.minlmcreatevj(n, m, x, state);
                    minlm.minlmsetacctype(state, 0);
                }
                if( rkind==4 )
                {
                    minlm.minlmcreatevj(n, m, x, state);
                    minlm.minlmsetacctype(state, 1);
                }
                if( rkind==5 )
                {
                    minlm.minlmcreatevj(n, m, x, state);
                    minlm.minlmsetacctype(state, 2);
                }
                while( minlm.minlmiteration(state) )
                {
                    if( state.needfi )
                    {
                        state.fi[0] = Math.Sin(state.x[0]);
                    }
                    if( state.needfij )
                    {
                        state.fi[0] = Math.Sin(state.x[0]);
                        state.j[0,0] = Math.Cos(state.x[0]);
                    }
                    if( (state.needf | state.needfg) | state.needfgh )
                    {
                        state.f = math.sqr(Math.Sin(state.x[0]));
                    }
                    if( state.needfg | state.needfgh )
                    {
                        state.g[0] = 2*Math.Sin(state.x[0])*Math.Cos(state.x[0]);
                    }
                    if( state.needfgh )
                    {
                        state.h[0,0] = 2*(Math.Cos(state.x[0])*Math.Cos(state.x[0])-Math.Sin(state.x[0])*Math.Sin(state.x[0]));
                    }
                    scerror = scerror | !rkindvsstatecheck(rkind, state);
                }
                minlm.minlmresults(state, ref x, rep);
                lin1error = rep.terminationtype<=0 | (double)(Math.Abs(x[0]/Math.PI-(int)Math.Round(x[0]/Math.PI)))>(double)(0.001);
            }
            
            //
            // Linear equations: test normal optimization and optimization with restarts
            //
            for(n=1; n<=10; n++)
            {
                
                //
                // Prepare task
                //
                h = 0.00001;
                matgen.rmatrixrndcond(n, 100, ref a);
                x = new double[n];
                xe = new double[n];
                b = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    xe[i] = 2*math.randomreal()-1;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += a[i,i_]*xe[i_];
                    }
                    b[i] = v;
                }
                
                //
                // Test different RKind
                //
                // NOTES: we also test negative RKind's corresponding to "inexact" schemes
                // which use approximate finite difference Jacobian.
                //
                for(rkind=-2; rkind<=5; rkind++)
                {
                    
                    //
                    // Solve task (first attempt)
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    if( rkind==-2 )
                    {
                        minlm.minlmcreatev(n, n, x, h, state);
                        minlm.minlmsetacctype(state, 1);
                    }
                    if( rkind==-1 )
                    {
                        minlm.minlmcreatev(n, n, x, h, state);
                        minlm.minlmsetacctype(state, 0);
                    }
                    if( rkind==0 )
                    {
                        minlm.minlmcreatefj(n, n, x, state);
                    }
                    if( rkind==1 )
                    {
                        minlm.minlmcreatefgj(n, n, x, state);
                    }
                    if( rkind==2 )
                    {
                        minlm.minlmcreatefgh(n, x, state);
                    }
                    if( rkind==3 )
                    {
                        minlm.minlmcreatevj(n, n, x, state);
                        minlm.minlmsetacctype(state, 0);
                    }
                    if( rkind==4 )
                    {
                        minlm.minlmcreatevj(n, n, x, state);
                        minlm.minlmsetacctype(state, 1);
                    }
                    if( rkind==5 )
                    {
                        minlm.minlmcreatevj(n, n, x, state);
                        minlm.minlmsetacctype(state, 2);
                    }
                    while( minlm.minlmiteration(state) )
                    {
                        axmb(state, a, b, n);
                        scerror = scerror | !rkindvsstatecheck(rkind, state);
                    }
                    minlm.minlmresults(state, ref x, rep);
                    eqerror = eqerror | rep.terminationtype<=0;
                    for(i=0; i<=n-1; i++)
                    {
                        eqerror = eqerror | (double)(Math.Abs(x[i]-xe[i]))>(double)(0.001);
                    }
                    
                    //
                    // Now we try to restart algorithm from new point
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    minlm.minlmrestartfrom(state, x);
                    while( minlm.minlmiteration(state) )
                    {
                        axmb(state, a, b, n);
                        scerror = scerror | !rkindvsstatecheck(rkind, state);
                    }
                    minlm.minlmresults(state, ref x, rep);
                    restartserror = restartserror | rep.terminationtype<=0;
                    for(i=0; i<=n-1; i++)
                    {
                        restartserror = restartserror | (double)(Math.Abs(x[i]-xe[i]))>(double)(0.001);
                    }
                }
            }
            
            //
            // Testing convergence properties using
            // different optimizer types and different conditions.
            //
            // Only limited subset of optimizers is tested because some
            // optimizers converge too quickly.
            //
            s = 100;
            for(rkind=0; rkind<=5; rkind++)
            {
                
                //
                // Skip FGH optimizer - it converges too quickly
                //
                if( rkind==2 )
                {
                    continue;
                }
                
                //
                // Test
                //
                for(ckind=0; ckind<=3; ckind++)
                {
                    epsg = 0;
                    epsf = 0;
                    epsx = 0;
                    maxits = 0;
                    if( ckind==0 )
                    {
                        epsf = 0.000001;
                    }
                    if( ckind==1 )
                    {
                        epsx = 0.000001;
                    }
                    if( ckind==2 )
                    {
                        maxits = 2;
                    }
                    if( ckind==3 )
                    {
                        epsg = 0.0001;
                    }
                    x = new double[3];
                    n = 3;
                    m = 3;
                    for(i=0; i<=2; i++)
                    {
                        x[i] = 6;
                    }
                    if( rkind==0 )
                    {
                        minlm.minlmcreatefj(n, m, x, state);
                    }
                    if( rkind==1 )
                    {
                        minlm.minlmcreatefgj(n, m, x, state);
                    }
                    ap.assert(rkind!=2);
                    if( rkind==3 )
                    {
                        minlm.minlmcreatevj(n, m, x, state);
                        minlm.minlmsetacctype(state, 0);
                    }
                    if( rkind==4 )
                    {
                        minlm.minlmcreatevj(n, m, x, state);
                        minlm.minlmsetacctype(state, 1);
                    }
                    if( rkind==5 )
                    {
                        minlm.minlmcreatevj(n, m, x, state);
                        minlm.minlmsetacctype(state, 2);
                    }
                    minlm.minlmsetcond(state, epsg, epsf, epsx, maxits);
                    while( minlm.minlmiteration(state) )
                    {
                        if( state.needfi | state.needfij )
                        {
                            state.fi[0] = s*(Math.Exp(state.x[0])-2);
                            state.fi[1] = math.sqr(state.x[1])+1;
                            state.fi[2] = state.x[2]-state.x[0];
                        }
                        if( state.needfij )
                        {
                            state.j[0,0] = s*Math.Exp(state.x[0]);
                            state.j[0,1] = 0;
                            state.j[0,2] = 0;
                            state.j[1,0] = 0;
                            state.j[1,1] = 2*state.x[1];
                            state.j[1,2] = 0;
                            state.j[2,0] = -1;
                            state.j[2,1] = 0;
                            state.j[2,2] = 1;
                        }
                        if( (state.needf | state.needfg) | state.needfgh )
                        {
                            state.f = s*math.sqr(Math.Exp(state.x[0])-2)+math.sqr(math.sqr(state.x[1])+1)+math.sqr(state.x[2]-state.x[0]);
                        }
                        if( state.needfg | state.needfgh )
                        {
                            state.g[0] = s*2*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[0]-state.x[2]);
                            state.g[1] = 2*(math.sqr(state.x[1])+1)*2*state.x[1];
                            state.g[2] = 2*(state.x[2]-state.x[0]);
                        }
                        if( state.needfgh )
                        {
                            state.h[0,0] = s*(4*math.sqr(Math.Exp(state.x[0]))-4*Math.Exp(state.x[0]))+2;
                            state.h[0,1] = 0;
                            state.h[0,2] = -2;
                            state.h[1,0] = 0;
                            state.h[1,1] = 12*math.sqr(state.x[1])+4;
                            state.h[1,2] = 0;
                            state.h[2,0] = -2;
                            state.h[2,1] = 0;
                            state.h[2,2] = 2;
                        }
                        scerror = scerror | !rkindvsstatecheck(rkind, state);
                    }
                    minlm.minlmresults(state, ref x, rep);
                    if( ckind==0 )
                    {
                        converror = converror | (double)(Math.Abs(x[0]-Math.Log(2)))>(double)(0.05);
                        converror = converror | (double)(Math.Abs(x[1]))>(double)(0.05);
                        converror = converror | (double)(Math.Abs(x[2]-Math.Log(2)))>(double)(0.05);
                        converror = converror | rep.terminationtype!=1;
                    }
                    if( ckind==1 )
                    {
                        converror = converror | (double)(Math.Abs(x[0]-Math.Log(2)))>(double)(0.05);
                        converror = converror | (double)(Math.Abs(x[1]))>(double)(0.05);
                        converror = converror | (double)(Math.Abs(x[2]-Math.Log(2)))>(double)(0.05);
                        converror = converror | rep.terminationtype!=2;
                    }
                    if( ckind==2 )
                    {
                        converror = (converror | rep.terminationtype!=5) | rep.iterationscount!=maxits;
                    }
                    if( ckind==3 )
                    {
                        converror = converror | (double)(Math.Abs(x[0]-Math.Log(2)))>(double)(0.05);
                        converror = converror | (double)(Math.Abs(x[1]))>(double)(0.05);
                        converror = converror | (double)(Math.Abs(x[2]-Math.Log(2)))>(double)(0.05);
                        converror = converror | rep.terminationtype!=4;
                    }
                }
            }
            
            //
            // Other properties:
            // 1. test reports (F should form monotone sequence)
            // 2. test maximum step
            //
            for(rkind=0; rkind<=5; rkind++)
            {
                
                //
                // reports:
                // * check that first report is initial point
                // * check that F is monotone decreasing
                // * check that last report is final result
                //
                n = 3;
                m = 3;
                s = 100;
                x = new double[n];
                xlast = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 6;
                }
                if( rkind==0 )
                {
                    minlm.minlmcreatefj(n, m, x, state);
                }
                if( rkind==1 )
                {
                    minlm.minlmcreatefgj(n, m, x, state);
                }
                if( rkind==2 )
                {
                    minlm.minlmcreatefgh(n, x, state);
                }
                if( rkind==3 )
                {
                    minlm.minlmcreatevj(n, m, x, state);
                    minlm.minlmsetacctype(state, 0);
                }
                if( rkind==4 )
                {
                    minlm.minlmcreatevj(n, m, x, state);
                    minlm.minlmsetacctype(state, 1);
                }
                if( rkind==5 )
                {
                    minlm.minlmcreatevj(n, m, x, state);
                    minlm.minlmsetacctype(state, 2);
                }
                minlm.minlmsetcond(state, 0, 0, 0, 4);
                minlm.minlmsetxrep(state, true);
                fprev = math.maxrealnumber;
                while( minlm.minlmiteration(state) )
                {
                    if( state.needfi | state.needfij )
                    {
                        state.fi[0] = Math.Sqrt(s)*(Math.Exp(state.x[0])-2);
                        state.fi[1] = state.x[1];
                        state.fi[2] = state.x[2]-state.x[0];
                    }
                    if( state.needfij )
                    {
                        state.j[0,0] = Math.Sqrt(s)*Math.Exp(state.x[0]);
                        state.j[0,1] = 0;
                        state.j[0,2] = 0;
                        state.j[1,0] = 0;
                        state.j[1,1] = 1;
                        state.j[1,2] = 0;
                        state.j[2,0] = -1;
                        state.j[2,1] = 0;
                        state.j[2,2] = 1;
                    }
                    if( (state.needf | state.needfg) | state.needfgh )
                    {
                        state.f = s*math.sqr(Math.Exp(state.x[0])-2)+math.sqr(state.x[1])+math.sqr(state.x[2]-state.x[0]);
                    }
                    if( state.needfg | state.needfgh )
                    {
                        state.g[0] = s*2*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[0]-state.x[2]);
                        state.g[1] = 2*state.x[1];
                        state.g[2] = 2*(state.x[2]-state.x[0]);
                    }
                    if( state.needfgh )
                    {
                        state.h[0,0] = s*(4*math.sqr(Math.Exp(state.x[0]))-4*Math.Exp(state.x[0]))+2;
                        state.h[0,1] = 0;
                        state.h[0,2] = -2;
                        state.h[1,0] = 0;
                        state.h[1,1] = 2;
                        state.h[1,2] = 0;
                        state.h[2,0] = -2;
                        state.h[2,1] = 0;
                        state.h[2,2] = 2;
                    }
                    scerror = scerror | !rkindvsstatecheck(rkind, state);
                    if( state.xupdated )
                    {
                        othererrors = othererrors | (double)(state.f)>(double)(fprev);
                        if( (double)(fprev)==(double)(math.maxrealnumber) )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                othererrors = othererrors | (double)(state.x[i])!=(double)(x[i]);
                            }
                        }
                        fprev = state.f;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            xlast[i_] = state.x[i_];
                        }
                    }
                }
                minlm.minlmresults(state, ref x, rep);
                for(i=0; i<=n-1; i++)
                {
                    othererrors = othererrors | (double)(x[i])!=(double)(xlast[i]);
                }
            }
            n = 1;
            x = new double[n];
            x[0] = 100;
            stpmax = 0.05+0.05*math.randomreal();
            minlm.minlmcreatefgh(n, x, state);
            minlm.minlmsetcond(state, 1.0E-9, 0, 0, 0);
            minlm.minlmsetstpmax(state, stpmax);
            minlm.minlmsetxrep(state, true);
            xprev = x[0];
            while( minlm.minlmiteration(state) )
            {
                if( (state.needf | state.needfg) | state.needfgh )
                {
                    state.f = Math.Exp(state.x[0])+Math.Exp(-state.x[0]);
                }
                if( state.needfg | state.needfgh )
                {
                    state.g[0] = Math.Exp(state.x[0])-Math.Exp(-state.x[0]);
                }
                if( state.needfgh )
                {
                    state.h[0,0] = Math.Exp(state.x[0])+Math.Exp(-state.x[0]);
                }
                othererrors = othererrors | (double)(Math.Abs(state.x[0]-xprev))>(double)((1+Math.Sqrt(math.machineepsilon))*stpmax);
                if( state.xupdated )
                {
                    xprev = state.x[0];
                }
            }
            
            //
            // end
            //
            waserrors = ((((((referror | lin1error) | lin2error) | eqerror) | converror) | scerror) | othererrors) | restartserror;
            if( !silent )
            {
                System.Console.Write("TESTING LEVENBERG-MARQUARDT OPTIMIZATION");
                System.Console.WriteLine();
                System.Console.Write("REFERENCE PROBLEMS:                       ");
                if( referror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("1-D PROBLEM #1:                           ");
                if( lin1error )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("1-D PROBLEM #2:                           ");
                if( lin2error )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LINEAR EQUATIONS:                         ");
                if( eqerror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("RESTARTS:                                 ");
                if( restartserror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("CONVERGENCE PROPERTIES:                   ");
                if( converror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("STATE FIELDS CONSISTENCY:                 ");
                if( scerror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("OTHER PROPERTIES:                         ");
                if( othererrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Asserts that State fields are consistent with RKind.
        Returns False otherwise.

        RKind is an algorithm selector:
        * -2 = V, AccType=1
        * -1 = V, AccType=0
        *  0 = FJ
        *  1 = FGJ
        *  2 = FGH
        *  3 = VJ, AccType=0
        *  4 = VJ, AccType=1
        *  5 = VJ, AccType=2

        *************************************************************************/
        private static bool rkindvsstatecheck(int rkind,
            minlm.minlmstate state)
        {
            bool result = new bool();
            int nset = 0;

            nset = 0;
            if( state.needfi )
            {
                nset = nset+1;
            }
            if( state.needf )
            {
                nset = nset+1;
            }
            if( state.needfg )
            {
                nset = nset+1;
            }
            if( state.needfij )
            {
                nset = nset+1;
            }
            if( state.needfgh )
            {
                nset = nset+1;
            }
            if( state.xupdated )
            {
                nset = nset+1;
            }
            if( nset!=1 )
            {
                result = false;
                return result;
            }
            if( rkind==-2 )
            {
                result = state.needfi | state.xupdated;
                return result;
            }
            if( rkind==-1 )
            {
                result = state.needfi | state.xupdated;
                return result;
            }
            if( rkind==0 )
            {
                result = (state.needf | state.needfij) | state.xupdated;
                return result;
            }
            if( rkind==1 )
            {
                result = ((state.needf | state.needfij) | state.needfg) | state.xupdated;
                return result;
            }
            if( rkind==2 )
            {
                result = ((state.needf | state.needfg) | state.needfgh) | state.xupdated;
                return result;
            }
            if( rkind==3 )
            {
                result = (state.needfi | state.needfij) | state.xupdated;
                return result;
            }
            if( rkind==4 )
            {
                result = (state.needfi | state.needfij) | state.xupdated;
                return result;
            }
            if( rkind==5 )
            {
                result = (state.needfi | state.needfij) | state.xupdated;
                return result;
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Calculates FI/F/G/H for problem min(||Ax-b||)
        *************************************************************************/
        private static void axmb(minlm.minlmstate state,
            double[,] a,
            double[] b,
            int n)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            int i_ = 0;

            if( (state.needf | state.needfg) | state.needfgh )
            {
                state.f = 0;
            }
            if( state.needfg | state.needfgh )
            {
                for(i=0; i<=n-1; i++)
                {
                    state.g[i] = 0;
                }
            }
            if( state.needfgh )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        state.h[i,j] = 0;
                    }
                }
            }
            for(i=0; i<=n-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=n-1;i_++)
                {
                    v += a[i,i_]*state.x[i_];
                }
                if( (state.needf | state.needfg) | state.needfgh )
                {
                    state.f = state.f+math.sqr(v-b[i]);
                }
                if( state.needfg | state.needfgh )
                {
                    for(j=0; j<=n-1; j++)
                    {
                        state.g[j] = state.g[j]+2*(v-b[i])*a[i,j];
                    }
                }
                if( state.needfgh )
                {
                    for(j=0; j<=n-1; j++)
                    {
                        for(k=0; k<=n-1; k++)
                        {
                            state.h[j,k] = state.h[j,k]+2*a[i,j]*a[i,k];
                        }
                    }
                }
                if( state.needfi )
                {
                    state.fi[i] = v-b[i];
                }
                if( state.needfij )
                {
                    state.fi[i] = v-b[i];
                    for(i_=0; i_<=n-1;i_++)
                    {
                        state.j[i,i_] = a[i,i_];
                    }
                }
            }
        }


    }
    public class testlsfitunit
    {
        public static bool testlsfit(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool llserrors = new bool();
            bool nlserrors = new bool();
            bool polfiterrors = new bool();
            bool ratfiterrors = new bool();
            bool splfiterrors = new bool();

            waserrors = false;
            testpolynomialfitting(ref polfiterrors);
            testrationalfitting(ref ratfiterrors);
            testsplinefitting(ref splfiterrors);
            testgeneralfitting(ref llserrors, ref nlserrors);
            
            //
            // report
            //
            waserrors = (((llserrors | nlserrors) | polfiterrors) | ratfiterrors) | splfiterrors;
            if( !silent )
            {
                System.Console.Write("TESTING LEAST SQUARES");
                System.Console.WriteLine();
                System.Console.Write("POLYNOMIAL LEAST SQUARES:                ");
                if( polfiterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("RATIONAL LEAST SQUARES:                  ");
                if( ratfiterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("SPLINE LEAST SQUARES:                    ");
                if( splfiterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LINEAR LEAST SQUARES:                    ");
                if( llserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("NON-LINEAR LEAST SQUARES:                ");
                if( nlserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            
            //
            // end
            //
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Unit test
        *************************************************************************/
        private static void testpolynomialfitting(ref bool fiterrors)
        {
            double threshold = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] w = new double[0];
            double[] x2 = new double[0];
            double[] y2 = new double[0];
            double[] w2 = new double[0];
            double[] xfull = new double[0];
            double[] yfull = new double[0];
            double t = 0;
            int i = 0;
            int k = 0;
            double[] xc = new double[0];
            double[] yc = new double[0];
            int[] dc = new int[0];
            int info = 0;
            int info2 = 0;
            double v = 0;
            double v0 = 0;
            double v1 = 0;
            double v2 = 0;
            double s = 0;
            double xmin = 0;
            double xmax = 0;
            double refrms = 0;
            double refavg = 0;
            double refavgrel = 0;
            double refmax = 0;
            ratint.barycentricinterpolant p = new ratint.barycentricinterpolant();
            ratint.barycentricinterpolant p1 = new ratint.barycentricinterpolant();
            ratint.barycentricinterpolant p2 = new ratint.barycentricinterpolant();
            lsfit.polynomialfitreport rep = new lsfit.polynomialfitreport();
            lsfit.polynomialfitreport rep2 = new lsfit.polynomialfitreport();
            int n = 0;
            int m = 0;
            int maxn = 0;
            int pass = 0;
            int passcount = 0;

            fiterrors = false;
            maxn = 5;
            passcount = 20;
            threshold = 1.0E8*math.machineepsilon;
            
            //
            // Test polunomial fitting
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // N=M+K fitting (i.e. interpolation)
                    //
                    for(k=0; k<=n-1; k++)
                    {
                        apserv.taskgenint1d(-1, 1, n, ref xfull, ref yfull);
                        x = new double[n-k];
                        y = new double[n-k];
                        w = new double[n-k];
                        if( k>0 )
                        {
                            xc = new double[k];
                            yc = new double[k];
                            dc = new int[k];
                        }
                        for(i=0; i<=n-k-1; i++)
                        {
                            x[i] = xfull[i];
                            y[i] = yfull[i];
                            w[i] = 1+math.randomreal();
                        }
                        for(i=0; i<=k-1; i++)
                        {
                            xc[i] = xfull[n-k+i];
                            yc[i] = yfull[n-k+i];
                            dc[i] = 0;
                        }
                        lsfit.polynomialfitwc(x, y, w, n-k, xc, yc, dc, k, n, ref info, p1, rep);
                        if( info<=0 )
                        {
                            fiterrors = true;
                        }
                        else
                        {
                            for(i=0; i<=n-k-1; i++)
                            {
                                fiterrors = fiterrors | (double)(Math.Abs(ratint.barycentriccalc(p1, x[i])-y[i]))>(double)(threshold);
                            }
                            for(i=0; i<=k-1; i++)
                            {
                                fiterrors = fiterrors | (double)(Math.Abs(ratint.barycentriccalc(p1, xc[i])-yc[i]))>(double)(threshold);
                            }
                        }
                    }
                    
                    //
                    // Testing constraints on derivatives.
                    // Special tasks which will always have solution:
                    // 1. P(0)=YC[0]
                    // 2. P(0)=YC[0], P'(0)=YC[1]
                    //
                    if( n>1 )
                    {
                        for(m=3; m<=5; m++)
                        {
                            for(k=1; k<=2; k++)
                            {
                                apserv.taskgenint1d(-1, 1, n, ref x, ref y);
                                w = new double[n];
                                xc = new double[2];
                                yc = new double[2];
                                dc = new int[2];
                                for(i=0; i<=n-1; i++)
                                {
                                    w[i] = 1+math.randomreal();
                                }
                                xc[0] = 0;
                                yc[0] = 2*math.randomreal()-1;
                                dc[0] = 0;
                                xc[1] = 0;
                                yc[1] = 2*math.randomreal()-1;
                                dc[1] = 1;
                                lsfit.polynomialfitwc(x, y, w, n, xc, yc, dc, k, m, ref info, p1, rep);
                                if( info<=0 )
                                {
                                    fiterrors = true;
                                }
                                else
                                {
                                    ratint.barycentricdiff1(p1, 0.0, ref v0, ref v1);
                                    fiterrors = fiterrors | (double)(Math.Abs(v0-yc[0]))>(double)(threshold);
                                    if( k==2 )
                                    {
                                        fiterrors = fiterrors | (double)(Math.Abs(v1-yc[1]))>(double)(threshold);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            for(m=2; m<=8; m++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // General fitting
                    //
                    // interpolating function through M nodes should have
                    // greater RMS error than fitting it through the same M nodes
                    //
                    n = 100;
                    x2 = new double[n];
                    y2 = new double[n];
                    w2 = new double[n];
                    xmin = 0;
                    xmax = 2*Math.PI;
                    for(i=0; i<=n-1; i++)
                    {
                        x2[i] = 2*Math.PI*math.randomreal();
                        y2[i] = Math.Sin(x2[i]);
                        w2[i] = 1;
                    }
                    x = new double[m];
                    y = new double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        x[i] = xmin+(xmax-xmin)*i/(m-1);
                        y[i] = Math.Sin(x[i]);
                    }
                    polint.polynomialbuild(x, y, m, p1);
                    lsfit.polynomialfitwc(x2, y2, w2, n, xc, yc, dc, 0, m, ref info, p2, rep);
                    if( info<=0 )
                    {
                        fiterrors = true;
                    }
                    else
                    {
                        
                        //
                        // calculate P1 (interpolant) RMS error, compare with P2 error
                        //
                        v1 = 0;
                        v2 = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            v1 = v1+math.sqr(ratint.barycentriccalc(p1, x2[i])-y2[i]);
                            v2 = v2+math.sqr(ratint.barycentriccalc(p2, x2[i])-y2[i]);
                        }
                        v1 = Math.Sqrt(v1/n);
                        v2 = Math.Sqrt(v2/n);
                        fiterrors = fiterrors | (double)(v2)>(double)(v1);
                        fiterrors = fiterrors | (double)(Math.Abs(v2-rep.rmserror))>(double)(threshold);
                    }
                    
                    //
                    // compare weighted and non-weighted
                    //
                    n = 20;
                    x = new double[n];
                    y = new double[n];
                    w = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                        y[i] = 2*math.randomreal()-1;
                        w[i] = 1;
                    }
                    lsfit.polynomialfitwc(x, y, w, n, xc, yc, dc, 0, m, ref info, p1, rep);
                    lsfit.polynomialfit(x, y, n, m, ref info2, p2, rep2);
                    if( info<=0 | info2<=0 )
                    {
                        fiterrors = true;
                    }
                    else
                    {
                        
                        //
                        // calculate P1 (interpolant), compare with P2 error
                        // compare RMS errors
                        //
                        t = 2*math.randomreal()-1;
                        v1 = ratint.barycentriccalc(p1, t);
                        v2 = ratint.barycentriccalc(p2, t);
                        fiterrors = fiterrors | (double)(v2)!=(double)(v1);
                        fiterrors = fiterrors | (double)(rep.rmserror)!=(double)(rep2.rmserror);
                        fiterrors = fiterrors | (double)(rep.avgerror)!=(double)(rep2.avgerror);
                        fiterrors = fiterrors | (double)(rep.avgrelerror)!=(double)(rep2.avgrelerror);
                        fiterrors = fiterrors | (double)(rep.maxerror)!=(double)(rep2.maxerror);
                    }
                }
            }
            for(m=1; m<=maxn; m++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    ap.assert(passcount>=2, "PassCount should be 2 or greater!");
                    
                    //
                    // solve simple task (all X[] are the same, Y[] are specially
                    // calculated to ensure simple form of all types of errors)
                    // and check correctness of the errors calculated by subroutines
                    //
                    // First pass is done with zero Y[], other passes - with random Y[].
                    // It should test both ability to correctly calculate errors and
                    // ability to not fail while working with zeros :)
                    //
                    n = 4*maxn;
                    if( pass==1 )
                    {
                        v1 = 0;
                        v2 = 0;
                        v = 0;
                    }
                    else
                    {
                        v1 = math.randomreal();
                        v2 = math.randomreal();
                        v = 1+math.randomreal();
                    }
                    x = new double[n];
                    y = new double[n];
                    w = new double[n];
                    for(i=0; i<=maxn-1; i++)
                    {
                        x[4*i+0] = i;
                        y[4*i+0] = v-v2;
                        w[4*i+0] = 1;
                        x[4*i+1] = i;
                        y[4*i+1] = v-v1;
                        w[4*i+1] = 1;
                        x[4*i+2] = i;
                        y[4*i+2] = v+v1;
                        w[4*i+2] = 1;
                        x[4*i+3] = i;
                        y[4*i+3] = v+v2;
                        w[4*i+3] = 1;
                    }
                    refrms = Math.Sqrt((math.sqr(v1)+math.sqr(v2))/2);
                    refavg = (Math.Abs(v1)+Math.Abs(v2))/2;
                    if( pass==1 )
                    {
                        refavgrel = 0;
                    }
                    else
                    {
                        refavgrel = 0.25*(Math.Abs(v2)/Math.Abs(v-v2)+Math.Abs(v1)/Math.Abs(v-v1)+Math.Abs(v1)/Math.Abs(v+v1)+Math.Abs(v2)/Math.Abs(v+v2));
                    }
                    refmax = Math.Max(v1, v2);
                    
                    //
                    // Test errors correctness
                    //
                    lsfit.polynomialfit(x, y, n, m, ref info, p, rep);
                    if( info<=0 )
                    {
                        fiterrors = true;
                    }
                    else
                    {
                        s = ratint.barycentriccalc(p, 0);
                        fiterrors = fiterrors | (double)(Math.Abs(s-v))>(double)(threshold);
                        fiterrors = fiterrors | (double)(Math.Abs(rep.rmserror-refrms))>(double)(threshold);
                        fiterrors = fiterrors | (double)(Math.Abs(rep.avgerror-refavg))>(double)(threshold);
                        fiterrors = fiterrors | (double)(Math.Abs(rep.avgrelerror-refavgrel))>(double)(threshold);
                        fiterrors = fiterrors | (double)(Math.Abs(rep.maxerror-refmax))>(double)(threshold);
                    }
                }
            }
        }


        private static void testrationalfitting(ref bool fiterrors)
        {
            double threshold = 0;
            int maxn = 0;
            int passcount = 0;
            ratint.barycentricinterpolant b1 = new ratint.barycentricinterpolant();
            ratint.barycentricinterpolant b2 = new ratint.barycentricinterpolant();
            double[] x = new double[0];
            double[] x2 = new double[0];
            double[] y = new double[0];
            double[] y2 = new double[0];
            double[] w = new double[0];
            double[] w2 = new double[0];
            double[] xc = new double[0];
            double[] yc = new double[0];
            int[] dc = new int[0];
            int n = 0;
            int m = 0;
            int i = 0;
            int k = 0;
            int pass = 0;
            double t = 0;
            double s = 0;
            double v = 0;
            double v0 = 0;
            double v1 = 0;
            double v2 = 0;
            int info = 0;
            int info2 = 0;
            double xmin = 0;
            double xmax = 0;
            double refrms = 0;
            double refavg = 0;
            double refavgrel = 0;
            double refmax = 0;
            lsfit.barycentricfitreport rep = new lsfit.barycentricfitreport();
            lsfit.barycentricfitreport rep2 = new lsfit.barycentricfitreport();

            fiterrors = false;
            
            //
            // PassCount        number of repeated passes
            // Threshold        error tolerance
            // LipschitzTol     Lipschitz constant increase allowed
            //                  when calculating constant on a twice denser grid
            //
            passcount = 5;
            maxn = 15;
            threshold = 1000000*math.machineepsilon;
            
            //
            // Test rational fitting:
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=2; n<=maxn; n++)
                {
                    
                    //
                    // N=M+K fitting (i.e. interpolation)
                    //
                    for(k=0; k<=n-1; k++)
                    {
                        x = new double[n-k];
                        y = new double[n-k];
                        w = new double[n-k];
                        if( k>0 )
                        {
                            xc = new double[k];
                            yc = new double[k];
                            dc = new int[k];
                        }
                        for(i=0; i<=n-k-1; i++)
                        {
                            x[i] = (double)i/(double)(n-1);
                            y[i] = 2*math.randomreal()-1;
                            w[i] = 1+math.randomreal();
                        }
                        for(i=0; i<=k-1; i++)
                        {
                            xc[i] = (double)(n-k+i)/(double)(n-1);
                            yc[i] = 2*math.randomreal()-1;
                            dc[i] = 0;
                        }
                        lsfit.barycentricfitfloaterhormannwc(x, y, w, n-k, xc, yc, dc, k, n, ref info, b1, rep);
                        if( info<=0 )
                        {
                            fiterrors = true;
                        }
                        else
                        {
                            for(i=0; i<=n-k-1; i++)
                            {
                                fiterrors = fiterrors | (double)(Math.Abs(ratint.barycentriccalc(b1, x[i])-y[i]))>(double)(threshold);
                            }
                            for(i=0; i<=k-1; i++)
                            {
                                fiterrors = fiterrors | (double)(Math.Abs(ratint.barycentriccalc(b1, xc[i])-yc[i]))>(double)(threshold);
                            }
                        }
                    }
                    
                    //
                    // Testing constraints on derivatives:
                    // * several M's are tried
                    // * several K's are tried - 1, 2.
                    // * constraints at the ends of the interval
                    //
                    for(m=3; m<=5; m++)
                    {
                        for(k=1; k<=2; k++)
                        {
                            x = new double[n];
                            y = new double[n];
                            w = new double[n];
                            xc = new double[2];
                            yc = new double[2];
                            dc = new int[2];
                            for(i=0; i<=n-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                                y[i] = 2*math.randomreal()-1;
                                w[i] = 1+math.randomreal();
                            }
                            xc[0] = -1;
                            yc[0] = 2*math.randomreal()-1;
                            dc[0] = 0;
                            xc[1] = 1;
                            yc[1] = 2*math.randomreal()-1;
                            dc[1] = 0;
                            lsfit.barycentricfitfloaterhormannwc(x, y, w, n, xc, yc, dc, k, m, ref info, b1, rep);
                            if( info<=0 )
                            {
                                fiterrors = true;
                            }
                            else
                            {
                                for(i=0; i<=k-1; i++)
                                {
                                    ratint.barycentricdiff1(b1, xc[i], ref v0, ref v1);
                                    fiterrors = fiterrors | (double)(Math.Abs(v0-yc[i]))>(double)(threshold);
                                }
                            }
                        }
                    }
                }
            }
            for(m=2; m<=8; m++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // General fitting
                    //
                    // interpolating function through M nodes should have
                    // greater RMS error than fitting it through the same M nodes
                    //
                    n = 100;
                    x2 = new double[n];
                    y2 = new double[n];
                    w2 = new double[n];
                    xmin = math.maxrealnumber;
                    xmax = -math.maxrealnumber;
                    for(i=0; i<=n-1; i++)
                    {
                        x2[i] = 2*Math.PI*math.randomreal();
                        y2[i] = Math.Sin(x2[i]);
                        w2[i] = 1;
                        xmin = Math.Min(xmin, x2[i]);
                        xmax = Math.Max(xmax, x2[i]);
                    }
                    x = new double[m];
                    y = new double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        x[i] = xmin+(xmax-xmin)*i/(m-1);
                        y[i] = Math.Sin(x[i]);
                    }
                    ratint.barycentricbuildfloaterhormann(x, y, m, 3, b1);
                    lsfit.barycentricfitfloaterhormannwc(x2, y2, w2, n, xc, yc, dc, 0, m, ref info, b2, rep);
                    if( info<=0 )
                    {
                        fiterrors = true;
                    }
                    else
                    {
                        
                        //
                        // calculate B1 (interpolant) RMS error, compare with B2 error
                        //
                        v1 = 0;
                        v2 = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            v1 = v1+math.sqr(ratint.barycentriccalc(b1, x2[i])-y2[i]);
                            v2 = v2+math.sqr(ratint.barycentriccalc(b2, x2[i])-y2[i]);
                        }
                        v1 = Math.Sqrt(v1/n);
                        v2 = Math.Sqrt(v2/n);
                        fiterrors = fiterrors | (double)(v2)>(double)(v1);
                        fiterrors = fiterrors | (double)(Math.Abs(v2-rep.rmserror))>(double)(threshold);
                    }
                    
                    //
                    // compare weighted and non-weighted
                    //
                    n = 20;
                    x = new double[n];
                    y = new double[n];
                    w = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                        y[i] = 2*math.randomreal()-1;
                        w[i] = 1;
                    }
                    lsfit.barycentricfitfloaterhormannwc(x, y, w, n, xc, yc, dc, 0, m, ref info, b1, rep);
                    lsfit.barycentricfitfloaterhormann(x, y, n, m, ref info2, b2, rep2);
                    if( info<=0 | info2<=0 )
                    {
                        fiterrors = true;
                    }
                    else
                    {
                        
                        //
                        // calculate B1 (interpolant), compare with B2
                        // compare RMS errors
                        //
                        t = 2*math.randomreal()-1;
                        v1 = ratint.barycentriccalc(b1, t);
                        v2 = ratint.barycentriccalc(b2, t);
                        fiterrors = fiterrors | (double)(v2)!=(double)(v1);
                        fiterrors = fiterrors | (double)(rep.rmserror)!=(double)(rep2.rmserror);
                        fiterrors = fiterrors | (double)(rep.avgerror)!=(double)(rep2.avgerror);
                        fiterrors = fiterrors | (double)(rep.avgrelerror)!=(double)(rep2.avgrelerror);
                        fiterrors = fiterrors | (double)(rep.maxerror)!=(double)(rep2.maxerror);
                    }
                }
            }
            for(pass=1; pass<=passcount; pass++)
            {
                ap.assert(passcount>=2, "PassCount should be 2 or greater!");
                
                //
                // solve simple task (all X[] are the same, Y[] are specially
                // calculated to ensure simple form of all types of errors)
                // and check correctness of the errors calculated by subroutines
                //
                // First pass is done with zero Y[], other passes - with random Y[].
                // It should test both ability to correctly calculate errors and
                // ability to not fail while working with zeros :)
                //
                n = 4;
                if( pass==1 )
                {
                    v1 = 0;
                    v2 = 0;
                    v = 0;
                }
                else
                {
                    v1 = math.randomreal();
                    v2 = math.randomreal();
                    v = 1+math.randomreal();
                }
                x = new double[4];
                y = new double[4];
                w = new double[4];
                x[0] = 0;
                y[0] = v-v2;
                w[0] = 1;
                x[1] = 0;
                y[1] = v-v1;
                w[1] = 1;
                x[2] = 0;
                y[2] = v+v1;
                w[2] = 1;
                x[3] = 0;
                y[3] = v+v2;
                w[3] = 1;
                refrms = Math.Sqrt((math.sqr(v1)+math.sqr(v2))/2);
                refavg = (Math.Abs(v1)+Math.Abs(v2))/2;
                if( pass==1 )
                {
                    refavgrel = 0;
                }
                else
                {
                    refavgrel = 0.25*(Math.Abs(v2)/Math.Abs(v-v2)+Math.Abs(v1)/Math.Abs(v-v1)+Math.Abs(v1)/Math.Abs(v+v1)+Math.Abs(v2)/Math.Abs(v+v2));
                }
                refmax = Math.Max(v1, v2);
                
                //
                // Test errors correctness
                //
                lsfit.barycentricfitfloaterhormann(x, y, 4, 2, ref info, b1, rep);
                if( info<=0 )
                {
                    fiterrors = true;
                }
                else
                {
                    s = ratint.barycentriccalc(b1, 0);
                    fiterrors = fiterrors | (double)(Math.Abs(s-v))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.rmserror-refrms))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.avgerror-refavg))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.avgrelerror-refavgrel))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.maxerror-refmax))>(double)(threshold);
                }
            }
        }


        private static void testsplinefitting(ref bool fiterrors)
        {
            double threshold = 0;
            double nonstrictthreshold = 0;
            int passcount = 0;
            int n = 0;
            int m = 0;
            int i = 0;
            int k = 0;
            int pass = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] w = new double[0];
            double[] w2 = new double[0];
            double[] xc = new double[0];
            double[] yc = new double[0];
            double[] d = new double[0];
            int[] dc = new int[0];
            double sa = 0;
            double sb = 0;
            int info = 0;
            int info1 = 0;
            int info2 = 0;
            spline1d.spline1dinterpolant c = new spline1d.spline1dinterpolant();
            spline1d.spline1dinterpolant c2 = new spline1d.spline1dinterpolant();
            lsfit.spline1dfitreport rep = new lsfit.spline1dfitreport();
            lsfit.spline1dfitreport rep2 = new lsfit.spline1dfitreport();
            double s = 0;
            double ds = 0;
            double d2s = 0;
            int stype = 0;
            double t = 0;
            double v = 0;
            double v1 = 0;
            double v2 = 0;
            double refrms = 0;
            double refavg = 0;
            double refavgrel = 0;
            double refmax = 0;
            double rho = 0;

            
            //
            // Valyes:
            // * pass count
            // * threshold - for tests which must be satisfied exactly
            // * nonstrictthreshold - for approximate tests
            //
            passcount = 20;
            threshold = 10000*math.machineepsilon;
            nonstrictthreshold = 1.0E-4;
            fiterrors = false;
            
            //
            // Test fitting by Cubic and Hermite splines (obsolete, but still supported)
            //
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Cubic splines
                // Ability to handle boundary constraints (1-4 constraints on F, dF/dx).
                //
                for(m=4; m<=8; m++)
                {
                    for(k=1; k<=4; k++)
                    {
                        if( k>=m )
                        {
                            continue;
                        }
                        n = 100;
                        x = new double[n];
                        y = new double[n];
                        w = new double[n];
                        xc = new double[4];
                        yc = new double[4];
                        dc = new int[4];
                        sa = 1+math.randomreal();
                        sb = 2*math.randomreal()-1;
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = sa*math.randomreal()+sb;
                            y[i] = 2*math.randomreal()-1;
                            w[i] = 1+math.randomreal();
                        }
                        xc[0] = sb;
                        yc[0] = 2*math.randomreal()-1;
                        dc[0] = 0;
                        xc[1] = sb;
                        yc[1] = 2*math.randomreal()-1;
                        dc[1] = 1;
                        xc[2] = sa+sb;
                        yc[2] = 2*math.randomreal()-1;
                        dc[2] = 0;
                        xc[3] = sa+sb;
                        yc[3] = 2*math.randomreal()-1;
                        dc[3] = 1;
                        lsfit.spline1dfitcubicwc(x, y, w, n, xc, yc, dc, k, m, ref info, c, rep);
                        if( info<=0 )
                        {
                            fiterrors = true;
                        }
                        else
                        {
                            
                            //
                            // Check that constraints are satisfied
                            //
                            for(i=0; i<=k-1; i++)
                            {
                                spline1d.spline1ddiff(c, xc[i], ref s, ref ds, ref d2s);
                                if( dc[i]==0 )
                                {
                                    fiterrors = fiterrors | (double)(Math.Abs(s-yc[i]))>(double)(threshold);
                                }
                                if( dc[i]==1 )
                                {
                                    fiterrors = fiterrors | (double)(Math.Abs(ds-yc[i]))>(double)(threshold);
                                }
                                if( dc[i]==2 )
                                {
                                    fiterrors = fiterrors | (double)(Math.Abs(d2s-yc[i]))>(double)(threshold);
                                }
                            }
                        }
                    }
                }
                
                //
                // Cubic splines
                // Ability to handle one internal constraint
                //
                for(m=4; m<=8; m++)
                {
                    n = 100;
                    x = new double[n];
                    y = new double[n];
                    w = new double[n];
                    xc = new double[1];
                    yc = new double[1];
                    dc = new int[1];
                    sa = 1+math.randomreal();
                    sb = 2*math.randomreal()-1;
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = sa*math.randomreal()+sb;
                        y[i] = 2*math.randomreal()-1;
                        w[i] = 1+math.randomreal();
                    }
                    xc[0] = sa*math.randomreal()+sb;
                    yc[0] = 2*math.randomreal()-1;
                    dc[0] = math.randominteger(2);
                    lsfit.spline1dfitcubicwc(x, y, w, n, xc, yc, dc, 1, m, ref info, c, rep);
                    if( info<=0 )
                    {
                        fiterrors = true;
                    }
                    else
                    {
                        
                        //
                        // Check that constraints are satisfied
                        //
                        spline1d.spline1ddiff(c, xc[0], ref s, ref ds, ref d2s);
                        if( dc[0]==0 )
                        {
                            fiterrors = fiterrors | (double)(Math.Abs(s-yc[0]))>(double)(threshold);
                        }
                        if( dc[0]==1 )
                        {
                            fiterrors = fiterrors | (double)(Math.Abs(ds-yc[0]))>(double)(threshold);
                        }
                        if( dc[0]==2 )
                        {
                            fiterrors = fiterrors | (double)(Math.Abs(d2s-yc[0]))>(double)(threshold);
                        }
                    }
                }
                
                //
                // Hermite splines
                // Ability to handle boundary constraints (1-4 constraints on F, dF/dx).
                //
                for(m=4; m<=8; m++)
                {
                    for(k=1; k<=4; k++)
                    {
                        if( k>=m )
                        {
                            continue;
                        }
                        if( m%2!=0 )
                        {
                            continue;
                        }
                        n = 100;
                        x = new double[n];
                        y = new double[n];
                        w = new double[n];
                        xc = new double[4];
                        yc = new double[4];
                        dc = new int[4];
                        sa = 1+math.randomreal();
                        sb = 2*math.randomreal()-1;
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = sa*math.randomreal()+sb;
                            y[i] = 2*math.randomreal()-1;
                            w[i] = 1+math.randomreal();
                        }
                        xc[0] = sb;
                        yc[0] = 2*math.randomreal()-1;
                        dc[0] = 0;
                        xc[1] = sb;
                        yc[1] = 2*math.randomreal()-1;
                        dc[1] = 1;
                        xc[2] = sa+sb;
                        yc[2] = 2*math.randomreal()-1;
                        dc[2] = 0;
                        xc[3] = sa+sb;
                        yc[3] = 2*math.randomreal()-1;
                        dc[3] = 1;
                        lsfit.spline1dfithermitewc(x, y, w, n, xc, yc, dc, k, m, ref info, c, rep);
                        if( info<=0 )
                        {
                            fiterrors = true;
                        }
                        else
                        {
                            
                            //
                            // Check that constraints are satisfied
                            //
                            for(i=0; i<=k-1; i++)
                            {
                                spline1d.spline1ddiff(c, xc[i], ref s, ref ds, ref d2s);
                                if( dc[i]==0 )
                                {
                                    fiterrors = fiterrors | (double)(Math.Abs(s-yc[i]))>(double)(threshold);
                                }
                                if( dc[i]==1 )
                                {
                                    fiterrors = fiterrors | (double)(Math.Abs(ds-yc[i]))>(double)(threshold);
                                }
                                if( dc[i]==2 )
                                {
                                    fiterrors = fiterrors | (double)(Math.Abs(d2s-yc[i]))>(double)(threshold);
                                }
                            }
                        }
                    }
                }
                
                //
                // Hermite splines
                // Ability to handle one internal constraint
                //
                for(m=4; m<=8; m++)
                {
                    if( m%2!=0 )
                    {
                        continue;
                    }
                    n = 100;
                    x = new double[n];
                    y = new double[n];
                    w = new double[n];
                    xc = new double[1];
                    yc = new double[1];
                    dc = new int[1];
                    sa = 1+math.randomreal();
                    sb = 2*math.randomreal()-1;
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = sa*math.randomreal()+sb;
                        y[i] = 2*math.randomreal()-1;
                        w[i] = 1+math.randomreal();
                    }
                    xc[0] = sa*math.randomreal()+sb;
                    yc[0] = 2*math.randomreal()-1;
                    dc[0] = math.randominteger(2);
                    lsfit.spline1dfithermitewc(x, y, w, n, xc, yc, dc, 1, m, ref info, c, rep);
                    if( info<=0 )
                    {
                        fiterrors = true;
                    }
                    else
                    {
                        
                        //
                        // Check that constraints are satisfied
                        //
                        spline1d.spline1ddiff(c, xc[0], ref s, ref ds, ref d2s);
                        if( dc[0]==0 )
                        {
                            fiterrors = fiterrors | (double)(Math.Abs(s-yc[0]))>(double)(threshold);
                        }
                        if( dc[0]==1 )
                        {
                            fiterrors = fiterrors | (double)(Math.Abs(ds-yc[0]))>(double)(threshold);
                        }
                        if( dc[0]==2 )
                        {
                            fiterrors = fiterrors | (double)(Math.Abs(d2s-yc[0]))>(double)(threshold);
                        }
                    }
                }
            }
            for(m=4; m<=8; m++)
            {
                for(stype=0; stype<=1; stype++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        if( stype==1 & m%2!=0 )
                        {
                            continue;
                        }
                        
                        //
                        // cubic/Hermite spline fitting:
                        // * generate "template spline" C2
                        // * generate 2*N points from C2, such that result of
                        //   ideal fit should be equal to C2
                        // * fit, store in C
                        // * compare C and C2
                        //
                        sa = 1+math.randomreal();
                        sb = 2*math.randomreal()-1;
                        if( stype==0 )
                        {
                            x = new double[m-2];
                            y = new double[m-2];
                            for(i=0; i<=m-2-1; i++)
                            {
                                x[i] = sa*i/(m-2-1)+sb;
                                y[i] = 2*math.randomreal()-1;
                            }
                            spline1d.spline1dbuildcubic(x, y, m-2, 1, 2*math.randomreal()-1, 1, 2*math.randomreal()-1, c2);
                        }
                        if( stype==1 )
                        {
                            x = new double[m/2];
                            y = new double[m/2];
                            d = new double[m/2];
                            for(i=0; i<=m/2-1; i++)
                            {
                                x[i] = sa*i/(m/2-1)+sb;
                                y[i] = 2*math.randomreal()-1;
                                d[i] = 2*math.randomreal()-1;
                            }
                            spline1d.spline1dbuildhermite(x, y, d, m/2, c2);
                        }
                        n = 50;
                        x = new double[2*n];
                        y = new double[2*n];
                        w = new double[2*n];
                        for(i=0; i<=n-1; i++)
                        {
                            
                            //
                            // "if i=0" and "if i=1" are needed to
                            // synchronize interval size for C2 and
                            // spline being fitted (i.e. C).
                            //
                            t = math.randomreal();
                            x[i] = sa*math.randomreal()+sb;
                            if( i==0 )
                            {
                                x[i] = sb;
                            }
                            if( i==1 )
                            {
                                x[i] = sa+sb;
                            }
                            v = spline1d.spline1dcalc(c2, x[i]);
                            y[i] = v+t;
                            w[i] = 1+math.randomreal();
                            x[n+i] = x[i];
                            y[n+i] = v-t;
                            w[n+i] = w[i];
                        }
                        if( stype==0 )
                        {
                            lsfit.spline1dfitcubicwc(x, y, w, 2*n, xc, yc, dc, 0, m, ref info, c, rep);
                        }
                        if( stype==1 )
                        {
                            lsfit.spline1dfithermitewc(x, y, w, 2*n, xc, yc, dc, 0, m, ref info, c, rep);
                        }
                        if( info<=0 )
                        {
                            fiterrors = true;
                        }
                        else
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                v = sa*math.randomreal()+sb;
                                fiterrors = fiterrors | (double)(Math.Abs(spline1d.spline1dcalc(c, v)-spline1d.spline1dcalc(c2, v)))>(double)(threshold);
                            }
                        }
                    }
                }
            }
            for(m=4; m<=8; m++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // prepare points/weights
                    //
                    sa = 1+math.randomreal();
                    sb = 2*math.randomreal()-1;
                    n = 10+math.randominteger(10);
                    x = new double[n];
                    y = new double[n];
                    w = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = sa*math.randomreal()+sb;
                        y[i] = 2*math.randomreal()-1;
                        w[i] = 1;
                    }
                    
                    //
                    // Fit cubic with unity weights, without weights, then compare
                    //
                    if( m>=4 )
                    {
                        lsfit.spline1dfitcubicwc(x, y, w, n, xc, yc, dc, 0, m, ref info1, c, rep);
                        lsfit.spline1dfitcubic(x, y, n, m, ref info2, c2, rep2);
                        if( info1<=0 | info2<=0 )
                        {
                            fiterrors = true;
                        }
                        else
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                v = sa*math.randomreal()+sb;
                                fiterrors = fiterrors | (double)(spline1d.spline1dcalc(c, v))!=(double)(spline1d.spline1dcalc(c2, v));
                                fiterrors = fiterrors | (double)(rep.taskrcond)!=(double)(rep2.taskrcond);
                                fiterrors = fiterrors | (double)(rep.rmserror)!=(double)(rep2.rmserror);
                                fiterrors = fiterrors | (double)(rep.avgerror)!=(double)(rep2.avgerror);
                                fiterrors = fiterrors | (double)(rep.avgrelerror)!=(double)(rep2.avgrelerror);
                                fiterrors = fiterrors | (double)(rep.maxerror)!=(double)(rep2.maxerror);
                            }
                        }
                    }
                    
                    //
                    // Fit Hermite with unity weights, without weights, then compare
                    //
                    if( m>=4 & m%2==0 )
                    {
                        lsfit.spline1dfithermitewc(x, y, w, n, xc, yc, dc, 0, m, ref info1, c, rep);
                        lsfit.spline1dfithermite(x, y, n, m, ref info2, c2, rep2);
                        if( info1<=0 | info2<=0 )
                        {
                            fiterrors = true;
                        }
                        else
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                v = sa*math.randomreal()+sb;
                                fiterrors = fiterrors | (double)(spline1d.spline1dcalc(c, v))!=(double)(spline1d.spline1dcalc(c2, v));
                                fiterrors = fiterrors | (double)(rep.taskrcond)!=(double)(rep2.taskrcond);
                                fiterrors = fiterrors | (double)(rep.rmserror)!=(double)(rep2.rmserror);
                                fiterrors = fiterrors | (double)(rep.avgerror)!=(double)(rep2.avgerror);
                                fiterrors = fiterrors | (double)(rep.avgrelerror)!=(double)(rep2.avgrelerror);
                                fiterrors = fiterrors | (double)(rep.maxerror)!=(double)(rep2.maxerror);
                            }
                        }
                    }
                }
            }
            
            //
            // check basic properties of penalized splines which are
            // preserved independently of Rho parameter.
            //
            for(m=4; m<=10; m++)
            {
                for(k=-5; k<=5; k++)
                {
                    rho = k;
                    
                    //
                    // when we have two points (even with different weights),
                    // resulting spline must be equal to the straight line
                    //
                    x = new double[2];
                    y = new double[2];
                    w = new double[2];
                    x[0] = -0.5-math.randomreal();
                    y[0] = 0.5+math.randomreal();
                    w[0] = 1+math.randomreal();
                    x[1] = 0.5+math.randomreal();
                    y[1] = 0.5+math.randomreal();
                    w[1] = 1+math.randomreal();
                    lsfit.spline1dfitpenalized(x, y, 2, m, rho, ref info, c, rep);
                    if( info>0 )
                    {
                        v = 2*math.randomreal()-1;
                        v1 = (v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0];
                        fiterrors = fiterrors | (double)(Math.Abs(v1-spline1d.spline1dcalc(c, v)))>(double)(nonstrictthreshold);
                    }
                    else
                    {
                        fiterrors = true;
                    }
                    lsfit.spline1dfitpenalizedw(x, y, w, 2, m, rho, ref info, c, rep);
                    if( info>0 )
                    {
                        v = 2*math.randomreal()-1;
                        v1 = (v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0];
                        fiterrors = fiterrors | (double)(Math.Abs(v1-spline1d.spline1dcalc(c, v)))>(double)(nonstrictthreshold);
                    }
                    else
                    {
                        fiterrors = true;
                    }
                    
                    //
                    // spline fitting is invariant with respect to
                    // scaling of weights (of course, ANY fitting algorithm
                    // must be invariant, but we want to test this property
                    // just to be sure that it is correctly implemented)
                    //
                    for(n=2; n<=2*m; n++)
                    {
                        x = new double[n];
                        y = new double[n];
                        w = new double[n];
                        w2 = new double[n];
                        s = 1+Math.Exp(10*math.randomreal());
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = (double)i/(double)(n-1);
                            y[i] = math.randomreal();
                            w[i] = 0.1+math.randomreal();
                            w2[i] = w[i]*s;
                        }
                        lsfit.spline1dfitpenalizedw(x, y, w, n, m, rho, ref info, c, rep);
                        lsfit.spline1dfitpenalizedw(x, y, w2, n, m, rho, ref info2, c2, rep2);
                        if( info>0 & info2>0 )
                        {
                            v = math.randomreal();
                            v1 = spline1d.spline1dcalc(c, v);
                            v2 = spline1d.spline1dcalc(c2, v);
                            fiterrors = fiterrors | (double)(Math.Abs(v1-v2))>(double)(nonstrictthreshold);
                        }
                        else
                        {
                            fiterrors = true;
                        }
                    }
                }
            }
            
            //
            // Advanced proprties:
            // * penalized spline with M about 5*N and sufficiently small Rho
            //   must pass through all points on equidistant grid
            //
            for(n=2; n<=10; n++)
            {
                m = 5*n;
                rho = -5;
                x = new double[n];
                y = new double[n];
                w = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = (double)i/(double)(n-1);
                    y[i] = math.randomreal();
                    w[i] = 0.1+math.randomreal();
                }
                lsfit.spline1dfitpenalized(x, y, n, m, rho, ref info, c, rep);
                if( info>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        fiterrors = fiterrors | (double)(Math.Abs(y[i]-spline1d.spline1dcalc(c, x[i])))>(double)(nonstrictthreshold);
                    }
                }
                else
                {
                    fiterrors = true;
                }
                lsfit.spline1dfitpenalizedw(x, y, w, n, m, rho, ref info, c, rep);
                if( info>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        fiterrors = fiterrors | (double)(Math.Abs(y[i]-spline1d.spline1dcalc(c, x[i])))>(double)(nonstrictthreshold);
                    }
                }
                else
                {
                    fiterrors = true;
                }
            }
            
            //
            // Check correctness of error reports
            //
            for(pass=1; pass<=passcount; pass++)
            {
                ap.assert(passcount>=2, "PassCount should be 2 or greater!");
                
                //
                // solve simple task (all X[] are the same, Y[] are specially
                // calculated to ensure simple form of all types of errors)
                // and check correctness of the errors calculated by subroutines
                //
                // First pass is done with zero Y[], other passes - with random Y[].
                // It should test both ability to correctly calculate errors and
                // ability to not fail while working with zeros :)
                //
                n = 4;
                if( pass==1 )
                {
                    v1 = 0;
                    v2 = 0;
                    v = 0;
                }
                else
                {
                    v1 = math.randomreal();
                    v2 = math.randomreal();
                    v = 1+math.randomreal();
                }
                x = new double[4];
                y = new double[4];
                w = new double[4];
                x[0] = 0;
                y[0] = v-v2;
                w[0] = 1;
                x[1] = 0;
                y[1] = v-v1;
                w[1] = 1;
                x[2] = 0;
                y[2] = v+v1;
                w[2] = 1;
                x[3] = 0;
                y[3] = v+v2;
                w[3] = 1;
                refrms = Math.Sqrt((math.sqr(v1)+math.sqr(v2))/2);
                refavg = (Math.Abs(v1)+Math.Abs(v2))/2;
                if( pass==1 )
                {
                    refavgrel = 0;
                }
                else
                {
                    refavgrel = 0.25*(Math.Abs(v2)/Math.Abs(v-v2)+Math.Abs(v1)/Math.Abs(v-v1)+Math.Abs(v1)/Math.Abs(v+v1)+Math.Abs(v2)/Math.Abs(v+v2));
                }
                refmax = Math.Max(v1, v2);
                
                //
                // Test penalized spline
                //
                lsfit.spline1dfitpenalizedw(x, y, w, 4, 4, 0.0, ref info, c, rep);
                if( info<=0 )
                {
                    fiterrors = true;
                }
                else
                {
                    s = spline1d.spline1dcalc(c, 0);
                    fiterrors = fiterrors | (double)(Math.Abs(s-v))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.rmserror-refrms))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.avgerror-refavg))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.avgrelerror-refavgrel))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.maxerror-refmax))>(double)(threshold);
                }
                
                //
                // Test cubic fitting
                //
                lsfit.spline1dfitcubic(x, y, 4, 4, ref info, c, rep);
                if( info<=0 )
                {
                    fiterrors = true;
                }
                else
                {
                    s = spline1d.spline1dcalc(c, 0);
                    fiterrors = fiterrors | (double)(Math.Abs(s-v))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.rmserror-refrms))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.avgerror-refavg))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.avgrelerror-refavgrel))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.maxerror-refmax))>(double)(threshold);
                }
                
                //
                // Test Hermite fitting
                //
                lsfit.spline1dfithermite(x, y, 4, 4, ref info, c, rep);
                if( info<=0 )
                {
                    fiterrors = true;
                }
                else
                {
                    s = spline1d.spline1dcalc(c, 0);
                    fiterrors = fiterrors | (double)(Math.Abs(s-v))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.rmserror-refrms))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.avgerror-refavg))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.avgrelerror-refavgrel))>(double)(threshold);
                    fiterrors = fiterrors | (double)(Math.Abs(rep.maxerror-refmax))>(double)(threshold);
                }
            }
        }


        private static void testgeneralfitting(ref bool llserrors,
            ref bool nlserrors)
        {
            double threshold = 0;
            double nlthreshold = 0;
            int maxn = 0;
            int maxm = 0;
            int passcount = 0;
            int n = 0;
            int m = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int pass = 0;
            double xscale = 0;
            double diffstep = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] w = new double[0];
            double[] w2 = new double[0];
            double[] c = new double[0];
            double[] c2 = new double[0];
            double[,] a = new double[0,0];
            double[,] a2 = new double[0,0];
            double[,] cm = new double[0,0];
            double v = 0;
            double v1 = 0;
            double v2 = 0;
            lsfit.lsfitreport rep = new lsfit.lsfitreport();
            lsfit.lsfitreport rep2 = new lsfit.lsfitreport();
            int info = 0;
            int info2 = 0;
            double refrms = 0;
            double refavg = 0;
            double refavgrel = 0;
            double refmax = 0;
            lsfit.lsfitstate state = new lsfit.lsfitstate();

            llserrors = false;
            nlserrors = false;
            threshold = 10000*math.machineepsilon;
            nlthreshold = 0.00001;
            diffstep = 0.0001;
            maxn = 6;
            maxm = 6;
            passcount = 4;
            
            //
            // Testing unconstrained least squares (linear/nonlinear)
            //
            for(n=1; n<=maxn; n++)
            {
                for(m=1; m<=maxm; m++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        
                        //
                        // Solve non-degenerate linear least squares task
                        // Use Chebyshev basis. Its condition number is very good.
                        //
                        a = new double[n, m];
                        x = new double[n];
                        y = new double[n];
                        w = new double[n];
                        xscale = 0.9+0.1*math.randomreal();
                        for(i=0; i<=n-1; i++)
                        {
                            if( n==1 )
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                            else
                            {
                                x[i] = xscale*((double)(2*i)/(double)(n-1)-1);
                            }
                            y[i] = 3*x[i]+Math.Exp(x[i]);
                            w[i] = 1+math.randomreal();
                            a[i,0] = 1;
                            if( m>1 )
                            {
                                a[i,1] = x[i];
                            }
                            for(j=2; j<=m-1; j++)
                            {
                                a[i,j] = 2*x[i]*a[i,j-1]-a[i,j-2];
                            }
                        }
                        
                        //
                        // 1. test weighted fitting (optimality)
                        // 2. Solve degenerate least squares task built on the basis
                        //    of previous task
                        //
                        lsfit.lsfitlinearw(y, w, a, n, m, ref info, ref c, rep);
                        if( info<=0 )
                        {
                            llserrors = true;
                        }
                        else
                        {
                            llserrors = llserrors | !isglssolution(n, m, 0, y, w, a, cm, c);
                        }
                        a2 = new double[n, 2*m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                a2[i,2*j+0] = a[i,j];
                                a2[i,2*j+1] = a[i,j];
                            }
                        }
                        lsfit.lsfitlinearw(y, w, a2, n, 2*m, ref info, ref c2, rep);
                        if( info<=0 )
                        {
                            llserrors = true;
                        }
                        else
                        {
                            
                            //
                            // test answer correctness using design matrix properties
                            // and previous task solution
                            //
                            for(j=0; j<=m-1; j++)
                            {
                                llserrors = llserrors | (double)(Math.Abs(c2[2*j+0]+c2[2*j+1]-c[j]))>(double)(threshold);
                            }
                        }
                        
                        //
                        // test non-weighted fitting
                        //
                        w2 = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            w2[i] = 1;
                        }
                        lsfit.lsfitlinearw(y, w2, a, n, m, ref info, ref c, rep);
                        lsfit.lsfitlinear(y, a, n, m, ref info2, ref c2, rep2);
                        if( info<=0 | info2<=0 )
                        {
                            llserrors = true;
                        }
                        else
                        {
                            
                            //
                            // test answer correctness
                            //
                            for(j=0; j<=m-1; j++)
                            {
                                llserrors = llserrors | (double)(Math.Abs(c[j]-c2[j]))>(double)(threshold);
                            }
                            llserrors = llserrors | (double)(Math.Abs(rep.taskrcond-rep2.taskrcond))>(double)(threshold);
                        }
                        
                        //
                        // test nonlinear fitting on the linear task
                        // (only non-degenerate tasks are tested)
                        // and compare with answer from linear fitting subroutine
                        //
                        if( n>=m )
                        {
                            c2 = new double[m];
                            
                            //
                            // test function/gradient/Hessian-based weighted fitting
                            //
                            lsfit.lsfitlinearw(y, w, a, n, m, ref info, ref c, rep);
                            for(i=0; i<=m-1; i++)
                            {
                                c2[i] = 2*math.randomreal()-1;
                            }
                            lsfit.lsfitcreatewf(a, y, w, c2, n, m, m, diffstep, state);
                            lsfit.lsfitsetcond(state, 0.0, nlthreshold, 0);
                            fitlinearnonlinear(m, 0, a, state, ref nlserrors);
                            lsfit.lsfitresults(state, ref info, ref c2, rep2);
                            if( info<=0 )
                            {
                                nlserrors = true;
                            }
                            else
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    nlserrors = nlserrors | (double)(Math.Abs(c[i]-c2[i]))>(double)(100*nlthreshold);
                                }
                            }
                            for(i=0; i<=m-1; i++)
                            {
                                c2[i] = 2*math.randomreal()-1;
                            }
                            lsfit.lsfitcreatewfg(a, y, w, c2, n, m, m, (double)(math.randomreal())>(double)(0.5), state);
                            lsfit.lsfitsetcond(state, 0.0, nlthreshold, 0);
                            fitlinearnonlinear(m, 1, a, state, ref nlserrors);
                            lsfit.lsfitresults(state, ref info, ref c2, rep2);
                            if( info<=0 )
                            {
                                nlserrors = true;
                            }
                            else
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    nlserrors = nlserrors | (double)(Math.Abs(c[i]-c2[i]))>(double)(100*nlthreshold);
                                }
                            }
                            for(i=0; i<=m-1; i++)
                            {
                                c2[i] = 2*math.randomreal()-1;
                            }
                            lsfit.lsfitcreatewfgh(a, y, w, c2, n, m, m, state);
                            lsfit.lsfitsetcond(state, 0.0, nlthreshold, 0);
                            fitlinearnonlinear(m, 2, a, state, ref nlserrors);
                            lsfit.lsfitresults(state, ref info, ref c2, rep2);
                            if( info<=0 )
                            {
                                nlserrors = true;
                            }
                            else
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    nlserrors = nlserrors | (double)(Math.Abs(c[i]-c2[i]))>(double)(100*nlthreshold);
                                }
                            }
                            
                            //
                            // test gradient-only or Hessian-based fitting without weights
                            //
                            lsfit.lsfitlinear(y, a, n, m, ref info, ref c, rep);
                            for(i=0; i<=m-1; i++)
                            {
                                c2[i] = 2*math.randomreal()-1;
                            }
                            lsfit.lsfitcreatef(a, y, c2, n, m, m, diffstep, state);
                            lsfit.lsfitsetcond(state, 0.0, nlthreshold, 0);
                            fitlinearnonlinear(m, 0, a, state, ref nlserrors);
                            lsfit.lsfitresults(state, ref info, ref c2, rep2);
                            if( info<=0 )
                            {
                                nlserrors = true;
                            }
                            else
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    nlserrors = nlserrors | (double)(Math.Abs(c[i]-c2[i]))>(double)(100*nlthreshold);
                                }
                            }
                            for(i=0; i<=m-1; i++)
                            {
                                c2[i] = 2*math.randomreal()-1;
                            }
                            lsfit.lsfitcreatefg(a, y, c2, n, m, m, (double)(math.randomreal())>(double)(0.5), state);
                            lsfit.lsfitsetcond(state, 0.0, nlthreshold, 0);
                            fitlinearnonlinear(m, 1, a, state, ref nlserrors);
                            lsfit.lsfitresults(state, ref info, ref c2, rep2);
                            if( info<=0 )
                            {
                                nlserrors = true;
                            }
                            else
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    nlserrors = nlserrors | (double)(Math.Abs(c[i]-c2[i]))>(double)(100*nlthreshold);
                                }
                            }
                            for(i=0; i<=m-1; i++)
                            {
                                c2[i] = 2*math.randomreal()-1;
                            }
                            lsfit.lsfitcreatefgh(a, y, c2, n, m, m, state);
                            lsfit.lsfitsetcond(state, 0.0, nlthreshold, 0);
                            fitlinearnonlinear(m, 2, a, state, ref nlserrors);
                            lsfit.lsfitresults(state, ref info, ref c2, rep2);
                            if( info<=0 )
                            {
                                nlserrors = true;
                            }
                            else
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    nlserrors = nlserrors | (double)(Math.Abs(c[i]-c2[i]))>(double)(100*nlthreshold);
                                }
                            }
                        }
                    }
                }
                
                //
                // test correctness of the RCond field
                //
                a = new double[n-1+1, n-1+1];
                x = new double[n-1+1];
                y = new double[n-1+1];
                w = new double[n-1+1];
                v1 = math.maxrealnumber;
                v2 = math.minrealnumber;
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 0.1+0.9*math.randomreal();
                    y[i] = 0.1+0.9*math.randomreal();
                    w[i] = 1;
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j )
                        {
                            a[i,i] = 0.1+0.9*math.randomreal();
                            v1 = Math.Min(v1, a[i,i]);
                            v2 = Math.Max(v2, a[i,i]);
                        }
                        else
                        {
                            a[i,j] = 0;
                        }
                    }
                }
                lsfit.lsfitlinearw(y, w, a, n, n, ref info, ref c, rep);
                if( info<=0 )
                {
                    llserrors = true;
                }
                else
                {
                    llserrors = llserrors | (double)(Math.Abs(rep.taskrcond-v1/v2))>(double)(threshold);
                }
            }
            
            //
            // Test constrained least squares
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    for(m=1; m<=maxm; m++)
                    {
                        
                        //
                        // test for K<>0
                        //
                        for(k=1; k<=m-1; k++)
                        {
                            
                            //
                            // Prepare Chebyshev basis. Its condition number is very good.
                            // Prepare constraints (random numbers)
                            //
                            a = new double[n, m];
                            x = new double[n];
                            y = new double[n];
                            w = new double[n];
                            xscale = 0.9+0.1*math.randomreal();
                            for(i=0; i<=n-1; i++)
                            {
                                if( n==1 )
                                {
                                    x[i] = 2*math.randomreal()-1;
                                }
                                else
                                {
                                    x[i] = xscale*((double)(2*i)/(double)(n-1)-1);
                                }
                                y[i] = 3*x[i]+Math.Exp(x[i]);
                                w[i] = 1+math.randomreal();
                                a[i,0] = 1;
                                if( m>1 )
                                {
                                    a[i,1] = x[i];
                                }
                                for(j=2; j<=m-1; j++)
                                {
                                    a[i,j] = 2*x[i]*a[i,j-1]-a[i,j-2];
                                }
                            }
                            cm = new double[k, m+1];
                            for(i=0; i<=k-1; i++)
                            {
                                for(j=0; j<=m; j++)
                                {
                                    cm[i,j] = 2*math.randomreal()-1;
                                }
                            }
                            
                            //
                            // Solve constrained task
                            //
                            lsfit.lsfitlinearwc(y, w, a, cm, n, m, k, ref info, ref c, rep);
                            if( info<=0 )
                            {
                                llserrors = true;
                            }
                            else
                            {
                                llserrors = llserrors | !isglssolution(n, m, k, y, w, a, cm, c);
                            }
                            
                            //
                            // test non-weighted fitting
                            //
                            w2 = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                w2[i] = 1;
                            }
                            lsfit.lsfitlinearwc(y, w2, a, cm, n, m, k, ref info, ref c, rep);
                            lsfit.lsfitlinearc(y, a, cm, n, m, k, ref info2, ref c2, rep2);
                            if( info<=0 | info2<=0 )
                            {
                                llserrors = true;
                            }
                            else
                            {
                                
                                //
                                // test answer correctness
                                //
                                for(j=0; j<=m-1; j++)
                                {
                                    llserrors = llserrors | (double)(Math.Abs(c[j]-c2[j]))>(double)(threshold);
                                }
                                llserrors = llserrors | (double)(Math.Abs(rep.taskrcond-rep2.taskrcond))>(double)(threshold);
                            }
                        }
                    }
                }
            }
            
            //
            // nonlinear task for nonlinear fitting:
            //
            //     f(X,C) = 1/(1+C*X^2),
            //     C(true) = 2.
            //
            n = 100;
            c = new double[1];
            c[0] = 1+2*math.randomreal();
            a = new double[n, 1];
            y = new double[n];
            for(i=0; i<=n-1; i++)
            {
                a[i,0] = 4*math.randomreal()-2;
                y[i] = 1/(1+2*math.sqr(a[i,0]));
            }
            lsfit.lsfitcreatefg(a, y, c, n, 1, 1, true, state);
            lsfit.lsfitsetcond(state, 0.0, nlthreshold, 0);
            while( lsfit.lsfititeration(state) )
            {
                if( state.needf )
                {
                    state.f = 1/(1+state.c[0]*math.sqr(state.x[0]));
                }
                if( state.needfg )
                {
                    state.f = 1/(1+state.c[0]*math.sqr(state.x[0]));
                    state.g[0] = -(math.sqr(state.x[0])/math.sqr(1+state.c[0]*math.sqr(state.x[0])));
                }
            }
            lsfit.lsfitresults(state, ref info, ref c, rep);
            if( info<=0 )
            {
                nlserrors = true;
            }
            else
            {
                nlserrors = nlserrors | (double)(Math.Abs(c[0]-2))>(double)(100*nlthreshold);
            }
            
            //
            // solve simple task (fitting by constant function) and check
            // correctness of the errors calculated by subroutines
            //
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // test on task with non-zero Yi
                //
                n = 4;
                v1 = math.randomreal();
                v2 = math.randomreal();
                v = 1+math.randomreal();
                c = new double[1];
                c[0] = 1+2*math.randomreal();
                a = new double[4, 1];
                y = new double[4];
                a[0,0] = 1;
                y[0] = v-v2;
                a[1,0] = 1;
                y[1] = v-v1;
                a[2,0] = 1;
                y[2] = v+v1;
                a[3,0] = 1;
                y[3] = v+v2;
                refrms = Math.Sqrt((math.sqr(v1)+math.sqr(v2))/2);
                refavg = (Math.Abs(v1)+Math.Abs(v2))/2;
                refavgrel = 0.25*(Math.Abs(v2)/Math.Abs(v-v2)+Math.Abs(v1)/Math.Abs(v-v1)+Math.Abs(v1)/Math.Abs(v+v1)+Math.Abs(v2)/Math.Abs(v+v2));
                refmax = Math.Max(v1, v2);
                
                //
                // Test LLS
                //
                lsfit.lsfitlinear(y, a, 4, 1, ref info, ref c, rep);
                if( info<=0 )
                {
                    llserrors = true;
                }
                else
                {
                    llserrors = llserrors | (double)(Math.Abs(c[0]-v))>(double)(threshold);
                    llserrors = llserrors | (double)(Math.Abs(rep.rmserror-refrms))>(double)(threshold);
                    llserrors = llserrors | (double)(Math.Abs(rep.avgerror-refavg))>(double)(threshold);
                    llserrors = llserrors | (double)(Math.Abs(rep.avgrelerror-refavgrel))>(double)(threshold);
                    llserrors = llserrors | (double)(Math.Abs(rep.maxerror-refmax))>(double)(threshold);
                }
                
                //
                // Test NLS
                //
                lsfit.lsfitcreatefg(a, y, c, 4, 1, 1, true, state);
                lsfit.lsfitsetcond(state, 0.0, nlthreshold, 0);
                while( lsfit.lsfititeration(state) )
                {
                    if( state.needf )
                    {
                        state.f = state.c[0];
                    }
                    if( state.needfg )
                    {
                        state.f = state.c[0];
                        state.g[0] = 1;
                    }
                }
                lsfit.lsfitresults(state, ref info, ref c, rep);
                if( info<=0 )
                {
                    nlserrors = true;
                }
                else
                {
                    nlserrors = nlserrors | (double)(Math.Abs(c[0]-v))>(double)(threshold);
                    nlserrors = nlserrors | (double)(Math.Abs(rep.rmserror-refrms))>(double)(threshold);
                    nlserrors = nlserrors | (double)(Math.Abs(rep.avgerror-refavg))>(double)(threshold);
                    nlserrors = nlserrors | (double)(Math.Abs(rep.avgrelerror-refavgrel))>(double)(threshold);
                    nlserrors = nlserrors | (double)(Math.Abs(rep.maxerror-refmax))>(double)(threshold);
                }
            }
        }


        /*************************************************************************
        Tests whether C is solution of (possibly) constrained LLS problem
        *************************************************************************/
        private static bool isglssolution(int n,
            int m,
            int k,
            double[] y,
            double[] w,
            double[,] fmatrix,
            double[,] cmatrix,
            double[] c)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;
            double[] c2 = new double[0];
            double[] sv = new double[0];
            double[] deltac = new double[0];
            double[] deltaproj = new double[0];
            double[,] u = new double[0,0];
            double[,] vt = new double[0,0];
            double v = 0;
            double s1 = 0;
            double s2 = 0;
            double s3 = 0;
            double delta = 0;
            double threshold = 0;
            int i_ = 0;

            c = (double[])c.Clone();

            
            //
            // Setup.
            // Threshold is small because CMatrix may be ill-conditioned
            //
            delta = 0.001;
            threshold = Math.Sqrt(math.machineepsilon);
            c2 = new double[m];
            deltac = new double[m];
            deltaproj = new double[m];
            
            //
            // test whether C is feasible point or not (projC must be close to C)
            //
            for(i=0; i<=k-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=m-1;i_++)
                {
                    v += cmatrix[i,i_]*c[i_];
                }
                if( (double)(Math.Abs(v-cmatrix[i,m]))>(double)(threshold) )
                {
                    result = false;
                    return result;
                }
            }
            
            //
            // find orthogonal basis of Null(CMatrix) (stored in rows from K to M-1)
            //
            if( k>0 )
            {
                svd.rmatrixsvd(cmatrix, k, m, 0, 2, 2, ref sv, ref u, ref vt);
            }
            
            //
            // Test result
            //
            result = true;
            s1 = getglserror(n, m, y, w, fmatrix, c);
            for(j=0; j<=m-1; j++)
            {
                
                //
                // prepare modification of C which leave us in the feasible set.
                //
                // let deltaC be increment on Jth coordinate, then project
                // deltaC in the Null(CMatrix) and store result in DeltaProj
                //
                for(i_=0; i_<=m-1;i_++)
                {
                    c2[i_] = c[i_];
                }
                for(i=0; i<=m-1; i++)
                {
                    if( i==j )
                    {
                        deltac[i] = delta;
                    }
                    else
                    {
                        deltac[i] = 0;
                    }
                }
                if( k==0 )
                {
                    for(i_=0; i_<=m-1;i_++)
                    {
                        deltaproj[i_] = deltac[i_];
                    }
                }
                else
                {
                    for(i=0; i<=m-1; i++)
                    {
                        deltaproj[i] = 0;
                    }
                    for(i=k; i<=m-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=m-1;i_++)
                        {
                            v += vt[i,i_]*deltac[i_];
                        }
                        for(i_=0; i_<=m-1;i_++)
                        {
                            deltaproj[i_] = deltaproj[i_] + v*vt[i,i_];
                        }
                    }
                }
                
                //
                // now we have DeltaProj such that if C is feasible,
                // then C+DeltaProj is feasible too
                //
                for(i_=0; i_<=m-1;i_++)
                {
                    c2[i_] = c[i_];
                }
                for(i_=0; i_<=m-1;i_++)
                {
                    c2[i_] = c2[i_] + deltaproj[i_];
                }
                s2 = getglserror(n, m, y, w, fmatrix, c2);
                for(i_=0; i_<=m-1;i_++)
                {
                    c2[i_] = c[i_];
                }
                for(i_=0; i_<=m-1;i_++)
                {
                    c2[i_] = c2[i_] - deltaproj[i_];
                }
                s3 = getglserror(n, m, y, w, fmatrix, c2);
                result = (result & (double)(s2)>=(double)(s1/(1+threshold))) & (double)(s3)>=(double)(s1/(1+threshold));
            }
            return result;
        }


        /*************************************************************************
        Tests whether C is solution of LLS problem
        *************************************************************************/
        private static double getglserror(int n,
            int m,
            double[] y,
            double[] w,
            double[,] fmatrix,
            double[] c)
        {
            double result = 0;
            int i = 0;
            double v = 0;
            int i_ = 0;

            result = 0;
            for(i=0; i<=n-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=m-1;i_++)
                {
                    v += fmatrix[i,i_]*c[i_];
                }
                result = result+math.sqr(w[i]*(v-y[i]));
            }
            return result;
        }


        /*************************************************************************
        Subroutine for nonlinear fitting of linear problem

        DerAvailable:
        * 0     when only function value should be used
        * 1     when we can provide gradient/function
        * 2     when we can provide Hessian/gradient/function

        When something which is not permitted by DerAvailable is requested,
        this function sets NLSErrors to True.
        *************************************************************************/
        private static void fitlinearnonlinear(int m,
            int deravailable,
            double[,] xy,
            lsfit.lsfitstate state,
            ref bool nlserrors)
        {
            int i = 0;
            int j = 0;
            double v = 0;
            int i_ = 0;

            while( lsfit.lsfititeration(state) )
            {
                
                //
                // assume that one and only one of flags is set
                // test that we didn't request hessian in hessian-free setting
                //
                if( deravailable<1 & state.needfg )
                {
                    nlserrors = true;
                }
                if( deravailable<2 & state.needfgh )
                {
                    nlserrors = true;
                }
                i = 0;
                if( state.needf )
                {
                    i = i+1;
                }
                if( state.needfg )
                {
                    i = i+1;
                }
                if( state.needfgh )
                {
                    i = i+1;
                }
                if( i!=1 )
                {
                    nlserrors = true;
                }
                
                //
                // test that PointIndex is consistent with actual point passed
                //
                for(i=0; i<=m-1; i++)
                {
                    nlserrors = nlserrors | (double)(xy[state.pointindex,i])!=(double)(state.x[i]);
                }
                
                //
                // calculate
                //
                if( state.needf )
                {
                    v = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        v += state.x[i_]*state.c[i_];
                    }
                    state.f = v;
                    continue;
                }
                if( state.needfg )
                {
                    v = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        v += state.x[i_]*state.c[i_];
                    }
                    state.f = v;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        state.g[i_] = state.x[i_];
                    }
                    continue;
                }
                if( state.needfgh )
                {
                    v = 0.0;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        v += state.x[i_]*state.c[i_];
                    }
                    state.f = v;
                    for(i_=0; i_<=m-1;i_++)
                    {
                        state.g[i_] = state.x[i_];
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            state.h[i,j] = 0;
                        }
                    }
                    continue;
                }
            }
        }


    }
    public class testpsplineunit
    {
        public static bool testpspline(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool p2errors = new bool();
            bool p3errors = new bool();
            double nonstrictthreshold = 0;
            double threshold = 0;
            int passcount = 0;
            double lstep = 0;
            double h = 0;
            int maxn = 0;
            int periodicity = 0;
            int skind = 0;
            int pkind = 0;
            bool periodic = new bool();
            double a = 0;
            double b = 0;
            int n = 0;
            int tmpn = 0;
            int i = 0;
            double vx = 0;
            double vy = 0;
            double vz = 0;
            double vx2 = 0;
            double vy2 = 0;
            double vz2 = 0;
            double vdx = 0;
            double vdy = 0;
            double vdz = 0;
            double vdx2 = 0;
            double vdy2 = 0;
            double vdz2 = 0;
            double vd2x = 0;
            double vd2y = 0;
            double vd2z = 0;
            double vd2x2 = 0;
            double vd2y2 = 0;
            double vd2z2 = 0;
            double v0 = 0;
            double v1 = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] z = new double[0];
            double[] t = new double[0];
            double[] t2 = new double[0];
            double[] t3 = new double[0];
            double[,] xy = new double[0,0];
            double[,] xyz = new double[0,0];
            pspline.pspline2interpolant p2 = new pspline.pspline2interpolant();
            pspline.pspline3interpolant p3 = new pspline.pspline3interpolant();
            spline1d.spline1dinterpolant s = new spline1d.spline1dinterpolant();
            int i_ = 0;

            waserrors = false;
            passcount = 20;
            lstep = 0.005;
            h = 0.00001;
            maxn = 10;
            threshold = 10000*math.machineepsilon;
            nonstrictthreshold = 0.00001;
            p2errors = false;
            p3errors = false;
            
            //
            // Test basic properties of 2- and 3-dimensional splines:
            // * PSpline2ParameterValues() properties
            // * values at nodes
            // * for periodic splines - periodicity properties
            //
            // Variables used:
            // * N              points count
            // * SKind          spline
            // * PKind          parameterization
            // * Periodicity    whether we have periodic spline or not
            //
            for(n=2; n<=maxn; n++)
            {
                for(skind=0; skind<=2; skind++)
                {
                    for(pkind=0; pkind<=2; pkind++)
                    {
                        for(periodicity=0; periodicity<=1; periodicity++)
                        {
                            periodic = periodicity==1;
                            
                            //
                            // skip unsupported combinations of parameters
                            //
                            if( periodic & n<3 )
                            {
                                continue;
                            }
                            if( periodic & skind==0 )
                            {
                                continue;
                            }
                            if( n<5 & skind==0 )
                            {
                                continue;
                            }
                            
                            //
                            // init
                            //
                            xy = new double[n, 2];
                            xyz = new double[n, 3];
                            apserv.taskgenint1dequidist(-1, 1, n, ref t2, ref x);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xy[i_,0] = x[i_];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xyz[i_,0] = x[i_];
                            }
                            apserv.taskgenint1dequidist(-1, 1, n, ref t2, ref y);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xy[i_,1] = y[i_];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xyz[i_,1] = y[i_];
                            }
                            apserv.taskgenint1dequidist(-1, 1, n, ref t2, ref z);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xyz[i_,2] = z[i_];
                            }
                            unsetp2(p2);
                            unsetp3(p3);
                            if( periodic )
                            {
                                pspline.pspline2buildperiodic(xy, n, skind, pkind, p2);
                                pspline.pspline3buildperiodic(xyz, n, skind, pkind, p3);
                            }
                            else
                            {
                                pspline.pspline2build(xy, n, skind, pkind, p2);
                                pspline.pspline3build(xyz, n, skind, pkind, p3);
                            }
                            
                            //
                            // PSpline2ParameterValues() properties
                            //
                            pspline.pspline2parametervalues(p2, ref tmpn, ref t2);
                            if( tmpn!=n )
                            {
                                p2errors = true;
                                continue;
                            }
                            pspline.pspline3parametervalues(p3, ref tmpn, ref t3);
                            if( tmpn!=n )
                            {
                                p3errors = true;
                                continue;
                            }
                            p2errors = p2errors | (double)(t2[0])!=(double)(0);
                            p3errors = p3errors | (double)(t3[0])!=(double)(0);
                            for(i=1; i<=n-1; i++)
                            {
                                p2errors = p2errors | (double)(t2[i])<=(double)(t2[i-1]);
                                p3errors = p3errors | (double)(t3[i])<=(double)(t3[i-1]);
                            }
                            if( periodic )
                            {
                                p2errors = p2errors | (double)(t2[n-1])>=(double)(1);
                                p3errors = p3errors | (double)(t3[n-1])>=(double)(1);
                            }
                            else
                            {
                                p2errors = p2errors | (double)(t2[n-1])!=(double)(1);
                                p3errors = p3errors | (double)(t3[n-1])!=(double)(1);
                            }
                            
                            //
                            // Now we have parameter values stored at T,
                            // and want to test whether the actully correspond to
                            // points
                            //
                            for(i=0; i<=n-1; i++)
                            {
                                
                                //
                                // 2-dimensional test
                                //
                                pspline.pspline2calc(p2, t2[i], ref vx, ref vy);
                                p2errors = p2errors | (double)(Math.Abs(vx-x[i]))>(double)(threshold);
                                p2errors = p2errors | (double)(Math.Abs(vy-y[i]))>(double)(threshold);
                                
                                //
                                // 3-dimensional test
                                //
                                pspline.pspline3calc(p3, t3[i], ref vx, ref vy, ref vz);
                                p3errors = p3errors | (double)(Math.Abs(vx-x[i]))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vy-y[i]))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vz-z[i]))>(double)(threshold);
                            }
                            
                            //
                            // Test periodicity (if needed)
                            //
                            if( periodic )
                            {
                                
                                //
                                // periodicity at nodes
                                //
                                for(i=0; i<=n-1; i++)
                                {
                                    
                                    //
                                    // 2-dimensional test
                                    //
                                    pspline.pspline2calc(p2, t2[i]+math.randominteger(10)-5, ref vx, ref vy);
                                    p2errors = p2errors | (double)(Math.Abs(vx-x[i]))>(double)(threshold);
                                    p2errors = p2errors | (double)(Math.Abs(vy-y[i]))>(double)(threshold);
                                    pspline.pspline2diff(p2, t2[i]+math.randominteger(10)-5, ref vx, ref vdx, ref vy, ref vdy);
                                    p2errors = p2errors | (double)(Math.Abs(vx-x[i]))>(double)(threshold);
                                    p2errors = p2errors | (double)(Math.Abs(vy-y[i]))>(double)(threshold);
                                    pspline.pspline2diff2(p2, t2[i]+math.randominteger(10)-5, ref vx, ref vdx, ref vd2x, ref vy, ref vdy, ref vd2y);
                                    p2errors = p2errors | (double)(Math.Abs(vx-x[i]))>(double)(threshold);
                                    p2errors = p2errors | (double)(Math.Abs(vy-y[i]))>(double)(threshold);
                                    
                                    //
                                    // 3-dimensional test
                                    //
                                    pspline.pspline3calc(p3, t3[i]+math.randominteger(10)-5, ref vx, ref vy, ref vz);
                                    p3errors = p3errors | (double)(Math.Abs(vx-x[i]))>(double)(threshold);
                                    p3errors = p3errors | (double)(Math.Abs(vy-y[i]))>(double)(threshold);
                                    p3errors = p3errors | (double)(Math.Abs(vz-z[i]))>(double)(threshold);
                                    pspline.pspline3diff(p3, t3[i]+math.randominteger(10)-5, ref vx, ref vdx, ref vy, ref vdy, ref vz, ref vdz);
                                    p3errors = p3errors | (double)(Math.Abs(vx-x[i]))>(double)(threshold);
                                    p3errors = p3errors | (double)(Math.Abs(vy-y[i]))>(double)(threshold);
                                    p3errors = p3errors | (double)(Math.Abs(vz-z[i]))>(double)(threshold);
                                    pspline.pspline3diff2(p3, t3[i]+math.randominteger(10)-5, ref vx, ref vdx, ref vd2x, ref vy, ref vdy, ref vd2y, ref vz, ref vdz, ref vd2z);
                                    p3errors = p3errors | (double)(Math.Abs(vx-x[i]))>(double)(threshold);
                                    p3errors = p3errors | (double)(Math.Abs(vy-y[i]))>(double)(threshold);
                                    p3errors = p3errors | (double)(Math.Abs(vz-z[i]))>(double)(threshold);
                                }
                                
                                //
                                // periodicity between nodes
                                //
                                v0 = math.randomreal();
                                pspline.pspline2calc(p2, v0, ref vx, ref vy);
                                pspline.pspline2calc(p2, v0+math.randominteger(10)-5, ref vx2, ref vy2);
                                p2errors = p2errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                                p2errors = p2errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                                pspline.pspline3calc(p3, v0, ref vx, ref vy, ref vz);
                                pspline.pspline3calc(p3, v0+math.randominteger(10)-5, ref vx2, ref vy2, ref vz2);
                                p3errors = p3errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vz-vz2))>(double)(threshold);
                                
                                //
                                // near-boundary test for continuity of function values and derivatives:
                                // 2-dimensional curve
                                //
                                ap.assert(skind==1 | skind==2, "TEST: unexpected spline type!");
                                v0 = 100*math.machineepsilon;
                                v1 = 1-v0;
                                pspline.pspline2calc(p2, v0, ref vx, ref vy);
                                pspline.pspline2calc(p2, v1, ref vx2, ref vy2);
                                p2errors = p2errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                                p2errors = p2errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                                pspline.pspline2diff(p2, v0, ref vx, ref vdx, ref vy, ref vdy);
                                pspline.pspline2diff(p2, v1, ref vx2, ref vdx2, ref vy2, ref vdy2);
                                p2errors = p2errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                                p2errors = p2errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                                p2errors = p2errors | (double)(Math.Abs(vdx-vdx2))>(double)(nonstrictthreshold);
                                p2errors = p2errors | (double)(Math.Abs(vdy-vdy2))>(double)(nonstrictthreshold);
                                pspline.pspline2diff2(p2, v0, ref vx, ref vdx, ref vd2x, ref vy, ref vdy, ref vd2y);
                                pspline.pspline2diff2(p2, v1, ref vx2, ref vdx2, ref vd2x2, ref vy2, ref vdy2, ref vd2y2);
                                p2errors = p2errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                                p2errors = p2errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                                p2errors = p2errors | (double)(Math.Abs(vdx-vdx2))>(double)(nonstrictthreshold);
                                p2errors = p2errors | (double)(Math.Abs(vdy-vdy2))>(double)(nonstrictthreshold);
                                if( skind==2 )
                                {
                                    
                                    //
                                    // second derivative test only for cubic splines
                                    //
                                    p2errors = p2errors | (double)(Math.Abs(vd2x-vd2x2))>(double)(nonstrictthreshold);
                                    p2errors = p2errors | (double)(Math.Abs(vd2y-vd2y2))>(double)(nonstrictthreshold);
                                }
                                
                                //
                                // near-boundary test for continuity of function values and derivatives:
                                // 3-dimensional curve
                                //
                                ap.assert(skind==1 | skind==2, "TEST: unexpected spline type!");
                                v0 = 100*math.machineepsilon;
                                v1 = 1-v0;
                                pspline.pspline3calc(p3, v0, ref vx, ref vy, ref vz);
                                pspline.pspline3calc(p3, v1, ref vx2, ref vy2, ref vz2);
                                p3errors = p3errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vz-vz2))>(double)(threshold);
                                pspline.pspline3diff(p3, v0, ref vx, ref vdx, ref vy, ref vdy, ref vz, ref vdz);
                                pspline.pspline3diff(p3, v1, ref vx2, ref vdx2, ref vy2, ref vdy2, ref vz2, ref vdz2);
                                p3errors = p3errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vz-vz2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vdx-vdx2))>(double)(nonstrictthreshold);
                                p3errors = p3errors | (double)(Math.Abs(vdy-vdy2))>(double)(nonstrictthreshold);
                                p3errors = p3errors | (double)(Math.Abs(vdz-vdz2))>(double)(nonstrictthreshold);
                                pspline.pspline3diff2(p3, v0, ref vx, ref vdx, ref vd2x, ref vy, ref vdy, ref vd2y, ref vz, ref vdz, ref vd2z);
                                pspline.pspline3diff2(p3, v1, ref vx2, ref vdx2, ref vd2x2, ref vy2, ref vdy2, ref vd2y2, ref vz2, ref vdz2, ref vd2z2);
                                p3errors = p3errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vz-vz2))>(double)(threshold);
                                p3errors = p3errors | (double)(Math.Abs(vdx-vdx2))>(double)(nonstrictthreshold);
                                p3errors = p3errors | (double)(Math.Abs(vdy-vdy2))>(double)(nonstrictthreshold);
                                p3errors = p3errors | (double)(Math.Abs(vdz-vdz2))>(double)(nonstrictthreshold);
                                if( skind==2 )
                                {
                                    
                                    //
                                    // second derivative test only for cubic splines
                                    //
                                    p3errors = p3errors | (double)(Math.Abs(vd2x-vd2x2))>(double)(nonstrictthreshold);
                                    p3errors = p3errors | (double)(Math.Abs(vd2y-vd2y2))>(double)(nonstrictthreshold);
                                    p3errors = p3errors | (double)(Math.Abs(vd2z-vd2z2))>(double)(nonstrictthreshold);
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // Test differentiation, tangents, calculation between nodes.
            //
            // Because differentiation is done in parameterization/spline/periodicity
            // oblivious manner, we don't have to test all possible combinations
            // of spline types and parameterizations.
            //
            // Actually we test special combination with properties which allow us
            // to easily solve this problem:
            // * 2 (3) variables
            // * first variable is sampled from equidistant grid on [0,1]
            // * other variables are random
            // * uniform parameterization is used
            // * periodicity - none
            // * spline type - any (we use cubic splines)
            // Same problem allows us to test calculation BETWEEN nodes.
            //
            for(n=2; n<=maxn; n++)
            {
                
                //
                // init
                //
                xy = new double[n, 2];
                xyz = new double[n, 3];
                apserv.taskgenint1dequidist(0, 1, n, ref t, ref x);
                for(i_=0; i_<=n-1;i_++)
                {
                    xy[i_,0] = x[i_];
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    xyz[i_,0] = x[i_];
                }
                apserv.taskgenint1dequidist(0, 1, n, ref t, ref y);
                for(i_=0; i_<=n-1;i_++)
                {
                    xy[i_,1] = y[i_];
                }
                for(i_=0; i_<=n-1;i_++)
                {
                    xyz[i_,1] = y[i_];
                }
                apserv.taskgenint1dequidist(0, 1, n, ref t, ref z);
                for(i_=0; i_<=n-1;i_++)
                {
                    xyz[i_,2] = z[i_];
                }
                unsetp2(p2);
                unsetp3(p3);
                pspline.pspline2build(xy, n, 2, 0, p2);
                pspline.pspline3build(xyz, n, 2, 0, p3);
                
                //
                // Test 2D/3D spline:
                // * build non-parametric cubic spline from T and X/Y
                // * calculate its value and derivatives at V0
                // * compare with Spline2Calc/Spline2Diff/Spline2Diff2
                // Because of task properties both variants should
                // return same answer.
                //
                v0 = math.randomreal();
                spline1d.spline1dbuildcubic(t, x, n, 0, 0.0, 0, 0.0, s);
                spline1d.spline1ddiff(s, v0, ref vx2, ref vdx2, ref vd2x2);
                spline1d.spline1dbuildcubic(t, y, n, 0, 0.0, 0, 0.0, s);
                spline1d.spline1ddiff(s, v0, ref vy2, ref vdy2, ref vd2y2);
                spline1d.spline1dbuildcubic(t, z, n, 0, 0.0, 0, 0.0, s);
                spline1d.spline1ddiff(s, v0, ref vz2, ref vdz2, ref vd2z2);
                
                //
                // 2D test
                //
                pspline.pspline2calc(p2, v0, ref vx, ref vy);
                p2errors = p2errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                pspline.pspline2diff(p2, v0, ref vx, ref vdx, ref vy, ref vdy);
                p2errors = p2errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vdx-vdx2))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vdy-vdy2))>(double)(threshold);
                pspline.pspline2diff2(p2, v0, ref vx, ref vdx, ref vd2x, ref vy, ref vdy, ref vd2y);
                p2errors = p2errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vdx-vdx2))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vdy-vdy2))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vd2x-vd2x2))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vd2y-vd2y2))>(double)(threshold);
                
                //
                // 3D test
                //
                pspline.pspline3calc(p3, v0, ref vx, ref vy, ref vz);
                p3errors = p3errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vz-vz2))>(double)(threshold);
                pspline.pspline3diff(p3, v0, ref vx, ref vdx, ref vy, ref vdy, ref vz, ref vdz);
                p3errors = p3errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vz-vz2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vdx-vdx2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vdy-vdy2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vdz-vdz2))>(double)(threshold);
                pspline.pspline3diff2(p3, v0, ref vx, ref vdx, ref vd2x, ref vy, ref vdy, ref vd2y, ref vz, ref vdz, ref vd2z);
                p3errors = p3errors | (double)(Math.Abs(vx-vx2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vy-vy2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vz-vz2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vdx-vdx2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vdy-vdy2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vdz-vdz2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vd2x-vd2x2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vd2y-vd2y2))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vd2z-vd2z2))>(double)(threshold);
                
                //
                // Test tangents for 2D/3D
                //
                pspline.pspline2tangent(p2, v0, ref vx, ref vy);
                p2errors = p2errors | (double)(Math.Abs(vx-vdx2/apserv.safepythag2(vdx2, vdy2)))>(double)(threshold);
                p2errors = p2errors | (double)(Math.Abs(vy-vdy2/apserv.safepythag2(vdx2, vdy2)))>(double)(threshold);
                pspline.pspline3tangent(p3, v0, ref vx, ref vy, ref vz);
                p3errors = p3errors | (double)(Math.Abs(vx-vdx2/apserv.safepythag3(vdx2, vdy2, vdz2)))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vy-vdy2/apserv.safepythag3(vdx2, vdy2, vdz2)))>(double)(threshold);
                p3errors = p3errors | (double)(Math.Abs(vz-vdz2/apserv.safepythag3(vdx2, vdy2, vdz2)))>(double)(threshold);
            }
            
            //
            // Arc length test.
            //
            // Simple problem with easy solution (points on a straight line with
            // uniform parameterization).
            //
            for(n=2; n<=maxn; n++)
            {
                xy = new double[n, 2];
                xyz = new double[n, 3];
                for(i=0; i<=n-1; i++)
                {
                    xy[i,0] = i;
                    xy[i,1] = i;
                    xyz[i,0] = i;
                    xyz[i,1] = i;
                    xyz[i,2] = i;
                }
                pspline.pspline2build(xy, n, 1, 0, p2);
                pspline.pspline3build(xyz, n, 1, 0, p3);
                a = math.randomreal();
                b = math.randomreal();
                p2errors = p2errors | (double)(Math.Abs(pspline.pspline2arclength(p2, a, b)-(b-a)*Math.Sqrt(2)*(n-1)))>(double)(nonstrictthreshold);
                p3errors = p3errors | (double)(Math.Abs(pspline.pspline3arclength(p3, a, b)-(b-a)*Math.Sqrt(3)*(n-1)))>(double)(nonstrictthreshold);
            }
            
            //
            // report
            //
            waserrors = p2errors | p3errors;
            if( !silent )
            {
                System.Console.Write("TESTING SPLINE INTERPOLATION");
                System.Console.WriteLine();
                
                //
                // Normal tests
                //
                System.Console.Write("2D TEST:                                 ");
                if( p2errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("3D TEST:                                 ");
                if( p3errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            
            //
            // end
            //
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Unset spline, i.e. initialize it with random garbage
        *************************************************************************/
        private static void unsetp2(pspline.pspline2interpolant p)
        {
            double[,] xy = new double[0,0];

            xy = new double[2, 2];
            xy[0,0] = -1;
            xy[0,1] = -1;
            xy[1,0] = 1;
            xy[1,1] = 1;
            pspline.pspline2build(xy, 2, 1, 0, p);
        }


        /*************************************************************************
        Unset spline, i.e. initialize it with random garbage
        *************************************************************************/
        private static void unsetp3(pspline.pspline3interpolant p)
        {
            double[,] xy = new double[0,0];

            xy = new double[2, 3];
            xy[0,0] = -1;
            xy[0,1] = -1;
            xy[0,2] = -1;
            xy[1,0] = 1;
            xy[1,1] = 1;
            xy[1,2] = 1;
            pspline.pspline3build(xy, 2, 1, 0, p);
        }


        /*************************************************************************
        Unsets real vector
        *************************************************************************/
        private static void unset1d(ref double[] x)
        {
            x = new double[1];
            x[0] = 2*math.randomreal()-1;
        }


    }
    public class testspline2dunit
    {
        public static bool testspline2d(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool blerrors = new bool();
            bool bcerrors = new bool();
            bool dserrors = new bool();
            bool cperrors = new bool();
            bool uperrors = new bool();
            bool lterrors = new bool();
            bool syerrors = new bool();
            bool rlerrors = new bool();
            bool rcerrors = new bool();
            int pass = 0;
            int passcount = 0;
            int jobtype = 0;
            double lstep = 0;
            double h = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            spline2d.spline2dinterpolant c = new spline2d.spline2dinterpolant();
            spline2d.spline2dinterpolant c2 = new spline2d.spline2dinterpolant();
            double[] lx = new double[0];
            double[] ly = new double[0];
            double[,] f = new double[0,0];
            double[,] fr = new double[0,0];
            double[,] ft = new double[0,0];
            double ax = 0;
            double ay = 0;
            double bx = 0;
            double by = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int m = 0;
            int n2 = 0;
            int m2 = 0;
            double err = 0;
            double t = 0;
            double t1 = 0;
            double t2 = 0;
            double l1 = 0;
            double l1x = 0;
            double l1y = 0;
            double l1xy = 0;
            double l2 = 0;
            double l2x = 0;
            double l2y = 0;
            double l2xy = 0;
            double fm = 0;
            double f1 = 0;
            double f2 = 0;
            double f3 = 0;
            double f4 = 0;
            double v1 = 0;
            double v1x = 0;
            double v1y = 0;
            double v1xy = 0;
            double v2 = 0;
            double v2x = 0;
            double v2y = 0;
            double v2xy = 0;
            double mf = 0;

            waserrors = false;
            passcount = 10;
            h = 0.00001;
            lstep = 0.001;
            blerrors = false;
            bcerrors = false;
            dserrors = false;
            cperrors = false;
            uperrors = false;
            lterrors = false;
            syerrors = false;
            rlerrors = false;
            rcerrors = false;
            
            //
            // Test: bilinear, bicubic
            //
            for(n=2; n<=7; n++)
            {
                for(m=2; m<=7; m++)
                {
                    x = new double[n-1+1];
                    y = new double[m-1+1];
                    lx = new double[2*n-2+1];
                    ly = new double[2*m-2+1];
                    f = new double[m-1+1, n-1+1];
                    ft = new double[n-1+1, m-1+1];
                    for(pass=1; pass<=passcount; pass++)
                    {
                        
                        //
                        // Prepare task:
                        // * X and Y stores grid
                        // * F stores function values
                        // * LX and LY stores twice dense grid (for Lipschitz testing)
                        //
                        ax = -1-math.randomreal();
                        bx = 1+math.randomreal();
                        ay = -1-math.randomreal();
                        by = 1+math.randomreal();
                        for(j=0; j<=n-1; j++)
                        {
                            x[j] = 0.5*(bx+ax)-0.5*(bx-ax)*Math.Cos(Math.PI*(2*j+1)/(2*n));
                            if( j==0 )
                            {
                                x[j] = ax;
                            }
                            if( j==n-1 )
                            {
                                x[j] = bx;
                            }
                            lx[2*j] = x[j];
                            if( j>0 )
                            {
                                lx[2*j-1] = 0.5*(x[j]+x[j-1]);
                            }
                        }
                        for(j=0; j<=n-1; j++)
                        {
                            k = math.randominteger(n);
                            if( k!=j )
                            {
                                t = x[j];
                                x[j] = x[k];
                                x[k] = t;
                            }
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            y[i] = 0.5*(by+ay)-0.5*(by-ay)*Math.Cos(Math.PI*(2*i+1)/(2*m));
                            if( i==0 )
                            {
                                y[i] = ay;
                            }
                            if( i==m-1 )
                            {
                                y[i] = by;
                            }
                            ly[2*i] = y[i];
                            if( i>0 )
                            {
                                ly[2*i-1] = 0.5*(y[i]+y[i-1]);
                            }
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            k = math.randominteger(m);
                            if( k!=i )
                            {
                                t = y[i];
                                y[i] = y[k];
                                y[k] = t;
                            }
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                f[i,j] = Math.Exp(0.6*x[j])-Math.Exp(-(0.3*y[i])+0.08*x[j])+2*Math.Cos(Math.PI*(x[j]+1.2*y[i]))+0.1*Math.Cos(20*x[j]+15*y[i]);
                            }
                        }
                        
                        //
                        // Test bilinear interpolation:
                        // * interpolation at the nodes
                        // * linearity
                        // * continuity
                        // * differentiation in the inner points
                        //
                        spline2d.spline2dbuildbilinear(x, y, f, m, n, c);
                        err = 0;
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                err = Math.Max(err, Math.Abs(f[i,j]-spline2d.spline2dcalc(c, x[j], y[i])));
                            }
                        }
                        blerrors = blerrors | (double)(err)>(double)(10000*math.machineepsilon);
                        err = 0;
                        for(i=0; i<=m-2; i++)
                        {
                            for(j=0; j<=n-2; j++)
                            {
                                
                                //
                                // Test for linearity between grid points
                                // (test point - geometric center of the cell)
                                //
                                fm = spline2d.spline2dcalc(c, lx[2*j+1], ly[2*i+1]);
                                f1 = spline2d.spline2dcalc(c, lx[2*j], ly[2*i]);
                                f2 = spline2d.spline2dcalc(c, lx[2*j+2], ly[2*i]);
                                f3 = spline2d.spline2dcalc(c, lx[2*j+2], ly[2*i+2]);
                                f4 = spline2d.spline2dcalc(c, lx[2*j], ly[2*i+2]);
                                err = Math.Max(err, Math.Abs(0.25*(f1+f2+f3+f4)-fm));
                            }
                        }
                        blerrors = blerrors | (double)(err)>(double)(10000*math.machineepsilon);
                        lconst(c, lx, ly, m, n, lstep, ref l1, ref l1x, ref l1y, ref l1xy);
                        lconst(c, lx, ly, m, n, lstep/3, ref l2, ref l2x, ref l2y, ref l2xy);
                        blerrors = blerrors | (double)(l2/l1)>(double)(1.2);
                        err = 0;
                        for(i=0; i<=m-2; i++)
                        {
                            for(j=0; j<=n-2; j++)
                            {
                                spline2d.spline2ddiff(c, lx[2*j+1], ly[2*i+1], ref v1, ref v1x, ref v1y, ref v1xy);
                                twodnumder(c, lx[2*j+1], ly[2*i+1], h, ref v2, ref v2x, ref v2y, ref v2xy);
                                err = Math.Max(err, Math.Abs(v1-v2));
                                err = Math.Max(err, Math.Abs(v1x-v2x));
                                err = Math.Max(err, Math.Abs(v1y-v2y));
                                err = Math.Max(err, Math.Abs(v1xy-v2xy));
                            }
                        }
                        dserrors = dserrors | (double)(err)>(double)(1.0E-3);
                        uperrors = uperrors | !testunpack(c, lx, ly);
                        lterrors = lterrors | !testlintrans(c, ax, bx, ay, by);
                        
                        //
                        // Test bicubic interpolation.
                        // * interpolation at the nodes
                        // * smoothness
                        // * differentiation
                        //
                        spline2d.spline2dbuildbicubic(x, y, f, m, n, c);
                        err = 0;
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                err = Math.Max(err, Math.Abs(f[i,j]-spline2d.spline2dcalc(c, x[j], y[i])));
                            }
                        }
                        bcerrors = bcerrors | (double)(err)>(double)(10000*math.machineepsilon);
                        lconst(c, lx, ly, m, n, lstep, ref l1, ref l1x, ref l1y, ref l1xy);
                        lconst(c, lx, ly, m, n, lstep/3, ref l2, ref l2x, ref l2y, ref l2xy);
                        bcerrors = bcerrors | (double)(l2/l1)>(double)(1.2);
                        bcerrors = bcerrors | (double)(l2x/l1x)>(double)(1.2);
                        bcerrors = bcerrors | (double)(l2y/l1y)>(double)(1.2);
                        if( (double)(l2xy)>(double)(0.01) & (double)(l1xy)>(double)(0.01) )
                        {
                            
                            //
                            // Cross-derivative continuity is tested only when
                            // bigger than 0.01. When the task size is too
                            // small, the d2F/dXdY is nearly zero and Lipschitz
                            // constant ratio is ill-conditioned.
                            //
                            bcerrors = bcerrors | (double)(l2xy/l1xy)>(double)(1.2);
                        }
                        err = 0;
                        for(i=0; i<=2*m-2; i++)
                        {
                            for(j=0; j<=2*n-2; j++)
                            {
                                spline2d.spline2ddiff(c, lx[j], ly[i], ref v1, ref v1x, ref v1y, ref v1xy);
                                twodnumder(c, lx[j], ly[i], h, ref v2, ref v2x, ref v2y, ref v2xy);
                                err = Math.Max(err, Math.Abs(v1-v2));
                                err = Math.Max(err, Math.Abs(v1x-v2x));
                                err = Math.Max(err, Math.Abs(v1y-v2y));
                                err = Math.Max(err, Math.Abs(v1xy-v2xy));
                            }
                        }
                        dserrors = dserrors | (double)(err)>(double)(1.0E-3);
                        uperrors = uperrors | !testunpack(c, lx, ly);
                        lterrors = lterrors | !testlintrans(c, ax, bx, ay, by);
                        
                        //
                        // Copy/Serialise test
                        //
                        if( (double)(math.randomreal())>(double)(0.5) )
                        {
                            spline2d.spline2dbuildbicubic(x, y, f, m, n, c);
                        }
                        else
                        {
                            spline2d.spline2dbuildbilinear(x, y, f, m, n, c);
                        }
                        unsetspline2d(c2);
                        spline2d.spline2dcopy(c, c2);
                        err = 0;
                        for(i=1; i<=5; i++)
                        {
                            t1 = ax+(bx-ax)*math.randomreal();
                            t2 = ay+(by-ay)*math.randomreal();
                            err = Math.Max(err, Math.Abs(spline2d.spline2dcalc(c, t1, t2)-spline2d.spline2dcalc(c2, t1, t2)));
                        }
                        cperrors = cperrors | (double)(err)>(double)(10000*math.machineepsilon);
                        
                        //
                        // Special symmetry test
                        //
                        err = 0;
                        for(jobtype=0; jobtype<=1; jobtype++)
                        {
                            
                            //
                            // Prepare
                            //
                            for(i=0; i<=m-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ft[j,i] = f[i,j];
                                }
                            }
                            if( jobtype==0 )
                            {
                                spline2d.spline2dbuildbilinear(x, y, f, m, n, c);
                                spline2d.spline2dbuildbilinear(y, x, ft, n, m, c2);
                            }
                            else
                            {
                                spline2d.spline2dbuildbicubic(x, y, f, m, n, c);
                                spline2d.spline2dbuildbicubic(y, x, ft, n, m, c2);
                            }
                            
                            //
                            // Test
                            //
                            for(i=1; i<=10; i++)
                            {
                                t1 = ax+(bx-ax)*math.randomreal();
                                t2 = ay+(by-ay)*math.randomreal();
                                err = Math.Max(err, Math.Abs(spline2d.spline2dcalc(c, t1, t2)-spline2d.spline2dcalc(c2, t2, t1)));
                            }
                        }
                        syerrors = syerrors | (double)(err)>(double)(10000*math.machineepsilon);
                    }
                }
            }
            
            //
            // Test resample
            //
            for(m=2; m<=6; m++)
            {
                for(n=2; n<=6; n++)
                {
                    f = new double[m-1+1, n-1+1];
                    x = new double[n-1+1];
                    y = new double[m-1+1];
                    for(j=0; j<=n-1; j++)
                    {
                        x[j] = (double)j/(double)(n-1);
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        y[i] = (double)i/(double)(m-1);
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            f[i,j] = Math.Exp(0.6*x[j])-Math.Exp(-(0.3*y[i])+0.08*x[j])+2*Math.Cos(Math.PI*(x[j]+1.2*y[i]))+0.1*Math.Cos(20*x[j]+15*y[i]);
                        }
                    }
                    for(m2=2; m2<=6; m2++)
                    {
                        for(n2=2; n2<=6; n2++)
                        {
                            for(pass=1; pass<=passcount; pass++)
                            {
                                for(jobtype=0; jobtype<=1; jobtype++)
                                {
                                    if( jobtype==0 )
                                    {
                                        spline2d.spline2dresamplebilinear(f, m, n, ref fr, m2, n2);
                                        spline2d.spline2dbuildbilinear(x, y, f, m, n, c);
                                    }
                                    if( jobtype==1 )
                                    {
                                        spline2d.spline2dresamplebicubic(f, m, n, ref fr, m2, n2);
                                        spline2d.spline2dbuildbicubic(x, y, f, m, n, c);
                                    }
                                    err = 0;
                                    mf = 0;
                                    for(i=0; i<=m2-1; i++)
                                    {
                                        for(j=0; j<=n2-1; j++)
                                        {
                                            v1 = spline2d.spline2dcalc(c, (double)j/(double)(n2-1), (double)i/(double)(m2-1));
                                            v2 = fr[i,j];
                                            err = Math.Max(err, Math.Abs(v1-v2));
                                            mf = Math.Max(mf, Math.Abs(v1));
                                        }
                                    }
                                    if( jobtype==0 )
                                    {
                                        rlerrors = rlerrors | (double)(err/mf)>(double)(10000*math.machineepsilon);
                                    }
                                    if( jobtype==1 )
                                    {
                                        rcerrors = rcerrors | (double)(err/mf)>(double)(10000*math.machineepsilon);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // report
            //
            waserrors = (((((((blerrors | bcerrors) | dserrors) | cperrors) | uperrors) | lterrors) | syerrors) | rlerrors) | rcerrors;
            if( !silent )
            {
                System.Console.Write("TESTING 2D INTERPOLATION");
                System.Console.WriteLine();
                
                //
                // Normal tests
                //
                System.Console.Write("BILINEAR TEST:                           ");
                if( blerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("BICUBIC TEST:                            ");
                if( bcerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("DIFFERENTIATION TEST:                    ");
                if( dserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("COPY/SERIALIZE TEST:                     ");
                if( cperrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("UNPACK TEST:                             ");
                if( uperrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LIN.TRANS. TEST:                         ");
                if( lterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("SPECIAL SYMMETRY TEST:                   ");
                if( syerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("BILINEAR RESAMPLING TEST:                ");
                if( rlerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("BICUBIC RESAMPLING TEST:                 ");
                if( rcerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                
                //
                // Summary
                //
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            
            //
            // end
            //
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Lipschitz constants for spline inself, first and second derivatives.
        *************************************************************************/
        private static void lconst(spline2d.spline2dinterpolant c,
            double[] lx,
            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;

            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(c, lx[j]-lstep/2, ly[i]-lstep/2, lstep/4, ref f1, ref fx1, ref fy1, ref fxy1);
                    twodnumder(c, lx[j]+lstep/2, ly[i]-lstep/2, lstep/4, ref f2, ref fx2, ref fy2, ref fxy2);
                    twodnumder(c, lx[j]+lstep/2, ly[i]+lstep/2, lstep/4, ref f3, ref fx3, ref fy3, ref fxy3);
                    twodnumder(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));
                }
            }
        }


        /*************************************************************************
        Numerical differentiation.
        *************************************************************************/
        private static void twodnumder(spline2d.spline2dinterpolant c,
            double x,
            double y,
            double h,
            ref double f,
            ref double fx,
            ref double fy,
            ref double fxy)
        {
            f = 0;
            fx = 0;
            fy = 0;
            fxy = 0;

            f = spline2d.spline2dcalc(c, x, y);
            fx = (spline2d.spline2dcalc(c, x+h, y)-spline2d.spline2dcalc(c, x-h, y))/(2*h);
            fy = (spline2d.spline2dcalc(c, x, y+h)-spline2d.spline2dcalc(c, x, y-h))/(2*h);
            fxy = (spline2d.spline2dcalc(c, x+h, y+h)-spline2d.spline2dcalc(c, x-h, y+h)-spline2d.spline2dcalc(c, x+h, y-h)+spline2d.spline2dcalc(c, x-h, y-h))/math.sqr(2*h);
        }


        /*************************************************************************
        Unpack test
        *************************************************************************/
        private static bool testunpack(spline2d.spline2dinterpolant c,
            double[] lx,
            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(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*math.randomreal())*(tbl[p,1]-tbl[p,0]);
                        ty = (0.001+0.999*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(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*math.machineepsilon);
            return result;
        }


        /*************************************************************************
        LinTrans test
        *************************************************************************/
        private static bool testlintrans(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*math.randomreal()-1;
                        }
                        while( (double)(a1)==(double)(0) );
                        a1 = a1*xjob;
                        b1 = 2*math.randomreal()-1;
                        do
                        {
                            a2 = 2*math.randomreal()-1;
                        }
                        while( (double)(a2)==(double)(0) );
                        a2 = a2*yjob;
                        b2 = 2*math.randomreal()-1;
                        
                        //
                        // Test XY
                        //
                        spline2d.spline2dcopy(c, c2);
                        spline2d.spline2dlintransxy(c2, a1, b1, a2, b2);
                        tx = ax+math.randomreal()*(bx-ax);
                        ty = ay+math.randomreal()*(by-ay);
                        if( xjob==0 )
                        {
                            tx = b1;
                            vx = ax+math.randomreal()*(bx-ax);
                        }
                        else
                        {
                            vx = (tx-b1)/a1;
                        }
                        if( yjob==0 )
                        {
                            ty = b2;
                            vy = ay+math.randomreal()*(by-ay);
                        }
                        else
                        {
                            vy = (ty-b2)/a2;
                        }
                        v1 = spline2d.spline2dcalc(c, tx, ty);
                        v2 = spline2d.spline2dcalc(c2, vx, vy);
                        err = Math.Max(err, Math.Abs(v1-v2));
                        
                        //
                        // Test F
                        //
                        spline2d.spline2dcopy(c, c2);
                        spline2d.spline2dlintransf(c2, a1, b1);
                        tx = ax+math.randomreal()*(bx-ax);
                        ty = ay+math.randomreal()*(by-ay);
                        v1 = spline2d.spline2dcalc(c, tx, ty);
                        v2 = spline2d.spline2dcalc(c2, tx, ty);
                        err = Math.Max(err, Math.Abs(a1*v1+b1-v2));
                    }
                }
            }
            result = (double)(err)<(double)(10000*math.machineepsilon);
            return result;
        }


        /*************************************************************************
        Unset spline, i.e. initialize it with random garbage
        *************************************************************************/
        private static void unsetspline2d(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, c);
        }


    }
    public class testspdgevdunit
    {
        /*************************************************************************
        Testing bidiagonal SVD decomposition subroutine
        *************************************************************************/
        public static bool testspdgevd(bool silent)
        {
            bool result = new bool();
            int pass = 0;
            int n = 0;
            int passcount = 0;
            int maxn = 0;
            int atask = 0;
            int btask = 0;
            double[] d = new double[0];
            double[] t1 = new double[0];
            double[,] a = new double[0,0];
            double[,] b = new double[0,0];
            double[,] afull = new double[0,0];
            double[,] bfull = new double[0,0];
            double[,] l = new double[0,0];
            double[,] z = new double[0,0];
            bool isuppera = new bool();
            bool isupperb = new bool();
            int i = 0;
            int j = 0;
            int minij = 0;
            double v = 0;
            double v1 = 0;
            double v2 = 0;
            double err = 0;
            double valerr = 0;
            double threshold = 0;
            bool waserrors = new bool();
            bool wfailed = new bool();
            bool wnsorted = new bool();
            int i_ = 0;

            threshold = 10000*math.machineepsilon;
            valerr = 0;
            wfailed = false;
            wnsorted = false;
            maxn = 20;
            passcount = 5;
            
            //
            // Main cycle
            //
            for(n=1; n<=maxn; n++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    for(atask=0; atask<=1; atask++)
                    {
                        for(btask=0; btask<=1; btask++)
                        {
                            isuppera = atask==0;
                            isupperb = btask==0;
                            
                            //
                            // Initialize A, B, AFull, BFull
                            //
                            t1 = new double[n-1+1];
                            a = new double[n-1+1, n-1+1];
                            b = new double[n-1+1, n-1+1];
                            afull = new double[n-1+1, n-1+1];
                            bfull = new double[n-1+1, n-1+1];
                            l = new double[n-1+1, n-1+1];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*math.randomreal()-1;
                                    a[j,i] = a[i,j];
                                    afull[i,j] = a[i,j];
                                    afull[j,i] = a[i,j];
                                }
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=i+1; j<=n-1; j++)
                                {
                                    l[i,j] = math.randomreal();
                                    l[j,i] = l[i,j];
                                }
                                l[i,i] = 1.5+math.randomreal();
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    minij = Math.Min(i, j);
                                    v = 0.0;
                                    for(i_=0; i_<=minij;i_++)
                                    {
                                        v += l[i,i_]*l[i_,j];
                                    }
                                    b[i,j] = v;
                                    b[j,i] = v;
                                    bfull[i,j] = v;
                                    bfull[j,i] = v;
                                }
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    if( isuppera )
                                    {
                                        if( j<i )
                                        {
                                            a[i,j] = 2*math.randomreal()-1;
                                        }
                                    }
                                    else
                                    {
                                        if( i<j )
                                        {
                                            a[i,j] = 2*math.randomreal()-1;
                                        }
                                    }
                                    if( isupperb )
                                    {
                                        if( j<i )
                                        {
                                            b[i,j] = 2*math.randomreal()-1;
                                        }
                                    }
                                    else
                                    {
                                        if( i<j )
                                        {
                                            b[i,j] = 2*math.randomreal()-1;
                                        }
                                    }
                                }
                            }
                            
                            //
                            // Problem 1
                            //
                            if( !spdgevd.smatrixgevd(a, n, isuppera, b, isupperb, 1, 1, ref d, ref z) )
                            {
                                wfailed = true;
                                continue;
                            }
                            err = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    v1 = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v1 += afull[i,i_]*z[i_,j];
                                    }
                                    v2 = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v2 += bfull[i,i_]*z[i_,j];
                                    }
                                    err = Math.Max(err, Math.Abs(v1-d[j]*v2));
                                }
                            }
                            valerr = Math.Max(err, valerr);
                            
                            //
                            // Problem 2
                            //
                            if( !spdgevd.smatrixgevd(a, n, isuppera, b, isupperb, 1, 2, ref d, ref z) )
                            {
                                wfailed = true;
                                continue;
                            }
                            err = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    v1 = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v1 += bfull[i,i_]*z[i_,j];
                                    }
                                    t1[i] = v1;
                                }
                                for(i=0; i<=n-1; i++)
                                {
                                    v2 = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v2 += afull[i,i_]*t1[i_];
                                    }
                                    err = Math.Max(err, Math.Abs(v2-d[j]*z[i,j]));
                                }
                            }
                            valerr = Math.Max(err, valerr);
                            
                            //
                            // Test problem 3
                            //
                            if( !spdgevd.smatrixgevd(a, n, isuppera, b, isupperb, 1, 3, ref d, ref z) )
                            {
                                wfailed = true;
                                continue;
                            }
                            err = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    v1 = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v1 += afull[i,i_]*z[i_,j];
                                    }
                                    t1[i] = v1;
                                }
                                for(i=0; i<=n-1; i++)
                                {
                                    v2 = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v2 += bfull[i,i_]*t1[i_];
                                    }
                                    err = Math.Max(err, Math.Abs(v2-d[j]*z[i,j]));
                                }
                            }
                            valerr = Math.Max(err, valerr);
                        }
                    }
                }
            }
            
            //
            // report
            //
            waserrors = ((double)(valerr)>(double)(threshold) | wfailed) | wnsorted;
            if( !silent )
            {
                System.Console.Write("TESTING SYMMETRIC GEVD");
                System.Console.WriteLine();
                System.Console.Write("Av-lambdav error (generalized):          ");
                System.Console.Write("{0,5:E3}",valerr);
                System.Console.WriteLine();
                System.Console.Write("Eigen values order:                      ");
                if( !wnsorted )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("Always converged:                        ");
                if( !wfailed )
                {
                    System.Console.Write("YES");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("NO");
                    System.Console.WriteLine();
                }
                System.Console.Write("Threshold:                               ");
                System.Console.Write("{0,5:E3}",threshold);
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


    }
    public class testinverseupdateunit
    {
        public static bool testinverseupdate(bool silent)
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            double[,] inva = new double[0,0];
            double[,] b1 = new double[0,0];
            double[,] b2 = new double[0,0];
            double[] u = new double[0];
            double[] v = new double[0];
            int n = 0;
            int maxn = 0;
            int i = 0;
            int updrow = 0;
            int updcol = 0;
            double val = 0;
            int pass = 0;
            int passcount = 0;
            bool waserrors = new bool();
            double threshold = 0;
            double c = 0;

            waserrors = false;
            maxn = 10;
            passcount = 100;
            threshold = 1.0E-6;
            
            //
            // process
            //
            for(n=1; n<=maxn; n++)
            {
                a = new double[n-1+1, n-1+1];
                b1 = new double[n-1+1, n-1+1];
                b2 = new double[n-1+1, n-1+1];
                u = new double[n-1+1];
                v = new double[n-1+1];
                for(pass=1; pass<=passcount; pass++)
                {
                    c = Math.Exp(math.randomreal()*Math.Log(10));
                    generaterandommatrixcond(ref a, n, c);
                    makeacopy(a, n, n, ref inva);
                    if( !invmat(ref inva, n) )
                    {
                        waserrors = true;
                        break;
                    }
                    
                    //
                    // Test simple update
                    //
                    updrow = math.randominteger(n);
                    updcol = math.randominteger(n);
                    val = 0.1*(2*math.randomreal()-1);
                    for(i=0; i<=n-1; i++)
                    {
                        if( i==updrow )
                        {
                            u[i] = val;
                        }
                        else
                        {
                            u[i] = 0;
                        }
                        if( i==updcol )
                        {
                            v[i] = 1;
                        }
                        else
                        {
                            v[i] = 0;
                        }
                    }
                    makeacopy(a, n, n, ref b1);
                    if( !updandinv(ref b1, u, v, n) )
                    {
                        waserrors = true;
                        break;
                    }
                    makeacopy(inva, n, n, ref b2);
                    inverseupdate.rmatrixinvupdatesimple(ref b2, n, updrow, updcol, val);
                    waserrors = waserrors | (double)(matrixdiff(b1, b2, n, n))>(double)(threshold);
                    
                    //
                    // Test row update
                    //
                    updrow = math.randominteger(n);
                    for(i=0; i<=n-1; i++)
                    {
                        if( i==updrow )
                        {
                            u[i] = 1;
                        }
                        else
                        {
                            u[i] = 0;
                        }
                        v[i] = 0.1*(2*math.randomreal()-1);
                    }
                    makeacopy(a, n, n, ref b1);
                    if( !updandinv(ref b1, u, v, n) )
                    {
                        waserrors = true;
                        break;
                    }
                    makeacopy(inva, n, n, ref b2);
                    inverseupdate.rmatrixinvupdaterow(ref b2, n, updrow, v);
                    waserrors = waserrors | (double)(matrixdiff(b1, b2, n, n))>(double)(threshold);
                    
                    //
                    // Test column update
                    //
                    updcol = math.randominteger(n);
                    for(i=0; i<=n-1; i++)
                    {
                        if( i==updcol )
                        {
                            v[i] = 1;
                        }
                        else
                        {
                            v[i] = 0;
                        }
                        u[i] = 0.1*(2*math.randomreal()-1);
                    }
                    makeacopy(a, n, n, ref b1);
                    if( !updandinv(ref b1, u, v, n) )
                    {
                        waserrors = true;
                        break;
                    }
                    makeacopy(inva, n, n, ref b2);
                    inverseupdate.rmatrixinvupdatecolumn(ref b2, n, updcol, u);
                    waserrors = waserrors | (double)(matrixdiff(b1, b2, n, n))>(double)(threshold);
                    
                    //
                    // Test full update
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        v[i] = 0.1*(2*math.randomreal()-1);
                        u[i] = 0.1*(2*math.randomreal()-1);
                    }
                    makeacopy(a, n, n, ref b1);
                    if( !updandinv(ref b1, u, v, n) )
                    {
                        waserrors = true;
                        break;
                    }
                    makeacopy(inva, n, n, ref b2);
                    inverseupdate.rmatrixinvupdateuv(ref b2, n, u, v);
                    waserrors = waserrors | (double)(matrixdiff(b1, b2, n, n))>(double)(threshold);
                }
            }
            
            //
            // report
            //
            if( !silent )
            {
                System.Console.Write("TESTING INVERSE UPDATE (REAL)");
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Copy
        *************************************************************************/
        private static void makeacopy(double[,] a,
            int m,
            int n,
            ref double[,] b)
        {
            int i = 0;
            int j = 0;

            b = new double[0,0];

            b = new double[m-1+1, n-1+1];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    b[i,j] = a[i,j];
                }
            }
        }


        /*************************************************************************
        LU decomposition
        *************************************************************************/
        private static void matlu(ref double[,] a,
            int m,
            int n,
            ref int[] pivots)
        {
            int i = 0;
            int j = 0;
            int jp = 0;
            double[] t1 = new double[0];
            double s = 0;
            int i_ = 0;

            pivots = new int[0];

            pivots = new int[Math.Min(m-1, n-1)+1];
            t1 = new double[Math.Max(m-1, n-1)+1];
            ap.assert(m>=0 & n>=0, "Error in LUDecomposition: incorrect function arguments");
            
            //
            // Quick return if possible
            //
            if( m==0 | n==0 )
            {
                return;
            }
            for(j=0; j<=Math.Min(m-1, n-1); j++)
            {
                
                //
                // Find pivot and test for singularity.
                //
                jp = j;
                for(i=j+1; i<=m-1; i++)
                {
                    if( (double)(Math.Abs(a[i,j]))>(double)(Math.Abs(a[jp,j])) )
                    {
                        jp = i;
                    }
                }
                pivots[j] = jp;
                if( (double)(a[jp,j])!=(double)(0) )
                {
                    
                    //
                    //Apply the interchange to rows
                    //
                    if( jp!=j )
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            t1[i_] = a[j,i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            a[j,i_] = a[jp,i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            a[jp,i_] = t1[i_];
                        }
                    }
                    
                    //
                    //Compute elements J+1:M of J-th column.
                    //
                    if( j<m )
                    {
                        jp = j+1;
                        s = 1/a[j,j];
                        for(i_=jp; i_<=m-1;i_++)
                        {
                            a[i_,j] = s*a[i_,j];
                        }
                    }
                }
                if( j<Math.Min(m, n)-1 )
                {
                    
                    //
                    //Update trailing submatrix.
                    //
                    jp = j+1;
                    for(i=j+1; i<=m-1; i++)
                    {
                        s = a[i,j];
                        for(i_=jp; i_<=n-1;i_++)
                        {
                            a[i,i_] = a[i,i_] - s*a[j,i_];
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Generate matrix with given condition number C (2-norm)
        *************************************************************************/
        private static void generaterandomorthogonalmatrix(ref double[,] a0,
            int n)
        {
            double t = 0;
            double lambdav = 0;
            int s = 0;
            int i = 0;
            int j = 0;
            double u1 = 0;
            double u2 = 0;
            double[] w = new double[0];
            double[] v = new double[0];
            double[,] a = new double[0,0];
            double sm = 0;
            int i_ = 0;

            if( n<=0 )
            {
                return;
            }
            w = new double[n+1];
            v = new double[n+1];
            a = new double[n+1, n+1];
            a0 = new double[n-1+1, n-1+1];
            
            //
            // Prepare A
            //
            for(i=1; i<=n; i++)
            {
                for(j=1; j<=n; j++)
                {
                    if( i==j )
                    {
                        a[i,j] = 1;
                    }
                    else
                    {
                        a[i,j] = 0;
                    }
                }
            }
            
            //
            // Calculate A using Stewart algorithm
            //
            for(s=2; s<=n; s++)
            {
                
                //
                // Prepare v and Lambda = v'*v
                //
                do
                {
                    i = 1;
                    while( i<=s )
                    {
                        u1 = 2*math.randomreal()-1;
                        u2 = 2*math.randomreal()-1;
                        sm = u1*u1+u2*u2;
                        if( (double)(sm)==(double)(0) | (double)(sm)>(double)(1) )
                        {
                            continue;
                        }
                        sm = Math.Sqrt(-(2*Math.Log(sm)/sm));
                        v[i] = u1*sm;
                        if( i+1<=s )
                        {
                            v[i+1] = u2*sm;
                        }
                        i = i+2;
                    }
                    lambdav = 0.0;
                    for(i_=1; i_<=s;i_++)
                    {
                        lambdav += v[i_]*v[i_];
                    }
                }
                while( (double)(lambdav)==(double)(0) );
                lambdav = 2/lambdav;
                
                //
                // A * (I - 2 vv'/v'v ) =
                //   = A - (2/v'v) * A * v * v' =
                //   = A - (2/v'v) * w * v'
                //  where w = Av
                //
                for(i=1; i<=s; i++)
                {
                    t = 0.0;
                    for(i_=1; i_<=s;i_++)
                    {
                        t += a[i,i_]*v[i_];
                    }
                    w[i] = t;
                }
                for(i=1; i<=s; i++)
                {
                    t = w[i]*lambdav;
                    for(i_=1; i_<=s;i_++)
                    {
                        a[i,i_] = a[i,i_] - t*v[i_];
                    }
                }
            }
            
            //
            //
            //
            for(i=1; i<=n; i++)
            {
                for(j=1; j<=n; j++)
                {
                    a0[i-1,j-1] = a[i,j];
                }
            }
        }


        private static void generaterandommatrixcond(ref double[,] a0,
            int n,
            double c)
        {
            double l1 = 0;
            double l2 = 0;
            double[,] q1 = new double[0,0];
            double[,] q2 = new double[0,0];
            double[] cc = new double[0];
            int i = 0;
            int j = 0;
            int k = 0;

            generaterandomorthogonalmatrix(ref q1, n);
            generaterandomorthogonalmatrix(ref q2, n);
            cc = new double[n-1+1];
            l1 = 0;
            l2 = Math.Log(1/c);
            cc[0] = Math.Exp(l1);
            for(i=1; i<=n-2; i++)
            {
                cc[i] = Math.Exp(math.randomreal()*(l2-l1)+l1);
            }
            cc[n-1] = Math.Exp(l2);
            a0 = new double[n-1+1, n-1+1];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a0[i,j] = 0;
                    for(k=0; k<=n-1; k++)
                    {
                        a0[i,j] = a0[i,j]+q1[i,k]*cc[k]*q2[j,k];
                    }
                }
            }
        }


        /*************************************************************************
        triangular inverse
        *************************************************************************/
        private static bool invmattr(ref double[,] a,
            int n,
            bool isupper,
            bool isunittriangular)
        {
            bool result = new bool();
            bool nounit = new bool();
            int i = 0;
            int j = 0;
            double v = 0;
            double ajj = 0;
            double[] t = new double[0];
            int i_ = 0;

            result = true;
            t = new double[n-1+1];
            
            //
            // Test the input parameters.
            //
            nounit = !isunittriangular;
            if( isupper )
            {
                
                //
                // Compute inverse of upper triangular matrix.
                //
                for(j=0; j<=n-1; j++)
                {
                    if( nounit )
                    {
                        if( (double)(a[j,j])==(double)(0) )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    
                    //
                    // Compute elements 1:j-1 of j-th column.
                    //
                    if( j>0 )
                    {
                        for(i_=0; i_<=j-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=0; i<=j-1; i++)
                        {
                            if( i<j-1 )
                            {
                                v = 0.0;
                                for(i_=i+1; i_<=j-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=0; i_<=j-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            else
            {
                
                //
                // Compute inverse of lower triangular matrix.
                //
                for(j=n-1; j>=0; j--)
                {
                    if( nounit )
                    {
                        if( (double)(a[j,j])==(double)(0) )
                        {
                            result = false;
                            return result;
                        }
                        a[j,j] = 1/a[j,j];
                        ajj = -a[j,j];
                    }
                    else
                    {
                        ajj = -1;
                    }
                    if( j<n-1 )
                    {
                        
                        //
                        // Compute elements j+1:n of j-th column.
                        //
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            t[i_] = a[i_,j];
                        }
                        for(i=j+1; i<=n-1; i++)
                        {
                            if( i>j+1 )
                            {
                                v = 0.0;
                                for(i_=j+1; i_<=i-1;i_++)
                                {
                                    v += a[i,i_]*t[i_];
                                }
                            }
                            else
                            {
                                v = 0;
                            }
                            if( nounit )
                            {
                                a[i,j] = v+a[i,i]*t[i];
                            }
                            else
                            {
                                a[i,j] = v+t[i];
                            }
                        }
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            a[i_,j] = ajj*a[i_,j];
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        LU inverse
        *************************************************************************/
        private static bool invmatlu(ref double[,] a,
            int[] pivots,
            int n)
        {
            bool result = new bool();
            double[] work = new double[0];
            int i = 0;
            int j = 0;
            int jp = 0;
            double v = 0;
            int i_ = 0;

            result = true;
            
            //
            // Quick return if possible
            //
            if( n==0 )
            {
                return result;
            }
            work = new double[n-1+1];
            
            //
            // Form inv(U)
            //
            if( !invmattr(ref a, n, true, false) )
            {
                result = false;
                return result;
            }
            
            //
            // Solve the equation inv(A)*L = inv(U) for inv(A).
            //
            for(j=n-1; j>=0; j--)
            {
                
                //
                // Copy current column of L to WORK and replace with zeros.
                //
                for(i=j+1; i<=n-1; i++)
                {
                    work[i] = a[i,j];
                    a[i,j] = 0;
                }
                
                //
                // Compute current column of inv(A).
                //
                if( j<n-1 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=j+1; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*work[i_];
                        }
                        a[i,j] = a[i,j]-v;
                    }
                }
            }
            
            //
            // Apply column interchanges.
            //
            for(j=n-2; j>=0; j--)
            {
                jp = pivots[j];
                if( jp!=j )
                {
                    for(i_=0; i_<=n-1;i_++)
                    {
                        work[i_] = a[i_,j];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        a[i_,j] = a[i_,jp];
                    }
                    for(i_=0; i_<=n-1;i_++)
                    {
                        a[i_,jp] = work[i_];
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Matrix inverse
        *************************************************************************/
        private static bool invmat(ref double[,] a,
            int n)
        {
            bool result = new bool();
            int[] pivots = new int[0];

            matlu(ref a, n, n, ref pivots);
            result = invmatlu(ref a, pivots, n);
            return result;
        }


        /*************************************************************************
        Diff
        *************************************************************************/
        private static double matrixdiff(double[,] a,
            double[,] b,
            int m,
            int n)
        {
            double result = 0;
            int i = 0;
            int j = 0;

            result = 0;
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    result = Math.Max(result, Math.Abs(b[i,j]-a[i,j]));
                }
            }
            return result;
        }


        /*************************************************************************
        Update and inverse
        *************************************************************************/
        private static bool updandinv(ref double[,] a,
            double[] u,
            double[] v,
            int n)
        {
            bool result = new bool();
            int[] pivots = new int[0];
            int i = 0;
            double r = 0;
            int i_ = 0;

            for(i=0; i<=n-1; i++)
            {
                r = u[i];
                for(i_=0; i_<=n-1;i_++)
                {
                    a[i,i_] = a[i,i_] + r*v[i_];
                }
            }
            matlu(ref a, n, n, ref pivots);
            result = invmatlu(ref a, pivots, n);
            return result;
        }


    }
    public class testschurunit
    {
        /*************************************************************************
        Testing Schur decomposition subroutine
        *************************************************************************/
        public static bool testschur(bool silent)
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            int n = 0;
            int maxn = 0;
            int i = 0;
            int j = 0;
            int pass = 0;
            int passcount = 0;
            bool waserrors = new bool();
            bool errstruct = new bool();
            bool wfailed = new bool();
            double materr = 0;
            double orterr = 0;
            double threshold = 0;

            materr = 0;
            orterr = 0;
            errstruct = false;
            wfailed = false;
            waserrors = false;
            maxn = 70;
            passcount = 1;
            threshold = 5*100*math.machineepsilon;
            a = new double[maxn-1+1, maxn-1+1];
            
            //
            // zero matrix, several cases
            //
            for(i=0; i<=maxn-1; i++)
            {
                for(j=0; j<=maxn-1; j++)
                {
                    a[i,j] = 0;
                }
            }
            for(n=1; n<=maxn; n++)
            {
                if( n>30 & n%2==0 )
                {
                    continue;
                }
                testschurproblem(a, n, ref materr, ref orterr, ref errstruct, ref wfailed);
            }
            
            //
            // Dense matrix
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    if( n>30 & n%2==0 )
                    {
                        continue;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 2*math.randomreal()-1;
                        }
                    }
                    testschurproblem(a, n, ref materr, ref orterr, ref errstruct, ref wfailed);
                }
            }
            
            //
            // Sparse matrices, very sparse matrices, incredible sparse matrices
            //
            for(pass=1; pass<=1; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    if( n>30 & n%3!=0 )
                    {
                        continue;
                    }
                    fillsparsea(ref a, n, 0.8);
                    testschurproblem(a, n, ref materr, ref orterr, ref errstruct, ref wfailed);
                    fillsparsea(ref a, n, 0.9);
                    testschurproblem(a, n, ref materr, ref orterr, ref errstruct, ref wfailed);
                    fillsparsea(ref a, n, 0.95);
                    testschurproblem(a, n, ref materr, ref orterr, ref errstruct, ref wfailed);
                    fillsparsea(ref a, n, 0.997);
                    testschurproblem(a, n, ref materr, ref orterr, ref errstruct, ref wfailed);
                }
            }
            
            //
            // report
            //
            waserrors = (((double)(materr)>(double)(threshold) | (double)(orterr)>(double)(threshold)) | errstruct) | wfailed;
            if( !silent )
            {
                System.Console.Write("TESTING SCHUR DECOMPOSITION");
                System.Console.WriteLine();
                System.Console.Write("Schur decomposition error:               ");
                System.Console.Write("{0,5:E3}",materr);
                System.Console.WriteLine();
                System.Console.Write("Schur orthogonality error:               ");
                System.Console.Write("{0,5:E3}",orterr);
                System.Console.WriteLine();
                System.Console.Write("T matrix structure:                      ");
                if( !errstruct )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("Always converged:                        ");
                if( !wfailed )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("Threshold:                               ");
                System.Console.Write("{0,5:E3}",threshold);
                System.Console.WriteLine();
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        private static void fillsparsea(ref double[,] a,
            int n,
            double sparcity)
        {
            int i = 0;
            int j = 0;

            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (double)(math.randomreal())>=(double)(sparcity) )
                    {
                        a[i,j] = 2*math.randomreal()-1;
                    }
                    else
                    {
                        a[i,j] = 0;
                    }
                }
            }
        }


        private static void testschurproblem(double[,] a,
            int n,
            ref double materr,
            ref double orterr,
            ref bool errstruct,
            ref bool wfailed)
        {
            double[,] s = new double[0,0];
            double[,] t = new double[0,0];
            double[] sr = new double[0];
            double[] astc = new double[0];
            double[] sastc = new double[0];
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            double locerr = 0;
            int i_ = 0;

            sr = new double[n-1+1];
            astc = new double[n-1+1];
            sastc = new double[n-1+1];
            
            //
            // Schur decomposition, convergence test
            //
            t = new double[n-1+1, n-1+1];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    t[i,j] = a[i,j];
                }
            }
            if( !schur.rmatrixschur(ref t, n, ref s) )
            {
                wfailed = true;
                return;
            }
            
            //
            // decomposition error
            //
            locerr = 0;
            for(j=0; j<=n-1; j++)
            {
                for(i_=0; i_<=n-1;i_++)
                {
                    sr[i_] = s[j,i_];
                }
                for(k=0; k<=n-1; k++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += t[k,i_]*sr[i_];
                    }
                    astc[k] = v;
                }
                for(k=0; k<=n-1; k++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += s[k,i_]*astc[i_];
                    }
                    sastc[k] = v;
                }
                for(k=0; k<=n-1; k++)
                {
                    locerr = Math.Max(locerr, Math.Abs(sastc[k]-a[k,j]));
                }
            }
            materr = Math.Max(materr, locerr);
            
            //
            // orthogonality error
            //
            locerr = 0;
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += s[i_,i]*s[i_,j];
                    }
                    if( i!=j )
                    {
                        locerr = Math.Max(locerr, Math.Abs(v));
                    }
                    else
                    {
                        locerr = Math.Max(locerr, Math.Abs(v-1));
                    }
                }
            }
            orterr = Math.Max(orterr, locerr);
            
            //
            // T matrix structure
            //
            for(j=0; j<=n-1; j++)
            {
                for(i=j+2; i<=n-1; i++)
                {
                    if( (double)(t[i,j])!=(double)(0) )
                    {
                        errstruct = true;
                    }
                }
            }
        }


    }
    public class testnlequnit
    {
        public static bool testnleq(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool basicserrors = new bool();
            bool converror = new bool();
            bool othererrors = new bool();
            int n = 0;
            double[] x = new double[0];
            int i = 0;
            int k = 0;
            double v = 0;
            double flast = 0;
            double[] xlast = new double[0];
            bool firstrep = new bool();
            int nfunc = 0;
            int njac = 0;
            int itcnt = 0;
            nleq.nleqstate state = new nleq.nleqstate();
            nleq.nleqreport rep = new nleq.nleqreport();
            int pass = 0;
            int passcount = 0;
            double epsf = 0;
            double stpmax = 0;
            int i_ = 0;

            waserrors = false;
            basicserrors = false;
            converror = false;
            othererrors = false;
            
            //
            // Basic tests
            //
            // Test with Himmelblau's function (M):
            // * ability to find correct result
            // * ability to work after soft restart (restart after finish)
            // * ability to work after hard restart (restart in the middle of optimization)
            //
            passcount = 100;
            for(pass=0; pass<=passcount-1; pass++)
            {
                
                //
                // Ability to find correct result
                //
                x = new double[2];
                x[0] = 20*math.randomreal()-10;
                x[1] = 20*math.randomreal()-10;
                nleq.nleqcreatelm(2, 2, x, state);
                epsf = 1.0E-9;
                nleq.nleqsetcond(state, epsf, 0);
                while( nleq.nleqiteration(state) )
                {
                    testfunchbm(state);
                }
                nleq.nleqresults(state, ref x, rep);
                if( rep.terminationtype>0 )
                {
                    basicserrors = basicserrors | (double)(math.sqr(x[0]*x[0]+x[1]-11)+math.sqr(x[0]+x[1]*x[1]-7))>(double)(math.sqr(epsf));
                }
                else
                {
                    basicserrors = true;
                }
                
                //
                // Ability to work after soft restart
                //
                x = new double[2];
                x[0] = 20*math.randomreal()-10;
                x[1] = 20*math.randomreal()-10;
                nleq.nleqcreatelm(2, 2, x, state);
                epsf = 1.0E-9;
                nleq.nleqsetcond(state, epsf, 0);
                while( nleq.nleqiteration(state) )
                {
                    testfunchbm(state);
                }
                nleq.nleqresults(state, ref x, rep);
                x = new double[2];
                x[0] = 20*math.randomreal()-10;
                x[1] = 20*math.randomreal()-10;
                nleq.nleqrestartfrom(state, x);
                while( nleq.nleqiteration(state) )
                {
                    testfunchbm(state);
                }
                nleq.nleqresults(state, ref x, rep);
                if( rep.terminationtype>0 )
                {
                    basicserrors = basicserrors | (double)(math.sqr(x[0]*x[0]+x[1]-11)+math.sqr(x[0]+x[1]*x[1]-7))>(double)(math.sqr(epsf));
                }
                else
                {
                    basicserrors = true;
                }
                
                //
                // Ability to work after hard restart:
                // * stopping condition: small F
                // * StpMax is so small that we need about 10000 iterations to
                //   find solution (steps are small)
                // * choose random K significantly less that 9999
                // * iterate for some time, then break, restart optimization
                //
                x = new double[2];
                x[0] = 100;
                x[1] = 100;
                nleq.nleqcreatelm(2, 2, x, state);
                epsf = 1.0E-9;
                nleq.nleqsetcond(state, epsf, 0);
                nleq.nleqsetstpmax(state, 0.01);
                k = 1+math.randominteger(100);
                for(i=0; i<=k-1; i++)
                {
                    if( !nleq.nleqiteration(state) )
                    {
                        break;
                    }
                    testfunchbm(state);
                }
                x = new double[2];
                x[0] = 20*math.randomreal()-10;
                x[1] = 20*math.randomreal()-10;
                nleq.nleqrestartfrom(state, x);
                while( nleq.nleqiteration(state) )
                {
                    testfunchbm(state);
                }
                nleq.nleqresults(state, ref x, rep);
                if( rep.terminationtype>0 )
                {
                    basicserrors = basicserrors | (double)(math.sqr(x[0]*x[0]+x[1]-11)+math.sqr(x[0]+x[1]*x[1]-7))>(double)(math.sqr(epsf));
                }
                else
                {
                    basicserrors = true;
                }
            }
            
            //
            // Basic tests
            //
            // Test with Himmelblau's function (1):
            // * ability to find correct result
            //
            passcount = 100;
            for(pass=0; pass<=passcount-1; pass++)
            {
                
                //
                // Ability to find correct result
                //
                x = new double[2];
                x[0] = 20*math.randomreal()-10;
                x[1] = 20*math.randomreal()-10;
                nleq.nleqcreatelm(2, 1, x, state);
                epsf = 1.0E-9;
                nleq.nleqsetcond(state, epsf, 0);
                while( nleq.nleqiteration(state) )
                {
                    testfunchb1(state);
                }
                nleq.nleqresults(state, ref x, rep);
                if( rep.terminationtype>0 )
                {
                    basicserrors = basicserrors | (double)(math.sqr(x[0]*x[0]+x[1]-11)+math.sqr(x[0]+x[1]*x[1]-7))>(double)(epsf);
                }
                else
                {
                    basicserrors = true;
                }
            }
            
            //
            // Basic tests
            //
            // Ability to detect situation when we can't find minimum
            //
            passcount = 100;
            for(pass=0; pass<=passcount-1; pass++)
            {
                x = new double[2];
                x[0] = 20*math.randomreal()-10;
                x[1] = 20*math.randomreal()-10;
                nleq.nleqcreatelm(2, 3, x, state);
                epsf = 1.0E-9;
                nleq.nleqsetcond(state, epsf, 0);
                while( nleq.nleqiteration(state) )
                {
                    testfuncshbm(state);
                }
                nleq.nleqresults(state, ref x, rep);
                basicserrors = basicserrors | rep.terminationtype!=-4;
            }
            
            //
            // Test correctness of intermediate reports and final report:
            // * first report is starting point
            // * function value decreases on subsequent reports
            // * function value is correctly reported
            // * last report is final point
            // * NFunc and NJac are compared with values counted directly
            // * IterationsCount is compared with value counter directly
            //
            n = 2;
            x = new double[n];
            xlast = new double[n];
            x[0] = 20*math.randomreal()-10;
            x[1] = 20*math.randomreal()-10;
            xlast[0] = math.maxrealnumber;
            xlast[1] = math.maxrealnumber;
            nleq.nleqcreatelm(n, 2, x, state);
            nleq.nleqsetcond(state, 1.0E-6, 0);
            nleq.nleqsetxrep(state, true);
            firstrep = true;
            flast = math.maxrealnumber;
            nfunc = 0;
            njac = 0;
            itcnt = 0;
            while( nleq.nleqiteration(state) )
            {
                if( state.xupdated )
                {
                    
                    //
                    // first report must be starting point
                    //
                    if( firstrep )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            othererrors = othererrors | (double)(state.x[i])!=(double)(x[i]);
                        }
                        firstrep = false;
                    }
                    
                    //
                    // function value must decrease
                    //
                    othererrors = othererrors | (double)(state.f)>(double)(flast);
                    
                    //
                    // check correctness of function value
                    //
                    v = math.sqr(state.x[0]*state.x[0]+state.x[1]-11)+math.sqr(state.x[0]+state.x[1]*state.x[1]-7);
                    othererrors = othererrors | (double)(Math.Abs(v-state.f)/Math.Max(v, 1))>(double)(100*math.machineepsilon);
                    
                    //
                    // update info and continue
                    //
                    for(i_=0; i_<=n-1;i_++)
                    {
                        xlast[i_] = state.x[i_];
                    }
                    flast = state.f;
                    itcnt = itcnt+1;
                    continue;
                }
                if( state.needf )
                {
                    nfunc = nfunc+1;
                }
                if( state.needfij )
                {
                    nfunc = nfunc+1;
                    njac = njac+1;
                }
                testfunchbm(state);
            }
            nleq.nleqresults(state, ref x, rep);
            if( rep.terminationtype>0 )
            {
                othererrors = (othererrors | (double)(xlast[0])!=(double)(x[0])) | (double)(xlast[1])!=(double)(x[1]);
                v = math.sqr(x[0]*x[0]+x[1]-11)+math.sqr(x[0]+x[1]*x[1]-7);
                othererrors = othererrors | (double)(Math.Abs(flast-v)/Math.Max(v, 1))>(double)(100*math.machineepsilon);
            }
            else
            {
                converror = true;
            }
            othererrors = othererrors | rep.nfunc!=nfunc;
            othererrors = othererrors | rep.njac!=njac;
            othererrors = othererrors | rep.iterationscount!=itcnt-1;
            
            //
            // Test ability to set limit on algorithm steps
            //
            x = new double[2];
            xlast = new double[2];
            x[0] = 20*math.randomreal()+20;
            x[1] = 20*math.randomreal()+20;
            xlast[0] = x[0];
            xlast[1] = x[1];
            stpmax = 0.1+0.1*math.randomreal();
            epsf = 1.0E-9;
            nleq.nleqcreatelm(2, 3, x, state);
            nleq.nleqsetstpmax(state, stpmax);
            nleq.nleqsetcond(state, epsf, 0);
            nleq.nleqsetxrep(state, true);
            while( nleq.nleqiteration(state) )
            {
                if( state.needf | state.needfij )
                {
                    testfunchbm(state);
                }
                if( (state.needf | state.needfij) | state.xupdated )
                {
                    othererrors = othererrors | (double)(Math.Sqrt(math.sqr(state.x[0]-xlast[0])+math.sqr(state.x[1]-xlast[1])))>(double)(1.00001*stpmax);
                }
                if( state.xupdated )
                {
                    xlast[0] = state.x[0];
                    xlast[1] = state.x[1];
                }
            }
            
            //
            // end
            //
            waserrors = (basicserrors | converror) | othererrors;
            if( !silent )
            {
                System.Console.Write("TESTING NLEQ SOLVER");
                System.Console.WriteLine();
                System.Console.Write("BASIC FUNCTIONALITY:                      ");
                if( basicserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("CONVERGENCE:                              ");
                if( converror )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("OTHER PROPERTIES:                         ");
                if( othererrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Himmelblau's function

            F = (x^2+y-11)^2 + (x+y^2-7)^2

        posed as system of M functions:

            f0 = x^2+y-11
            f1 = x+y^2-7

        *************************************************************************/
        private static void testfunchbm(nleq.nleqstate state)
        {
            double x = 0;
            double y = 0;

            ap.assert(state.needf | state.needfij, "TestNLEQUnit: internal error!");
            x = state.x[0];
            y = state.x[1];
            if( state.needf )
            {
                state.f = math.sqr(x*x+y-11)+math.sqr(x+y*y-7);
                return;
            }
            if( state.needfij )
            {
                state.fi[0] = x*x+y-11;
                state.fi[1] = x+y*y-7;
                state.j[0,0] = 2*x;
                state.j[0,1] = 1;
                state.j[1,0] = 1;
                state.j[1,1] = 2*y;
                return;
            }
        }


        /*************************************************************************
        Himmelblau's function

            F = (x^2+y-11)^2 + (x+y^2-7)^2

        posed as system of 1 function
        *************************************************************************/
        private static void testfunchb1(nleq.nleqstate state)
        {
            double x = 0;
            double y = 0;

            ap.assert(state.needf | state.needfij, "TestNLEQUnit: internal error!");
            x = state.x[0];
            y = state.x[1];
            if( state.needf )
            {
                state.f = math.sqr(math.sqr(x*x+y-11)+math.sqr(x+y*y-7));
                return;
            }
            if( state.needfij )
            {
                state.fi[0] = math.sqr(x*x+y-11)+math.sqr(x+y*y-7);
                state.j[0,0] = 2*(x*x+y-11)*2*x+2*(x+y*y-7);
                state.j[0,1] = 2*(x*x+y-11)+2*(x+y*y-7)*2*y;
                return;
            }
        }


        /*************************************************************************
        Shifted Himmelblau's function

            F = (x^2+y-11)^2 + (x+y^2-7)^2 + 1

        posed as system of M functions:

            f0 = x^2+y-11
            f1 = x+y^2-7
            f2 = 1

        This function is used to test algorithm on problem which has no solution.
        *************************************************************************/
        private static void testfuncshbm(nleq.nleqstate state)
        {
            double x = 0;
            double y = 0;

            ap.assert(state.needf | state.needfij, "TestNLEQUnit: internal error!");
            x = state.x[0];
            y = state.x[1];
            if( state.needf )
            {
                state.f = math.sqr(x*x+y-11)+math.sqr(x+y*y-7)+1;
                return;
            }
            if( state.needfij )
            {
                state.fi[0] = x*x+y-11;
                state.fi[1] = x+y*y-7;
                state.fi[2] = 1;
                state.j[0,0] = 2*x;
                state.j[0,1] = 1;
                state.j[1,0] = 1;
                state.j[1,1] = 2*y;
                state.j[2,0] = 0;
                state.j[2,1] = 0;
                return;
            }
        }


    }
    public class testchebyshevunit
    {
        public static bool testchebyshev(bool silent)
        {
            bool result = new bool();
            double err = 0;
            double sumerr = 0;
            double cerr = 0;
            double ferr = 0;
            double threshold = 0;
            double x = 0;
            double v = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int maxn = 0;
            double[] c = new double[0];
            double[] p1 = new double[0];
            double[] p2 = new double[0];
            double[,] a = new double[0,0];
            bool waserrors = new bool();
            int i_ = 0;

            err = 0;
            sumerr = 0;
            cerr = 0;
            ferr = 0;
            threshold = 1.0E-9;
            waserrors = false;
            
            //
            // Testing Chebyshev polynomials of the first kind
            //
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 0, 0.00)-1));
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 0, 0.33)-1));
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 0, -0.42)-1));
            x = 0.2;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 1, x)-0.2));
            x = 0.4;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 1, x)-0.4));
            x = 0.6;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 1, x)-0.6));
            x = 0.8;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 1, x)-0.8));
            x = 1.0;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 1, x)-1.0));
            x = 0.2;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 2, x)+0.92));
            x = 0.4;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 2, x)+0.68));
            x = 0.6;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 2, x)+0.28));
            x = 0.8;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 2, x)-0.28));
            x = 1.0;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, 2, x)-1.00));
            n = 10;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, n, 0.2)-0.4284556288));
            n = 11;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, n, 0.2)+0.7996160205));
            n = 12;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(1, n, 0.2)+0.7483020370));
            
            //
            // Testing Chebyshev polynomials of the second kind
            //
            n = 0;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(2, n, 0.2)-1.0000000000));
            n = 1;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(2, n, 0.2)-0.4000000000));
            n = 2;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(2, n, 0.2)+0.8400000000));
            n = 3;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(2, n, 0.2)+0.7360000000));
            n = 4;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(2, n, 0.2)-0.5456000000));
            n = 10;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(2, n, 0.2)-0.6128946176));
            n = 11;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(2, n, 0.2)+0.6770370970));
            n = 12;
            err = Math.Max(err, Math.Abs(chebyshev.chebyshevcalculate(2, n, 0.2)+0.8837094564));
            
            //
            // Testing Clenshaw summation
            //
            maxn = 20;
            c = new double[maxn+1];
            for(k=1; k<=2; k++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    x = 2*math.randomreal()-1;
                    v = 0;
                    for(n=0; n<=maxn; n++)
                    {
                        c[n] = 2*math.randomreal()-1;
                        v = v+chebyshev.chebyshevcalculate(k, n, x)*c[n];
                        sumerr = Math.Max(sumerr, Math.Abs(v-chebyshev.chebyshevsum(c, k, n, x)));
                    }
                }
            }
            
            //
            // Testing coefficients
            //
            chebyshev.chebyshevcoefficients(0, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-1));
            chebyshev.chebyshevcoefficients(1, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-0));
            cerr = Math.Max(cerr, Math.Abs(c[1]-1));
            chebyshev.chebyshevcoefficients(2, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]+1));
            cerr = Math.Max(cerr, Math.Abs(c[1]-0));
            cerr = Math.Max(cerr, Math.Abs(c[2]-2));
            chebyshev.chebyshevcoefficients(3, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-0));
            cerr = Math.Max(cerr, Math.Abs(c[1]+3));
            cerr = Math.Max(cerr, Math.Abs(c[2]-0));
            cerr = Math.Max(cerr, Math.Abs(c[3]-4));
            chebyshev.chebyshevcoefficients(4, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-1));
            cerr = Math.Max(cerr, Math.Abs(c[1]-0));
            cerr = Math.Max(cerr, Math.Abs(c[2]+8));
            cerr = Math.Max(cerr, Math.Abs(c[3]-0));
            cerr = Math.Max(cerr, Math.Abs(c[4]-8));
            chebyshev.chebyshevcoefficients(9, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-0));
            cerr = Math.Max(cerr, Math.Abs(c[1]-9));
            cerr = Math.Max(cerr, Math.Abs(c[2]-0));
            cerr = Math.Max(cerr, Math.Abs(c[3]+120));
            cerr = Math.Max(cerr, Math.Abs(c[4]-0));
            cerr = Math.Max(cerr, Math.Abs(c[5]-432));
            cerr = Math.Max(cerr, Math.Abs(c[6]-0));
            cerr = Math.Max(cerr, Math.Abs(c[7]+576));
            cerr = Math.Max(cerr, Math.Abs(c[8]-0));
            cerr = Math.Max(cerr, Math.Abs(c[9]-256));
            
            //
            // Testing FromChebyshev
            //
            maxn = 10;
            a = new double[maxn+1, maxn+1];
            for(i=0; i<=maxn; i++)
            {
                for(j=0; j<=maxn; j++)
                {
                    a[i,j] = 0;
                }
                chebyshev.chebyshevcoefficients(i, ref c);
                for(i_=0; i_<=i;i_++)
                {
                    a[i,i_] = c[i_];
                }
            }
            c = new double[maxn+1];
            p1 = new double[maxn+1];
            for(n=0; n<=maxn; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    for(i=0; i<=n; i++)
                    {
                        p1[i] = 0;
                    }
                    for(i=0; i<=n; i++)
                    {
                        c[i] = 2*math.randomreal()-1;
                        v = c[i];
                        for(i_=0; i_<=i;i_++)
                        {
                            p1[i_] = p1[i_] + v*a[i,i_];
                        }
                    }
                    chebyshev.fromchebyshev(c, n, ref p2);
                    for(i=0; i<=n; i++)
                    {
                        ferr = Math.Max(ferr, Math.Abs(p1[i]-p2[i]));
                    }
                }
            }
            
            //
            // Reporting
            //
            waserrors = (((double)(err)>(double)(threshold) | (double)(sumerr)>(double)(threshold)) | (double)(cerr)>(double)(threshold)) | (double)(ferr)>(double)(threshold);
            if( !silent )
            {
                System.Console.Write("TESTING CALCULATION OF THE CHEBYSHEV POLYNOMIALS");
                System.Console.WriteLine();
                System.Console.Write("Max error against table                   ");
                System.Console.Write("{0,5:E2}",err);
                System.Console.WriteLine();
                System.Console.Write("Summation error                           ");
                System.Console.Write("{0,5:E2}",sumerr);
                System.Console.WriteLine();
                System.Console.Write("Coefficients error                        ");
                System.Console.Write("{0,5:E2}",cerr);
                System.Console.WriteLine();
                System.Console.Write("FrobChebyshev error                       ");
                System.Console.Write("{0,5:E2}",ferr);
                System.Console.WriteLine();
                System.Console.Write("Threshold                                 ");
                System.Console.Write("{0,5:E2}",threshold);
                System.Console.WriteLine();
                if( !waserrors )
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


    }
    public class testhermiteunit
    {
        public static bool testhermite(bool silent)
        {
            bool result = new bool();
            double err = 0;
            double sumerr = 0;
            double cerr = 0;
            double threshold = 0;
            int n = 0;
            int maxn = 0;
            int pass = 0;
            double[] c = new double[0];
            double x = 0;
            double v = 0;
            bool waserrors = new bool();

            err = 0;
            sumerr = 0;
            cerr = 0;
            threshold = 1.0E-9;
            waserrors = false;
            
            //
            // Testing Hermite polynomials
            //
            n = 0;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)-1));
            n = 1;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)-2));
            n = 2;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)-2));
            n = 3;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)+4));
            n = 4;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)+20));
            n = 5;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)+8));
            n = 6;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)-184));
            n = 7;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)-464));
            n = 11;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)-230848));
            n = 12;
            err = Math.Max(err, Math.Abs(hermite.hermitecalculate(n, 1)-280768));
            
            //
            // Testing Clenshaw summation
            //
            maxn = 10;
            c = new double[maxn+1];
            for(pass=1; pass<=10; pass++)
            {
                x = 2*math.randomreal()-1;
                v = 0;
                for(n=0; n<=maxn; n++)
                {
                    c[n] = 2*math.randomreal()-1;
                    v = v+hermite.hermitecalculate(n, x)*c[n];
                    sumerr = Math.Max(sumerr, Math.Abs(v-hermite.hermitesum(c, n, x)));
                }
            }
            
            //
            // Testing coefficients
            //
            hermite.hermitecoefficients(0, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-1));
            hermite.hermitecoefficients(1, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-0));
            cerr = Math.Max(cerr, Math.Abs(c[1]-2));
            hermite.hermitecoefficients(2, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]+2));
            cerr = Math.Max(cerr, Math.Abs(c[1]-0));
            cerr = Math.Max(cerr, Math.Abs(c[2]-4));
            hermite.hermitecoefficients(3, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-0));
            cerr = Math.Max(cerr, Math.Abs(c[1]+12));
            cerr = Math.Max(cerr, Math.Abs(c[2]-0));
            cerr = Math.Max(cerr, Math.Abs(c[3]-8));
            hermite.hermitecoefficients(4, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-12));
            cerr = Math.Max(cerr, Math.Abs(c[1]-0));
            cerr = Math.Max(cerr, Math.Abs(c[2]+48));
            cerr = Math.Max(cerr, Math.Abs(c[3]-0));
            cerr = Math.Max(cerr, Math.Abs(c[4]-16));
            hermite.hermitecoefficients(5, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-0));
            cerr = Math.Max(cerr, Math.Abs(c[1]-120));
            cerr = Math.Max(cerr, Math.Abs(c[2]-0));
            cerr = Math.Max(cerr, Math.Abs(c[3]+160));
            cerr = Math.Max(cerr, Math.Abs(c[4]-0));
            cerr = Math.Max(cerr, Math.Abs(c[5]-32));
            hermite.hermitecoefficients(6, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]+120));
            cerr = Math.Max(cerr, Math.Abs(c[1]-0));
            cerr = Math.Max(cerr, Math.Abs(c[2]-720));
            cerr = Math.Max(cerr, Math.Abs(c[3]-0));
            cerr = Math.Max(cerr, Math.Abs(c[4]+480));
            cerr = Math.Max(cerr, Math.Abs(c[5]-0));
            cerr = Math.Max(cerr, Math.Abs(c[6]-64));
            
            //
            // Reporting
            //
            waserrors = ((double)(err)>(double)(threshold) | (double)(sumerr)>(double)(threshold)) | (double)(cerr)>(double)(threshold);
            if( !silent )
            {
                System.Console.Write("TESTING CALCULATION OF THE HERMITE POLYNOMIALS");
                System.Console.WriteLine();
                System.Console.Write("Max error                                 ");
                System.Console.Write("{0,5:E2}",err);
                System.Console.WriteLine();
                System.Console.Write("Summation error                           ");
                System.Console.Write("{0,5:E2}",sumerr);
                System.Console.WriteLine();
                System.Console.Write("Coefficients error                        ");
                System.Console.Write("{0,5:E2}",cerr);
                System.Console.WriteLine();
                System.Console.Write("Threshold                                 ");
                System.Console.Write("{0,5:E2}",threshold);
                System.Console.WriteLine();
                if( !waserrors )
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


    }
    public class testlaguerreunit
    {
        public static bool testlaguerre(bool silent)
        {
            bool result = new bool();
            double err = 0;
            double sumerr = 0;
            double cerr = 0;
            double threshold = 0;
            int n = 0;
            int maxn = 0;
            int pass = 0;
            double[] c = new double[0];
            double x = 0;
            double v = 0;
            bool waserrors = new bool();

            err = 0;
            sumerr = 0;
            cerr = 0;
            threshold = 1.0E-9;
            waserrors = false;
            
            //
            // Testing Laguerre polynomials
            //
            n = 0;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)-1.0000000000));
            n = 1;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)-0.5000000000));
            n = 2;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)-0.1250000000));
            n = 3;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.1458333333));
            n = 4;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.3307291667));
            n = 5;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.4455729167));
            n = 6;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.5041449653));
            n = 7;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.5183392237));
            n = 8;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.4983629984));
            n = 9;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.4529195204));
            n = 10;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.3893744141));
            n = 11;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.3139072988));
            n = 12;
            err = Math.Max(err, Math.Abs(laguerre.laguerrecalculate(n, 0.5)+0.2316496389));
            
            //
            // Testing Clenshaw summation
            //
            maxn = 20;
            c = new double[maxn+1];
            for(pass=1; pass<=10; pass++)
            {
                x = 2*math.randomreal()-1;
                v = 0;
                for(n=0; n<=maxn; n++)
                {
                    c[n] = 2*math.randomreal()-1;
                    v = v+laguerre.laguerrecalculate(n, x)*c[n];
                    sumerr = Math.Max(sumerr, Math.Abs(v-laguerre.laguerresum(c, n, x)));
                }
            }
            
            //
            // Testing coefficients
            //
            laguerre.laguerrecoefficients(0, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-1));
            laguerre.laguerrecoefficients(1, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-1));
            cerr = Math.Max(cerr, Math.Abs(c[1]+1));
            laguerre.laguerrecoefficients(2, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-(double)2/(double)2));
            cerr = Math.Max(cerr, Math.Abs(c[1]+(double)4/(double)2));
            cerr = Math.Max(cerr, Math.Abs(c[2]-(double)1/(double)2));
            laguerre.laguerrecoefficients(3, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-(double)6/(double)6));
            cerr = Math.Max(cerr, Math.Abs(c[1]+(double)18/(double)6));
            cerr = Math.Max(cerr, Math.Abs(c[2]-(double)9/(double)6));
            cerr = Math.Max(cerr, Math.Abs(c[3]+(double)1/(double)6));
            laguerre.laguerrecoefficients(4, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-(double)24/(double)24));
            cerr = Math.Max(cerr, Math.Abs(c[1]+(double)96/(double)24));
            cerr = Math.Max(cerr, Math.Abs(c[2]-(double)72/(double)24));
            cerr = Math.Max(cerr, Math.Abs(c[3]+(double)16/(double)24));
            cerr = Math.Max(cerr, Math.Abs(c[4]-(double)1/(double)24));
            laguerre.laguerrecoefficients(5, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-(double)120/(double)120));
            cerr = Math.Max(cerr, Math.Abs(c[1]+(double)600/(double)120));
            cerr = Math.Max(cerr, Math.Abs(c[2]-(double)600/(double)120));
            cerr = Math.Max(cerr, Math.Abs(c[3]+(double)200/(double)120));
            cerr = Math.Max(cerr, Math.Abs(c[4]-(double)25/(double)120));
            cerr = Math.Max(cerr, Math.Abs(c[5]+(double)1/(double)120));
            laguerre.laguerrecoefficients(6, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-(double)720/(double)720));
            cerr = Math.Max(cerr, Math.Abs(c[1]+(double)4320/(double)720));
            cerr = Math.Max(cerr, Math.Abs(c[2]-(double)5400/(double)720));
            cerr = Math.Max(cerr, Math.Abs(c[3]+(double)2400/(double)720));
            cerr = Math.Max(cerr, Math.Abs(c[4]-(double)450/(double)720));
            cerr = Math.Max(cerr, Math.Abs(c[5]+(double)36/(double)720));
            cerr = Math.Max(cerr, Math.Abs(c[6]-(double)1/(double)720));
            
            //
            // Reporting
            //
            waserrors = ((double)(err)>(double)(threshold) | (double)(sumerr)>(double)(threshold)) | (double)(cerr)>(double)(threshold);
            if( !silent )
            {
                System.Console.Write("TESTING CALCULATION OF THE LAGUERRE POLYNOMIALS");
                System.Console.WriteLine();
                System.Console.Write("Max error                                 ");
                System.Console.Write("{0,5:E2}",err);
                System.Console.WriteLine();
                System.Console.Write("Summation error                           ");
                System.Console.Write("{0,5:E2}",sumerr);
                System.Console.WriteLine();
                System.Console.Write("Coefficients error                        ");
                System.Console.Write("{0,5:E2}",cerr);
                System.Console.WriteLine();
                System.Console.Write("Threshold                                 ");
                System.Console.Write("{0,5:E2}",threshold);
                System.Console.WriteLine();
                if( !waserrors )
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


    }
    public class testlegendreunit
    {
        public static bool testlegendre(bool silent)
        {
            bool result = new bool();
            double err = 0;
            double sumerr = 0;
            double cerr = 0;
            double threshold = 0;
            int n = 0;
            int maxn = 0;
            int i = 0;
            int pass = 0;
            double[] c = new double[0];
            double x = 0;
            double v = 0;
            double t = 0;
            bool waserrors = new bool();

            err = 0;
            sumerr = 0;
            cerr = 0;
            threshold = 1.0E-9;
            waserrors = false;
            
            //
            // Testing Legendre polynomials values
            //
            for(n=0; n<=10; n++)
            {
                legendre.legendrecoefficients(n, ref c);
                for(pass=1; pass<=10; pass++)
                {
                    x = 2*math.randomreal()-1;
                    v = legendre.legendrecalculate(n, x);
                    t = 1;
                    for(i=0; i<=n; i++)
                    {
                        v = v-c[i]*t;
                        t = t*x;
                    }
                    err = Math.Max(err, Math.Abs(v));
                }
            }
            
            //
            // Testing Clenshaw summation
            //
            maxn = 20;
            c = new double[maxn+1];
            for(pass=1; pass<=10; pass++)
            {
                x = 2*math.randomreal()-1;
                v = 0;
                for(n=0; n<=maxn; n++)
                {
                    c[n] = 2*math.randomreal()-1;
                    v = v+legendre.legendrecalculate(n, x)*c[n];
                    sumerr = Math.Max(sumerr, Math.Abs(v-legendre.legendresum(c, n, x)));
                }
            }
            
            //
            // Testing coefficients
            //
            legendre.legendrecoefficients(0, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-1));
            legendre.legendrecoefficients(1, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-0));
            cerr = Math.Max(cerr, Math.Abs(c[1]-1));
            legendre.legendrecoefficients(2, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]+(double)1/(double)2));
            cerr = Math.Max(cerr, Math.Abs(c[1]-0));
            cerr = Math.Max(cerr, Math.Abs(c[2]-(double)3/(double)2));
            legendre.legendrecoefficients(3, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-0));
            cerr = Math.Max(cerr, Math.Abs(c[1]+(double)3/(double)2));
            cerr = Math.Max(cerr, Math.Abs(c[2]-0));
            cerr = Math.Max(cerr, Math.Abs(c[3]-(double)5/(double)2));
            legendre.legendrecoefficients(4, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-(double)3/(double)8));
            cerr = Math.Max(cerr, Math.Abs(c[1]-0));
            cerr = Math.Max(cerr, Math.Abs(c[2]+(double)30/(double)8));
            cerr = Math.Max(cerr, Math.Abs(c[3]-0));
            cerr = Math.Max(cerr, Math.Abs(c[4]-(double)35/(double)8));
            legendre.legendrecoefficients(9, ref c);
            cerr = Math.Max(cerr, Math.Abs(c[0]-0));
            cerr = Math.Max(cerr, Math.Abs(c[1]-(double)315/(double)128));
            cerr = Math.Max(cerr, Math.Abs(c[2]-0));
            cerr = Math.Max(cerr, Math.Abs(c[3]+(double)4620/(double)128));
            cerr = Math.Max(cerr, Math.Abs(c[4]-0));
            cerr = Math.Max(cerr, Math.Abs(c[5]-(double)18018/(double)128));
            cerr = Math.Max(cerr, Math.Abs(c[6]-0));
            cerr = Math.Max(cerr, Math.Abs(c[7]+(double)25740/(double)128));
            cerr = Math.Max(cerr, Math.Abs(c[8]-0));
            cerr = Math.Max(cerr, Math.Abs(c[9]-(double)12155/(double)128));
            
            //
            // Reporting
            //
            waserrors = ((double)(err)>(double)(threshold) | (double)(sumerr)>(double)(threshold)) | (double)(cerr)>(double)(threshold);
            if( !silent )
            {
                System.Console.Write("TESTING CALCULATION OF THE LEGENDRE POLYNOMIALS");
                System.Console.WriteLine();
                System.Console.Write("Max error                                 ");
                System.Console.Write("{0,5:E2}",err);
                System.Console.WriteLine();
                System.Console.Write("Summation error                           ");
                System.Console.Write("{0,5:E2}",sumerr);
                System.Console.WriteLine();
                System.Console.Write("Coefficients error                        ");
                System.Console.Write("{0,5:E2}",cerr);
                System.Console.WriteLine();
                System.Console.Write("Threshold                                 ");
                System.Console.Write("{0,5:E2}",threshold);
                System.Console.WriteLine();
                if( !waserrors )
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


    }
    public class testalglibbasicsunit
    {
        public class rec1
        {
            public bool bfield;
            public double rfield;
            public int ifield;
            public complex cfield;
            public bool[] b1field;
            public double[] r1field;
            public int[] i1field;
            public complex[] c1field;
            public bool[,] b2field;
            public double[,] r2field;
            public int[,] i2field;
            public complex[,] c2field;
            public rec1()
            {
                b1field = new bool[0];
                r1field = new double[0];
                i1field = new int[0];
                c1field = new complex[0];
                b2field = new bool[0,0];
                r2field = new double[0,0];
                i2field = new int[0,0];
                c2field = new complex[0,0];
            }
        };


        public class rec4serialization
        {
            public bool[] b;
            public int[] i;
            public double[] r;
            public rec4serialization()
            {
                b = new bool[0];
                i = new int[0];
                r = new double[0];
            }
        };




        public static void rec4serializationalloc(alglib.serializer s,
            rec4serialization v)
        {
            int i = 0;

            
            //
            // boolean fields
            //
            s.alloc_entry();
            for(i=0; i<=ap.len(v.b)-1; i++)
            {
                s.alloc_entry();
            }
            
            //
            // integer fields
            //
            s.alloc_entry();
            for(i=0; i<=ap.len(v.i)-1; i++)
            {
                s.alloc_entry();
            }
            
            //
            // real fields
            //
            s.alloc_entry();
            for(i=0; i<=ap.len(v.r)-1; i++)
            {
                s.alloc_entry();
            }
        }


        public static void rec4serializationserialize(alglib.serializer s,
            rec4serialization v)
        {
            int i = 0;

            
            //
            // boolean fields
            //
            s.serialize_int(ap.len(v.b));
            for(i=0; i<=ap.len(v.b)-1; i++)
            {
                s.serialize_bool(v.b[i]);
            }
            
            //
            // integer fields
            //
            s.serialize_int(ap.len(v.i));
            for(i=0; i<=ap.len(v.i)-1; i++)
            {
                s.serialize_int(v.i[i]);
            }
            
            //
            // real fields
            //
            s.serialize_int(ap.len(v.r));
            for(i=0; i<=ap.len(v.r)-1; i++)
            {
                s.serialize_double(v.r[i]);
            }
        }


        public static void rec4serializationunserialize(alglib.serializer s,
            rec4serialization v)
        {
            int i = 0;
            int k = 0;
            bool bv = new bool();
            int iv = 0;
            double rv = 0;

            
            //
            // boolean fields
            //
            k = s.unserialize_int();
            if( k>0 )
            {
                v.b = new bool[k];
                for(i=0; i<=k-1; i++)
                {
                    bv = s.unserialize_bool();
                    v.b[i] = bv;
                }
            }
            
            //
            // integer fields
            //
            k = s.unserialize_int();
            if( k>0 )
            {
                v.i = new int[k];
                for(i=0; i<=k-1; i++)
                {
                    iv = s.unserialize_int();
                    v.i[i] = iv;
                }
            }
            
            //
            // real fields
            //
            k = s.unserialize_int();
            if( k>0 )
            {
                v.r = new double[k];
                for(i=0; i<=k-1; i++)
                {
                    rv = s.unserialize_double();
                    v.r[i] = rv;
                }
            }
        }


        public static bool testalglibbasics(bool silent)
        {
            bool result = new bool();

            result = true;
            result = result & testcomplexarithmetics(silent);
            result = result & testieeespecial(silent);
            result = result & testswapfunctions(silent);
            result = result & testserializationfunctions(silent);
            if( !silent )
            {
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            return result;
        }


        /*************************************************************************
        Complex arithmetics test
        *************************************************************************/
        private static bool testcomplexarithmetics(bool silent)
        {
            bool result = new bool();
            bool absc = new bool();
            bool addcc = new bool();
            bool addcr = new bool();
            bool addrc = new bool();
            bool subcc = new bool();
            bool subcr = new bool();
            bool subrc = new bool();
            bool mulcc = new bool();
            bool mulcr = new bool();
            bool mulrc = new bool();
            bool divcc = new bool();
            bool divcr = new bool();
            bool divrc = new bool();
            complex ca = 0;
            complex cb = 0;
            complex res = 0;
            double ra = 0;
            double rb = 0;
            double threshold = 0;
            int pass = 0;
            int passcount = 0;

            threshold = 100*math.machineepsilon;
            passcount = 1000;
            result = true;
            absc = true;
            addcc = true;
            addcr = true;
            addrc = true;
            subcc = true;
            subcr = true;
            subrc = true;
            mulcc = true;
            mulcr = true;
            mulrc = true;
            divcc = true;
            divcr = true;
            divrc = true;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Test AbsC
                //
                ca.x = 2*math.randomreal()-1;
                ca.y = 2*math.randomreal()-1;
                ra = math.abscomplex(ca);
                absc = absc & (double)(Math.Abs(ra-Math.Sqrt(math.sqr(ca.x)+math.sqr(ca.y))))<(double)(threshold);
                
                //
                // test Add
                //
                ca.x = 2*math.randomreal()-1;
                ca.y = 2*math.randomreal()-1;
                cb.x = 2*math.randomreal()-1;
                cb.y = 2*math.randomreal()-1;
                ra = 2*math.randomreal()-1;
                rb = 2*math.randomreal()-1;
                res = ca+cb;
                addcc = (addcc & (double)(Math.Abs(res.x-ca.x-cb.x))<(double)(threshold)) & (double)(Math.Abs(res.y-ca.y-cb.y))<(double)(threshold);
                res = ca+rb;
                addcr = (addcr & (double)(Math.Abs(res.x-ca.x-rb))<(double)(threshold)) & (double)(Math.Abs(res.y-ca.y))<(double)(threshold);
                res = ra+cb;
                addrc = (addrc & (double)(Math.Abs(res.x-ra-cb.x))<(double)(threshold)) & (double)(Math.Abs(res.y-cb.y))<(double)(threshold);
                
                //
                // test Sub
                //
                ca.x = 2*math.randomreal()-1;
                ca.y = 2*math.randomreal()-1;
                cb.x = 2*math.randomreal()-1;
                cb.y = 2*math.randomreal()-1;
                ra = 2*math.randomreal()-1;
                rb = 2*math.randomreal()-1;
                res = ca-cb;
                subcc = (subcc & (double)(Math.Abs(res.x-(ca.x-cb.x)))<(double)(threshold)) & (double)(Math.Abs(res.y-(ca.y-cb.y)))<(double)(threshold);
                res = ca-rb;
                subcr = (subcr & (double)(Math.Abs(res.x-(ca.x-rb)))<(double)(threshold)) & (double)(Math.Abs(res.y-ca.y))<(double)(threshold);
                res = ra-cb;
                subrc = (subrc & (double)(Math.Abs(res.x-(ra-cb.x)))<(double)(threshold)) & (double)(Math.Abs(res.y+cb.y))<(double)(threshold);
                
                //
                // test Mul
                //
                ca.x = 2*math.randomreal()-1;
                ca.y = 2*math.randomreal()-1;
                cb.x = 2*math.randomreal()-1;
                cb.y = 2*math.randomreal()-1;
                ra = 2*math.randomreal()-1;
                rb = 2*math.randomreal()-1;
                res = ca*cb;
                mulcc = (mulcc & (double)(Math.Abs(res.x-(ca.x*cb.x-ca.y*cb.y)))<(double)(threshold)) & (double)(Math.Abs(res.y-(ca.x*cb.y+ca.y*cb.x)))<(double)(threshold);
                res = ca*rb;
                mulcr = (mulcr & (double)(Math.Abs(res.x-ca.x*rb))<(double)(threshold)) & (double)(Math.Abs(res.y-ca.y*rb))<(double)(threshold);
                res = ra*cb;
                mulrc = (mulrc & (double)(Math.Abs(res.x-ra*cb.x))<(double)(threshold)) & (double)(Math.Abs(res.y-ra*cb.y))<(double)(threshold);
                
                //
                // test Div
                //
                ca.x = 2*math.randomreal()-1;
                ca.y = 2*math.randomreal()-1;
                do
                {
                    cb.x = 2*math.randomreal()-1;
                    cb.y = 2*math.randomreal()-1;
                }
                while( (double)(math.abscomplex(cb))<=(double)(0.5) );
                ra = 2*math.randomreal()-1;
                do
                {
                    rb = 2*math.randomreal()-1;
                }
                while( (double)(Math.Abs(rb))<=(double)(0.5) );
                res = ca/cb;
                divcc = (divcc & (double)(Math.Abs((res*cb).x-ca.x))<(double)(threshold)) & (double)(Math.Abs((res*cb).y-ca.y))<(double)(threshold);
                res = ca/rb;
                divcr = (divcr & (double)(Math.Abs(res.x-ca.x/rb))<(double)(threshold)) & (double)(Math.Abs(res.y-ca.y/rb))<(double)(threshold);
                res = ra/cb;
                divrc = (divrc & (double)(Math.Abs((res*cb).x-ra))<(double)(threshold)) & (double)(Math.Abs((res*cb).y))<(double)(threshold);
            }
            
            //
            // summary
            //
            result = result & absc;
            result = result & addcc;
            result = result & addcr;
            result = result & addrc;
            result = result & subcc;
            result = result & subcr;
            result = result & subrc;
            result = result & mulcc;
            result = result & mulcr;
            result = result & mulrc;
            result = result & divcc;
            result = result & divcr;
            result = result & divrc;
            if( !silent )
            {
                if( result )
                {
                    System.Console.Write("COMPLEX ARITHMETICS:                     OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("COMPLEX ARITHMETICS:                     FAILED");
                    System.Console.WriteLine();
                    System.Console.Write("* AddCC - - - - - - - - - - - - - - - -  ");
                    if( addcc )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* AddCR - - - - - - - - - - - - - - - -  ");
                    if( addcr )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* AddRC - - - - - - - - - - - - - - - -  ");
                    if( addrc )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* SubCC - - - - - - - - - - - - - - - -  ");
                    if( subcc )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* SubCR - - - - - - - - - - - - - - - -  ");
                    if( subcr )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* SubRC - - - - - - - - - - - - - - - -  ");
                    if( subrc )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* MulCC - - - - - - - - - - - - - - - -  ");
                    if( mulcc )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* MulCR - - - - - - - - - - - - - - - -  ");
                    if( mulcr )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* MulRC - - - - - - - - - - - - - - - -  ");
                    if( mulrc )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* DivCC - - - - - - - - - - - - - - - -  ");
                    if( divcc )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* DivCR - - - - - - - - - - - - - - - -  ");
                    if( divcr )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* DivRC - - - - - - - - - - - - - - - -  ");
                    if( divrc )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Tests for IEEE special quantities
        *************************************************************************/
        private static bool testieeespecial(bool silent)
        {
            bool result = new bool();
            bool oknan = new bool();
            bool okinf = new bool();
            bool okother = new bool();
            double v1 = 0;
            double v2 = 0;

            result = true;
            oknan = true;
            okinf = true;
            okother = true;
            
            //
            // Test classification functions
            //
            okother = okother & !Double.IsInfinity(Double.NaN);
            okother = okother & Double.IsInfinity(Double.PositiveInfinity);
            okother = okother & !Double.IsInfinity(math.maxrealnumber);
            okother = okother & !Double.IsInfinity(1.0);
            okother = okother & !Double.IsInfinity(math.minrealnumber);
            okother = okother & !Double.IsInfinity(0.0);
            okother = okother & !Double.IsInfinity(-math.minrealnumber);
            okother = okother & !Double.IsInfinity(-1.0);
            okother = okother & !Double.IsInfinity(-math.maxrealnumber);
            okother = okother & Double.IsInfinity(Double.NegativeInfinity);
            okother = okother & !Double.IsPositiveInfinity(Double.NaN);
            okother = okother & Double.IsPositiveInfinity(Double.PositiveInfinity);
            okother = okother & !Double.IsPositiveInfinity(math.maxrealnumber);
            okother = okother & !Double.IsPositiveInfinity(1.0);
            okother = okother & !Double.IsPositiveInfinity(math.minrealnumber);
            okother = okother & !Double.IsPositiveInfinity(0.0);
            okother = okother & !Double.IsPositiveInfinity(-math.minrealnumber);
            okother = okother & !Double.IsPositiveInfinity(-1.0);
            okother = okother & !Double.IsPositiveInfinity(-math.maxrealnumber);
            okother = okother & !Double.IsPositiveInfinity(Double.NegativeInfinity);
            okother = okother & !Double.IsNegativeInfinity(Double.NaN);
            okother = okother & !Double.IsNegativeInfinity(Double.PositiveInfinity);
            okother = okother & !Double.IsNegativeInfinity(math.maxrealnumber);
            okother = okother & !Double.IsNegativeInfinity(1.0);
            okother = okother & !Double.IsNegativeInfinity(math.minrealnumber);
            okother = okother & !Double.IsNegativeInfinity(0.0);
            okother = okother & !Double.IsNegativeInfinity(-math.minrealnumber);
            okother = okother & !Double.IsNegativeInfinity(-1.0);
            okother = okother & !Double.IsNegativeInfinity(-math.maxrealnumber);
            okother = okother & Double.IsNegativeInfinity(Double.NegativeInfinity);
            okother = okother & Double.IsNaN(Double.NaN);
            okother = okother & !Double.IsNaN(Double.PositiveInfinity);
            okother = okother & !Double.IsNaN(math.maxrealnumber);
            okother = okother & !Double.IsNaN(1.0);
            okother = okother & !Double.IsNaN(math.minrealnumber);
            okother = okother & !Double.IsNaN(0.0);
            okother = okother & !Double.IsNaN(-math.minrealnumber);
            okother = okother & !Double.IsNaN(-1.0);
            okother = okother & !Double.IsNaN(-math.maxrealnumber);
            okother = okother & !Double.IsNaN(Double.NegativeInfinity);
            okother = okother & !math.isfinite(Double.NaN);
            okother = okother & !math.isfinite(Double.PositiveInfinity);
            okother = okother & math.isfinite(math.maxrealnumber);
            okother = okother & math.isfinite(1.0);
            okother = okother & math.isfinite(math.minrealnumber);
            okother = okother & math.isfinite(0.0);
            okother = okother & math.isfinite(-math.minrealnumber);
            okother = okother & math.isfinite(-1.0);
            okother = okother & math.isfinite(-math.maxrealnumber);
            okother = okother & !math.isfinite(Double.NegativeInfinity);
            
            //
            // Test NAN
            //
            v1 = Double.NaN;
            v2 = Double.NaN;
            oknan = oknan & Double.IsNaN(v1);
            oknan = oknan & (double)(v1)!=(double)(v2);
            oknan = oknan & !((double)(v1)==(double)(v2));
            
            //
            // Test INF:
            // * basic properties
            // * comparisons involving PosINF on one of the sides
            // * comparisons involving NegINF on one of the sides
            //
            v1 = Double.PositiveInfinity;
            v2 = Double.NegativeInfinity;
            okinf = okinf & Double.IsInfinity(Double.PositiveInfinity);
            okinf = okinf & Double.IsInfinity(v1);
            okinf = okinf & Double.IsInfinity(Double.NegativeInfinity);
            okinf = okinf & Double.IsInfinity(v2);
            okinf = okinf & Double.IsPositiveInfinity(Double.PositiveInfinity);
            okinf = okinf & Double.IsPositiveInfinity(v1);
            okinf = okinf & !Double.IsPositiveInfinity(Double.NegativeInfinity);
            okinf = okinf & !Double.IsPositiveInfinity(v2);
            okinf = okinf & !Double.IsNegativeInfinity(Double.PositiveInfinity);
            okinf = okinf & !Double.IsNegativeInfinity(v1);
            okinf = okinf & Double.IsNegativeInfinity(Double.NegativeInfinity);
            okinf = okinf & Double.IsNegativeInfinity(v2);
            okinf = okinf & (double)(Double.PositiveInfinity)==(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(Double.PositiveInfinity)==(double)(v1);
            okinf = okinf & !((double)(Double.PositiveInfinity)==(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(Double.PositiveInfinity)==(double)(v2));
            okinf = okinf & !((double)(Double.PositiveInfinity)==(double)(0));
            okinf = okinf & !((double)(Double.PositiveInfinity)==(double)(1.2));
            okinf = okinf & !((double)(Double.PositiveInfinity)==(double)(-1.2));
            okinf = okinf & (double)(v1)==(double)(Double.PositiveInfinity);
            okinf = okinf & !((double)(Double.NegativeInfinity)==(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(v2)==(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(0)==(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(1.2)==(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(-1.2)==(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(Double.PositiveInfinity)!=(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(Double.PositiveInfinity)!=(double)(v1));
            okinf = okinf & (double)(Double.PositiveInfinity)!=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(Double.PositiveInfinity)!=(double)(v2);
            okinf = okinf & (double)(Double.PositiveInfinity)!=(double)(0);
            okinf = okinf & (double)(Double.PositiveInfinity)!=(double)(1.2);
            okinf = okinf & (double)(Double.PositiveInfinity)!=(double)(-1.2);
            okinf = okinf & !((double)(v1)!=(double)(Double.PositiveInfinity));
            okinf = okinf & (double)(Double.NegativeInfinity)!=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(v2)!=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(0)!=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(1.2)!=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(-1.2)!=(double)(Double.PositiveInfinity);
            okinf = okinf & !((double)(Double.PositiveInfinity)<(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(Double.PositiveInfinity)<(double)(v1));
            okinf = okinf & !((double)(Double.PositiveInfinity)<(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(Double.PositiveInfinity)<(double)(v2));
            okinf = okinf & !((double)(Double.PositiveInfinity)<(double)(0));
            okinf = okinf & !((double)(Double.PositiveInfinity)<(double)(1.2));
            okinf = okinf & !((double)(Double.PositiveInfinity)<(double)(-1.2));
            okinf = okinf & !((double)(v1)<(double)(Double.PositiveInfinity));
            okinf = okinf & (double)(Double.NegativeInfinity)<(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(v2)<(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(0)<(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(1.2)<(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(-1.2)<(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(Double.PositiveInfinity)<=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(Double.PositiveInfinity)<=(double)(v1);
            okinf = okinf & !((double)(Double.PositiveInfinity)<=(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(Double.PositiveInfinity)<=(double)(v2));
            okinf = okinf & !((double)(Double.PositiveInfinity)<=(double)(0));
            okinf = okinf & !((double)(Double.PositiveInfinity)<=(double)(1.2));
            okinf = okinf & !((double)(Double.PositiveInfinity)<=(double)(-1.2));
            okinf = okinf & (double)(v1)<=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(Double.NegativeInfinity)<=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(v2)<=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(0)<=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(1.2)<=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(-1.2)<=(double)(Double.PositiveInfinity);
            okinf = okinf & !((double)(Double.PositiveInfinity)>(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(Double.PositiveInfinity)>(double)(v1));
            okinf = okinf & (double)(Double.PositiveInfinity)>(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(Double.PositiveInfinity)>(double)(v2);
            okinf = okinf & (double)(Double.PositiveInfinity)>(double)(0);
            okinf = okinf & (double)(Double.PositiveInfinity)>(double)(1.2);
            okinf = okinf & (double)(Double.PositiveInfinity)>(double)(-1.2);
            okinf = okinf & !((double)(v1)>(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)>(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(v2)>(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(0)>(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(1.2)>(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(-1.2)>(double)(Double.PositiveInfinity));
            okinf = okinf & (double)(Double.PositiveInfinity)>=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(Double.PositiveInfinity)>=(double)(v1);
            okinf = okinf & (double)(Double.PositiveInfinity)>=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(Double.PositiveInfinity)>=(double)(v2);
            okinf = okinf & (double)(Double.PositiveInfinity)>=(double)(0);
            okinf = okinf & (double)(Double.PositiveInfinity)>=(double)(1.2);
            okinf = okinf & (double)(Double.PositiveInfinity)>=(double)(-1.2);
            okinf = okinf & (double)(v1)>=(double)(Double.PositiveInfinity);
            okinf = okinf & !((double)(Double.NegativeInfinity)>=(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(v2)>=(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(0)>=(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(1.2)>=(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(-1.2)>=(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)==(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)==(double)(v1));
            okinf = okinf & (double)(Double.NegativeInfinity)==(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(Double.NegativeInfinity)==(double)(v2);
            okinf = okinf & !((double)(Double.NegativeInfinity)==(double)(0));
            okinf = okinf & !((double)(Double.NegativeInfinity)==(double)(1.2));
            okinf = okinf & !((double)(Double.NegativeInfinity)==(double)(-1.2));
            okinf = okinf & !((double)(v1)==(double)(Double.NegativeInfinity));
            okinf = okinf & (double)(Double.NegativeInfinity)==(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(v2)==(double)(Double.NegativeInfinity);
            okinf = okinf & !((double)(0)==(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(1.2)==(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(-1.2)==(double)(Double.NegativeInfinity));
            okinf = okinf & (double)(Double.NegativeInfinity)!=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(Double.NegativeInfinity)!=(double)(v1);
            okinf = okinf & !((double)(Double.NegativeInfinity)!=(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)!=(double)(v2));
            okinf = okinf & (double)(Double.NegativeInfinity)!=(double)(0);
            okinf = okinf & (double)(Double.NegativeInfinity)!=(double)(1.2);
            okinf = okinf & (double)(Double.NegativeInfinity)!=(double)(-1.2);
            okinf = okinf & (double)(v1)!=(double)(Double.NegativeInfinity);
            okinf = okinf & !((double)(Double.NegativeInfinity)!=(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(v2)!=(double)(Double.NegativeInfinity));
            okinf = okinf & (double)(0)!=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(1.2)!=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(-1.2)!=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(Double.NegativeInfinity)<(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(Double.NegativeInfinity)<(double)(v1);
            okinf = okinf & !((double)(Double.NegativeInfinity)<(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)<(double)(v2));
            okinf = okinf & (double)(Double.NegativeInfinity)<(double)(0);
            okinf = okinf & (double)(Double.NegativeInfinity)<(double)(1.2);
            okinf = okinf & (double)(Double.NegativeInfinity)<(double)(-1.2);
            okinf = okinf & !((double)(v1)<(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)<(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(v2)<(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(0)<(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(1.2)<(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(-1.2)<(double)(Double.NegativeInfinity));
            okinf = okinf & (double)(Double.NegativeInfinity)<=(double)(Double.PositiveInfinity);
            okinf = okinf & (double)(Double.NegativeInfinity)<=(double)(v1);
            okinf = okinf & (double)(Double.NegativeInfinity)<=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(Double.NegativeInfinity)<=(double)(v2);
            okinf = okinf & (double)(Double.NegativeInfinity)<=(double)(0);
            okinf = okinf & (double)(Double.NegativeInfinity)<=(double)(1.2);
            okinf = okinf & (double)(Double.NegativeInfinity)<=(double)(-1.2);
            okinf = okinf & !((double)(v1)<=(double)(Double.NegativeInfinity));
            okinf = okinf & (double)(Double.NegativeInfinity)<=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(v2)<=(double)(Double.NegativeInfinity);
            okinf = okinf & !((double)(0)<=(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(1.2)<=(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(-1.2)<=(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)>(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)>(double)(v1));
            okinf = okinf & !((double)(Double.NegativeInfinity)>(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)>(double)(v2));
            okinf = okinf & !((double)(Double.NegativeInfinity)>(double)(0));
            okinf = okinf & !((double)(Double.NegativeInfinity)>(double)(1.2));
            okinf = okinf & !((double)(Double.NegativeInfinity)>(double)(-1.2));
            okinf = okinf & (double)(v1)>(double)(Double.NegativeInfinity);
            okinf = okinf & !((double)(Double.NegativeInfinity)>(double)(Double.NegativeInfinity));
            okinf = okinf & !((double)(v2)>(double)(Double.NegativeInfinity));
            okinf = okinf & (double)(0)>(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(1.2)>(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(-1.2)>(double)(Double.NegativeInfinity);
            okinf = okinf & !((double)(Double.NegativeInfinity)>=(double)(Double.PositiveInfinity));
            okinf = okinf & !((double)(Double.NegativeInfinity)>=(double)(v1));
            okinf = okinf & (double)(Double.NegativeInfinity)>=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(Double.NegativeInfinity)>=(double)(v2);
            okinf = okinf & !((double)(Double.NegativeInfinity)>=(double)(0));
            okinf = okinf & !((double)(Double.NegativeInfinity)>=(double)(1.2));
            okinf = okinf & !((double)(Double.NegativeInfinity)>=(double)(-1.2));
            okinf = okinf & (double)(v1)>=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(Double.NegativeInfinity)>=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(v2)>=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(0)>=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(1.2)>=(double)(Double.NegativeInfinity);
            okinf = okinf & (double)(-1.2)>=(double)(Double.NegativeInfinity);
            
            //
            // summary
            //
            result = result & oknan;
            result = result & okinf;
            result = result & okother;
            if( !silent )
            {
                if( result )
                {
                    System.Console.Write("IEEE SPECIAL VALUES:                     OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("IEEE SPECIAL VALUES:                     FAILED");
                    System.Console.WriteLine();
                    System.Console.Write("* NAN - - - - - - - - - - - - - - - - -  ");
                    if( oknan )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* INF - - - - - - - - - - - - - - - - -  ");
                    if( okinf )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* FUNCTIONS - - - - - - - - - - - - - -  ");
                    if( okother )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Tests for swapping functions
        *************************************************************************/
        private static bool testswapfunctions(bool silent)
        {
            bool result = new bool();
            bool okb1 = new bool();
            bool okb2 = new bool();
            bool oki1 = new bool();
            bool oki2 = new bool();
            bool okr1 = new bool();
            bool okr2 = new bool();
            bool okc1 = new bool();
            bool okc2 = new bool();
            bool[] b11 = new bool[0];
            bool[] b12 = new bool[0];
            int[] i11 = new int[0];
            int[] i12 = new int[0];
            double[] r11 = new double[0];
            double[] r12 = new double[0];
            complex[] c11 = new complex[0];
            complex[] c12 = new complex[0];
            bool[,] b21 = new bool[0,0];
            bool[,] b22 = new bool[0,0];
            int[,] i21 = new int[0,0];
            int[,] i22 = new int[0,0];
            double[,] r21 = new double[0,0];
            double[,] r22 = new double[0,0];
            complex[,] c21 = new complex[0,0];
            complex[,] c22 = new complex[0,0];

            result = true;
            okb1 = true;
            okb2 = true;
            oki1 = true;
            oki2 = true;
            okr1 = true;
            okr2 = true;
            okc1 = true;
            okc2 = true;
            
            //
            // Test B1 swaps
            //
            b11 = new bool[1];
            b12 = new bool[2];
            b11[0] = true;
            b12[0] = false;
            b12[1] = true;
            ap.swap(ref b11, ref b12);
            if( ap.len(b11)==2 & ap.len(b12)==1 )
            {
                okb1 = okb1 & !b11[0];
                okb1 = okb1 & b11[1];
                okb1 = okb1 & b12[0];
            }
            else
            {
                okb1 = false;
            }
            
            //
            // Test I1 swaps
            //
            i11 = new int[1];
            i12 = new int[2];
            i11[0] = 1;
            i12[0] = 2;
            i12[1] = 3;
            ap.swap(ref i11, ref i12);
            if( ap.len(i11)==2 & ap.len(i12)==1 )
            {
                oki1 = oki1 & i11[0]==2;
                oki1 = oki1 & i11[1]==3;
                oki1 = oki1 & i12[0]==1;
            }
            else
            {
                oki1 = false;
            }
            
            //
            // Test R1 swaps
            //
            r11 = new double[1];
            r12 = new double[2];
            r11[0] = 1.5;
            r12[0] = 2.5;
            r12[1] = 3.5;
            ap.swap(ref r11, ref r12);
            if( ap.len(r11)==2 & ap.len(r12)==1 )
            {
                okr1 = okr1 & (double)(r11[0])==(double)(2.5);
                okr1 = okr1 & (double)(r11[1])==(double)(3.5);
                okr1 = okr1 & (double)(r12[0])==(double)(1.5);
            }
            else
            {
                okr1 = false;
            }
            
            //
            // Test C1 swaps
            //
            c11 = new complex[1];
            c12 = new complex[2];
            c11[0] = 1;
            c12[0] = 2;
            c12[1] = 3;
            ap.swap(ref c11, ref c12);
            if( ap.len(c11)==2 & ap.len(c12)==1 )
            {
                okc1 = okc1 & c11[0]==2;
                okc1 = okc1 & c11[1]==3;
                okc1 = okc1 & c12[0]==1;
            }
            else
            {
                okc1 = false;
            }
            
            //
            // Test B2 swaps
            //
            b21 = new bool[1, 2];
            b22 = new bool[2, 1];
            b21[0,0] = true;
            b21[0,1] = false;
            b22[0,0] = false;
            b22[1,0] = true;
            ap.swap(ref b21, ref b22);
            if( ((ap.rows(b21)==2 & ap.cols(b21)==1) & ap.rows(b22)==1) & ap.cols(b22)==2 )
            {
                okb2 = okb2 & !b21[0,0];
                okb2 = okb2 & b21[1,0];
                okb2 = okb2 & b22[0,0];
                okb2 = okb2 & !b22[0,1];
            }
            else
            {
                okb2 = false;
            }
            
            //
            // Test I2 swaps
            //
            i21 = new int[1, 2];
            i22 = new int[2, 1];
            i21[0,0] = 1;
            i21[0,1] = 2;
            i22[0,0] = 3;
            i22[1,0] = 4;
            ap.swap(ref i21, ref i22);
            if( ((ap.rows(i21)==2 & ap.cols(i21)==1) & ap.rows(i22)==1) & ap.cols(i22)==2 )
            {
                oki2 = oki2 & i21[0,0]==3;
                oki2 = oki2 & i21[1,0]==4;
                oki2 = oki2 & i22[0,0]==1;
                oki2 = oki2 & i22[0,1]==2;
            }
            else
            {
                oki2 = false;
            }
            
            //
            // Test R2 swaps
            //
            r21 = new double[1, 2];
            r22 = new double[2, 1];
            r21[0,0] = 1;
            r21[0,1] = 2;
            r22[0,0] = 3;
            r22[1,0] = 4;
            ap.swap(ref r21, ref r22);
            if( ((ap.rows(r21)==2 & ap.cols(r21)==1) & ap.rows(r22)==1) & ap.cols(r22)==2 )
            {
                okr2 = okr2 & (double)(r21[0,0])==(double)(3);
                okr2 = okr2 & (double)(r21[1,0])==(double)(4);
                okr2 = okr2 & (double)(r22[0,0])==(double)(1);
                okr2 = okr2 & (double)(r22[0,1])==(double)(2);
            }
            else
            {
                okr2 = false;
            }
            
            //
            // Test C2 swaps
            //
            c21 = new complex[1, 2];
            c22 = new complex[2, 1];
            c21[0,0] = 1;
            c21[0,1] = 2;
            c22[0,0] = 3;
            c22[1,0] = 4;
            ap.swap(ref c21, ref c22);
            if( ((ap.rows(c21)==2 & ap.cols(c21)==1) & ap.rows(c22)==1) & ap.cols(c22)==2 )
            {
                okc2 = okc2 & c21[0,0]==3;
                okc2 = okc2 & c21[1,0]==4;
                okc2 = okc2 & c22[0,0]==1;
                okc2 = okc2 & c22[0,1]==2;
            }
            else
            {
                okc2 = false;
            }
            
            //
            // summary
            //
            result = result & okb1;
            result = result & okb2;
            result = result & oki1;
            result = result & oki2;
            result = result & okr1;
            result = result & okr2;
            result = result & okc1;
            result = result & okc2;
            if( !silent )
            {
                if( result )
                {
                    System.Console.Write("SWAPPING FUNCTIONS:                      OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("SWAPPING FUNCTIONS:                      FAILED");
                    System.Console.WriteLine();
                }
            }
            return result;
        }


        /*************************************************************************
        Tests for swapping functions
        *************************************************************************/
        private static bool testserializationfunctions(bool silent)
        {
            bool result = new bool();
            bool okb = new bool();
            bool oki = new bool();
            bool okr = new bool();
            int nb = 0;
            int ni = 0;
            int nr = 0;
            int i = 0;
            rec4serialization r0 = new rec4serialization();
            rec4serialization r1 = new rec4serialization();

            result = true;
            okb = true;
            oki = true;
            okr = true;
            for(nb=1; nb<=4; nb++)
            {
                for(ni=1; ni<=4; ni++)
                {
                    for(nr=1; nr<=4; nr++)
                    {
                        r0.b = new bool[nb];
                        for(i=0; i<=nb-1; i++)
                        {
                            r0.b[i] = math.randominteger(2)!=0;
                        }
                        r0.i = new int[ni];
                        for(i=0; i<=ni-1; i++)
                        {
                            r0.i[i] = math.randominteger(10)-5;
                        }
                        r0.r = new double[nr];
                        for(i=0; i<=nr-1; i++)
                        {
                            r0.r[i] = 2*math.randomreal()-1;
                        }
                        {
                            //
                            // This code passes data structure through serializers
                            // (serializes it to string and loads back)
                            //
                            serializer _local_serializer;
                            string _local_str;
                            
                            _local_serializer = new serializer();
                            _local_serializer.alloc_start();
                            testalglibbasicsunit.rec4serializationalloc(_local_serializer, r0);
                            _local_serializer.sstart_str();
                            testalglibbasicsunit.rec4serializationserialize(_local_serializer, r0);
                            _local_serializer.stop();
                            _local_str = _local_serializer.get_string();
                            
                            _local_serializer = new serializer();
                            _local_serializer.ustart_str(_local_str);
                            testalglibbasicsunit.rec4serializationunserialize(_local_serializer, r1);
                            _local_serializer.stop();
                        }
                        if( (ap.len(r0.b)==ap.len(r1.b) & ap.len(r0.i)==ap.len(r1.i)) & ap.len(r0.r)==ap.len(r1.r) )
                        {
                            for(i=0; i<=nb-1; i++)
                            {
                                okb = okb & ((r0.b[i] & r1.b[i]) | (!r0.b[i] & !r1.b[i]));
                            }
                            for(i=0; i<=ni-1; i++)
                            {
                                oki = oki & r0.i[i]==r1.i[i];
                            }
                            for(i=0; i<=nr-1; i++)
                            {
                                okr = okr & (double)(r0.r[i])==(double)(r1.r[i]);
                            }
                        }
                        else
                        {
                            oki = false;
                        }
                    }
                }
            }
            
            //
            // summary
            //
            result = result & okb;
            result = result & oki;
            result = result & okr;
            if( !silent )
            {
                if( result )
                {
                    System.Console.Write("SERIALIZATION FUNCTIONS:                 OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("SERIALIZATION FUNCTIONS:                 FAILED");
                    System.Console.WriteLine();
                    System.Console.Write("* BOOLEAN - - - - - - - - - - - - - - -  ");
                    if( okb )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* INTEGER - - - - - - - - - - - - - - -  ");
                    if( oki )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* REAL  - - - - - - - - - - - - - - - -  ");
                    if( okr )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                }
            }
            return result;
        }


    }
}

public class _Test
{
	public delegate bool TestDelegate(bool silent);
    public static bool call_unittest(int seed, TestDelegate test, ref int sticky)
    {
        try
        {
            alglib.math.rndobject = new System.Random(seed);
            if( !test(true) )
            {
                sticky = 1;
                return false;
            }
        }
        catch(alglib.alglibexception e)
        {
            sticky = 1;
            return false;
        }
        return true;
    }
    public static int Main(string[] args)
	{
        int seed;
        int result;
        if( args.Length==1 )
            seed = Convert.ToInt32(args[0]);
        else
            seed = System.DateTime.Now.Millisecond + 1000*System.DateTime.Now.Second + 60*1000*System.DateTime.Now.Minute;
        result = 0;
        try
        {
            if( call_unittest(seed, new TestDelegate(alglib.testhqrndunit.testhqrnd), ref result) )
                System.Console.WriteLine("hqrnd                            OK");
            else
            {
                System.Console.Write("hqrnd                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testtsortunit.testtsort), ref result) )
                System.Console.WriteLine("tsort                            OK");
            else
            {
                System.Console.Write("tsort                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testnearestneighborunit.testnearestneighbor), ref result) )
                System.Console.WriteLine("nearestneighbor                  OK");
            else
            {
                System.Console.Write("nearestneighbor                  FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testablasunit.testablas), ref result) )
                System.Console.WriteLine("ablas                            OK");
            else
            {
                System.Console.Write("ablas                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testbasestatunit.testbasestat), ref result) )
                System.Console.WriteLine("basestat                         OK");
            else
            {
                System.Console.Write("basestat                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testbdssunit.testbdss), ref result) )
                System.Console.WriteLine("bdss                             OK");
            else
            {
                System.Console.Write("bdss                             FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testdforestunit.testdforest), ref result) )
                System.Console.WriteLine("dforest                          OK");
            else
            {
                System.Console.Write("dforest                          FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testblasunit.testblas), ref result) )
                System.Console.WriteLine("blas                             OK");
            else
            {
                System.Console.Write("blas                             FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testkmeansunit.testkmeans), ref result) )
                System.Console.WriteLine("kmeans                           OK");
            else
            {
                System.Console.Write("kmeans                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testhblasunit.testhblas), ref result) )
                System.Console.WriteLine("hblas                            OK");
            else
            {
                System.Console.Write("hblas                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testreflectionsunit.testreflections), ref result) )
                System.Console.WriteLine("reflections                      OK");
            else
            {
                System.Console.Write("reflections                      FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testcreflectionsunit.testcreflections), ref result) )
                System.Console.WriteLine("creflections                     OK");
            else
            {
                System.Console.Write("creflections                     FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testsblasunit.testsblas), ref result) )
                System.Console.WriteLine("sblas                            OK");
            else
            {
                System.Console.Write("sblas                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testortfacunit.testortfac), ref result) )
                System.Console.WriteLine("ortfac                           OK");
            else
            {
                System.Console.Write("ortfac                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testevdunit.testevd), ref result) )
                System.Console.WriteLine("evd                              OK");
            else
            {
                System.Console.Write("evd                              FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testmatgenunit.testmatgen), ref result) )
                System.Console.WriteLine("matgen                           OK");
            else
            {
                System.Console.Write("matgen                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testtrfacunit.testtrfac), ref result) )
                System.Console.WriteLine("trfac                            OK");
            else
            {
                System.Console.Write("trfac                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testtrlinsolveunit.testtrlinsolve), ref result) )
                System.Console.WriteLine("trlinsolve                       OK");
            else
            {
                System.Console.Write("trlinsolve                       FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testsafesolveunit.testsafesolve), ref result) )
                System.Console.WriteLine("safesolve                        OK");
            else
            {
                System.Console.Write("safesolve                        FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testrcondunit.testrcond), ref result) )
                System.Console.WriteLine("rcond                            OK");
            else
            {
                System.Console.Write("rcond                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testmatinvunit.testmatinv), ref result) )
                System.Console.WriteLine("matinv                           OK");
            else
            {
                System.Console.Write("matinv                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testldaunit.testlda), ref result) )
                System.Console.WriteLine("lda                              OK");
            else
            {
                System.Console.Write("lda                              FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testgammafuncunit.testgammafunc), ref result) )
                System.Console.WriteLine("gammafunc                        OK");
            else
            {
                System.Console.Write("gammafunc                        FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testbdsvdunit.testbdsvd), ref result) )
                System.Console.WriteLine("bdsvd                            OK");
            else
            {
                System.Console.Write("bdsvd                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testsvdunit.testsvd), ref result) )
                System.Console.WriteLine("svd                              OK");
            else
            {
                System.Console.Write("svd                              FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testlinregunit.testlinreg), ref result) )
                System.Console.WriteLine("linreg                           OK");
            else
            {
                System.Console.Write("linreg                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testxblasunit.testxblas), ref result) )
                System.Console.WriteLine("xblas                            OK");
            else
            {
                System.Console.Write("xblas                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testdensesolverunit.testdensesolver), ref result) )
                System.Console.WriteLine("densesolver                      OK");
            else
            {
                System.Console.Write("densesolver                      FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testlinminunit.testlinmin), ref result) )
                System.Console.WriteLine("linmin                           OK");
            else
            {
                System.Console.Write("linmin                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testmincgunit.testmincg), ref result) )
                System.Console.WriteLine("mincg                            OK");
            else
            {
                System.Console.Write("mincg                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testminbleicunit.testminbleic), ref result) )
                System.Console.WriteLine("minbleic                         OK");
            else
            {
                System.Console.Write("minbleic                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testmcpdunit.testmcpd), ref result) )
                System.Console.WriteLine("mcpd                             OK");
            else
            {
                System.Console.Write("mcpd                             FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testfblsunit.testfbls), ref result) )
                System.Console.WriteLine("fbls                             OK");
            else
            {
                System.Console.Write("fbls                             FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testminlbfgsunit.testminlbfgs), ref result) )
                System.Console.WriteLine("minlbfgs                         OK");
            else
            {
                System.Console.Write("minlbfgs                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testmlptrainunit.testmlptrain), ref result) )
                System.Console.WriteLine("mlptrain                         OK");
            else
            {
                System.Console.Write("mlptrain                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testmlpeunit.testmlpe), ref result) )
                System.Console.WriteLine("mlpe                             OK");
            else
            {
                System.Console.Write("mlpe                             FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testpcaunit.testpca), ref result) )
                System.Console.WriteLine("pca                              OK");
            else
            {
                System.Console.Write("pca                              FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testodesolverunit.testodesolver), ref result) )
                System.Console.WriteLine("odesolver                        OK");
            else
            {
                System.Console.Write("odesolver                        FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testfftunit.testfft), ref result) )
                System.Console.WriteLine("fft                              OK");
            else
            {
                System.Console.Write("fft                              FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testconvunit.testconv), ref result) )
                System.Console.WriteLine("conv                             OK");
            else
            {
                System.Console.Write("conv                             FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testcorrunit.testcorr), ref result) )
                System.Console.WriteLine("corr                             OK");
            else
            {
                System.Console.Write("corr                             FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testfhtunit.testfht), ref result) )
                System.Console.WriteLine("fht                              OK");
            else
            {
                System.Console.Write("fht                              FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testgqunit.testgq), ref result) )
                System.Console.WriteLine("gq                               OK");
            else
            {
                System.Console.Write("gq                               FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testgkqunit.testgkq), ref result) )
                System.Console.WriteLine("gkq                              OK");
            else
            {
                System.Console.Write("gkq                              FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testautogkunit.testautogk), ref result) )
                System.Console.WriteLine("autogk                           OK");
            else
            {
                System.Console.Write("autogk                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testidwintunit.testidwint), ref result) )
                System.Console.WriteLine("idwint                           OK");
            else
            {
                System.Console.Write("idwint                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testratintunit.testratint), ref result) )
                System.Console.WriteLine("ratint                           OK");
            else
            {
                System.Console.Write("ratint                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testpolintunit.testpolint), ref result) )
                System.Console.WriteLine("polint                           OK");
            else
            {
                System.Console.Write("polint                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testspline1dunit.testspline1d), ref result) )
                System.Console.WriteLine("spline1d                         OK");
            else
            {
                System.Console.Write("spline1d                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testminlmunit.testminlm), ref result) )
                System.Console.WriteLine("minlm                            OK");
            else
            {
                System.Console.Write("minlm                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testlsfitunit.testlsfit), ref result) )
                System.Console.WriteLine("lsfit                            OK");
            else
            {
                System.Console.Write("lsfit                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testpsplineunit.testpspline), ref result) )
                System.Console.WriteLine("pspline                          OK");
            else
            {
                System.Console.Write("pspline                          FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testspline2dunit.testspline2d), ref result) )
                System.Console.WriteLine("spline2d                         OK");
            else
            {
                System.Console.Write("spline2d                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testspdgevdunit.testspdgevd), ref result) )
                System.Console.WriteLine("spdgevd                          OK");
            else
            {
                System.Console.Write("spdgevd                          FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testinverseupdateunit.testinverseupdate), ref result) )
                System.Console.WriteLine("inverseupdate                    OK");
            else
            {
                System.Console.Write("inverseupdate                    FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testschurunit.testschur), ref result) )
                System.Console.WriteLine("schur                            OK");
            else
            {
                System.Console.Write("schur                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testnlequnit.testnleq), ref result) )
                System.Console.WriteLine("nleq                             OK");
            else
            {
                System.Console.Write("nleq                             FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testchebyshevunit.testchebyshev), ref result) )
                System.Console.WriteLine("chebyshev                        OK");
            else
            {
                System.Console.Write("chebyshev                        FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testhermiteunit.testhermite), ref result) )
                System.Console.WriteLine("hermite                          OK");
            else
            {
                System.Console.Write("hermite                          FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testlaguerreunit.testlaguerre), ref result) )
                System.Console.WriteLine("laguerre                         OK");
            else
            {
                System.Console.Write("laguerre                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testlegendreunit.testlegendre), ref result) )
                System.Console.WriteLine("legendre                         OK");
            else
            {
                System.Console.Write("legendre                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testalglibbasicsunit.testalglibbasics), ref result) )
                System.Console.WriteLine("alglibbasics                     OK");
            else
            {
                System.Console.Write("alglibbasics                     FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
        }
        catch(Exception e)
        {
            System.Console.WriteLine("Unhandled exception being thrown!");
            return 1;
        }
        return result;
	}
}

