#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 k = 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];
            int[] bins = new int[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 unit2errors = new bool();
            bool experrors = new bool();
            double expsigmaerr = 0;
            bool discreteerr = new bool();
            bool continuouserr = new bool();
            hqrnd.hqrndstate state = new hqrnd.hqrndstate();

            waserrors = false;
            sigmathreshold = 7;
            samplesize = 100000;
            passcount = 50;
            seederrors = false;
            urerrors = false;
            uierrors = false;
            normerrors = false;
            experrors = false;
            unit2errors = 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 unit2
            //
            unsetstate(state);
            hqrnd.hqrndrandomize(state);
            n = 1000000;
            bins = new int[10];
            for(i=0; i<=alglib.ap.len(bins)-1; i++)
            {
                bins[i] = 0;
            }
            for(pass=0; pass<=n-1; pass++)
            {
                hqrnd.hqrndunit2(state, ref r1, ref r2);
                apserv.seterrorflag(ref unit2errors, (double)(Math.Abs(r1*r1+r2*r2-1))>(double)(100*math.machineepsilon));
                k = (int)Math.Floor((Math.Atan2(r1, r2)+Math.PI)/(2*Math.PI)*alglib.ap.len(bins));
                if( k<0 )
                {
                    k = 0;
                }
                if( k>=alglib.ap.len(bins) )
                {
                    k = alglib.ap.len(bins)-1;
                }
                bins[k] = bins[k]+1;
            }
            for(i=0; i<=alglib.ap.len(bins)-1; i++)
            {
                apserv.seterrorflag(ref unit2errors, (double)(bins[i])<(double)(0.9*n/alglib.ap.len(bins)) || (double)(bins[i])>(double)(1.1*n/alglib.ap.len(bins)));
            }
            
            //
            // 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);
            
            //
            //Discrete/Continuous tests
            //
            discreteerr = hqrnddiscretetest(true);
            continuouserr = hqrndcontinuoustest(true);
            
            //
            // Final report
            //
            waserrors = ((((((seederrors || urerrors) || uierrors) || normerrors) || unit2errors) || experrors) || discreteerr) || continuouserr;
            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("UNIT2:                                   ");
                if( !unit2errors )
                {
                    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();
                }
                System.Console.Write("DISCRETE:                                ");
                if( !discreteerr )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("CONTINUOUS:                              ");
                if( !continuouserr )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testhqrnd(bool silent)
        {
            return testhqrnd(silent);
        }


        /*************************************************************************
        Function for test HQRNDContinuous function
        *************************************************************************/
        public static bool hqrndcontinuoustest(bool silent)
        {
            bool result = new bool();
            double[] sample = new double[0];
            int[] bins = new int[0];
            double[] binbounds = new double[0];
            int nb = 0;
            int samplesize = 0;
            hqrnd.hqrndstate state = new hqrnd.hqrndstate();
            int xp = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            double sigma = 0;
            double sigmamax = 0;

            result = false;
            
            //
            // Test for sample size equal to 1
            //
            sample = new double[1];
            sample[0] = math.randomreal();
            hqrnd.hqrndrandomize(state);
            result = result || (double)(hqrnd.hqrndcontinuous(state, sample, 1))!=(double)(sample[0]);
            
            //
            // Test for larger samples
            //
            xp = 100000;
            sigmamax = 10.0;
            for(samplesize=2; samplesize<=5; samplesize++)
            {
                
                //
                // 1. Generate random sample with SampleSize points
                // 2. Generate NB=3*(SampleSize-1) bins, with bounds as prescribed by (BinBounds[I],BinBounds[I+1]).
                //    Bin bounds are generated in such a way that value can fall into any bin with same probability
                // 3. Generate many random values
                // 4. Calculate number of values which fall into each bin
                // 5. Bins[I] should have binomial distribution with mean XP/NB and 
                //    variance XP*(1/NB)*(1-1/NB)
                //
                nb = 3*(samplesize-1);
                sigma = Math.Sqrt(xp*((double)1/(double)nb)*(1-(double)1/(double)nb));
                sample = new double[samplesize];
                sample[0] = 2*math.randomreal()-1;
                for(i=0; i<=samplesize-2; i++)
                {
                    sample[i+1] = sample[i]+0.1+math.randomreal();
                }
                bins = new int[nb];
                binbounds = new double[nb+1];
                for(i=0; i<=samplesize-2; i++)
                {
                    bins[3*i+0] = 0;
                    bins[3*i+1] = 0;
                    bins[3*i+2] = 0;
                    binbounds[3*i+0] = sample[i];
                    binbounds[3*i+1] = sample[i]+(sample[i+1]-sample[i])/3;
                    binbounds[3*i+2] = sample[i]+(sample[i+1]-sample[i])*2/3;
                }
                binbounds[nb] = sample[samplesize-1];
                hqrnd.hqrndrandomize(state);
                for(i=0; i<=xp-1; i++)
                {
                    v = hqrnd.hqrndcontinuous(state, sample, samplesize);
                    for(j=0; j<=nb-1; j++)
                    {
                        if( (double)(v)>(double)(binbounds[j]) && (double)(v)<(double)(binbounds[j+1]) )
                        {
                            bins[j] = bins[j]+1;
                            break;
                        }
                    }
                }
                for(i=0; i<=nb-1; i++)
                {
                    result = result || (double)(Math.Abs(bins[i]-(double)xp/(double)nb))>(double)(sigma*sigmamax);
                }
            }
            return result;
        }


        /*************************************************************************
        Function for test HQRNDContinuous function
        *************************************************************************/
        public static bool hqrnddiscretetest(bool silent)
        {
            bool result = new bool();
            double[] sample = new double[0];
            double sigma = 0;
            double sigmathreshold = 0;
            double tsample = 0;
            double max = 0;
            double min = 0;
            int i = 0;
            int j = 0;
            int s1 = 0;
            int s2 = 0;
            int binscount = 0;
            int xp = 0;
            int[] nn = new int[0];
            hqrnd.hqrndstate state = new hqrnd.hqrndstate();

            
            //
            // We test that all values from discrete sample are generated with same probability.
            // To do this, we generate random values many times, then we calculate actual probabilities
            // and compare them with theoretical ones.
            //
            max = 100;
            min = -100;
            xp = 100000;
            sigmathreshold = 10.0;
            for(binscount=1; binscount<=5; binscount++)
            {
                sigma = Math.Sqrt(xp*((double)1/(double)binscount)*(1-(double)1/(double)binscount));
                nn = new int[binscount];
                for(i=0; i<=binscount-1; i++)
                {
                    nn[i] = 0;
                }
                sample = new double[binscount];
                sample[0] = (max-min)*math.randomreal()+min;
                for(i=1; i<=binscount-1; i++)
                {
                    sample[i] = sample[i-1]+max*math.randomreal()+0.001;
                }
                s1 = 1+math.randominteger(32000);
                s2 = 1+math.randominteger(32000);
                hqrnd.hqrndseed(s1, s2, state);
                for(i=0; i<=xp-1; i++)
                {
                    tsample = hqrnd.hqrnddiscrete(state, sample, binscount);
                    for(j=0; j<=binscount-1; j++)
                    {
                        if( (double)(tsample)==(double)(sample[j]) )
                        {
                            nn[j] = nn[j]+1;
                            break;
                        }
                    }
                }
                for(i=0; i<=binscount-1; i++)
                {
                    if( (double)(nn[i])<(double)((double)xp/(double)binscount-sigmathreshold*sigma) || (double)(nn[i])>(double)((double)xp/(double)binscount+sigmathreshold*sigma) )
                    {
                        if( !silent )
                        {
                            System.Console.Write("HQRNDDiscreteTest::ErrorReport::");
                            System.Console.WriteLine();
                            System.Console.Write("nn[");
                            System.Console.Write("{0,0:d}",i);
                            System.Console.Write("]=");
                            System.Console.Write("{0,0:d}",nn[i]);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                            System.Console.Write("   xp/BinsCount=");
                            System.Console.Write("{0,0:F5}",(double)xp/(double)binscount);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                            System.Console.Write("   C*sigma=");
                            System.Console.Write("{0,0:F5}",sigmathreshold*sigma);
                            System.Console.WriteLine();
                            System.Console.Write("HQRNDDiscreteTest: test is FAILED!");
                            System.Console.WriteLine();
                        }
                        result = true;
                        return result;
                    }
                }
                if( !silent )
                {
                    System.Console.Write("HQRNDDiscreteTest: test is OK.");
                    System.Console.WriteLine();
                }
            }
            result = false;
            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.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 m = 0;
            int offs = 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];
            int[] i1 = new int[0];
            int[] i2 = new int[0];
            int[] i3 = new int[0];
            int[] a4 = new int[0];
            int[] pa4 = new int[0];
            double[] ar = new double[0];
            double[] ar2 = 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];
            bool distinctvals = new bool();

            waserrors = false;
            maxn = 100;
            passcount = 10;
            
            //
            // Test tagsort
            //
            for(n=1; n<=maxn; n++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    
                    //
                    // Pprobably distinct sort:
                    // * generate array of integer random numbers.
                    //   Because of birthday paradox, random numbers have to be VERY large
                    //   in order to avoid situation when we have distinct values.
                    // * 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];
                    a4 = new int[n];
                    ar = new double[n];
                    ar2 = new double[n];
                    ai = new int[n];
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = math.randominteger(100000000);
                        a0[i] = a[i];
                        a1[i] = a[i];
                        a2[i] = a[i];
                        a3[i] = a[i];
                        a4[i] = (int)Math.Round(a[i]);
                        ar[i] = i;
                        ar2[i] = i;
                        ai[i] = i;
                    }
                    tsort.tagsort(ref a0, n, ref p1, ref p2);
                    testsortresults(a0, p1, p2, a, n, ref waserrors);
                    distinctvals = true;
                    for(i=1; i<=n-1; i++)
                    {
                        distinctvals = distinctvals && (double)(a0[i])!=(double)(a0[i-1]);
                    }
                    if( distinctvals )
                    {
                        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]);
                        }
                        tsort.tagsortmiddleir(ref a4, ref ar2, 0, n);
                        for(i=0; i<=n-1; i++)
                        {
                            waserrors = (waserrors || (double)(a4[i])!=(double)(a0[i])) || (double)(ar2[i])!=(double)(p1[i]);
                        }
                    }
                    
                    //
                    // Non-distinct sort.
                    // We test that keys are correctly reordered, but do NOT test order of values.
                    //
                    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];
                    a4 = new int[n];
                    ar = new double[n];
                    ar2 = new double[n];
                    ai = new int[n];
                    for(i=0; i<=n-1; i++)
                    {
                        a[i] = (n-i)/2;
                        a0[i] = a[i];
                        a1[i] = a[i];
                        a2[i] = a[i];
                        a3[i] = a[i];
                        a4[i] = (int)Math.Round(a[i]);
                        ar[i] = i;
                        ar2[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]);
                    }
                    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]);
                    }
                    tsort.tagsortfast(ref a3, ref bufr1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors || (double)(a3[i])!=(double)(a0[i]);
                    }
                    tsort.tagsortmiddleir(ref a4, ref ar2, 0, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors || (double)(a4[i])!=(double)(a0[i]);
                    }
                    
                    //
                    // 'All same' sort
                    // We test that keys are correctly reordered, but do NOT test order of values.
                    //
                    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];
                    a4 = new int[n];
                    ar = new double[n];
                    ar2 = 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];
                        a4[i] = (int)Math.Round(a[i]);
                        ar[i] = i;
                        ar2[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]);
                    }
                    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]);
                    }
                    tsort.tagsortfast(ref a3, ref bufr1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors || (double)(a3[i])!=(double)(a0[i]);
                    }
                    tsort.tagsortmiddleir(ref a4, ref ar2, 0, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors || (double)(a4[i])!=(double)(a0[i]);
                    }
                    
                    //
                    // 0-1 sort
                    // We test that keys are correctly reordered, but do NOT test order of values.
                    //
                    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];
                    a4 = new int[n];
                    ar = new double[n];
                    ar2 = 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];
                        a4[i] = (int)Math.Round(a[i]);
                        ar[i] = i;
                        ar2[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]);
                    }
                    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]);
                    }
                    tsort.tagsortfast(ref a3, ref bufr1, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors || (double)(a3[i])!=(double)(a0[i]);
                    }
                    tsort.tagsortmiddleir(ref a4, ref ar2, 0, n);
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors || (double)(a4[i])!=(double)(a0[i]);
                    }
                    
                    //
                    // Special test for TagSortMiddleIR: sorting in the middle gives same results
                    // as sorting in the beginning of the array
                    //
                    m = 3*n;
                    offs = math.randominteger(n);
                    i1 = new int[m];
                    i2 = new int[m];
                    i3 = new int[m];
                    ar = new double[m];
                    ar2 = new double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        i1[i] = math.randominteger(100000000);
                        i2[i] = i1[i];
                        i3[i] = i1[i];
                        ar[i] = i;
                        ar2[i] = i;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        i1[i] = i1[offs+i];
                        ar[i] = ar[offs+i];
                    }
                    tsort.tagsortmiddleir(ref i1, ref ar, 0, n);
                    for(i=1; i<=n-1; i++)
                    {
                        distinctvals = distinctvals && i1[i]!=i1[i-1];
                    }
                    if( distinctvals )
                    {
                        tsort.tagsortmiddleir(ref i2, ref ar2, offs, n);
                        for(i=0; i<=n-1; i++)
                        {
                            waserrors = (waserrors || i2[offs+i]!=i1[i]) || (double)(ar2[offs+i])!=(double)(ar[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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testtsort(bool silent)
        {
            return testtsort(silent);
        }


        /*************************************************************************
        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);
                    }
                }
            }
            kdterrors = kdterrors || testspecialcases();
            
            //
            // 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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testnearestneighbor(bool silent)
        {
            return testnearestneighbor(silent);
        }


        /*************************************************************************
        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++)
                    {
                        alglib.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 || alglib.ap.rows(qx)!=k) || alglib.ap.cols(qx)!=nx;
                        kdterrors = (kdterrors || alglib.ap.rows(qxy)!=k) || alglib.ap.cols(qxy)!=nx+ny;
                        kdterrors = kdterrors || alglib.ap.len(qtags)!=k;
                        kdterrors = kdterrors || alglib.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<=alglib.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;
            }
        }


        /*************************************************************************
        This function tests different special cases:
        * Kd-tree for a zero number of points
        * Kd-tree for array with a lot of duplicates (early versions of ALGLIB
          raised stack overflow on such datasets)

        It returns True on errors, False on success.
        *************************************************************************/
        private static bool testspecialcases()
        {
            bool result = new bool();
            nearestneighbor.kdtree kdt = new nearestneighbor.kdtree();
            double[,] xy = new double[0,0];
            int[] tags = new int[0];
            double[] x = new double[0];
            int n = 0;
            int nk = 0;
            int nx = 0;
            int ny = 0;
            int normtype = 0;
            int i = 0;
            int j = 0;
            double v = 0;

            result = false;
            for(nx=1; nx<=3; nx++)
            {
                for(ny=0; ny<=3; ny++)
                {
                    for(normtype=0; normtype<=2; normtype++)
                    {
                        
                        //
                        // Build tree
                        //
                        if( (double)(math.randomreal())>(double)(0.5) )
                        {
                            nearestneighbor.kdtreebuildtagged(xy, tags, 0, nx, ny, normtype, kdt);
                        }
                        else
                        {
                            nearestneighbor.kdtreebuild(xy, 0, nx, ny, normtype, kdt);
                        }
                        
                        //
                        // Test different queries
                        //
                        x = new double[nx];
                        for(i=0; i<=nx-1; i++)
                        {
                            x[i] = math.randomreal();
                        }
                        result = result || nearestneighbor.kdtreequeryknn(kdt, x, 1, true)>0;
                        result = result || nearestneighbor.kdtreequeryrnn(kdt, x, 1.0E6, true)>0;
                        result = result || nearestneighbor.kdtreequeryaknn(kdt, x, 1, true, 2.0)>0;
                    }
                }
            }
            
            //
            // Ability to handle array with a lot of duplicates without causing
            // stack overflow.
            //
            // Two situations are handled:
            // * array where ALL N elements are duplicates
            // * array where there are NK distinct elements and N-NK duplicates
            //
            nx = 2;
            ny = 1;
            n = 100000;
            nk = 100;
            v = math.randomreal();
            xy = new double[n, nx+ny];
            x = new double[nx];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=nx+ny-1; j++)
                {
                    xy[i,j] = v;
                }
            }
            nearestneighbor.kdtreebuild(xy, n, nx, ny, 2, kdt);
            for(j=0; j<=nx-1; j++)
            {
                x[j] = v;
            }
            result = result || nearestneighbor.kdtreequeryrnn(kdt, x, 0.0001, true)!=n;
            for(i=0; i<=nk-1; i++)
            {
                for(j=0; j<=nx+ny-1; j++)
                {
                    xy[i,j] = math.randomreal();
                }
            }
            nearestneighbor.kdtreebuild(xy, n, nx, ny, 2, kdt);
            result = result || nearestneighbor.kdtreequeryrnn(kdt, x, 0.0001, true)<n-nk;
            return result;
        }


    }
    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);
            gemmerrors = gemmerrors || testgemm(8*ablas.ablasblocksize(ra)-1, 8*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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testablas(bool silent)
        {
            return testablas(silent);
        }


        /*************************************************************************
        ?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, 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, 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, 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, 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.cmatrixherk(n-xoffsi, k-xoffsj, alpha, cc, xoffsi, xoffsj, 0, beta, ca1, aoffsi, aoffsj, uppertype==0);
                    refcmatrixherk(n-xoffsi, k-xoffsj, alpha, cc, xoffsi, xoffsj, 0, beta, ref ca2, aoffsi, aoffsj, uppertype==0);
                }
                else
                {
                    ablas.cmatrixherk(n-xoffsi, k-xoffsj, alpha, cct, xoffsj, xoffsi, 2, beta, ca1, aoffsi, aoffsj, uppertype==0);
                    refcmatrixherk(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 old version of HERK (named SYRK)
                // 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, ca1, aoffsi, aoffsj, uppertype==0);
                    refcmatrixherk(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, ca1, aoffsi, aoffsj, uppertype==0);
                    refcmatrixherk(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, 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, 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, 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, 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, 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 refcmatrixherk(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();
            bool rankerrors = 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;
            double tmean = 0;
            double tvariance = 0;
            double tskewness = 0;
            double tkurtosis = 0;
            int i_ = 0;

            
            //
            // Primary settings
            //
            waserrors = false;
            s1errors = false;
            covcorrerrors = false;
            rankerrors = false;
            threshold = 1000*math.machineepsilon;
            
            //
            // Ranking
            //
            testranking(ref rankerrors);
            
            //
            // * prepare X and Y - two test samples
            // * test 1-sample coefficients
            // * test for SampleMean, SampleVariance,
            //   SampleSkewness, SampleKurtosis.
            //
            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);
            tmean = basestat.samplemean(x, n);
            tvariance = basestat.samplevariance(x, n);
            tskewness = basestat.sampleskewness(x, n);
            tkurtosis = basestat.samplekurtosis(x, n);
            s1errors = s1errors || (double)(mean-tmean)!=(double)(0);
            s1errors = s1errors || (double)(variance-tvariance)!=(double)(0);
            s1errors = s1errors || (double)(skewness-tskewness)!=(double)(0);
            s1errors = s1errors || (double)(kurtosis-tkurtosis)!=(double)(0);
            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) || rankerrors;
            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();
                }
                System.Console.Write("* RANKING:                               ");
                if( !rankerrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testbasestat(bool silent)
        {
            return testbasestat(silent);
        }


        /*************************************************************************
        This function tests ranking functionality. In case  of  failure  it  sets
        Err parameter to True; this parameter is left unchanged otherwise.
        *************************************************************************/
        private static void testranking(ref bool err)
        {
            int testk = 0;
            int npoints = 0;
            int nfeatures = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double[,] xy0 = new double[0,0];
            double[,] xy1 = new double[0,0];
            double[,] xy2 = new double[0,0];
            double v = 0;

            
            //
            // Test 1 - large array, unique ranks, each row is obtained as follows:
            // * we generate X[i=0..N-1] = I
            // * we add random noise: X[i] := X[i] + 0.2*randomreal()-0.1
            // * we perform random permutation
            //
            // Such dataset has following properties:
            // * all data are unique within their rows
            // * rank(X[i]) = round(X[i])
            //
            // We perform several tests with different NPoints/NFeatures.
            //
            for(testk=0; testk<=1; testk++)
            {
                
                //
                // Select problem size
                //
                if( testk==0 )
                {
                    npoints = 200;
                    nfeatures = 1000;
                }
                else
                {
                    npoints = 1000;
                    nfeatures = 200;
                }
                
                //
                // Generate XY0, XY1, XY2
                //
                xy0 = new double[npoints, nfeatures];
                xy1 = new double[npoints, nfeatures];
                xy2 = new double[npoints, nfeatures];
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=0; j<=nfeatures-1; j++)
                    {
                        xy0[i,j] = j+0.2*math.randomreal()-0.1;
                    }
                    for(j=0; j<=nfeatures-2; j++)
                    {
                        k = math.randominteger(nfeatures-j);
                        if( k!=0 )
                        {
                            v = xy0[i,j];
                            xy0[i,j] = xy0[i,j+k];
                            xy0[i,j+k] = v;
                        }
                    }
                    for(j=0; j<=nfeatures-1; j++)
                    {
                        xy1[i,j] = xy0[i,j];
                        xy2[i,j] = xy0[i,j];
                    }
                }
                
                //
                // Test uncentered ranks
                //
                basestat.rankdata(xy0, npoints, nfeatures);
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=0; j<=nfeatures-1; j++)
                    {
                        if( (double)(xy0[i,j])!=(double)((int)Math.Round(xy2[i,j])) )
                        {
                            err = true;
                        }
                    }
                }
                
                //
                // Test centered ranks:
                // they must be equal to uncentered ranks minus (NFeatures-1)/2
                //
                basestat.rankdatacentered(xy1, npoints, nfeatures);
                for(i=0; i<=npoints-1; i++)
                {
                    for(j=0; j<=nfeatures-1; j++)
                    {
                        if( (double)(xy1[i,j])!=(double)((int)Math.Round(xy2[i,j])-(double)(nfeatures-1)/(double)2) )
                        {
                            err = true;
                        }
                    }
                }
            }
            
            //
            // Test correct handling of tied ranks
            //
            npoints = 3;
            nfeatures = 4;
            xy0 = new double[npoints, nfeatures];
            xy1 = new double[npoints, nfeatures];
            xy0[0,0] = 2.25;
            xy0[0,1] = 3.75;
            xy0[0,2] = 3.25;
            xy0[0,3] = 2.25;
            xy0[1,0] = 2;
            xy0[1,1] = 2;
            xy0[1,2] = 2;
            xy0[1,3] = 7;
            xy0[2,0] = 9;
            xy0[2,1] = 9;
            xy0[2,2] = 9;
            xy0[2,3] = 9;
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=nfeatures-1; j++)
                {
                    xy1[i,j] = xy0[i,j];
                }
            }
            basestat.rankdata(xy0, npoints, nfeatures);
            if( (double)(Math.Abs(xy0[0,0]-0.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[0,1]-3.0))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[0,2]-2.0))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[0,3]-0.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[1,0]-1.0))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[1,1]-1.0))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[1,2]-1.0))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[1,3]-3.0))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[2,0]-1.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[2,1]-1.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[2,2]-1.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy0[2,3]-1.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            basestat.rankdatacentered(xy1, npoints, nfeatures);
            if( (double)(Math.Abs(xy1[0,0]+1.0))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy1[0,1]-1.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy1[0,2]-0.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy1[0,3]+1.0))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy1[1,0]+0.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy1[1,1]+0.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy1[1,2]+0.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(Math.Abs(xy1[1,3]-1.5))>(double)(10*math.machineepsilon) )
            {
                err = true;
            }
            if( (double)(xy1[2,0])!=(double)(0) )
            {
                err = true;
            }
            if( (double)(xy1[2,1])!=(double)(0) )
            {
                err = true;
            }
            if( (double)(xy1[2,2])!=(double)(0) )
            {
                err = true;
            }
            if( (double)(xy1[2,3])!=(double)(0) )
            {
                err = true;
            }
        }


    }
    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] = 99;
                        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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testbdss(bool silent)
        {
            return testbdss(silent);
        }


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


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testblas(bool silent)
        {
            return testblas(silent);
        }


        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;
            }
            alglib.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 testclusteringunit
    {
        /*************************************************************************
        Testing clustering
        *************************************************************************/
        public static bool testclustering(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool basicahcerrors = new bool();
            bool ahcerrors = new bool();
            bool kmeansconverrors = new bool();
            bool kmeanssimpleerrors = new bool();
            bool kmeansothererrors = new bool();
            bool kmeansrestartserrors = new bool();
            int passcount = 0;
            int nf = 0;
            int nc = 0;

            
            //
            // AHC tests
            //
            basicahcerrors = basicahctests();
            ahcerrors = advancedahctests();
            
            //
            // k-means tests
            //
            passcount = 10;
            kmeansconverrors = false;
            kmeansothererrors = false;
            kmeanssimpleerrors = false;
            kmeansrestartserrors = false;
            kmeansspecialtests(ref kmeansothererrors);
            kmeansinfinitelooptest(ref kmeansothererrors);
            kmeansrestartstest(ref kmeansconverrors, ref kmeansrestartserrors);
            for(nf=1; nf<=5; nf++)
            {
                for(nc=1; nc<=5; nc++)
                {
                    kmeanssimpletest1(nf, nc, passcount, ref kmeansconverrors, ref kmeansothererrors, ref kmeanssimpleerrors);
                }
            }
            
            //
            // Results
            //
            waserrors = false;
            waserrors = waserrors || (basicahcerrors || ahcerrors);
            waserrors = waserrors || (((kmeansconverrors || kmeansothererrors) || kmeanssimpleerrors) || kmeansrestartserrors);
            if( !silent )
            {
                System.Console.Write("TESTING CLUSTERING");
                System.Console.WriteLine();
                System.Console.Write("AHC:                                ");
                System.Console.WriteLine();
                System.Console.Write("* BASIC TESTS                       ");
                if( !basicahcerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* GENERAL TESTS                     ");
                if( !ahcerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("K-MEANS:                            ");
                System.Console.WriteLine();
                System.Console.Write("* CONVERGENCE                       ");
                if( !kmeansconverrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SIMPLE TASKS                      ");
                if( !kmeanssimpleerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* OTHER PROPERTIES                  ");
                if( !kmeansothererrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* RESTARTS PROPERTIES               ");
                if( !kmeansrestartserrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testclustering(bool silent)
        {
            return testclustering(silent);
        }


        /*************************************************************************
        Basic agglomerative hierarchical clustering tests:
        returns True on failure, False on success.

        Basic tests study algorithm behavior on simple,  hand-made  datasets  with
        small number of points (1..10).
        *************************************************************************/
        private static bool basicahctests()
        {
            bool result = new bool();
            clustering.clusterizerstate s = new clustering.clusterizerstate();
            clustering.ahcreport rep = new clustering.ahcreport();
            double[,] xy = new double[0,0];
            double[,] d = new double[0,0];
            double[,] c = new double[0,0];
            bool berr = new bool();
            int ahcalgo = 0;
            int i = 0;
            int j = 0;
            int npoints = 0;
            int k = 0;
            int[] cidx = new int[0];
            int[] cz = new int[0];
            int[] cidx2 = new int[0];
            int[] cz2 = new int[0];

            result = true;
            
            //
            // Test on empty problem
            //
            clustering.clusterizercreate(s);
            clustering.clusterizerrunahc(s, rep);
            if( rep.npoints!=0 )
            {
                return result;
            }
            
            //
            // Test on problem with one point
            //
            xy = new double[1, 2];
            xy[0,0] = math.randomreal();
            xy[0,1] = math.randomreal();
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, 1, 2, 0);
            clustering.clusterizerrunahc(s, rep);
            if( rep.npoints!=1 )
            {
                return result;
            }
            
            //
            // Test on problem with two points
            //
            xy = new double[2, 2];
            xy[0,0] = math.randomreal();
            xy[0,1] = math.randomreal();
            xy[1,0] = math.randomreal();
            xy[1,1] = math.randomreal();
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, 2, 2, 0);
            clustering.clusterizerrunahc(s, rep);
            if( (rep.npoints!=2 || alglib.ap.rows(rep.z)!=1) || alglib.ap.cols(rep.z)!=2 )
            {
                return result;
            }
            if( rep.z[0,0]!=0 || rep.z[0,1]!=1 )
            {
                return result;
            }
            
            //
            // Test on specially designed problem which should have
            // following dendrogram:
            //
            //   ------
            //   |    |
            // ----  ----
            // |  |  |  |
            // 0  1  2  3
            //
            // ...with first merge performed on 0 and 1, second merge
            // performed on 2 and 3. Complete linkage is used.
            //
            // Additionally we test ClusterizerSeparatedByDist() on this
            // problem for different distances. Test is performed by
            // comparing function result with ClusterizerGetKClusters()
            // for known K.
            //
            xy = new double[4, 1];
            xy[0,0] = 0.0;
            xy[1,0] = 1.0;
            xy[2,0] = 3.0;
            xy[3,0] = 4.1;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, 4, 1, 0);
            clustering.clusterizersetahcalgo(s, 0);
            clustering.clusterizerrunahc(s, rep);
            if( (((rep.npoints!=4 || alglib.ap.rows(rep.z)!=3) || alglib.ap.cols(rep.z)!=2) || alglib.ap.rows(rep.pz)!=3) || alglib.ap.cols(rep.pz)!=2 )
            {
                return result;
            }
            berr = false;
            berr = (berr || rep.z[0,0]!=0) || rep.z[0,1]!=1;
            berr = (berr || rep.z[1,0]!=2) || rep.z[1,1]!=3;
            berr = (berr || rep.z[2,0]!=4) || rep.z[2,1]!=5;
            berr = (((berr || rep.p[0]!=0) || rep.p[1]!=1) || rep.p[2]!=2) || rep.p[3]!=3;
            berr = (berr || rep.pz[0,0]!=0) || rep.pz[0,1]!=1;
            berr = (berr || rep.pz[1,0]!=2) || rep.pz[1,1]!=3;
            berr = (berr || rep.pz[2,0]!=4) || rep.pz[2,1]!=5;
            berr = (((berr || rep.pm[0,0]!=0) || rep.pm[0,1]!=0) || rep.pm[0,2]!=1) || rep.pm[0,3]!=1;
            berr = (((berr || rep.pm[1,0]!=2) || rep.pm[1,1]!=2) || rep.pm[1,2]!=3) || rep.pm[1,3]!=3;
            berr = (((berr || rep.pm[2,0]!=0) || rep.pm[2,1]!=1) || rep.pm[2,2]!=2) || rep.pm[2,3]!=3;
            if( berr )
            {
                return result;
            }
            clustering.clusterizerseparatedbydist(rep, 0.5, ref k, ref cidx, ref cz);
            clustering.clusterizergetkclusters(rep, 4, ref cidx2, ref cz2);
            if( k!=4 )
            {
                return result;
            }
            if( ((cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1]) || cidx[2]!=cidx2[2]) || cidx[3]!=cidx2[3] )
            {
                return result;
            }
            if( ((cz[0]!=cz2[0] || cz[1]!=cz2[1]) || cz[2]!=cz2[2]) || cz[3]!=cz2[3] )
            {
                return result;
            }
            clustering.clusterizerseparatedbydist(rep, 1.05, ref k, ref cidx, ref cz);
            clustering.clusterizergetkclusters(rep, 3, ref cidx2, ref cz2);
            if( k!=3 )
            {
                return result;
            }
            if( ((cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1]) || cidx[2]!=cidx2[2]) || cidx[3]!=cidx2[3] )
            {
                return result;
            }
            if( (cz[0]!=cz2[0] || cz[1]!=cz2[1]) || cz[2]!=cz2[2] )
            {
                return result;
            }
            clustering.clusterizerseparatedbydist(rep, 1.15, ref k, ref cidx, ref cz);
            clustering.clusterizergetkclusters(rep, 2, ref cidx2, ref cz2);
            if( k!=2 )
            {
                return result;
            }
            if( ((cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1]) || cidx[2]!=cidx2[2]) || cidx[3]!=cidx2[3] )
            {
                return result;
            }
            if( cz[0]!=cz2[0] || cz[1]!=cz2[1] )
            {
                return result;
            }
            
            //
            // Test on specially designed problem with Pearson distance
            // which should have following dendrogram:
            //
            //   ------
            //   |    |
            // ----  ----
            // |  |  |  |
            // 0  1  2  3
            //
            // This problem is used to test ClusterizerSeparatedByDist().
            // The test is performed by comparing function result with
            // ClusterizerGetKClusters() for known K.
            //
            // NOTE:
            // * corr(a0,a1) = 0.866
            // * corr(a2,a3) = 0.990
            // * corr(a0/a1, a2/a3)<=0.5
            //
            xy = new double[4, 3];
            xy[0,0] = 0.3;
            xy[0,1] = 0.5;
            xy[0,2] = 0.3;
            xy[1,0] = 0.3;
            xy[1,1] = 0.5;
            xy[1,2] = 0.4;
            xy[2,0] = 0.1;
            xy[2,1] = 0.5;
            xy[2,2] = 0.9;
            xy[3,0] = 0.1;
            xy[3,1] = 0.4;
            xy[3,2] = 0.9;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, 4, 3, 10);
            clustering.clusterizersetahcalgo(s, 1);
            clustering.clusterizerrunahc(s, rep);
            clustering.clusterizerseparatedbycorr(rep, 0.999, ref k, ref cidx, ref cz);
            clustering.clusterizergetkclusters(rep, 4, ref cidx2, ref cz2);
            if( k!=4 )
            {
                return result;
            }
            if( ((cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1]) || cidx[2]!=cidx2[2]) || cidx[3]!=cidx2[3] )
            {
                return result;
            }
            if( ((cz[0]!=cz2[0] || cz[1]!=cz2[1]) || cz[2]!=cz2[2]) || cz[3]!=cz2[3] )
            {
                return result;
            }
            clustering.clusterizerseparatedbycorr(rep, 0.900, ref k, ref cidx, ref cz);
            clustering.clusterizergetkclusters(rep, 3, ref cidx2, ref cz2);
            if( k!=3 )
            {
                return result;
            }
            if( ((cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1]) || cidx[2]!=cidx2[2]) || cidx[3]!=cidx2[3] )
            {
                return result;
            }
            if( (cz[0]!=cz2[0] || cz[1]!=cz2[1]) || cz[2]!=cz2[2] )
            {
                return result;
            }
            clustering.clusterizerseparatedbycorr(rep, 0.600, ref k, ref cidx, ref cz);
            clustering.clusterizergetkclusters(rep, 2, ref cidx2, ref cz2);
            if( k!=2 )
            {
                return result;
            }
            if( ((cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1]) || cidx[2]!=cidx2[2]) || cidx[3]!=cidx2[3] )
            {
                return result;
            }
            if( cz[0]!=cz2[0] || cz[1]!=cz2[1] )
            {
                return result;
            }
            
            //
            // Single linkage vs. complete linkage
            //
            xy = new double[6, 1];
            xy[0,0] = 0.0;
            xy[1,0] = 1.0;
            xy[2,0] = 2.1;
            xy[3,0] = 3.3;
            xy[4,0] = 6.0;
            xy[5,0] = 4.6;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, 6, 1, 0);
            clustering.clusterizersetahcalgo(s, 0);
            clustering.clusterizerrunahc(s, rep);
            if( rep.npoints!=6 || alglib.ap.len(rep.p)!=6 )
            {
                return result;
            }
            if( ((alglib.ap.rows(rep.z)!=5 || alglib.ap.cols(rep.z)!=2) || alglib.ap.rows(rep.pz)!=5) || alglib.ap.cols(rep.pz)!=2 )
            {
                return result;
            }
            berr = false;
            berr = berr || rep.p[0]!=2;
            berr = berr || rep.p[1]!=3;
            berr = berr || rep.p[2]!=4;
            berr = berr || rep.p[3]!=5;
            berr = berr || rep.p[4]!=0;
            berr = berr || rep.p[5]!=1;
            berr = (berr || rep.z[0,0]!=0) || rep.z[0,1]!=1;
            berr = (berr || rep.z[1,0]!=2) || rep.z[1,1]!=3;
            berr = (berr || rep.z[2,0]!=4) || rep.z[2,1]!=5;
            berr = (berr || rep.z[3,0]!=6) || rep.z[3,1]!=7;
            berr = (berr || rep.z[4,0]!=8) || rep.z[4,1]!=9;
            berr = (berr || rep.pz[0,0]!=2) || rep.pz[0,1]!=3;
            berr = (berr || rep.pz[1,0]!=4) || rep.pz[1,1]!=5;
            berr = (berr || rep.pz[2,0]!=0) || rep.pz[2,1]!=1;
            berr = (berr || rep.pz[3,0]!=6) || rep.pz[3,1]!=7;
            berr = (berr || rep.pz[4,0]!=8) || rep.pz[4,1]!=9;
            if( berr )
            {
                return result;
            }
            clustering.clusterizersetahcalgo(s, 1);
            clustering.clusterizerrunahc(s, rep);
            if( (rep.npoints!=6 || alglib.ap.rows(rep.z)!=5) || alglib.ap.cols(rep.z)!=2 )
            {
                return result;
            }
            berr = false;
            berr = (berr || rep.z[0,0]!=0) || rep.z[0,1]!=1;
            berr = (berr || rep.z[1,0]!=2) || rep.z[1,1]!=6;
            berr = (berr || rep.z[2,0]!=3) || rep.z[2,1]!=7;
            berr = (berr || rep.z[3,0]!=5) || rep.z[3,1]!=8;
            berr = (berr || rep.z[4,0]!=4) || rep.z[4,1]!=9;
            if( berr )
            {
                return result;
            }
            
            //
            // Test which differentiates complete linkage and average linkage from
            // single linkage:
            // * we have cluster C0={(-0.5), (0)},
            //   cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)},
            //   and point P between them - (10.0)
            // * we try three different strategies - single linkage, complete
            //   linkage, average linkage.
            // * any strategy will merge C0 first, then merge points of C1,
            //   and then merge P with C0 or C1 (depending on linkage type)
            // * we test that:
            //   a) C0 is merged first
            //   b) after 5 merges (including merge of C0), P is merged with C0 or C1
            //   c) P is merged with C1 when we have single linkage, with C0 otherwise
            //
            xy = new double[8, 1];
            xy[0,0] = -0.5;
            xy[1,0] = 0.0;
            xy[2,0] = 10.0;
            xy[3,0] = 19.0;
            xy[4,0] = 20.0;
            xy[5,0] = 21.0;
            xy[6,0] = 22.0;
            xy[7,0] = 23.0;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, 8, 1, 0);
            for(ahcalgo=0; ahcalgo<=2; ahcalgo++)
            {
                clustering.clusterizersetahcalgo(s, ahcalgo);
                clustering.clusterizerrunahc(s, rep);
                if( (rep.npoints!=8 || alglib.ap.rows(rep.z)!=7) || alglib.ap.cols(rep.z)!=2 )
                {
                    return result;
                }
                if( rep.z[0,0]!=0 || rep.z[0,1]!=1 )
                {
                    return result;
                }
                if( rep.z[5,0]!=2 && rep.z[5,1]!=2 )
                {
                    return result;
                }
                if( rep.z[5,0]!=2 && rep.z[5,1]!=2 )
                {
                    return result;
                }
                if( (ahcalgo==0 || ahcalgo==2) && (rep.z[5,0]!=8 && rep.z[5,1]!=8) )
                {
                    return result;
                }
                if( ahcalgo==1 && (rep.z[5,0]==8 || rep.z[5,1]==8) )
                {
                    return result;
                }
            }
            
            //
            // Test which differentiates single linkage and average linkage from
            // complete linkage:
            // * we have cluster C0={(-2.5), (-2.0)},
            //   cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)},
            //   and point P between them - (10.0)
            // * we try three different strategies - single linkage, complete
            //   linkage, average linkage.
            // * any strategy will merge C0 first, then merge points of C1,
            //   and then merge P with C0 or C1 (depending on linkage type)
            // * we test that:
            //   a) C0 is merged first
            //   b) after 5 merges (including merge of C0), P is merged with C0 or C1
            //   c) P is merged with C0 when we have complete linkage, with C1 otherwise
            //
            xy = new double[8, 1];
            xy[0,0] = -2.5;
            xy[1,0] = -2.0;
            xy[2,0] = 10.0;
            xy[3,0] = 19.0;
            xy[4,0] = 20.0;
            xy[5,0] = 21.0;
            xy[6,0] = 22.0;
            xy[7,0] = 23.0;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, 8, 1, 0);
            for(ahcalgo=0; ahcalgo<=2; ahcalgo++)
            {
                clustering.clusterizersetahcalgo(s, ahcalgo);
                clustering.clusterizerrunahc(s, rep);
                if( (rep.npoints!=8 || alglib.ap.rows(rep.z)!=7) || alglib.ap.cols(rep.z)!=2 )
                {
                    return result;
                }
                if( rep.z[0,0]!=0 || rep.z[0,1]!=1 )
                {
                    return result;
                }
                if( rep.z[5,0]!=2 && rep.z[5,1]!=2 )
                {
                    return result;
                }
                if( rep.z[5,0]!=2 && rep.z[5,1]!=2 )
                {
                    return result;
                }
                if( ahcalgo==0 && (rep.z[5,0]!=8 && rep.z[5,1]!=8) )
                {
                    return result;
                }
                if( (ahcalgo==1 || ahcalgo==2) && (rep.z[5,0]==8 || rep.z[5,1]==8) )
                {
                    return result;
                }
            }
            
            //
            // Test which differentiates weighred average linkage from unweighted average linkage:
            // * we have cluster C0={(0.0), (1.5), (2.5)},
            //   cluster C1={(7.5), (7.99)},
            //   and point P between them - (4.5)
            // * we try two different strategies - weighted average linkage and unweighted average linkage
            // * any strategy will merge C1 first, then merge points of C0,
            //   and then merge P with C0 or C1 (depending on linkage type)
            // * we test that:
            //   a) C1 is merged first, C0 is merged after that
            //   b) after first 3 merges P is merged with C0 or C1
            //   c) P is merged with C1 when we have weighted average linkage, with C0 otherwise
            //
            xy = new double[6, 1];
            xy[0,0] = 0.0;
            xy[1,0] = 1.5;
            xy[2,0] = 2.5;
            xy[3,0] = 4.5;
            xy[4,0] = 7.5;
            xy[5,0] = 7.99;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, 6, 1, 0);
            for(ahcalgo=2; ahcalgo<=3; ahcalgo++)
            {
                clustering.clusterizersetahcalgo(s, ahcalgo);
                clustering.clusterizerrunahc(s, rep);
                if( (rep.npoints!=6 || alglib.ap.rows(rep.z)!=5) || alglib.ap.cols(rep.z)!=2 )
                {
                    return result;
                }
                if( rep.z[0,0]!=4 || rep.z[0,1]!=5 )
                {
                    return result;
                }
                if( rep.z[1,0]!=1 || rep.z[1,1]!=2 )
                {
                    return result;
                }
                if( rep.z[2,0]!=0 || rep.z[2,1]!=7 )
                {
                    return result;
                }
                if( rep.z[3,0]!=3 )
                {
                    return result;
                }
                if( ahcalgo==2 && rep.z[3,1]!=8 )
                {
                    return result;
                }
                if( ahcalgo==3 && rep.z[3,1]!=6 )
                {
                    return result;
                }
            }
            
            //
            // Test which checks correctness of Ward's method on very basic problem
            //
            xy = new double[4, 1];
            xy[0,0] = 0.0;
            xy[1,0] = 1.0;
            xy[2,0] = 3.1;
            xy[3,0] = 4.0;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, alglib.ap.rows(xy), alglib.ap.cols(xy), 2);
            clustering.clusterizergetdistances(xy, alglib.ap.rows(xy), alglib.ap.cols(xy), 2, ref d);
            clustering.clusterizersetahcalgo(s, 4);
            clustering.clusterizerrunahc(s, rep);
            if( errorsinmerges(d, xy, alglib.ap.rows(xy), alglib.ap.cols(xy), rep, 4) )
            {
                return result;
            }
            
            //
            // One more Ward's test
            //
            xy = new double[8, 2];
            xy[0,0] = 0.4700566262;
            xy[0,1] = 0.4565938448;
            xy[1,0] = 0.2394499506;
            xy[1,1] = 0.1750209592;
            xy[2,0] = 0.6518417019;
            xy[2,1] = 0.6151370746;
            xy[3,0] = 0.9863942841;
            xy[3,1] = 0.7855012189;
            xy[4,0] = 0.1517812919;
            xy[4,1] = 0.2600174758;
            xy[5,0] = 0.7840203638;
            xy[5,1] = 0.9023597604;
            xy[6,0] = 0.2604194835;
            xy[6,1] = 0.9792704661;
            xy[7,0] = 0.6353096042;
            xy[7,1] = 0.8252606906;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, alglib.ap.rows(xy), alglib.ap.cols(xy), 2);
            clustering.clusterizergetdistances(xy, alglib.ap.rows(xy), alglib.ap.cols(xy), 2, ref d);
            clustering.clusterizersetahcalgo(s, 4);
            clustering.clusterizerrunahc(s, rep);
            if( rep.z[0,0]!=1 || rep.z[0,1]!=4 )
            {
                return result;
            }
            if( rep.z[1,0]!=5 || rep.z[1,1]!=7 )
            {
                return result;
            }
            if( rep.z[2,0]!=0 || rep.z[2,1]!=2 )
            {
                return result;
            }
            if( rep.z[3,0]!=3 || rep.z[3,1]!=9 )
            {
                return result;
            }
            if( rep.z[4,0]!=10 || rep.z[4,1]!=11 )
            {
                return result;
            }
            if( rep.z[5,0]!=6 || rep.z[5,1]!=12 )
            {
                return result;
            }
            if( rep.z[6,0]!=8 || rep.z[6,1]!=13 )
            {
                return result;
            }
            if( errorsinmerges(d, xy, alglib.ap.rows(xy), alglib.ap.cols(xy), rep, 4) )
            {
                return result;
            }
            
            //
            // Ability to solve problems with zero distance matrix
            //
            npoints = 20;
            d = new double[npoints, npoints];
            for(i=0; i<=npoints-1; i++)
            {
                for(j=0; j<=npoints-1; j++)
                {
                    d[i,j] = 0.0;
                }
            }
            for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
            {
                clustering.clusterizercreate(s);
                clustering.clusterizersetdistances(s, d, npoints, true);
                clustering.clusterizersetahcalgo(s, ahcalgo);
                clustering.clusterizerrunahc(s, rep);
                if( (rep.npoints!=npoints || alglib.ap.rows(rep.z)!=npoints-1) || alglib.ap.cols(rep.z)!=2 )
                {
                    return result;
                }
            }
            
            //
            // Test GetKClusters()
            //
            xy = new double[8, 1];
            xy[0,0] = -2.5;
            xy[1,0] = -2.0;
            xy[2,0] = 10.0;
            xy[3,0] = 19.0;
            xy[4,0] = 20.0;
            xy[5,0] = 21.0;
            xy[6,0] = 22.0;
            xy[7,0] = 23.0;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, 8, 1, 0);
            clustering.clusterizersetahcalgo(s, 0);
            clustering.clusterizerrunahc(s, rep);
            clustering.clusterizergetkclusters(rep, 3, ref cidx, ref cz);
            if( ((((((cidx[0]!=1 || cidx[1]!=1) || cidx[2]!=0) || cidx[3]!=2) || cidx[4]!=2) || cidx[5]!=2) || cidx[6]!=2) || cidx[7]!=2 )
            {
                return result;
            }
            if( (cz[0]!=2 || cz[1]!=8) || cz[2]!=12 )
            {
                return result;
            }
            
            //
            // Test is done
            //
            result = false;
            return result;
        }


        /*************************************************************************
        Advanced  agglomerative  hierarchical  clustering  tests : returns True on
        failure, False on success.

        Advanced testing subroutine perform several automatically generated tests.
        *************************************************************************/
        private static bool advancedahctests()
        {
            bool result = new bool();
            clustering.clusterizerstate s = new clustering.clusterizerstate();
            clustering.ahcreport rep = new clustering.ahcreport();
            double[,] xy = new double[0,0];
            double[,] dm = new double[0,0];
            double[,] dm2 = new double[0,0];
            int[] idx = new int[0];
            int[] disttypes = new int[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            int d = 0;
            int n = 0;
            int npoints = 0;
            int ahcalgo = 0;
            int disttype = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            int t = 0;
            int euclidean = 0;
            int i_ = 0;

            result = false;
            euclidean = 2;
            
            //
            // Test on D-dimensional problem:
            // * D = 2...5
            // * D clusters, each has N points;
            //   centers are located at x=(0 ... 1 ... 0);
            //   cluster radii are approximately 0.1
            // * single/complete/unweighted_average/weighted_average linkage/Ward's method are tested
            // * Euclidean distance is used, either:
            //   a) one given by distance matrix (ClusterizerSetDistances)
            //   b) one calculated from dataset (ClusterizerSetPoints)
            // * we have N*D points, and N*D-1 merges in total
            // * points are randomly rearranged after generation
            //
            // For all kinds of linkage we perform following test:
            // * for each point we remember index of its cluster
            //   (one which is determined during dataset generation)
            // * we clusterize points with ALGLIB capabilities
            // * we scan Rep.Z and perform first D*(N-1) merges
            // * for each merge we check that it merges points
            //   from same cluster;
            //
            // Additonally, we call ErrorsInMerges(). See function comments 
            // for more information about specific tests performed. This function
            // allows us to check that clusters are built exactly as specified by
            // definition of the clustering algorithm.
            //
            for(d=2; d<=5; d++)
            {
                for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
                {
                    n = (int)Math.Round(Math.Pow(3, math.randominteger(3)));
                    npoints = d*n;
                    
                    //
                    // 1. generate dataset.
                    // 2. fill Idx (array of cluster indexes):
                    //    * first N*D elements store cluster indexes
                    //    * next D*(N-1) elements are filled during merges
                    // 3. build distance matrix DM
                    //
                    xy = new double[n*d, d];
                    idx = new int[n*d+d*(n-1)];
                    for(i=0; i<=n*d-1; i++)
                    {
                        for(j=0; j<=d-1; j++)
                        {
                            xy[i,j] = 0.2*math.randomreal()-0.1;
                        }
                        xy[i,i%d] = xy[i,i%d]+1.0;
                        idx[i] = i%d;
                    }
                    for(i=0; i<=n*d-1; i++)
                    {
                        k = math.randominteger(n*d);
                        if( k!=i )
                        {
                            for(j=0; j<=d-1; j++)
                            {
                                v = xy[i,j];
                                xy[i,j] = xy[k,j];
                                xy[k,j] = v;
                            }
                            t = idx[k];
                            idx[k] = idx[i];
                            idx[i] = t;
                        }
                    }
                    dm = new double[npoints, npoints];
                    x0 = new double[d];
                    x1 = new double[d];
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=npoints-1; j++)
                        {
                            for(i_=0; i_<=d-1;i_++)
                            {
                                x0[i_] = xy[i,i_];
                            }
                            for(i_=0; i_<=d-1;i_++)
                            {
                                x1[i_] = xy[j,i_];
                            }
                            dm[i,j] = distfunc(x0, x1, d, euclidean);
                        }
                    }
                    
                    //
                    // Clusterize with SetPoints()
                    //
                    clustering.clusterizercreate(s);
                    clustering.clusterizersetpoints(s, xy, n*d, d, euclidean);
                    clustering.clusterizersetahcalgo(s, ahcalgo);
                    clustering.clusterizerrunahc(s, rep);
                    
                    //
                    // Tests:
                    // * replay first D*(N-1) merges; these merges should take place
                    //   within clusters, intercluster merges will be performed at the
                    //   last stages of the processing.
                    // * test with ErrorsInMerges()
                    //
                    if( rep.npoints!=npoints )
                    {
                        result = true;
                        return result;
                    }
                    for(i=0; i<=d*(n-1)-1; i++)
                    {
                        
                        //
                        // Check correctness of I-th row of Z
                        //
                        if( (rep.z[i,0]<0 || rep.z[i,0]>=rep.z[i,1]) || rep.z[i,1]>=d*n+i )
                        {
                            result = true;
                            return result;
                        }
                        
                        //
                        // Check that merge is performed within cluster
                        //
                        if( idx[rep.z[i,0]]!=idx[rep.z[i,1]] )
                        {
                            result = true;
                            return result;
                        }
                        
                        //
                        // Write new entry of Idx.
                        // Both points from the same cluster, so result of the merge
                        // belongs to the same cluster
                        //
                        idx[n*d+i] = idx[rep.z[i,1]];
                    }
                    if( ((ahcalgo==0 || ahcalgo==1) || ahcalgo==2) || ahcalgo==4 )
                    {
                        if( errorsinmerges(dm, xy, d*n, d, rep, ahcalgo) )
                        {
                            result = true;
                            return result;
                        }
                    }
                    
                    //
                    // Clusterize one more time, now with distance matrix
                    //
                    clustering.clusterizercreate(s);
                    clustering.clusterizersetdistances(s, dm, n*d, (double)(math.randomreal())>(double)(0.5));
                    clustering.clusterizersetahcalgo(s, ahcalgo);
                    clustering.clusterizerrunahc(s, rep);
                    
                    //
                    // Tests:
                    // * replay first D*(N-1) merges; these merges should take place
                    //   within clusters, intercluster merges will be performed at the
                    //   last stages of the processing.
                    // * test with ErrorsInMerges()
                    //
                    if( rep.npoints!=npoints )
                    {
                        result = true;
                        return result;
                    }
                    for(i=0; i<=d*(n-1)-1; i++)
                    {
                        
                        //
                        // Check correctness of I-th row of Z
                        //
                        if( (rep.z[i,0]<0 || rep.z[i,0]>=rep.z[i,1]) || rep.z[i,1]>=d*n+i )
                        {
                            result = true;
                            return result;
                        }
                        
                        //
                        // Check that merge is performed within cluster
                        //
                        if( idx[rep.z[i,0]]!=idx[rep.z[i,1]] )
                        {
                            result = true;
                            return result;
                        }
                        
                        //
                        // Write new entry of Idx.
                        // Both points from the same cluster, so result of the merge
                        // belongs to the same cluster
                        //
                        idx[n*d+i] = idx[rep.z[i,1]];
                    }
                    if( ((ahcalgo==0 || ahcalgo==1) || ahcalgo==2) || ahcalgo==4 )
                    {
                        if( errorsinmerges(dm, xy, d*n, d, rep, ahcalgo) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            
            //
            // Test on random D-dimensional problem:
            // * D = 2...5
            // * N=1..16 random points from unit hypercube
            // * single/complete/unweighted_average linkage/Ward's method are tested
            // * different distance functions are tested
            // * we call ErrorsInMerges() and we check distance matrix
            //   calculated by unit test against one returned by GetDistances()
            //
            disttypes = new int[9];
            disttypes[0] = 0;
            disttypes[1] = 1;
            disttypes[2] = 2;
            disttypes[3] = 10;
            disttypes[4] = 11;
            disttypes[5] = 12;
            disttypes[6] = 13;
            disttypes[7] = 20;
            disttypes[8] = 21;
            for(disttype=0; disttype<=alglib.ap.len(disttypes)-1; disttype++)
            {
                for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
                {
                    if( ahcalgo==3 )
                    {
                        continue;
                    }
                    if( ahcalgo==4 && disttype!=2 )
                    {
                        continue;
                    }
                    npoints = (int)Math.Round(Math.Pow(2, math.randominteger(5)));
                    d = 2+math.randominteger(4);
                    
                    //
                    // Generate dataset and distance matrix
                    //
                    xy = new double[npoints, d];
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=d-1; j++)
                        {
                            xy[i,j] = math.randomreal();
                        }
                    }
                    dm = new double[npoints, npoints];
                    x0 = new double[d];
                    x1 = new double[d];
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=npoints-1; j++)
                        {
                            for(i_=0; i_<=d-1;i_++)
                            {
                                x0[i_] = xy[i,i_];
                            }
                            for(i_=0; i_<=d-1;i_++)
                            {
                                x1[i_] = xy[j,i_];
                            }
                            dm[i,j] = distfunc(x0, x1, d, disttypes[disttype]);
                        }
                    }
                    
                    //
                    // Clusterize
                    //
                    clustering.clusterizercreate(s);
                    clustering.clusterizersetpoints(s, xy, npoints, d, disttypes[disttype]);
                    clustering.clusterizersetahcalgo(s, ahcalgo);
                    clustering.clusterizerrunahc(s, rep);
                    
                    //
                    // Test with ErrorsInMerges()
                    //
                    if( errorsinmerges(dm, xy, npoints, d, rep, ahcalgo) )
                    {
                        result = true;
                        return result;
                    }
                    
                    //
                    // Test distance matrix
                    //
                    clustering.clusterizergetdistances(xy, npoints, d, disttypes[disttype], ref dm2);
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=npoints-1; j++)
                        {
                            if( !math.isfinite(dm2[i,j]) || (double)(Math.Abs(dm[i,j]-dm2[i,j]))>(double)(1.0E5*math.machineepsilon) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Simple test 1: ellipsoid in NF-dimensional space.
        compare k-means centers with random centers
        *************************************************************************/
        private static void kmeanssimpletest1(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 pass = 0;
            int restarts = 0;
            double ekmeans = 0;
            double erandom = 0;
            double dclosest = 0;
            int cclosest = 0;
            clustering.clusterizerstate s = new clustering.clusterizerstate();
            clustering.kmeansreport rep = new clustering.kmeansreport();
            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
                //
                clustering.clusterizercreate(s);
                clustering.clusterizersetpoints(s, xy, npoints, nvars, 2);
                clustering.clusterizersetkmeanslimits(s, restarts, 0);
                clustering.clusterizerrunkmeans(s, nc, rep);
                if( rep.terminationtype<=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_] - rep.c[j,i_];
                        }
                        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!=rep.cidx[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_] - rep.c[rep.cidx[i],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 test perform several checks for special properties

        On failure sets error flag, on success leaves it unchanged.
        *************************************************************************/
        private static void kmeansspecialtests(ref bool othererrors)
        {
            int npoints = 0;
            int nfeatures = 0;
            int nclusters = 0;
            int initalgo = 0;
            double[,] xy = new double[0,0];
            double[,] c = new double[0,0];
            int idx0 = 0;
            int idx1 = 0;
            int idx2 = 0;
            int i = 0;
            int j = 0;
            int pass = 0;
            int passcount = 0;
            int separation = 0;
            int[] xyc = new int[0];
            int[] xycref = new int[0];
            double[] xydist2 = new double[0];
            double[] xydist2ref = new double[0];
            double[] energies = new double[0];
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            clustering.clusterizerstate s = new clustering.clusterizerstate();
            clustering.kmeansreport rep = new clustering.kmeansreport();
            alglib.smp.shared_pool bufferpool = new alglib.smp.shared_pool();
            apserv.apbuffers bufferseed = new apserv.apbuffers();
            int[] pointslist = new int[0];
            int[] featureslist = new int[0];
            int[] clusterslist = new int[0];

            hqrnd.hqrndrandomize(rs);
            
            //
            // Compare different initialization algorithms:
            // * dataset is K balls, chosen at random gaussian points, with
            //   radius equal to 2^(-Separation).
            // * we generate random sample, run k-means initialization algorithm
            //   and calculate mean energy for each initialization algorithm.
            //   In order to suppress Lloyd's iteration we use KmeansDbgNoIts
            //   debug flag.
            // * then, we compare mean energies; kmeans++ must be best one,
            //   random initialization must be worst one.
            //
            energies = new double[4];
            passcount = 1000;
            npoints = 100;
            nfeatures = 3;
            nclusters = 6;
            xy = new double[npoints, nfeatures];
            c = new double[nclusters, nfeatures];
            clustering.clusterizercreate(s);
            s.kmeansdbgnoits = true;
            for(separation=2; separation<=5; separation++)
            {
                
                //
                // Try different init algorithms
                //
                for(initalgo=1; initalgo<=3; initalgo++)
                {
                    energies[initalgo] = 0.0;
                    clustering.clusterizersetkmeansinit(s, initalgo);
                    for(pass=1; pass<=passcount; pass++)
                    {
                        
                        //
                        // Generate centers of balls
                        //
                        for(i=0; i<=nclusters-1; i++)
                        {
                            for(j=0; j<=nfeatures-1; j++)
                            {
                                c[i,j] = hqrnd.hqrndnormal(rs);
                            }
                        }
                        
                        //
                        // Generate points
                        //
                        for(i=0; i<=npoints-1; i++)
                        {
                            for(j=0; j<=nfeatures-1; j++)
                            {
                                xy[i,j] = hqrnd.hqrndnormal(rs)*Math.Pow(2, -separation)+c[i%nclusters,j];
                            }
                        }
                        
                        //
                        // Run clusterization
                        //
                        clustering.clusterizersetpoints(s, xy, npoints, nfeatures, 2);
                        clustering.clusterizerrunkmeans(s, nclusters, rep);
                        apserv.seterrorflag(ref othererrors, rep.terminationtype<=0);
                        energies[initalgo] = energies[initalgo]+rep.energy/passcount;
                    }
                }
                
                //
                // Compare
                //
                apserv.seterrorflag(ref othererrors, !((double)(energies[2])<(double)(energies[1])));
                apserv.seterrorflag(ref othererrors, !((double)(energies[3])<(double)(energies[1])));
            }
            
            //
            // Test distance calculation algorithm
            //
            pointslist = new int[6];
            pointslist[0] = 1;
            pointslist[1] = 10;
            pointslist[2] = 32;
            pointslist[3] = 100;
            pointslist[4] = 512;
            pointslist[5] = 8000;
            featureslist = new int[5];
            featureslist[0] = 1;
            featureslist[1] = 5;
            featureslist[2] = 32;
            featureslist[3] = 50;
            featureslist[4] = 96;
            clusterslist = new int[5];
            clusterslist[0] = 1;
            clusterslist[1] = 5;
            clusterslist[2] = 32;
            clusterslist[3] = 50;
            clusterslist[4] = 96;
            alglib.smp.ae_shared_pool_set_seed(bufferpool, bufferseed);
            for(idx0=0; idx0<=alglib.ap.len(pointslist)-1; idx0++)
            {
                for(idx1=0; idx1<=alglib.ap.len(featureslist)-1; idx1++)
                {
                    for(idx2=0; idx2<=alglib.ap.len(clusterslist)-1; idx2++)
                    {
                        npoints = pointslist[idx0];
                        nfeatures = featureslist[idx1];
                        nclusters = clusterslist[idx2];
                        xy = new double[npoints, nfeatures];
                        for(i=0; i<=npoints-1; i++)
                        {
                            for(j=0; j<=nfeatures-1; j++)
                            {
                                xy[i,j] = hqrnd.hqrndnormal(rs);
                            }
                        }
                        c = new double[nclusters, nfeatures];
                        for(i=0; i<=nclusters-1; i++)
                        {
                            for(j=0; j<=nfeatures-1; j++)
                            {
                                c[i,j] = hqrnd.hqrndnormal(rs);
                            }
                        }
                        xyc = new int[npoints];
                        xycref = new int[npoints];
                        xydist2 = new double[npoints];
                        xydist2ref = new double[npoints];
                        
                        //
                        // Test
                        //
                        clustering.kmeansupdatedistances(xy, 0, npoints, nfeatures, c, 0, nclusters, xyc, xydist2, bufferpool);
                        kmeansreferenceupdatedistances(xy, npoints, nfeatures, c, nclusters, xycref, xydist2ref);
                        for(i=0; i<=npoints-1; i++)
                        {
                            apserv.seterrorflag(ref othererrors, xyc[i]!=xycref[i]);
                            apserv.seterrorflag(ref othererrors, (double)(Math.Abs(xydist2[i]-xydist2ref[i]))>(double)(1.0E-6));
                        }
                    }
                }
            }
            
            //
            // Test degenerate dataset (less than NClusters distinct points)
            //
            for(nclusters=2; nclusters<=10; nclusters++)
            {
                for(initalgo=0; initalgo<=3; initalgo++)
                {
                    for(pass=1; pass<=10; pass++)
                    {
                        
                        //
                        // Initialize points. Two algorithms are used:
                        // * initialization by small integers (no rounding problems)
                        // * initialization by "long" fraction
                        //
                        npoints = 100;
                        nfeatures = 10;
                        xy = new double[npoints, nfeatures];
                        if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                        {
                            for(i=0; i<=nclusters-2; i++)
                            {
                                for(j=0; j<=nfeatures-1; j++)
                                {
                                    xy[i,j] = Math.Sin(hqrnd.hqrndnormal(rs));
                                }
                            }
                        }
                        else
                        {
                            for(i=0; i<=nclusters-2; i++)
                            {
                                for(j=0; j<=nfeatures-1; j++)
                                {
                                    xy[i,j] = hqrnd.hqrnduniformi(rs, 50);
                                }
                            }
                        }
                        for(i=nclusters-1; i<=npoints-1; i++)
                        {
                            idx0 = hqrnd.hqrnduniformi(rs, nclusters-1);
                            for(j=0; j<=nfeatures-1; j++)
                            {
                                xy[i,j] = xy[idx0,j];
                            }
                        }
                        
                        //
                        // Clusterize with unlimited number of iterations.
                        // Correct error code must be returned.
                        //
                        clustering.clusterizercreate(s);
                        clustering.clusterizersetpoints(s, xy, npoints, nfeatures, 2);
                        clustering.clusterizersetkmeanslimits(s, 1, 0);
                        clustering.clusterizersetkmeansinit(s, initalgo);
                        clustering.clusterizerrunkmeans(s, nclusters, rep);
                        apserv.seterrorflag(ref othererrors, rep.terminationtype!=-3);
                    }
                }
            }
        }


        /*************************************************************************
        This test checks algorithm ability to handle degenerate problems without
        causing infinite loop.
        *************************************************************************/
        private static void kmeansinfinitelooptest(ref bool othererrors)
        {
            int npoints = 0;
            int nfeatures = 0;
            int nclusters = 0;
            int restarts = 0;
            double[,] xy = new double[0,0];
            int i = 0;
            int j = 0;
            clustering.clusterizerstate s = new clustering.clusterizerstate();
            clustering.kmeansreport rep = new clustering.kmeansreport();

            
            //
            // Problem 1: all points are same.
            //
            // For NClusters=1 we must get correct solution, for NClusters>1 we must get failure.
            //
            npoints = 100;
            nfeatures = 1;
            restarts = 5;
            xy = new double[npoints, nfeatures];
            for(j=0; j<=nfeatures-1; j++)
            {
                xy[0,j] = math.randomreal();
            }
            for(i=1; i<=npoints-1; i++)
            {
                for(j=0; j<=nfeatures-1; j++)
                {
                    xy[i,j] = xy[0,j];
                }
            }
            nclusters = 1;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, npoints, nfeatures, 2);
            clustering.clusterizersetkmeanslimits(s, restarts, 0);
            clustering.clusterizerrunkmeans(s, nclusters, rep);
            othererrors = othererrors || rep.terminationtype<=0;
            for(i=0; i<=nfeatures-1; i++)
            {
                othererrors = othererrors || (double)(Math.Abs(rep.c[0,i]-xy[0,i]))>(double)(1000*math.machineepsilon);
            }
            for(i=0; i<=npoints-1; i++)
            {
                othererrors = othererrors || rep.cidx[i]!=0;
            }
            nclusters = 5;
            clustering.clusterizerrunkmeans(s, nclusters, rep);
            othererrors = othererrors || rep.terminationtype>0;
            
            //
            // Problem 2: degenerate dataset (report by Andreas).
            //
            npoints = 57;
            nfeatures = 1;
            restarts = 1;
            nclusters = 4;
            xy = new double[npoints, nfeatures];
            xy[0,0] = 12.244689632138986;
            xy[1,0] = 12.244689632138982;
            xy[2,0] = 12.244689632138986;
            xy[3,0] = 12.244689632138982;
            xy[4,0] = 12.244689632138986;
            xy[5,0] = 12.244689632138986;
            xy[6,0] = 12.244689632138986;
            xy[7,0] = 12.244689632138986;
            xy[8,0] = 12.244689632138986;
            xy[9,0] = 12.244689632138986;
            xy[10,0] = 12.244689632138989;
            xy[11,0] = 12.244689632138984;
            xy[12,0] = 12.244689632138986;
            xy[13,0] = 12.244689632138986;
            xy[14,0] = 12.244689632138989;
            xy[15,0] = 12.244689632138986;
            xy[16,0] = 12.244689632138986;
            xy[17,0] = 12.244689632138986;
            xy[18,0] = 12.244689632138986;
            xy[19,0] = 12.244689632138989;
            xy[20,0] = 12.244689632138972;
            xy[21,0] = 12.244689632138986;
            xy[22,0] = 12.244689632138986;
            xy[23,0] = 12.244689632138986;
            xy[24,0] = 12.244689632138984;
            xy[25,0] = 12.244689632138982;
            xy[26,0] = 12.244689632138986;
            xy[27,0] = 12.244689632138986;
            xy[28,0] = 12.244689632138986;
            xy[29,0] = 12.244689632138986;
            xy[30,0] = 12.244689632138986;
            xy[31,0] = 12.244689632138986;
            xy[32,0] = 12.244689632138986;
            xy[33,0] = 12.244689632138986;
            xy[34,0] = 12.244689632138986;
            xy[35,0] = 12.244689632138982;
            xy[36,0] = 12.244689632138989;
            xy[37,0] = 12.244689632138986;
            xy[38,0] = 12.244689632138986;
            xy[39,0] = 12.244689632138986;
            xy[40,0] = 12.244689632138986;
            xy[41,0] = 12.244689632138986;
            xy[42,0] = 12.244689632138986;
            xy[43,0] = 12.244689632138986;
            xy[44,0] = 12.244689632138986;
            xy[45,0] = 12.244689632138986;
            xy[46,0] = 12.244689632138986;
            xy[47,0] = 12.244689632138986;
            xy[48,0] = 12.244689632138986;
            xy[49,0] = 12.244689632138986;
            xy[50,0] = 12.244689632138984;
            xy[51,0] = 12.244689632138986;
            xy[52,0] = 12.244689632138986;
            xy[53,0] = 12.244689632138986;
            xy[54,0] = 12.244689632138986;
            xy[55,0] = 12.244689632138986;
            xy[56,0] = 12.244689632138986;
            clustering.clusterizercreate(s);
            clustering.clusterizersetpoints(s, xy, npoints, nfeatures, 2);
            clustering.clusterizersetkmeanslimits(s, restarts, 0);
            clustering.clusterizerrunkmeans(s, nclusters, rep);
            othererrors = othererrors || rep.terminationtype<=0;
        }


        /*************************************************************************
        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 kmeansrestartstest(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[] tmp = new double[0];
            int i = 0;
            int j = 0;
            int pass = 0;
            double ea = 0;
            double eb = 0;
            double v = 0;
            clustering.clusterizerstate state = new clustering.clusterizerstate();
            clustering.kmeansreport rep1 = new clustering.kmeansreport();
            clustering.kmeansreport rep2 = new clustering.kmeansreport();
            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;
                    }
                }
                clustering.clusterizercreate(state);
                clustering.clusterizersetpoints(state, xy, npoints, nvars, 2);
                
                //
                // Test: Restarts=1
                //
                clustering.clusterizersetkmeanslimits(state, 1, 0);
                clustering.clusterizerrunkmeans(state, nclusters, rep1);
                if( rep1.terminationtype<=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_] - rep1.c[rep1.cidx[i],i_];
                    }
                    v = 0.0;
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        v += tmp[i_]*tmp[i_];
                    }
                    ea = ea+v;
                }
                
                //
                // Test: Restarts>1
                //
                clustering.clusterizersetkmeanslimits(state, restarts, 0);
                clustering.clusterizerrunkmeans(state, nclusters, rep2);
                if( rep2.terminationtype<=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_] - rep2.c[rep2.cidx[i],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_];
            }
        }


        /*************************************************************************
        Distance function: distance between X0 and X1

        X0, X1 - array[D], points
        DistType - distance type
        *************************************************************************/
        private static double distfunc(double[] x0,
            double[] x1,
            int d,
            int disttype)
        {
            double result = 0;
            int i = 0;
            double s0 = 0;
            double s1 = 0;

            alglib.ap.assert((((((((disttype==0 || disttype==1) || disttype==2) || disttype==10) || disttype==11) || disttype==12) || disttype==13) || disttype==20) || disttype==21);
            if( disttype==0 )
            {
                result = 0.0;
                for(i=0; i<=d-1; i++)
                {
                    result = Math.Max(result, Math.Abs(x0[i]-x1[i]));
                }
                return result;
            }
            if( disttype==1 )
            {
                result = 0.0;
                for(i=0; i<=d-1; i++)
                {
                    result = result+Math.Abs(x0[i]-x1[i]);
                }
                return result;
            }
            if( disttype==2 )
            {
                result = 0.0;
                for(i=0; i<=d-1; i++)
                {
                    result = result+math.sqr(x0[i]-x1[i]);
                }
                result = Math.Sqrt(result);
                return result;
            }
            if( disttype==10 )
            {
                result = Math.Max(1-basestat.pearsoncorr2(x0, x1, d), 0.0);
                return result;
            }
            if( disttype==11 )
            {
                result = Math.Max(1-Math.Abs(basestat.pearsoncorr2(x0, x1, d)), 0.0);
                return result;
            }
            if( disttype==12 || disttype==13 )
            {
                s0 = 0.0;
                s1 = 0.0;
                for(i=0; i<=d-1; i++)
                {
                    s0 = s0+math.sqr(x0[i])/d;
                    s1 = s1+math.sqr(x1[i])/d;
                }
                s0 = Math.Sqrt(s0);
                s1 = Math.Sqrt(s1);
                result = 0;
                for(i=0; i<=d-1; i++)
                {
                    result = result+x0[i]/s0*(x1[i]/s1)/d;
                }
                if( disttype==12 )
                {
                    result = Math.Max(1-result, 0.0);
                }
                else
                {
                    result = Math.Max(1-Math.Abs(result), 0.0);
                }
                return result;
            }
            if( disttype==20 )
            {
                result = Math.Max(1-basestat.spearmancorr2(x0, x1, d), 0.0);
                return result;
            }
            if( disttype==21 )
            {
                result = Math.Max(1-Math.Abs(basestat.spearmancorr2(x0, x1, d)), 0.0);
                return result;
            }
            result = 0;
            return result;
        }


        /*************************************************************************
        This function replays merges and checks that:
        * Rep.NPoints, Rep.Z, Rep.PZ and Rep.PM are consistent and correct
        * Rep.MergeDist is consistent with distance between clusters being merged
        * clusters with minimal distance are merged at each step
        * GetKClusters() correctly unpacks clusters for each K

        NOTE: this algorithm correctly handle ties, i.e. situations where several
              pairs  of  clusters  have  same intercluster distance, and we can't
              unambiguously choose clusters to merge.

        INPUT PARAMETERS
            D           -   distance matrix, array[NPoints,NPoints], full matrix
                            is given (including both triangles and zeros on the
                            main diagonal)
            XY          -   dataset matrix, array[NPoints,NF]
            NPoints     -   dataset size
            NF          -   number of features
            Rep         -   clusterizer report
            AHCAlgo     -   AHC algorithm:
                            * 0 - complete linkage
                            * 1 - single linkage
                            * 2 - unweighted average linkage

        This function returns True on failure, False on success.
        *************************************************************************/
        private static bool errorsinmerges(double[,] d,
            double[,] xy,
            int npoints,
            int nf,
            clustering.ahcreport rep,
            int ahcalgo)
        {
            bool result = new bool();
            double[,] dm = new double[0,0];
            int[,] cm = new int[0,0];
            int[] clustersizes = new int[0];
            int[] clusterheights = new int[0];
            bool[] b = new bool[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            bool bflag = new bool();
            int i = 0;
            int j = 0;
            int k = 0;
            int i0 = 0;
            int i1 = 0;
            int c0 = 0;
            int c1 = 0;
            int s0 = 0;
            int s1 = 0;
            double v = 0;
            int t = 0;
            int mergeidx = 0;
            int[] kidx = new int[0];
            int[] kidxz = new int[0];
            int currentelement = 0;

            alglib.ap.assert(ahcalgo!=3, "integrity error");
            result = false;
            x0 = new double[nf];
            x1 = new double[nf];
            
            //
            // Basic checks:
            // * positive completion code
            // * sizes of arrays
            // * Rep.P is correct permutation
            // * Rep.Z contains correct cluster indexes
            // * Rep.PZ is consistent with Rep.P/Rep.Z
            // * Rep.PM contains consistent indexes
            // * GetKClusters() for K=NPoints
            //
            bflag = false;
            bflag = bflag || rep.terminationtype<=0;
            if( bflag )
            {
                result = true;
                return result;
            }
            bflag = bflag || rep.npoints!=npoints;
            bflag = (bflag || alglib.ap.rows(rep.z)!=npoints-1) || (npoints>1 && alglib.ap.cols(rep.z)!=2);
            bflag = (bflag || alglib.ap.rows(rep.pz)!=npoints-1) || (npoints>1 && alglib.ap.cols(rep.pz)!=2);
            bflag = (bflag || alglib.ap.rows(rep.pm)!=npoints-1) || (npoints>1 && alglib.ap.cols(rep.pm)!=6);
            bflag = bflag || alglib.ap.len(rep.mergedist)!=npoints-1;
            bflag = bflag || alglib.ap.len(rep.p)!=npoints;
            if( bflag )
            {
                result = true;
                return result;
            }
            b = new bool[npoints];
            for(i=0; i<=npoints-1; i++)
            {
                b[i] = false;
            }
            for(i=0; i<=npoints-1; i++)
            {
                if( (rep.p[i]<0 || rep.p[i]>=npoints) || b[rep.p[i]] )
                {
                    result = true;
                    return result;
                }
                b[rep.p[i]] = true;
            }
            for(i=0; i<=npoints-2; i++)
            {
                if( (rep.z[i,0]<0 || rep.z[i,0]>=rep.z[i,1]) || rep.z[i,1]>=npoints+i )
                {
                    result = true;
                    return result;
                }
                if( (rep.pz[i,0]<0 || rep.pz[i,0]>=rep.pz[i,1]) || rep.pz[i,1]>=npoints+i )
                {
                    result = true;
                    return result;
                }
            }
            for(i=0; i<=npoints-2; i++)
            {
                c0 = rep.z[i,0];
                c1 = rep.z[i,1];
                s0 = rep.pz[i,0];
                s1 = rep.pz[i,1];
                if( c0<npoints )
                {
                    c0 = rep.p[c0];
                }
                if( c1<npoints )
                {
                    c1 = rep.p[c1];
                }
                if( c0!=s0 || c1!=s1 )
                {
                    result = true;
                    return result;
                }
            }
            clustering.clusterizergetkclusters(rep, npoints, ref kidx, ref kidxz);
            if( alglib.ap.len(kidx)!=npoints || alglib.ap.len(kidxz)!=npoints )
            {
                result = true;
                return result;
            }
            for(i=0; i<=npoints-1; i++)
            {
                if( kidxz[i]!=i || kidx[i]!=i )
                {
                    result = true;
                    return result;
                }
            }
            
            //
            // Test description:
            // * we generate (2*NPoints-1)x(2*NPoints-1) matrix of distances DM and
            //   (2*NPoints-1)xNPoints matrix of clusters CM (I-th row contains indexes
            //   of elements which belong to I-th cluster, negative indexes denote
            //   empty cells). Leading N*N square of DM is just a distance matrix,
            //   other elements are filled by some large number M (used to mark empty
            //   elements).
            // * we replay all merges
            // * every time we merge clusters I and J into K, we:
            //   * check that distance between I and J is equal to the smallest
            //     element of DM (note: we account for rounding errors when we
            //     decide on that)
            //   * check that distance is consistent with Rep.MergeDist
            //   * then, we enumerate all elements in clusters being merged,
            //     and check that after permutation their indexes fall into range
            //     prescribed by Rep.PM
            //   * fill K-th column/row of D by distances to cluster K
            //   * merge I-th and J-th rows of CM and store result into K-th row
            //   * clear DM and CM: fill I-th and J-th column/row of DM by large
            //     number M, fill I-th and J-th row of CM by -1.
            //
            // NOTE: DM is initialized by distance metric specific to AHC algorithm
            //       being used. CLINK, SLINK and average linkage use user-provided
            //       distance measure, say Euclidean one, without any modifications.
            //       Ward's method uses (and reports) squared and scaled Euclidean
            //       distances.
            //
            dm = new double[2*npoints-1, 2*npoints-1];
            cm = new int[2*npoints-1, npoints];
            clustersizes = new int[2*npoints-1];
            for(i=0; i<=2*npoints-2; i++)
            {
                for(j=0; j<=2*npoints-2; j++)
                {
                    if( i<npoints && j<npoints )
                    {
                        dm[i,j] = d[i,j];
                        if( ahcalgo==4 )
                        {
                            dm[i,j] = 0.5*math.sqr(dm[i,j]);
                        }
                    }
                    else
                    {
                        dm[i,j] = math.maxrealnumber;
                    }
                }
            }
            for(i=0; i<=2*npoints-2; i++)
            {
                for(j=0; j<=npoints-1; j++)
                {
                    cm[i,j] = -1;
                }
            }
            for(i=0; i<=npoints-1; i++)
            {
                cm[i,0] = i;
                clustersizes[i] = 1;
            }
            for(i=npoints; i<=2*npoints-2; i++)
            {
                clustersizes[i] = 0;
            }
            clusterheights = new int[2*npoints-1];
            for(i=0; i<=npoints-1; i++)
            {
                clusterheights[i] = 0;
            }
            for(mergeidx=0; mergeidx<=npoints-2; mergeidx++)
            {
                
                //
                // Check that clusters with minimum distance are merged,
                // and that MergeDist is consistent with results.
                //
                // NOTE: we do not check for specific cluster indexes,
                //       because it is possible to have a tie. We just
                //       check that distance between clusters is a true
                //       minimum over all possible clusters.
                //
                v = math.maxrealnumber;
                for(i=0; i<=2*npoints-2; i++)
                {
                    for(j=0; j<=2*npoints-2; j++)
                    {
                        if( i!=j )
                        {
                            v = Math.Min(v, dm[i,j]);
                        }
                    }
                }
                c0 = rep.z[mergeidx,0];
                c1 = rep.z[mergeidx,1];
                if( (double)(dm[c0,c1])>(double)(v+10000*math.machineepsilon) )
                {
                    result = true;
                    return result;
                }
                if( (double)(rep.mergedist[mergeidx])>(double)(v+10000*math.machineepsilon) )
                {
                    result = true;
                    return result;
                }
                
                //
                // Check that indexes of elements fall into range prescribed by Rep.PM,
                // and Rep.PM correctly described merge operation
                //
                s0 = clustersizes[c0];
                s1 = clustersizes[c1];
                for(j=0; j<=clustersizes[c0]-1; j++)
                {
                    if( rep.p[cm[c0,j]]<rep.pm[mergeidx,0] || rep.p[cm[c0,j]]>rep.pm[mergeidx,1] )
                    {
                        
                        //
                        // Element falls outside of range described by PM
                        //
                        result = true;
                        return result;
                    }
                }
                for(j=0; j<=clustersizes[c1]-1; j++)
                {
                    if( rep.p[cm[c1,j]]<rep.pm[mergeidx,2] || rep.p[cm[c1,j]]>rep.pm[mergeidx,3] )
                    {
                        
                        //
                        // Element falls outside of range described by PM
                        //
                        result = true;
                        return result;
                    }
                }
                if( (rep.pm[mergeidx,1]-rep.pm[mergeidx,0]!=s0-1 || rep.pm[mergeidx,3]-rep.pm[mergeidx,2]!=s1-1) || rep.pm[mergeidx,2]!=rep.pm[mergeidx,1]+1 )
                {
                    
                    //
                    // Cluster size (as given by PM) is inconsistent with its actual size.
                    //
                    result = true;
                    return result;
                }
                if( rep.pm[mergeidx,4]!=clusterheights[rep.z[mergeidx,0]] || rep.pm[mergeidx,5]!=clusterheights[rep.z[mergeidx,1]] )
                {
                    
                    //
                    // Heights of subdendrograms as returned by PM are inconsistent with heights
                    // calculated by us.
                    //
                    result = true;
                    return result;
                }
                
                //
                // Update cluster heights
                //
                clusterheights[mergeidx+npoints] = Math.Max(clusterheights[rep.z[mergeidx,0]], clusterheights[rep.z[mergeidx,1]])+1;
                
                //
                // Update CM
                //
                t = 0;
                for(j=0; j<=clustersizes[rep.z[mergeidx,0]]-1; j++)
                {
                    cm[npoints+mergeidx,t] = cm[rep.z[mergeidx,0],j];
                    t = t+1;
                }
                for(j=0; j<=clustersizes[rep.z[mergeidx,1]]-1; j++)
                {
                    cm[npoints+mergeidx,t] = cm[rep.z[mergeidx,1],j];
                    t = t+1;
                }
                clustersizes[npoints+mergeidx] = t;
                clustersizes[rep.z[mergeidx,0]] = 0;
                clustersizes[rep.z[mergeidx,1]] = 0;
                
                //
                // Update distance matrix D
                //
                for(i=0; i<=2*npoints-2; i++)
                {
                    
                    //
                    // "Remove" columns/rows corresponding to clusters being merged
                    //
                    dm[i,rep.z[mergeidx,0]] = math.maxrealnumber;
                    dm[i,rep.z[mergeidx,1]] = math.maxrealnumber;
                    dm[rep.z[mergeidx,0],i] = math.maxrealnumber;
                    dm[rep.z[mergeidx,1],i] = math.maxrealnumber;
                }
                for(i=0; i<=npoints+mergeidx-1; i++)
                {
                    if( clustersizes[i]>0 )
                    {
                        
                        //
                        // Calculate column/row corresponding to new cluster
                        //
                        if( ahcalgo==0 )
                        {
                            
                            //
                            // Calculate distance between clusters I and NPoints+MergeIdx for CLINK
                            //
                            v = 0.0;
                            for(i0=0; i0<=clustersizes[i]-1; i0++)
                            {
                                for(i1=0; i1<=clustersizes[npoints+mergeidx]-1; i1++)
                                {
                                    v = Math.Max(v, d[cm[i,i0],cm[npoints+mergeidx,i1]]);
                                }
                            }
                        }
                        if( ahcalgo==1 )
                        {
                            
                            //
                            // Calculate distance between clusters I and NPoints+MergeIdx for SLINK
                            //
                            v = math.maxrealnumber;
                            for(i0=0; i0<=clustersizes[i]-1; i0++)
                            {
                                for(i1=0; i1<=clustersizes[npoints+mergeidx]-1; i1++)
                                {
                                    v = Math.Min(v, d[cm[i,i0],cm[npoints+mergeidx,i1]]);
                                }
                            }
                        }
                        if( ahcalgo==2 )
                        {
                            
                            //
                            // Calculate distance between clusters I and NPoints+MergeIdx for unweighted average
                            //
                            v = 0.0;
                            t = 0;
                            for(i0=0; i0<=clustersizes[i]-1; i0++)
                            {
                                for(i1=0; i1<=clustersizes[npoints+mergeidx]-1; i1++)
                                {
                                    v = v+d[cm[i,i0],cm[npoints+mergeidx,i1]];
                                    t = t+1;
                                }
                            }
                            v = v/t;
                        }
                        if( ahcalgo==3 )
                        {
                            alglib.ap.assert(false);
                        }
                        if( ahcalgo==4 )
                        {
                            
                            //
                            // Calculate distance between clusters I and NPoints+MergeIdx for Ward's method:
                            // * X0 = center of mass for cluster I
                            // * X1 = center of mass for cluster NPoints+MergeIdx
                            // * S0 = size of cluster I
                            // * S1 = size of cluster NPoints+MergeIdx
                            // * distance between clusters is S0*S1/(S0+S1)*|X0-X1|^2
                            //
                            //
                            for(j=0; j<=nf-1; j++)
                            {
                                x0[j] = 0.0;
                                x1[j] = 0.0;
                            }
                            for(i0=0; i0<=clustersizes[i]-1; i0++)
                            {
                                for(j=0; j<=nf-1; j++)
                                {
                                    x0[j] = x0[j]+xy[cm[i,i0],j]/clustersizes[i];
                                }
                            }
                            for(i1=0; i1<=clustersizes[npoints+mergeidx]-1; i1++)
                            {
                                for(j=0; j<=nf-1; j++)
                                {
                                    x1[j] = x1[j]+xy[cm[npoints+mergeidx,i1],j]/clustersizes[npoints+mergeidx];
                                }
                            }
                            v = 0.0;
                            for(j=0; j<=nf-1; j++)
                            {
                                v = v+math.sqr(x0[j]-x1[j]);
                            }
                            v = v*clustersizes[i]*clustersizes[npoints+mergeidx]/(clustersizes[i]+clustersizes[npoints+mergeidx]);
                        }
                        dm[i,npoints+mergeidx] = v;
                        dm[npoints+mergeidx,i] = v;
                    }
                }
                
                //
                // Check that GetKClusters() correctly unpacks clusters for K=NPoints-(MergeIdx+1):
                // * check lengths of arays
                // * check consistency of CIdx/CZ parameters
                // * scan clusters (CZ parameter), for each cluster scan CM matrix which stores
                //   cluster elements (according to our replay of merges), for each element of
                //   the current cluster check that CIdx array correctly reflects its status.
                //
                k = npoints-(mergeidx+1);
                clustering.clusterizergetkclusters(rep, k, ref kidx, ref kidxz);
                if( alglib.ap.len(kidx)!=npoints || alglib.ap.len(kidxz)!=k )
                {
                    result = true;
                    return result;
                }
                for(i=0; i<=k-2; i++)
                {
                    if( (kidxz[i]<0 || kidxz[i]>=kidxz[i+1]) || kidxz[i+1]>2*npoints-2 )
                    {
                        
                        //
                        // CZ is inconsistent
                        //
                        result = true;
                        return result;
                    }
                }
                for(i=0; i<=npoints-1; i++)
                {
                    if( kidx[i]<0 || kidx[i]>=k )
                    {
                        
                        //
                        // CIdx is inconsistent
                        //
                        result = true;
                        return result;
                    }
                }
                for(i=0; i<=k-1; i++)
                {
                    for(j=0; j<=clustersizes[kidxz[i]]-1; j++)
                    {
                        currentelement = cm[kidxz[i],j];
                        if( kidx[currentelement]!=i )
                        {
                            
                            //
                            // We've found element which belongs to I-th cluster (according to CM
                            // matrix, which reflects current status of agglomerative clustering),
                            // but this element does not belongs to I-th cluster according to
                            // results of ClusterizerGetKClusters()
                            //
                            result = true;
                            return result;
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        This procedure is a reference version of KMeansUpdateDistances().

        INPUT PARAMETERS:
            XY          -   dataset, array [0..NPoints-1,0..NVars-1].
            NPoints     -   dataset size, NPoints>=K
            NVars       -   number of variables, NVars>=1
            CT          -   matrix of centers, centers are stored in rows
            K           -   number of centers, K>=1
            XYC         -   preallocated output buffer
            XYDist2     -   preallocated output buffer

        OUTPUT PARAMETERS:
            XYC         -   new assignment of points to centers
            XYDist2     -   squared distances

          -- ALGLIB --
             Copyright 21.01.2015 by Bochkanov Sergey
        *************************************************************************/
        private static void kmeansreferenceupdatedistances(double[,] xy,
            int npoints,
            int nvars,
            double[,] ct,
            int k,
            int[] xyc,
            double[] xydist2)
        {
            int i = 0;
            int j = 0;
            int cclosest = 0;
            double dclosest = 0;
            double v = 0;
            double[] tmp = new double[0];
            int i_ = 0;

            tmp = new double[nvars];
            for(i=0; i<=npoints-1; i++)
            {
                cclosest = -1;
                dclosest = math.maxrealnumber;
                for(j=0; j<=k-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_] - ct[j,i_];
                    }
                    v = 0.0;
                    for(i_=0; i_<=nvars-1;i_++)
                    {
                        v += tmp[i_]*tmp[i_];
                    }
                    if( (double)(v)<(double)(dclosest) )
                    {
                        cclosest = j;
                        dclosest = v;
                    }
                }
                alglib.ap.assert(cclosest>=0, "KMeansUpdateDistances: internal error");
                xyc[i] = cclosest;
                xydist2[i] = dclosest;
            }
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testdforest(bool silent)
        {
            return testdforest(silent);
        }


        /*************************************************************************
        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;
            }
        }


        /*************************************************************************
        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 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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testgammafunc(bool silent)
        {
            return testgammafunc(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testhblas(bool silent)
        {
            return testhblas(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testreflections(bool silent)
        {
            return testreflections(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testcreflections(bool silent)
        {
            return testcreflections(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testsblas(bool silent)
        {
            return testsblas(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testortfac(bool silent)
        {
            return testortfac(silent);
        }


        /*************************************************************************
        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;
            }
            alglib.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 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();
            double materr = 0;
            double orterr = 0;
            double threshold = 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;
            d = new double[maxn-1+1];
            e = new double[maxn-2+1];
            
            //
            // 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 = ((wfailed || (double)(materr)>(double)(threshold)) || (double)(orterr)>(double)(threshold)) || !wsorted;
            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("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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testbdsvd(bool silent)
        {
            return testbdsvd(silent);
        }


        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 checksvdmultiplication(double[] d,
            double[] e,
            int n,
            bool isupper,
            double[,] u,
            double[,] c,
            double[] w,
            double[,] vt,
            ref double err)
        {
            int i = 0;
            int j = 0;
            double[] wt = new double[0];
            double[,] u2 = new double[0,0];
            double[,] c2 = new double[0,0];
            double[,] vt2 = new double[0,0];
            double[,] u1 = new double[0,0];
            double[,] c1 = new double[0,0];
            double[,] vt1 = new double[0,0];
            int nru = 0;
            int ncc = 0;
            int ncvt = 0;
            int pass = 0;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            double v = 0;
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            wt = new double[n];
            
            //
            // Perform nonsquare SVD
            //
            for(pass=1; pass<=20; pass++)
            {
                
                //
                // Problem size
                //
                nru = hqrnd.hqrnduniformi(rs, 2*n);
                ncc = hqrnd.hqrnduniformi(rs, 2*n);
                ncvt = hqrnd.hqrnduniformi(rs, 2*n);
                
                //
                // Reference matrices (copy 1) and working matrices (copy 2)
                //
                for(i=0; i<=n-1; i++)
                {
                    wt[i] = d[i];
                }
                if( nru>0 )
                {
                    
                    //
                    // init U1/U2
                    //
                    u1 = new double[nru, n];
                    u2 = new double[nru, n];
                    for(i=0; i<=alglib.ap.rows(u1)-1; i++)
                    {
                        for(j=0; j<=alglib.ap.cols(u1)-1; j++)
                        {
                            u1[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            u2[i,j] = u1[i,j];
                        }
                    }
                }
                else
                {
                    
                    //
                    // Set U1/U2 to 1x1 matrices; working with 1x1 matrices allows
                    // to test correctness of code which passes them to MKL.
                    //
                    u1 = new double[1, 1];
                    u2 = new double[1, 1];
                }
                if( ncc>0 )
                {
                    c1 = new double[n, ncc];
                    c2 = new double[n, ncc];
                    for(i=0; i<=alglib.ap.rows(c1)-1; i++)
                    {
                        for(j=0; j<=alglib.ap.cols(c1)-1; j++)
                        {
                            c1[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            c2[i,j] = c1[i,j];
                        }
                    }
                }
                else
                {
                    
                    //
                    // Set C1/C1 to 1x1 matrices; working with 1x1 matrices allows
                    // to test correctness of code which passes them to MKL.
                    //
                    c1 = new double[1, 1];
                    c2 = new double[1, 1];
                }
                if( ncvt>0 )
                {
                    vt1 = new double[n, ncvt];
                    vt2 = new double[n, ncvt];
                    for(i=0; i<=alglib.ap.rows(vt1)-1; i++)
                    {
                        for(j=0; j<=alglib.ap.cols(vt1)-1; j++)
                        {
                            vt1[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            vt2[i,j] = vt1[i,j];
                        }
                    }
                }
                else
                {
                    
                    //
                    // Set VT1/VT1 to 1x1 matrices; working with 1x1 matrices allows
                    // to test correctness of code which passes them to MKL.
                    //
                    vt1 = new double[1, 1];
                    vt2 = new double[1, 1];
                }
                
                //
                // SVD with non-square U/C/VT
                //
                if( !bdsvd.rmatrixbdsvd(ref wt, e, n, isupper, (double)(hqrnd.hqrnduniformr(rs))>(double)(0), ref u2, nru, ref c2, ncc, ref vt2, ncvt) )
                {
                    err = 1.0;
                    return;
                }
                for(i=0; i<=nru-1; i++)
                {
                    for(j=0; j<=alglib.ap.cols(u2)-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += u1[i,i_]*u[i_,j];
                        }
                        err = Math.Max(err, Math.Abs(v-u2[i,j]));
                    }
                }
                for(i=0; i<=alglib.ap.rows(c2)-1; i++)
                {
                    for(j=0; j<=ncc-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += c[i,i_]*c1[i_,j];
                        }
                        err = Math.Max(err, Math.Abs(v-c2[i,j]));
                    }
                }
                for(i=0; i<=alglib.ap.rows(vt2)-1; i++)
                {
                    for(j=0; j<=ncvt-1; j++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += vt[i,i_]*vt1[i_,j];
                        }
                        err = Math.Max(err, Math.Abs(v-vt2[i,j]));
                    }
                }
            }
        }


        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);
            checksvdmultiplication(d, e, n, true, u, c, w, vt, ref materr);
            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);
            checksvdmultiplication(d, e, n, true, u, c, w, vt, ref materr);
            
            //
            // 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);
            checksvdmultiplication(d, e, n, false, u, c, w, vt, ref materr);
            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);
            checksvdmultiplication(d, e, n, false, u, c, w, vt, ref materr);
            
            //
            // 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 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;
            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 = (((wfailed || (double)(materr)>(double)(threshold)) || (double)(orterr)>(double)(threshold)) || (double)(othererr)>(double)(threshold)) || !wsorted;
            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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testsvd(bool silent)
        {
            return testsvd(silent);
        }


        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.
            //
            //
            m1 = 0;
            m2 = -1;
            n1 = 0;
            n2 = -1;
            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 )
                            {
                                alglib.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 )
                            {
                                alglib.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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testlinreg(bool silent)
        {
            return testlinreg(silent);
        }


        /*************************************************************************
        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);
            alglib.ap.assert(info>0);
        }


    }
    public class testfiltersunit
    {
        public static bool testfilters(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool smaerrors = new bool();
            bool emaerrors = new bool();
            bool lrmaerrors = new bool();

            smaerrors = testsma(true);
            emaerrors = testema(true);
            lrmaerrors = testlrma(true);
            
            //
            // Final report
            //
            waserrors = (smaerrors || emaerrors) || lrmaerrors;
            if( !silent )
            {
                System.Console.Write("FILTERS TEST");
                System.Console.WriteLine();
                System.Console.Write("* SMA:                                   ");
                if( !smaerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* EMA:                                   ");
                if( !emaerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* LRMA:                                  ");
                if( !lrmaerrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testfilters(bool silent)
        {
            return testfilters(silent);
        }


        /*************************************************************************
        This function tests SMA(k) filter. It returns True on error.

        Additional IsSilent parameter controls detailed error reporting.
        *************************************************************************/
        public static bool testsma(bool issilent)
        {
            bool result = new bool();
            double[] x = new double[0];
            bool precomputederrors = new bool();
            bool zerohandlingerrors = new bool();
            double threshold = 0;

            threshold = 1000*math.machineepsilon;
            if( !issilent )
            {
                System.Console.Write("SMA(K) TEST");
                System.Console.WriteLine();
            }
            
            //
            // Test several pre-computed problems.
            //
            // NOTE: tests below rely on the fact that floating point
            //       additions and subtractions are exact when dealing
            //       with integer values.
            //
            precomputederrors = false;
            x = new double[1];
            x[0] = 7;
            filters.filtersma(ref x, 1, 1);
            precomputederrors = precomputederrors || (double)(x[0])!=(double)(7);
            x = new double[3];
            x[0] = 7;
            x[1] = 8;
            x[2] = 9;
            filters.filtersma(ref x, 3, 1);
            precomputederrors = ((precomputederrors || (double)(x[0])!=(double)(7)) || (double)(x[1])!=(double)(8)) || (double)(x[2])!=(double)(9);
            filters.filtersma(ref x, 3, 2);
            precomputederrors = ((precomputederrors || (double)(x[0])!=(double)(7)) || (double)(x[1])!=(double)(7.5)) || (double)(x[2])!=(double)(8.5);
            x = new double[3];
            x[0] = 7;
            x[1] = 8;
            x[2] = 9;
            filters.filtersma(ref x, 3, 4);
            precomputederrors = ((precomputederrors || (double)(x[0])!=(double)(7)) || (double)(x[1])!=(double)(7.5)) || (double)(x[2])!=(double)(8);
            
            //
            // Test zero-handling:
            // a) when we have non-zero sequence (N1 elements) followed by zero sequence
            //    (N2 elements), then first N1+K-1 elements of the processed sequence are
            //    non-zero, but elements since (N1+K)th must be exactly zero.
            // b) similar property holds for zero sequence followed by non-zero one
            //
            // Naive implementation of SMA does not have such property.
            //
            // NOTE: it is important to initialize X with non-integer elements with long
            // binary mantissas, because this test tries to test behaviour in the presence
            // of roundoff errors, and it will be useless when used with integer inputs.
            //
            zerohandlingerrors = false;
            x = new double[10];
            x[0] = Math.Sqrt(2);
            x[1] = Math.Sqrt(3);
            x[2] = Math.Sqrt(5);
            x[3] = Math.Sqrt(6);
            x[4] = Math.Sqrt(7);
            x[5] = 0;
            x[6] = 0;
            x[7] = 0;
            x[8] = 0;
            x[9] = 0;
            filters.filtersma(ref x, 10, 3);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[0]-Math.Sqrt(2)))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[1]-(Math.Sqrt(2)+Math.Sqrt(3))/2))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[2]-(Math.Sqrt(2)+Math.Sqrt(3)+Math.Sqrt(5))/3))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[3]-(Math.Sqrt(3)+Math.Sqrt(5)+Math.Sqrt(6))/3))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[4]-(Math.Sqrt(5)+Math.Sqrt(6)+Math.Sqrt(7))/3))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[5]-(Math.Sqrt(6)+Math.Sqrt(7))/3))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[6]-Math.Sqrt(7)/3))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(x[7])!=(double)(0);
            zerohandlingerrors = zerohandlingerrors || (double)(x[8])!=(double)(0);
            zerohandlingerrors = zerohandlingerrors || (double)(x[9])!=(double)(0);
            x[0] = 0;
            x[1] = 0;
            x[2] = 0;
            x[3] = 0;
            x[4] = 0;
            x[5] = Math.Sqrt(2);
            x[6] = Math.Sqrt(3);
            x[7] = Math.Sqrt(5);
            x[8] = Math.Sqrt(6);
            x[9] = Math.Sqrt(7);
            filters.filtersma(ref x, 10, 3);
            zerohandlingerrors = zerohandlingerrors || (double)(x[0])!=(double)(0);
            zerohandlingerrors = zerohandlingerrors || (double)(x[1])!=(double)(0);
            zerohandlingerrors = zerohandlingerrors || (double)(x[2])!=(double)(0);
            zerohandlingerrors = zerohandlingerrors || (double)(x[3])!=(double)(0);
            zerohandlingerrors = zerohandlingerrors || (double)(x[4])!=(double)(0);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[5]-Math.Sqrt(2)/3))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[6]-(Math.Sqrt(2)+Math.Sqrt(3))/3))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[7]-(Math.Sqrt(2)+Math.Sqrt(3)+Math.Sqrt(5))/3))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[8]-(Math.Sqrt(3)+Math.Sqrt(5)+Math.Sqrt(6))/3))>(double)(threshold);
            zerohandlingerrors = zerohandlingerrors || (double)(Math.Abs(x[9]-(Math.Sqrt(5)+Math.Sqrt(6)+Math.Sqrt(7))/3))>(double)(threshold);
            
            //
            // Final result
            //
            result = precomputederrors || zerohandlingerrors;
            return result;
        }


        /*************************************************************************
        This function tests EMA(alpha) filter. It returns True on error.

        Additional IsSilent parameter controls detailed error reporting.
        *************************************************************************/
        public static bool testema(bool issilent)
        {
            bool result = new bool();
            double[] x = new double[0];
            bool precomputederrors = new bool();
            double threshold = 0;

            threshold = 1000*math.machineepsilon;
            if( !issilent )
            {
                System.Console.Write("EMA(alpha) TEST");
                System.Console.WriteLine();
            }
            
            //
            // Test several pre-computed problems.
            //
            // NOTE: tests below rely on the fact that floating point
            //       additions and subtractions are exact when dealing
            //       with integer values.
            //
            precomputederrors = false;
            x = new double[1];
            x[0] = 7;
            filters.filterema(ref x, 1, 1.0);
            precomputederrors = precomputederrors || (double)(x[0])!=(double)(7);
            filters.filterema(ref x, 1, 0.5);
            precomputederrors = precomputederrors || (double)(x[0])!=(double)(7);
            x = new double[3];
            x[0] = 7;
            x[1] = 8;
            x[2] = 9;
            filters.filterema(ref x, 3, 1.0);
            precomputederrors = ((precomputederrors || (double)(x[0])!=(double)(7)) || (double)(x[1])!=(double)(8)) || (double)(x[2])!=(double)(9);
            filters.filterema(ref x, 3, 0.5);
            precomputederrors = ((precomputederrors || (double)(x[0])!=(double)(7)) || (double)(x[1])!=(double)(7.5)) || (double)(x[2])!=(double)(8.25);
            
            //
            // Final result
            //
            result = precomputederrors;
            return result;
        }


        /*************************************************************************
        This function tests LRMA(k) filter. It returns True on error.

        Additional IsSilent parameter controls detailed error reporting.
        *************************************************************************/
        public static bool testlrma(bool issilent)
        {
            bool result = new bool();
            double[] x = new double[0];
            bool precomputederrors = new bool();
            double threshold = 0;

            threshold = 1000*math.machineepsilon;
            if( !issilent )
            {
                System.Console.Write("LRMA(K) TEST");
                System.Console.WriteLine();
            }
            precomputederrors = false;
            
            //
            // First, check that filter does not changes points for K=1 or K=2
            //
            x = new double[1];
            x[0] = 7;
            filters.filterlrma(ref x, 1, 1);
            precomputederrors = precomputederrors || (double)(x[0])!=(double)(7);
            x = new double[6];
            x[0] = 7;
            x[1] = 8;
            x[2] = 9;
            x[3] = 10;
            x[4] = 11;
            x[5] = 12;
            filters.filterlrma(ref x, 6, 1);
            precomputederrors = (((((precomputederrors || (double)(x[0])!=(double)(7)) || (double)(x[1])!=(double)(8)) || (double)(x[2])!=(double)(9)) || (double)(x[3])!=(double)(10)) || (double)(x[4])!=(double)(11)) || (double)(x[5])!=(double)(12);
            filters.filterlrma(ref x, 6, 2);
            precomputederrors = (((((precomputederrors || (double)(x[0])!=(double)(7)) || (double)(x[1])!=(double)(8)) || (double)(x[2])!=(double)(9)) || (double)(x[3])!=(double)(10)) || (double)(x[4])!=(double)(11)) || (double)(x[5])!=(double)(12);
            
            //
            // Check several precomputed problems
            //
            x = new double[6];
            x[0] = 7;
            x[1] = 8;
            x[2] = 9;
            x[3] = 10;
            x[4] = 11;
            x[5] = 12;
            filters.filterlrma(ref x, 6, 3);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[0]-7))>(double)(threshold);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[1]-8))>(double)(threshold);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[2]-9))>(double)(threshold);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[3]-10))>(double)(threshold);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[4]-11))>(double)(threshold);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[5]-12))>(double)(threshold);
            x = new double[6];
            x[0] = 7;
            x[1] = 8;
            x[2] = 8;
            x[3] = 9;
            x[4] = 12;
            x[5] = 12;
            filters.filterlrma(ref x, 6, 3);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[0]-7.0000000000))>(double)(1.0E-5);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[1]-8.0000000000))>(double)(1.0E-5);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[2]-8.1666666667))>(double)(1.0E-5);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[3]-8.8333333333))>(double)(1.0E-5);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[4]-11.6666666667))>(double)(1.0E-5);
            precomputederrors = precomputederrors || (double)(Math.Abs(x[5]-12.5000000000))>(double)(1.0E-5);
            
            //
            // Final result
            //
            result = precomputederrors;
            return result;
        }


    }
    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 = 1.0E-8;
            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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testevd(bool silent)
        {
            return testevd(silent);
        }


        /*************************************************************************
        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,0];

            if( alglib.ap.rows(a)*alglib.ap.cols(a)>0 )
            {
                a = new double[1, 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];

            if( alglib.ap.len(a)>0 )
            {
                a = new double[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)
        {
            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;
            double worstseparation = 0;
            double requiredseparation = 0;
            double specialthreshold = 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];
            }
            unset2d(ref z);
            wsucc = evd.smatrixtdevd(ref lambdav, ee, n, 2, ref z);
            if( !wsucc )
            {
                apserv.seterrorflag(ref tderrors, true);
                return;
            }
            apserv.seterrorflag(ref tderrors, (double)(tdtestproduct(d, e, n, z, lambdav))>(double)(threshold));
            apserv.seterrorflag(ref tderrors, (double)(testort(z, n))>(double)(threshold));
            for(i=0; i<=n-2; i++)
            {
                if( (double)(lambdav[i+1])<(double)(lambdav[i]) )
                {
                    apserv.seterrorflag(ref 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];
            }
            unset2d(ref z);
            wsucc = evd.smatrixtdevd(ref lambda2, ee, n, 0, ref z);
            if( !wsucc )
            {
                apserv.seterrorflag(ref tderrors, true);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                apserv.seterrorflag(ref 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];
                }
            }
            wsucc = evd.smatrixtdevd(ref lambda2, ee, n, 1, ref a1);
            if( !wsucc )
            {
                apserv.seterrorflag(ref tderrors, true);
                return;
            }
            for(i=0; i<=n-1; i++)
            {
                apserv.seterrorflag(ref 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
                    //
                    apserv.seterrorflag(ref tderrors, (double)(Math.Abs(v-a1[i,j]))>(double)(threshold) && (double)(Math.Abs(v+a1[i,j]))>(double)(threshold));
                }
            }
            
            //
            // Test first row variant.
            //
            // NOTE: this test is special because ZNeeded=3 is ALGLIB-specific feature
            //       which is NOT supported by Intel MKL. Thus, MKL-capable version of
            //       ALGLIB will use different algorithms for ZNeeded=3 and for ZNeeded<3.
            //
            //       In most cases it is OK, but when problem happened to be degenerate
            //       (two close eigenvalues), Z computed by ALGLIB may be different from
            //       Z computed by MKL (up to arbitrary rotation), which will lead to
            //       failure of the test, because ZNeeded=2 is used as reference value
            //       for ZNeeded=3.
            //
            //       That's why this test is performed only for well-separated matrices,
            //       and with custom threshold.
            //
            requiredseparation = 1.0E-6;
            specialthreshold = 1.0E-6;
            worstseparation = math.maxrealnumber;
            for(i=0; i<=n-2; i++)
            {
                worstseparation = Math.Min(worstseparation, Math.Abs(lambdav[i+1]-lambdav[i]));
            }
            if( (double)(worstseparation)>(double)(requiredseparation) )
            {
                for(i=0; i<=n-1; i++)
                {
                    lambda2[i] = d[i];
                }
                for(i=0; i<=n-2; i++)
                {
                    ee[i] = e[i];
                }
                unset2d(ref z);
                wsucc = evd.smatrixtdevd(ref lambda2, ee, n, 3, ref z);
                if( !wsucc )
                {
                    apserv.seterrorflag(ref tderrors, true);
                    return;
                }
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref 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
                    //
                    apserv.seterrorflag(ref tderrors, (double)(Math.Abs(z[0,i]-zref[0,i]))>(double)(specialthreshold) && (double)(Math.Abs(z[0,i]+zref[0,i]))>(double)(specialthreshold));
                }
            }
        }


        /*************************************************************************
        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)
        {
            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;
            double vnorm = 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
            //
            if( !evd.rmatrixevd(a, n, 0, ref wr0, ref wi0, ref vl, ref vr) )
            {
                apserv.seterrorflag(ref nserrors, true);
                return;
            }
            
            //
            // Test different jobs
            //
            for(vjob=1; vjob<=3; vjob++)
            {
                needr = vjob==1 || vjob==3;
                needl = vjob==2 || vjob==3;
                if( !evd.rmatrixevd(a, n, vjob, ref wr1, ref wi1, ref vl, ref vr) )
                {
                    apserv.seterrorflag(ref nserrors, true);
                    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++)
                {
                    apserv.seterrorflag(ref nserrors, (double)(Math.Abs(wr0s[i]-wr1s[i]))>(double)(threshold));
                    apserv.seterrorflag(ref nserrors, (double)(Math.Abs(wi0s[i]-wi1s[i]))>(double)(threshold));
                }
                
                //
                // Test right vectors
                //
                if( needr )
                {
                    k = 0;
                    while( k<=n-1 )
                    {
                        curwr = 0;
                        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] = 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];
                        }
                        vnorm = 0.0;
                        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;
                            vnorm = vnorm+math.sqr(vec1r[i])+math.sqr(vec1i[i]);
                        }
                        vnorm = Math.Sqrt(vnorm);
                        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_];
                        }
                        apserv.seterrorflag(ref nserrors, (double)(vnorm)<(double)(1.0E-3) || !math.isfinite(vnorm));
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref nserrors, (double)(Math.Abs(vec2r[i]-vec3r[i]))>(double)(threshold));
                            apserv.seterrorflag(ref nserrors, (double)(Math.Abs(vec2i[i]-vec3i[i]))>(double)(threshold));
                        }
                        k = k+1;
                    }
                }
                
                //
                // Test left vectors
                //
                curwr = 0;
                curwi = 0;
                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];
                        }
                        vnorm = 0.0;
                        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;
                            vnorm = vnorm+math.sqr(vec1r[j])+math.sqr(vec1i[j]);
                        }
                        vnorm = Math.Sqrt(vnorm);
                        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_];
                        }
                        apserv.seterrorflag(ref nserrors, (double)(vnorm)<(double)(1.0E-3) || !math.isfinite(vnorm));
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref nserrors, (double)(Math.Abs(vec2r[i]-vec3r[i]))>(double)(threshold));
                            apserv.seterrorflag(ref 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);
                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);
            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);
            rmatrixfillsparsea(ref ra, n, n, 0.995);
            cmatrixfillsparsea(ref ca, n, n, 0.995);
            testnsevdproblem(ra, n, threshold, ref nserrors);
        }


    }
    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();
            bool eulerr = new bool();
            int i_ = 0;

            rerr = false;
            cerr = false;
            serr = false;
            herr = false;
            spderr = false;
            hpderr = false;
            eulerr = 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];
                            }
                        }
                    }
                }
            }
            
            //
            // Test for symmetric matrices
            //
            eulerr = testeult();
            
            //
            // report
            //
            waserrors = (((((rerr || cerr) || serr) || spderr) || herr) || hpderr) || eulerr;
            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();
                }
                System.Console.Write("TEST FOR SYMMETRIC MATRICES:             ");
                if( !eulerr )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testmatgen(bool silent)
        {
            return testmatgen(silent);
        }


        /*************************************************************************
        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.
            //
            alglib.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;
        }


        /*************************************************************************
        The function check, that upper triangle from symmetric matrix is equal to
        lower triangle.
        *************************************************************************/
        private static bool testeult()
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            complex[,] b = new complex[0,0];
            double c = 0;
            double range = 0;
            double eps = 0;
            int n = 0;
            int i = 0;
            int j = 0;

            eps = 2*math.machineepsilon;
            range = 100*(2*math.randomreal()-1);
            for(n=1; n<=15; n++)
            {
                c = 900*math.randomreal()+100;
                
                //
                // Generate symmetric matrix and check it
                //
                matgen.smatrixrndcond(n, c, ref a);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (double)(Math.Abs(a[i,j]-a[j,i]))>(double)(eps) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
                matgen.spdmatrixrndcond(n, c, ref a);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (double)(Math.Abs(a[i,j]-a[j,i]))>(double)(eps) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
                matgen.hmatrixrndcond(n, c, ref b);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (double)(Math.Abs(b[i,j].x-b[j,i].x))>(double)(eps) || (double)(Math.Abs(b[i,j].y+b[j,i].y))>(double)(eps) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
                matgen.hpdmatrixrndcond(n, c, ref b);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (double)(Math.Abs(b[i,j].x-b[j,i].x))>(double)(eps) || (double)(Math.Abs(b[i,j].y+b[j,i].y))>(double)(eps) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
                
                //
                // Prepare symmetric matrix with real values
                //
                for(i=0; i<=n-1; i++)
                {
                    for(j=i; j<=n-1; j++)
                    {
                        a[i,j] = range*(2*math.randomreal()-1);
                    }
                }
                for(i=0; i<=n-2; i++)
                {
                    for(j=i+1; j<=n-1; j++)
                    {
                        a[j,i] = a[i,j];
                    }
                }
                matgen.smatrixrndmultiply(ref a, n);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (double)(Math.Abs(a[i,j]-a[j,i]))>(double)(eps) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
                
                //
                // Prepare symmetric matrix with complex values
                //
                for(i=0; i<=n-1; i++)
                {
                    for(j=i; j<=n-1; j++)
                    {
                        b[i,j].x = range*(2*math.randomreal()-1);
                        if( i!=j )
                        {
                            b[i,j].y = range*(2*math.randomreal()-1);
                        }
                        else
                        {
                            b[i,j].y = 0;
                        }
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=i+1; j<=n-1; j++)
                    {
                        b[i,j].x = b[j,i].x;
                        b[i,j].y = -b[j,i].y;
                    }
                }
                matgen.hmatrixrndmultiply(ref b, n);
                for(i=0; i<=n-1; i++)
                {
                    b[i,i].y = 0;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (double)(Math.Abs(b[i,j].x-b[j,i].x))>(double)(eps) || (double)(Math.Abs(b[i,j].y+b[j,i].y))>(double)(eps) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            result = false;
            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;
            l = n;
            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;
            }
            nm = 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 testsparseunit
    {
        public class sparsegenerator : apobject
        {
            public int n;
            public int m;
            public int matkind;
            public int triangle;
            public double[,] bufa;
            public hqrnd.hqrndstate rs;
            public rcommstate rcs;
            public sparsegenerator()
            {
                init();
            }
            public override void init()
            {
                bufa = new double[0,0];
                rs = new hqrnd.hqrndstate();
                rcs = new rcommstate();
            }
            public override alglib.apobject make_copy()
            {
                sparsegenerator _result = new sparsegenerator();
                _result.n = n;
                _result.m = m;
                _result.matkind = matkind;
                _result.triangle = triangle;
                _result.bufa = (double[,])bufa.Clone();
                _result.rs = (hqrnd.hqrndstate)rs.make_copy();
                _result.rcs = (rcommstate)rcs.make_copy();
                return _result;
            }
        };




        public static bool testsparse(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool basicerrors = new bool();
            bool linearerrors = new bool();
            bool basicrnderrors = new bool();
            bool level2unsymmetricerrors = new bool();
            bool level2symmetricerrors = new bool();
            bool level2triangularerrors = new bool();
            bool level3unsymmetricerrors = new bool();
            bool level3symmetricerrors = new bool();
            bool linearserrors = new bool();
            bool linearmmerrors = new bool();
            bool linearsmmerrors = new bool();
            bool getrowerrors = new bool();
            bool copyerrors = new bool();
            bool basiccopyerrors = new bool();
            bool enumerateerrors = new bool();
            bool rewriteexistingerr = new bool();
            bool skserrors = new bool();

            getrowerrors = false;
            skserrors = skstest();
            basicerrors = basicfunctest() || testgcmatrixtype();
            basicrnderrors = basicfuncrandomtest();
            linearerrors = linearfunctionstest();
            level2unsymmetricerrors = testlevel2unsymmetric();
            level2symmetricerrors = testlevel2symmetric();
            level2triangularerrors = testlevel2triangular();
            level3unsymmetricerrors = testlevel3unsymmetric();
            level3symmetricerrors = testlevel3symmetric();
            linearserrors = linearfunctionsstest();
            linearmmerrors = linearfunctionsmmtest();
            linearsmmerrors = linearfunctionssmmtest();
            copyerrors = copyfunctest(true) || testconvertsm();
            basiccopyerrors = basiccopyfunctest(true);
            enumerateerrors = enumeratetest();
            rewriteexistingerr = rewriteexistingtest();
            testgetrow(ref getrowerrors);
            
            //
            // report
            //
            waserrors = (((((((((((((((skserrors || getrowerrors) || basicerrors) || linearerrors) || basicrnderrors) || level2unsymmetricerrors) || level2symmetricerrors) || level2triangularerrors) || level3unsymmetricerrors) || level3symmetricerrors) || linearserrors) || linearmmerrors) || linearsmmerrors) || copyerrors) || basiccopyerrors) || enumerateerrors) || rewriteexistingerr;
            if( !silent )
            {
                System.Console.Write("TESTING SPARSE");
                System.Console.WriteLine();
                System.Console.Write("STORAGE FORMATS:");
                System.Console.WriteLine();
                System.Console.Write("* SKS:                                   ");
                if( !skserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("OPERATIONS:");
                System.Console.WriteLine();
                System.Console.Write("* GETROW:                                ");
                if( !getrowerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("BLAS:");
                System.Console.WriteLine();
                System.Console.Write("* LEVEL 2 GENERAL:                       ");
                if( !level2unsymmetricerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* LEVEL 2 SYMMETRIC:                     ");
                if( !level2symmetricerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* LEVEL 2 TRIANGULAR:                    ");
                if( !level2triangularerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* LEVEL 3 GENERAL:                       ");
                if( !level3unsymmetricerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("* LEVEL 3 SYMMETRIC:                     ");
                if( !level3symmetricerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("BASIC TEST:                              ");
                if( !basicerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("COPY TEST:                               ");
                if( !copyerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("BASIC_COPY TEST:                         ");
                if( !basiccopyerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("BASIC_RND TEST:                          ");
                if( !basicrnderrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("LINEAR TEST:                             ");
                if( !linearerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("LINEAR TEST FOR SYMMETRIC MATRICES:      ");
                if( !linearserrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("LINEAR MxM TEST:                         ");
                if( !linearmmerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("LINEAR MxM TEST FOR SYMMETRIC MATRICES:  ");
                if( !linearsmmerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("ENUMERATE TEST:                          ");
                if( !enumerateerrors )
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                System.Console.Write("REWRITE EXISTING TEST:                   ");
                if( !rewriteexistingerr )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testsparse(bool silent)
        {
            return testsparse(silent);
        }


        /*************************************************************************
        Function for testing basic SKS functional.
        Returns True on errors, False on success.

          -- ALGLIB PROJECT --
             Copyright 16.01.1014 by Bochkanov Sergey
        *************************************************************************/
        public static bool skstest()
        {
            bool result = new bool();
            sparse.sparsematrix s0 = new sparse.sparsematrix();
            sparse.sparsematrix s1 = new sparse.sparsematrix();
            sparse.sparsematrix s2 = new sparse.sparsematrix();
            sparse.sparsematrix s3 = new sparse.sparsematrix();
            sparse.sparsematrix s4 = new sparse.sparsematrix();
            sparse.sparsematrix s5 = new sparse.sparsematrix();
            sparse.sparsematrix s6 = new sparse.sparsematrix();
            int n = 0;
            int nz = 0;
            double pnz = 0;
            int i = 0;
            int j = 0;
            int t0 = 0;
            int t1 = 0;
            double[,] a = new double[0,0];
            bool[,] wasenumerated = new bool[0,0];
            int[] d = new int[0];
            int[] u = new int[0];
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            double v0 = 0;
            double v1 = 0;
            int uppercnt = 0;
            int lowercnt = 0;

            result = false;
            hqrnd.hqrndrandomize(rs);
            for(n=1; n<=20; n++)
            {
                nz = n*n-n;
                while( true )
                {
                    
                    //
                    // Generate N*N matrix where probability of non-diagonal element
                    // being non-zero is PNZ. We also generate D and U - subdiagonal
                    // and superdiagonal profile sizes.
                    //
                    if( n>1 )
                    {
                        pnz = (double)nz/(double)(n*n-n);
                    }
                    else
                    {
                        pnz = 1.0;
                    }
                    d = new int[n];
                    u = new int[n];
                    for(i=0; i<=n-1; i++)
                    {
                        d[i] = 0;
                        u[i] = 0;
                    }
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( i==j || (double)(hqrnd.hqrnduniformr(rs))<=(double)(pnz) )
                            {
                                a[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                                if( j<i )
                                {
                                    d[i] = Math.Max(d[i], i-j);
                                }
                                else
                                {
                                    u[j] = Math.Max(u[j], j-i);
                                }
                            }
                            else
                            {
                                a[i,j] = 0.0;
                            }
                        }
                    }
                    uppercnt = 0;
                    lowercnt = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        uppercnt = uppercnt+u[i];
                        lowercnt = lowercnt+d[i];
                    }
                    
                    //
                    // Create matrix in SKS storage format, fill with RewriteExisting() calls.
                    // Convert to several different formats, check their contents with SparseGet().
                    //
                    sparse.sparsecreatesks(n, n, d, u, s0);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (double)(a[i,j])!=(double)(0) )
                            {
                                apserv.seterrorflag(ref result, !sparse.sparserewriteexisting(s0, i, j, a[i,j]));
                            }
                        }
                    }
                    sparse.sparsecopy(s0, s1);
                    sparse.sparseconverttocrs(s1);
                    sparse.sparsecopytocrs(s0, s2);
                    sparse.sparsecopytocrsbuf(s0, s3);
                    sparse.sparsecopytohash(s0, s4);
                    sparse.sparsecopytohashbuf(s0, s5);
                    sparse.sparsecopy(s0, s6);
                    sparse.sparseconverttohash(s6);
                    apserv.seterrorflag(ref result, sparse.sparsegetnrows(s0)!=n);
                    apserv.seterrorflag(ref result, sparse.sparsegetncols(s0)!=n);
                    apserv.seterrorflag(ref result, sparse.sparsegetmatrixtype(s0)!=2);
                    apserv.seterrorflag(ref result, !sparse.sparseissks(s0));
                    apserv.seterrorflag(ref result, sparse.sparseiscrs(s0));
                    apserv.seterrorflag(ref result, sparse.sparseishash(s0));
                    apserv.seterrorflag(ref result, sparse.sparseissks(s1));
                    apserv.seterrorflag(ref result, !sparse.sparseiscrs(s1));
                    apserv.seterrorflag(ref result, sparse.sparseishash(s1));
                    for(i=0; i<=n-1; i++)
                    {
                        v1 = a[i,i];
                        v0 = sparse.sparsegetdiagonal(s0, i);
                        apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            v1 = a[i,j];
                            v0 = sparse.sparseget(s0, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                            v0 = sparse.sparseget(s1, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                            v0 = sparse.sparseget(s2, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                            v0 = sparse.sparseget(s3, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                            v0 = sparse.sparseget(s4, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                            v0 = sparse.sparseget(s5, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                            v0 = sparse.sparseget(s6, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                        }
                    }
                    
                    //
                    // Check enumeration capabilities:
                    // * each element returned by SparseEnumerate() is returned only once
                    // * each non-zero element of A was enumerated
                    //
                    wasenumerated = new bool[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            wasenumerated[i,j] = false;
                        }
                    }
                    t0 = 0;
                    t1 = 0;
                    while( sparse.sparseenumerate(s0, ref t0, ref t1, ref i, ref j, ref v0) )
                    {
                        apserv.seterrorflag(ref result, wasenumerated[i,j]);
                        wasenumerated[i,j] = true;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (double)(a[i,j])!=(double)(0) )
                            {
                                apserv.seterrorflag(ref result, !wasenumerated[i,j]);
                            }
                        }
                    }
                    
                    //
                    // Check UpperCnt()/LowerCnt()
                    //
                    apserv.seterrorflag(ref result, sparse.sparsegetuppercount(s0)!=uppercnt);
                    apserv.seterrorflag(ref result, sparse.sparsegetlowercount(s0)!=lowercnt);
                    
                    //
                    // Check in-place transposition
                    //
                    sparse.sparsecopy(s0, s1);
                    sparse.sparsetransposesks(s1);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            v0 = sparse.sparseget(s0, i, j);
                            v1 = sparse.sparseget(s1, j, i);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                        }
                    }
                    
                    //
                    // One more check - matrix is initially created in some other format
                    // (CRS or Hash) and converted to SKS later.
                    //
                    sparse.sparsecreate(n, n, 0, s0);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (double)(a[i,j])!=(double)(0) )
                            {
                                sparse.sparseset(s0, i, j, a[i,j]);
                            }
                        }
                    }
                    sparse.sparsecopy(s0, s1);
                    sparse.sparseconverttosks(s1);
                    sparse.sparsecopytosks(s0, s2);
                    sparse.sparsecopytosksbuf(s0, s3);
                    apserv.seterrorflag(ref result, !sparse.sparseissks(s1));
                    apserv.seterrorflag(ref result, sparse.sparseiscrs(s1));
                    apserv.seterrorflag(ref result, sparse.sparseishash(s1));
                    apserv.seterrorflag(ref result, !sparse.sparseissks(s2));
                    apserv.seterrorflag(ref result, sparse.sparseiscrs(s2));
                    apserv.seterrorflag(ref result, sparse.sparseishash(s2));
                    apserv.seterrorflag(ref result, !sparse.sparseissks(s3));
                    apserv.seterrorflag(ref result, sparse.sparseiscrs(s3));
                    apserv.seterrorflag(ref result, sparse.sparseishash(s3));
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            v1 = a[i,j];
                            v0 = sparse.sparseget(s1, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                            v0 = sparse.sparseget(s2, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                            v0 = sparse.sparseget(s3, i, j);
                            apserv.seterrorflag(ref result, (double)(v0)!=(double)(v1));
                        }
                    }
                    
                    //
                    // Increase problem sparcity and try one more time. 
                    // Stop after testing NZ=0.
                    //
                    if( nz==0 )
                    {
                        break;
                    }
                    nz = nz/2;
                }
            }
            return result;
        }


        /*************************************************************************
        Function for testing basic functional

          -- ALGLIB PROJECT --
             Copyright 14.10.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool basicfunctest()
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            int n = 0;
            int m = 0;
            int i = 0;
            int j = 0;
            int i1 = 0;
            int j1 = 0;
            int uppercnt = 0;
            int lowercnt = 0;
            double[,] a = new double[0,0];

            n = 10;
            m = 10;
            result = false;
            for(i=1; i<=m-1; i++)
            {
                for(j=1; j<=n-1; j++)
                {
                    sparse.sparsecreate(i, j, 1, s);
                    a = new double[i, j];
                    
                    //
                    // Checking for Matrix with hash table type
                    //
                    uppercnt = 0;
                    lowercnt = 0;
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            if( j1>i1 )
                            {
                                apserv.inc(ref uppercnt);
                            }
                            if( j1<i1 )
                            {
                                apserv.inc(ref lowercnt);
                            }
                            a[i1,j1] = i1+j1+(double)((i+j)*(m+n))/(double)2;
                            a[i1,j1] = a[i1,j1]+1;
                            sparse.sparseset(s, i1, j1, i1+j1+(double)((i+j)*(m+n))/(double)2);
                            sparse.sparseadd(s, i1, j1, 1);
                            if( (double)(a[i1,j1])!=(double)(sparse.sparseget(s, i1, j1)) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                    for(i1=0; i1<=Math.Min(i, j)-1; i1++)
                    {
                        if( (double)(a[i1,i1])!=(double)(sparse.sparsegetdiagonal(s, i1)) )
                        {
                            result = true;
                            return result;
                        }
                    }
                    apserv.seterrorflag(ref result, sparse.sparsegetuppercount(s)!=uppercnt);
                    apserv.seterrorflag(ref result, sparse.sparsegetlowercount(s)!=lowercnt);
                    
                    //
                    // Checking for Matrix with CRS type
                    //
                    sparse.sparseconverttocrs(s);
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            if( (double)(a[i1,j1])!=(double)(sparse.sparseget(s, i1, j1)) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                    for(i1=0; i1<=Math.Min(i, j)-1; i1++)
                    {
                        if( (double)(a[i1,i1])!=(double)(sparse.sparsegetdiagonal(s, i1)) )
                        {
                            result = true;
                            return result;
                        }
                    }
                    apserv.seterrorflag(ref result, sparse.sparsegetuppercount(s)!=uppercnt);
                    apserv.seterrorflag(ref result, sparse.sparsegetlowercount(s)!=lowercnt);
                }
            }
            return result;
        }


        /*************************************************************************
        Function for testing Level 2 unsymmetric linear algebra functions.
        Additionally it tests SparseGet() for several matrix formats.
        Returns True on failure.

          -- ALGLIB PROJECT --
             Copyright 20.01.2014 by Bochkanov Sergey
        *************************************************************************/
        public static bool testlevel2unsymmetric()
        {
            bool result = new bool();
            int m = 0;
            int n = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] y0 = new double[0];
            double[] y1 = new double[0];
            int i = 0;
            int j = 0;
            double[,] a = new double[0,0];
            sparse.sparsematrix s0 = new sparse.sparsematrix();
            sparse.sparsematrix sa = new sparse.sparsematrix();
            double eps = 0;
            double v = 0;
            sparsegenerator g = new sparsegenerator();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int i_ = 0;

            eps = 10000*math.machineepsilon;
            result = false;
            hqrnd.hqrndrandomize(rs);
            
            //
            // Test linear algebra functions
            //
            for(m=1; m<=20; m++)
            {
                for(n=1; n<=20; n++)
                {
                    initgenerator(m, n, 0, 0, g);
                    while( generatenext(g, ref a, sa) )
                    {
                        
                        //
                        // Convert SA to desired storage format:
                        // * to CRS if M<>N
                        // * with 50% probability to CRS or SKS, if M=N
                        //
                        if( m!=n || (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5) )
                        {
                            sparse.sparsecopytocrs(sa, s0);
                        }
                        else
                        {
                            sparse.sparsecopytosks(sa, s0);
                        }
                        
                        //
                        // Test SparseGet() for SA and S0 against matrix returned in A
                        //
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                apserv.seterrorflag(ref result, (double)(Math.Abs(sparse.sparseget(sa, i, j)-a[i,j]))>(double)(eps));
                                apserv.seterrorflag(ref result, (double)(Math.Abs(sparse.sparseget(s0, i, j)-a[i,j]))>(double)(eps));
                            }
                        }
                        
                        //
                        // Test SparseMV
                        //
                        x0 = new double[n];
                        x1 = new double[n];
                        for(j=0; j<=n-1; j++)
                        {
                            x0[j] = hqrnd.hqrnduniformr(rs)-0.5;
                            x1[j] = x0[j];
                        }
                        sparse.sparsemv(s0, x0, ref y0);
                        apserv.seterrorflag(ref result, alglib.ap.len(y0)<m);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*x1[i_];
                            }
                            apserv.seterrorflag(ref result, (double)(Math.Abs(v-y0[i]))>(double)(eps));
                        }
                        
                        //
                        // Test SparseMTV
                        //
                        x0 = new double[m];
                        x1 = new double[m];
                        for(j=0; j<=m-1; j++)
                        {
                            x0[j] = hqrnd.hqrnduniformr(rs)-0.5;
                            x1[j] = x0[j];
                        }
                        sparse.sparsemtv(s0, x0, ref y0);
                        apserv.seterrorflag(ref result, alglib.ap.len(y0)<n);
                        if( result )
                        {
                            return result;
                        }
                        for(j=0; j<=n-1; j++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=m-1;i_++)
                            {
                                v += a[i_,j]*x1[i_];
                            }
                            apserv.seterrorflag(ref result, (double)(Math.Abs(v-y0[j]))>(double)(eps));
                        }
                        
                        //
                        // Test SparseMV2
                        //
                        if( m==n )
                        {
                            x0 = new double[n];
                            x1 = new double[n];
                            for(j=0; j<=n-1; j++)
                            {
                                x0[j] = hqrnd.hqrnduniformr(rs)-0.5;
                                x1[j] = x0[j];
                            }
                            sparse.sparsemv2(s0, x0, ref y0, ref y1);
                            apserv.seterrorflag(ref result, alglib.ap.len(y0)<n);
                            apserv.seterrorflag(ref result, alglib.ap.len(y1)<n);
                            if( result )
                            {
                                return result;
                            }
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[j,i_]*x1[i_];
                                }
                                apserv.seterrorflag(ref result, (double)(Math.Abs(v-y0[j]))>(double)(eps));
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i_,j]*x1[i_];
                                }
                                apserv.seterrorflag(ref result, (double)(Math.Abs(v-y1[j]))>(double)(eps));
                            }
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Function for testing Level 3 unsymmetric linear algebra functions.
        Additionally it tests SparseGet() for several matrix formats.
        Returns True on failure.

          -- ALGLIB PROJECT --
             Copyright 20.01.2014 by Bochkanov Sergey
        *************************************************************************/
        public static bool testlevel3unsymmetric()
        {
            bool result = new bool();
            int m = 0;
            int n = 0;
            int k = 0;
            double[,] x0 = new double[0,0];
            double[,] x1 = new double[0,0];
            double[,] y0 = new double[0,0];
            double[,] y1 = new double[0,0];
            int i = 0;
            int j = 0;
            double[,] a = new double[0,0];
            sparse.sparsematrix s0 = new sparse.sparsematrix();
            sparse.sparsematrix sa = new sparse.sparsematrix();
            double eps = 0;
            double v = 0;
            sparsegenerator g = new sparsegenerator();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int i_ = 0;

            eps = 10000*math.machineepsilon;
            result = false;
            hqrnd.hqrndrandomize(rs);
            
            //
            // Test linear algebra functions
            //
            for(m=1; m<=20; m++)
            {
                for(n=1; n<=20; n++)
                {
                    initgenerator(m, n, 0, 0, g);
                    while( generatenext(g, ref a, sa) )
                    {
                        
                        //
                        // Choose matrix width K
                        //
                        k = 1+hqrnd.hqrnduniformi(rs, 20);
                        
                        //
                        // Convert SA to desired storage format:
                        // * to CRS if M<>N
                        // * with 50% probability to CRS or SKS, if M=N
                        //
                        if( m!=n || (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5) )
                        {
                            sparse.sparsecopytocrs(sa, s0);
                        }
                        else
                        {
                            sparse.sparsecopytosks(sa, s0);
                        }
                        
                        //
                        // Test SparseGet() for SA and S0 against matrix returned in A
                        //
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                apserv.seterrorflag(ref result, (double)(sparse.sparseget(sa, i, j))!=(double)(a[i,j]));
                                apserv.seterrorflag(ref result, (double)(sparse.sparseget(s0, i, j))!=(double)(a[i,j]));
                            }
                        }
                        
                        //
                        // Test SparseMV
                        //
                        x0 = new double[n, k];
                        x1 = new double[n, k];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=k-1; j++)
                            {
                                x0[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                                x1[i,j] = x0[i,j];
                            }
                        }
                        sparse.sparsemm(s0, x0, k, ref y0);
                        apserv.seterrorflag(ref result, alglib.ap.rows(y0)<m);
                        apserv.seterrorflag(ref result, alglib.ap.cols(y0)<k);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=k-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*x1[i_,j];
                                }
                                apserv.seterrorflag(ref result, (double)(Math.Abs(v-y0[i,j]))>(double)(eps));
                            }
                        }
                        
                        //
                        // Test SparseMTM
                        //
                        x0 = new double[m, k];
                        x1 = new double[m, k];
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=k-1; j++)
                            {
                                x0[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                                x1[i,j] = x0[i,j];
                            }
                        }
                        sparse.sparsemtm(s0, x0, k, ref y0);
                        apserv.seterrorflag(ref result, alglib.ap.rows(y0)<n);
                        apserv.seterrorflag(ref result, alglib.ap.cols(y0)<k);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=k-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=m-1;i_++)
                                {
                                    v += a[i_,i]*x1[i_,j];
                                }
                                apserv.seterrorflag(ref result, (double)(Math.Abs(v-y0[i,j]))>(double)(eps));
                            }
                        }
                        
                        //
                        // Test SparseMM2
                        //
                        if( m==n )
                        {
                            x0 = new double[n, k];
                            x1 = new double[n, k];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=k-1; j++)
                                {
                                    x0[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                                    x1[i,j] = x0[i,j];
                                }
                            }
                            sparse.sparsemm2(s0, x0, k, ref y0, ref y1);
                            apserv.seterrorflag(ref result, alglib.ap.rows(y0)<n);
                            apserv.seterrorflag(ref result, alglib.ap.cols(y0)<k);
                            apserv.seterrorflag(ref result, alglib.ap.rows(y1)<n);
                            apserv.seterrorflag(ref result, alglib.ap.cols(y1)<k);
                            if( result )
                            {
                                return result;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=k-1; j++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += a[i,i_]*x1[i_,j];
                                    }
                                    apserv.seterrorflag(ref result, (double)(Math.Abs(v-y0[i,j]))>(double)(eps));
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += a[i_,i]*x1[i_,j];
                                    }
                                    apserv.seterrorflag(ref result, (double)(Math.Abs(v-y1[i,j]))>(double)(eps));
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Function for testing Level 2 symmetric linear algebra functions.
        Additionally it tests SparseGet() for several matrix formats.
        Returns True on failure.

          -- ALGLIB PROJECT --
             Copyright 20.01.2014 by Bochkanov Sergey
        *************************************************************************/
        public static bool testlevel2symmetric()
        {
            bool result = new bool();
            int n = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] y0 = new double[0];
            double[] y1 = new double[0];
            int i = 0;
            int j = 0;
            double[,] a = new double[0,0];
            sparse.sparsematrix s0 = new sparse.sparsematrix();
            sparse.sparsematrix s1 = new sparse.sparsematrix();
            sparse.sparsematrix sa = new sparse.sparsematrix();
            double eps = 0;
            double v = 0;
            double va = 0;
            double vb = 0;
            sparsegenerator g = new sparsegenerator();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            bool isupper = new bool();
            int triangletype = 0;
            int i_ = 0;

            eps = 10000*math.machineepsilon;
            result = false;
            hqrnd.hqrndrandomize(rs);
            
            //
            // Test linear algebra functions
            //
            for(n=1; n<=20; n++)
            {
                for(triangletype=-1; triangletype<=1; triangletype++)
                {
                    isupper = (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5);
                    if( triangletype<0 )
                    {
                        isupper = false;
                    }
                    if( triangletype>0 )
                    {
                        isupper = true;
                    }
                    initgenerator(n, n, 0, triangletype, g);
                    while( generatenext(g, ref a, sa) )
                    {
                        
                        //
                        // Convert SA to desired storage format:
                        // * S0 stores unmodified copy
                        // * S1 stores copy with unmodified triangle corresponding
                        //   to IsUpper and another triangle being spoiled by random
                        //   trash
                        //
                        sparse.sparsecopytohash(sa, s1);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j<i && isupper) || (j>i && !isupper) )
                                {
                                    sparse.sparseset(s1, i, j, hqrnd.hqrnduniformr(rs));
                                }
                            }
                        }
                        if( (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5) )
                        {
                            sparse.sparsecopytocrs(sa, s0);
                            sparse.sparseconverttocrs(s1);
                        }
                        else
                        {
                            sparse.sparsecopytosks(sa, s0);
                            sparse.sparseconverttosks(s1);
                        }
                        
                        //
                        // Test SparseGet() for SA and S0 against matrix returned in A
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                apserv.seterrorflag(ref result, (double)(Math.Abs(sparse.sparseget(sa, i, j)-a[i,j]))>(double)(eps));
                                apserv.seterrorflag(ref result, (double)(Math.Abs(sparse.sparseget(s0, i, j)-a[i,j]))>(double)(eps));
                                apserv.seterrorflag(ref result, (j<i && triangletype==1) && (double)(sparse.sparseget(s0, i, j))!=(double)(0));
                                apserv.seterrorflag(ref result, (j>i && triangletype==-1) && (double)(sparse.sparseget(s0, i, j))!=(double)(0));
                            }
                        }
                        
                        //
                        // Before we proceed with testing, update empty triangle of A
                        // with its copy from another part of the matrix.
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j<i && isupper) || (j>i && !isupper) )
                                {
                                    a[i,j] = a[j,i];
                                }
                            }
                        }
                        
                        //
                        // Test SparseSMV
                        //
                        x0 = new double[n];
                        x1 = new double[n];
                        for(j=0; j<=n-1; j++)
                        {
                            x0[j] = hqrnd.hqrnduniformr(rs)-0.5;
                            x1[j] = x0[j];
                        }
                        sparse.sparsesmv(s0, isupper, x0, ref y0);
                        apserv.seterrorflag(ref result, alglib.ap.len(y0)<n);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*x1[i_];
                            }
                            apserv.seterrorflag(ref result, (double)(Math.Abs(v-y0[i]))>(double)(eps));
                        }
                        sparse.sparsesmv(s1, isupper, x0, ref y1);
                        apserv.seterrorflag(ref result, alglib.ap.len(y1)<n);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*x1[i_];
                            }
                            apserv.seterrorflag(ref result, (double)(Math.Abs(v-y1[i]))>(double)(eps));
                        }
                        
                        //
                        // Test SparseVSMV
                        //
                        x0 = new double[n];
                        x1 = new double[n];
                        for(j=0; j<=n-1; j++)
                        {
                            x0[j] = hqrnd.hqrnduniformr(rs)-0.5;
                            x1[j] = x0[j];
                        }
                        vb = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                vb = vb+x1[i]*a[i,j]*x1[j];
                            }
                        }
                        va = sparse.sparsevsmv(s0, isupper, x0);
                        apserv.seterrorflag(ref result, (double)(Math.Abs(va-vb))>(double)(eps));
                        va = sparse.sparsevsmv(s1, isupper, x0);
                        apserv.seterrorflag(ref result, (double)(Math.Abs(va-vb))>(double)(eps));
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Function for testing Level 2 symmetric linear algebra functions.
        Additionally it tests SparseGet() for several matrix formats.
        Returns True on failure.

          -- ALGLIB PROJECT --
             Copyright 14.10.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool testlevel3symmetric()
        {
            bool result = new bool();
            int n = 0;
            int k = 0;
            double[,] x0 = new double[0,0];
            double[,] x1 = new double[0,0];
            double[,] y0 = new double[0,0];
            double[,] y1 = new double[0,0];
            int i = 0;
            int j = 0;
            double[,] a = new double[0,0];
            sparse.sparsematrix s0 = new sparse.sparsematrix();
            sparse.sparsematrix s1 = new sparse.sparsematrix();
            sparse.sparsematrix sa = new sparse.sparsematrix();
            double eps = 0;
            double v = 0;
            sparsegenerator g = new sparsegenerator();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            bool isupper = new bool();
            int triangletype = 0;
            int i_ = 0;

            eps = 10000*math.machineepsilon;
            result = false;
            hqrnd.hqrndrandomize(rs);
            
            //
            // Test linear algebra functions
            //
            for(n=1; n<=20; n++)
            {
                for(triangletype=-1; triangletype<=1; triangletype++)
                {
                    isupper = (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5);
                    if( triangletype<0 )
                    {
                        isupper = false;
                    }
                    if( triangletype>0 )
                    {
                        isupper = true;
                    }
                    initgenerator(n, n, 0, triangletype, g);
                    while( generatenext(g, ref a, sa) )
                    {
                        
                        //
                        // Choose matrix width K
                        //
                        k = 1+hqrnd.hqrnduniformi(rs, 20);
                        
                        //
                        // Convert SA to desired storage format:
                        // * S0 stores unmodified copy
                        // * S1 stores copy with unmodified triangle corresponding
                        //   to IsUpper and another triangle being spoiled by random
                        //   trash
                        //
                        sparse.sparsecopytohash(sa, s1);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j<i && isupper) || (j>i && !isupper) )
                                {
                                    sparse.sparseset(s1, i, j, hqrnd.hqrnduniformr(rs));
                                }
                            }
                        }
                        if( (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5) )
                        {
                            sparse.sparsecopytocrs(sa, s0);
                            sparse.sparseconverttocrs(s1);
                        }
                        else
                        {
                            sparse.sparsecopytosks(sa, s0);
                            sparse.sparseconverttosks(s1);
                        }
                        
                        //
                        // Test SparseGet() for SA and S0 against matrix returned in A
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                apserv.seterrorflag(ref result, (double)(Math.Abs(sparse.sparseget(sa, i, j)-a[i,j]))>(double)(eps));
                                apserv.seterrorflag(ref result, (double)(Math.Abs(sparse.sparseget(s0, i, j)-a[i,j]))>(double)(eps));
                                apserv.seterrorflag(ref result, (j<i && triangletype==1) && (double)(sparse.sparseget(s0, i, j))!=(double)(0));
                                apserv.seterrorflag(ref result, (j>i && triangletype==-1) && (double)(sparse.sparseget(s0, i, j))!=(double)(0));
                            }
                        }
                        
                        //
                        // Before we proceed with testing, update empty triangle of A
                        // with its copy from another part of the matrix.
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j<i && isupper) || (j>i && !isupper) )
                                {
                                    a[i,j] = a[j,i];
                                }
                            }
                        }
                        
                        //
                        // Test SparseSMM
                        //
                        x0 = new double[n, k];
                        x1 = new double[n, k];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=k-1; j++)
                            {
                                x0[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                                x1[i,j] = x0[i,j];
                            }
                        }
                        sparse.sparsesmm(s0, isupper, x0, k, ref y0);
                        apserv.seterrorflag(ref result, alglib.ap.rows(y0)<n);
                        apserv.seterrorflag(ref result, alglib.ap.cols(y0)<k);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=k-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*x1[i_,j];
                                }
                                apserv.seterrorflag(ref result, (double)(Math.Abs(v-y0[i,j]))>(double)(eps));
                            }
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Function for testing Level 2 triangular linear algebra functions.
        Returns True on failure.

          -- ALGLIB PROJECT --
             Copyright 20.01.2014 by Bochkanov Sergey
        *************************************************************************/
        public static bool testlevel2triangular()
        {
            bool result = new bool();
            int n = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] y0 = new double[0];
            double[] y1 = new double[0];
            double[] ey = new double[0];
            int i = 0;
            int j = 0;
            int i1 = 0;
            int j1 = 0;
            double[,] a = new double[0,0];
            double[,] ea = new double[0,0];
            sparse.sparsematrix s0 = new sparse.sparsematrix();
            sparse.sparsematrix s1 = new sparse.sparsematrix();
            sparse.sparsematrix sa = new sparse.sparsematrix();
            double eps = 0;
            double v = 0;
            sparsegenerator g = new sparsegenerator();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            bool isupper = new bool();
            bool isunit = new bool();
            int optype = 0;
            int triangletype = 0;
            int i_ = 0;

            eps = 10000*math.machineepsilon;
            result = false;
            hqrnd.hqrndrandomize(rs);
            
            //
            // Test sparseTRMV
            //
            for(n=1; n<=20; n++)
            {
                for(triangletype=-1; triangletype<=1; triangletype++)
                {
                    isupper = (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5);
                    if( triangletype<0 )
                    {
                        isupper = false;
                    }
                    if( triangletype>0 )
                    {
                        isupper = true;
                    }
                    initgenerator(n, n, 0, triangletype, g);
                    while( generatenext(g, ref a, sa) )
                    {
                        
                        //
                        // Settings (IsUpper was already set, handle the rest)
                        //
                        isunit = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                        optype = hqrnd.hqrnduniformi(rs, 2);
                        
                        //
                        // Convert SA to desired storage format:
                        // * S0 stores unmodified copy
                        // * S1 stores copy with unmodified triangle corresponding
                        //   to IsUpper and another triangle being spoiled by random
                        //   trash
                        //
                        sparse.sparsecopytohash(sa, s1);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j<i && isupper) || (j>i && !isupper) )
                                {
                                    sparse.sparseset(s1, i, j, hqrnd.hqrnduniformr(rs));
                                }
                            }
                        }
                        if( (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5) )
                        {
                            sparse.sparsecopytocrs(sa, s0);
                            sparse.sparseconverttocrs(s1);
                        }
                        else
                        {
                            sparse.sparsecopytosks(sa, s0);
                            sparse.sparseconverttosks(s1);
                        }
                        
                        //
                        // Generate "effective A"
                        //
                        ea = new double[n, n];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ea[i,j] = 0;
                            }
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j>=i && isupper) || (j<=i && !isupper) )
                                {
                                    i1 = i;
                                    j1 = j;
                                    if( optype==1 )
                                    {
                                        apserv.swapi(ref i1, ref j1);
                                    }
                                    ea[i1,j1] = a[i,j];
                                    if( isunit && i1==j1 )
                                    {
                                        ea[i1,j1] = 1.0;
                                    }
                                }
                            }
                        }
                        
                        //
                        // Test SparseTRMV
                        //
                        x0 = new double[n];
                        x1 = new double[n];
                        for(j=0; j<=n-1; j++)
                        {
                            x0[j] = hqrnd.hqrnduniformr(rs)-0.5;
                            x1[j] = x0[j];
                        }
                        sparse.sparsetrmv(s0, isupper, isunit, optype, x0, ref y0);
                        apserv.seterrorflag(ref result, alglib.ap.len(y0)<n);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += ea[i,i_]*x1[i_];
                            }
                            apserv.seterrorflag(ref result, (double)(Math.Abs(v-y0[i]))>(double)(eps));
                        }
                        sparse.sparsetrmv(s0, isupper, isunit, optype, x0, ref y1);
                        apserv.seterrorflag(ref result, alglib.ap.len(y1)<n);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += ea[i,i_]*x1[i_];
                            }
                            apserv.seterrorflag(ref result, (double)(Math.Abs(v-y1[i]))>(double)(eps));
                        }
                    }
                }
            }
            
            //
            // Test sparseTRSV
            //
            for(n=1; n<=20; n++)
            {
                for(triangletype=-1; triangletype<=1; triangletype++)
                {
                    isupper = (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5);
                    if( triangletype==-1 )
                    {
                        isupper = false;
                    }
                    if( triangletype==1 )
                    {
                        isupper = true;
                    }
                    initgenerator(n, n, 1, triangletype, g);
                    while( generatenext(g, ref a, sa) )
                    {
                        
                        //
                        // Settings (IsUpper was already set, handle the rest)
                        //
                        isunit = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                        optype = hqrnd.hqrnduniformi(rs, 2);
                        
                        //
                        // Convert SA to desired storage format:
                        // * S0 stores unmodified copy
                        // * S1 stores copy with unmodified triangle corresponding
                        //   to IsUpper and another triangle being spoiled by random
                        //   trash
                        //
                        sparse.sparsecopytohash(sa, s1);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j<i && isupper) || (j>i && !isupper) )
                                {
                                    sparse.sparseset(s1, i, j, hqrnd.hqrnduniformr(rs));
                                }
                            }
                        }
                        if( (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5) )
                        {
                            sparse.sparsecopytocrs(sa, s0);
                            sparse.sparseconverttocrs(s1);
                        }
                        else
                        {
                            sparse.sparsecopytosks(sa, s0);
                            sparse.sparseconverttosks(s1);
                        }
                        
                        //
                        // Generate "effective A" and EY = inv(EA)*x0
                        //
                        ea = new double[n, n];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ea[i,j] = 0;
                            }
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (j>=i && isupper) || (j<=i && !isupper) )
                                {
                                    i1 = i;
                                    j1 = j;
                                    if( optype==1 )
                                    {
                                        apserv.swapi(ref i1, ref j1);
                                    }
                                    ea[i1,j1] = a[i,j];
                                    if( isunit && i1==j1 )
                                    {
                                        ea[i1,j1] = 1.0;
                                    }
                                }
                            }
                        }
                        ey = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            ey[i] = hqrnd.hqrnduniformr(rs)-0.5;
                        }
                        x0 = new double[n];
                        x1 = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += ea[i,i_]*ey[i_];
                            }
                            x0[i] = v;
                            x1[i] = v;
                        }
                        
                        //
                        // Test SparseTRSV
                        //
                        sparse.sparsetrsv(s0, isupper, isunit, optype, x0);
                        apserv.seterrorflag(ref result, alglib.ap.len(x0)<n);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref result, (double)(Math.Abs(ey[i]-x0[i]))>(double)(eps));
                        }
                        sparse.sparsetrsv(s1, isupper, isunit, optype, x1);
                        apserv.seterrorflag(ref result, alglib.ap.len(x1)<n);
                        if( result )
                        {
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref result, (double)(Math.Abs(ey[i]-x1[i]))>(double)(eps));
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Function for testing basic functional

          -- ALGLIB PROJECT --
             Copyright 14.10.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool basicfuncrandomtest()
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            int n = 0;
            int m = 0;
            int i = 0;
            int j = 0;
            int i1 = 0;
            int j1 = 0;
            double[,] a = new double[0,0];
            int mfigure = 0;
            int temp = 0;

            n = 20;
            m = 20;
            mfigure = 10;
            for(i=1; i<=m-1; i++)
            {
                for(j=1; j<=n-1; j++)
                {
                    sparse.sparsecreate(i, j, 0, s);
                    a = new double[i, j];
                    
                    //
                    // Checking for Matrix with hash table type
                    //
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            temp = 2*math.randominteger(mfigure)-mfigure;
                            a[i1,j1] = temp;
                            if( math.randominteger(2)==0 )
                            {
                                sparse.sparseset(s, i1, j1, temp);
                                sparse.sparseset(s, i1, j1, temp);
                            }
                            else
                            {
                                sparse.sparseadd(s, i1, j1, temp);
                                sparse.sparseadd(s, i1, j1, 0);
                            }
                            if( (double)(a[i1,j1])!=(double)(sparse.sparseget(s, i1, j1)) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                    
                    //
                    // Nulling all elements
                    //
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            if( math.randominteger(2)==0 )
                            {
                                sparse.sparseset(s, i1, j1, 0);
                            }
                            else
                            {
                                sparse.sparseadd(s, i1, j1, -(1*sparse.sparseget(s, i1, j1)));
                            }
                        }
                    }
                    
                    //
                    // Again initialization of the matrix and check new values
                    //
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            temp = 2*math.randominteger(mfigure)-mfigure;
                            a[i1,j1] = temp;
                            if( math.randominteger(2)==0 )
                            {
                                sparse.sparseset(s, i1, j1, temp);
                            }
                            else
                            {
                                sparse.sparseadd(s, i1, j1, temp);
                            }
                            if( (double)(a[i1,j1])!=(double)(sparse.sparseget(s, i1, j1)) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                    
                    //
                    // Checking for Matrix with CRS type
                    //
                    sparse.sparseconverttocrs(s);
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            if( (double)(a[i1,j1])!=(double)(sparse.sparseget(s, i1, j1)) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing multyplication matrix with vector

          -- ALGLIB PROJECT --
             Copyright 14.10.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool linearfunctionstest()
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            int n = 0;
            int m = 0;
            int i = 0;
            int j = 0;
            int i1 = 0;
            int j1 = 0;
            double lb = 0;
            double rb = 0;
            double[,] a = new double[0,0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] ty = new double[0];
            double[] tyt = new double[0];
            double[] y = new double[0];
            double[] yt = new double[0];
            double[] y0 = new double[0];
            double[] yt0 = new double[0];
            double eps = 0;

            
            //
            // Accuracy
            //
            eps = 1000*math.machineepsilon;
            
            //
            // Size of the matrix (m*n)
            //
            n = 10;
            m = 10;
            
            //
            // Left and right borders, limiting matrix values
            //
            lb = -10;
            rb = 10;
            
            //
            // Test linear algebra functions for:
            // a) sparse matrix converted to CRS from Hash-Table
            // b) sparse matrix initially created as CRS
            //
            for(i=1; i<=m-1; i++)
            {
                for(j=1; j<=n-1; j++)
                {
                    
                    //
                    // Prepare test problem
                    //
                    createrandom(i, j, -1, -1, -1, -1, ref a, s);
                    
                    //
                    // Initialize temporaries
                    //
                    ty = new double[i];
                    tyt = new double[j];
                    for(i1=0; i1<=i-1; i1++)
                    {
                        ty[i1] = 0;
                    }
                    for(i1=0; i1<=j-1; i1++)
                    {
                        tyt[i1] = 0;
                    }
                    x0 = new double[j];
                    x1 = new double[i];
                    for(i1=0; i1<=j-1; i1++)
                    {
                        x0[i1] = (rb-lb)*math.randomreal()+lb;
                    }
                    for(i1=0; i1<=i-1; i1++)
                    {
                        x1[i1] = (rb-lb)*math.randomreal()+lb;
                    }
                    
                    //
                    // Consider two cases: square matrix, and non-square matrix
                    //
                    if( i!=j )
                    {
                        
                        //
                        // Searching true result
                        //
                        for(i1=0; i1<=i-1; i1++)
                        {
                            for(j1=0; j1<=j-1; j1++)
                            {
                                ty[i1] = ty[i1]+a[i1,j1]*x0[j1];
                                tyt[j1] = tyt[j1]+a[i1,j1]*x1[i1];
                            }
                        }
                        
                        //
                        // Multiplication
                        //
                        sparse.sparsemv(s, x0, ref y);
                        sparse.sparsemtv(s, x1, ref yt);
                        
                        //
                        // Check for MV-result
                        //
                        for(i1=0; i1<=i-1; i1++)
                        {
                            if( (double)(Math.Abs(y[i1]-ty[i1]))>=(double)(eps) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        
                        //
                        // Check for MTV-result
                        //
                        for(i1=0; i1<=j-1; i1++)
                        {
                            if( (double)(Math.Abs(yt[i1]-tyt[i1]))>=(double)(eps) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                    else
                    {
                        
                        //
                        // Searching true result
                        //
                        for(i1=0; i1<=i-1; i1++)
                        {
                            for(j1=0; j1<=j-1; j1++)
                            {
                                ty[i1] = ty[i1]+a[i1,j1]*x0[j1];
                                tyt[j1] = tyt[j1]+a[i1,j1]*x0[i1];
                            }
                        }
                        sparse.sparsemv(s, x0, ref y);
                        sparse.sparsemtv(s, x0, ref yt);
                        sparse.sparsemv2(s, x0, ref y0, ref yt0);
                        
                        //
                        // Check for MV2-result
                        //
                        for(i1=0; i1<=i-1; i1++)
                        {
                            if( (double)(Math.Abs(y0[i1]-ty[i1]))>=(double)(eps) || (double)(Math.Abs(yt0[i1]-tyt[i1]))>=(double)(eps) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        
                        //
                        // Check for MV- and MTV-result by help MV2
                        //
                        for(i1=0; i1<=i-1; i1++)
                        {
                            if( (double)(Math.Abs(y0[i1]-y[i1]))>(double)(eps) || (double)(Math.Abs(yt0[i1]-yt[i1]))>(double)(eps) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing multyplication for simmetric matrix with vector

          -- ALGLIB PROJECT --
             Copyright 14.10.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool linearfunctionsstest()
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            int m = 0;
            int i = 0;
            int i1 = 0;
            int j1 = 0;
            double lb = 0;
            double rb = 0;
            double[,] a = new double[0,0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] ty = new double[0];
            double[] tyt = new double[0];
            double[] y = new double[0];
            double[] yt = new double[0];
            double eps = 0;

            
            //
            //Accuracy
            //
            eps = 1000*math.machineepsilon;
            
            //
            // Size of the matrix (m*m)
            //
            m = 10;
            
            //
            // Left and right borders, limiting matrix values
            //
            lb = -10;
            rb = 10;
            
            //
            // Test linear algebra functions for:
            // a) sparse matrix converted to CRS from Hash-Table
            // b) sparse matrix initially created as CRS
            //
            for(i=1; i<=m-1; i++)
            {
                
                //
                // Prepare test problem
                //
                createrandom(i, i, -1, -1, -1, -1, ref a, s);
                
                //
                // Initialize temporaries
                //
                ty = new double[i];
                tyt = new double[i];
                x0 = new double[i];
                x1 = new double[i];
                for(i1=0; i1<=i-1; i1++)
                {
                    ty[i1] = 0;
                    tyt[i1] = 0;
                    x0[i1] = (rb-lb)*math.randomreal()+lb;
                    x1[i1] = (rb-lb)*math.randomreal()+lb;
                }
                
                //
                // Searching true result for upper and lower triangles
                // of the matrix
                //
                for(i1=0; i1<=i-1; i1++)
                {
                    for(j1=i1; j1<=i-1; j1++)
                    {
                        ty[i1] = ty[i1]+a[i1,j1]*x0[j1];
                        if( i1!=j1 )
                        {
                            ty[j1] = ty[j1]+a[i1,j1]*x0[i1];
                        }
                    }
                }
                for(i1=0; i1<=i-1; i1++)
                {
                    for(j1=0; j1<=i1; j1++)
                    {
                        tyt[i1] = tyt[i1]+a[i1,j1]*x1[j1];
                        if( i1!=j1 )
                        {
                            tyt[j1] = tyt[j1]+a[i1,j1]*x1[i1];
                        }
                    }
                }
                
                //
                // Multiplication
                //
                sparse.sparsesmv(s, true, x0, ref y);
                sparse.sparsesmv(s, false, x1, ref yt);
                
                //
                // Check for SMV-result
                //
                for(i1=0; i1<=i-1; i1++)
                {
                    if( (double)(Math.Abs(y[i1]-ty[i1]))>=(double)(eps) || (double)(Math.Abs(yt[i1]-tyt[i1]))>=(double)(eps) )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing multyplication sparse matrix with nerrow dense matrix

          -- ALGLIB PROJECT --
             Copyright 14.10.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool linearfunctionsmmtest()
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            int n = 0;
            int m = 0;
            int kmax = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int i1 = 0;
            int j1 = 0;
            int k1 = 0;
            double lb = 0;
            double rb = 0;
            double[,] a = new double[0,0];
            double[,] x0 = new double[0,0];
            double[,] x1 = new double[0,0];
            double[,] ty = new double[0,0];
            double[,] tyt = new double[0,0];
            double[,] y = new double[0,0];
            double[,] yt = new double[0,0];
            double[,] y0 = new double[0,0];
            double[,] yt0 = new double[0,0];
            double eps = 0;

            
            //
            // Accuracy
            //
            eps = 1000*math.machineepsilon;
            
            //
            // Size of the matrix (m*n)
            //
            n = 32;
            m = 32;
            kmax = 32;
            
            //
            // Left and right borders, limiting matrix values
            //
            lb = -10;
            rb = 10;
            
            //
            // Test linear algebra functions for:
            // a) sparse matrix converted to CRS from Hash-Table
            // b) sparse matrix initially created as CRS
            //
            for(i=1; i<=m-1; i++)
            {
                for(j=1; j<=n-1; j++)
                {
                    
                    //
                    // Prepare test problem
                    //
                    createrandom(i, j, -1, -1, -1, -1, ref a, s);
                    x0 = new double[j, kmax];
                    x1 = new double[i, kmax];
                    for(i1=0; i1<=j-1; i1++)
                    {
                        for(j1=0; j1<=kmax-1; j1++)
                        {
                            x0[i1,j1] = (rb-lb)*math.randomreal()+lb;
                        }
                    }
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=kmax-1; j1++)
                        {
                            x1[i1,j1] = (rb-lb)*math.randomreal()+lb;
                        }
                    }
                    ty = new double[i, kmax];
                    tyt = new double[j, kmax];
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=kmax-1; j1++)
                        {
                            ty[i1,j1] = 0;
                        }
                    }
                    for(i1=0; i1<=j-1; i1++)
                    {
                        for(j1=0; j1<=kmax-1; j1++)
                        {
                            tyt[i1,j1] = 0;
                        }
                    }
                    if( i!=j )
                    {
                        for(i1=0; i1<=i-1; i1++)
                        {
                            for(k1=0; k1<=kmax-1; k1++)
                            {
                                for(j1=0; j1<=j-1; j1++)
                                {
                                    ty[i1,k1] = ty[i1,k1]+a[i1,j1]*x0[j1,k1];
                                    tyt[j1,k1] = tyt[j1,k1]+a[i1,j1]*x1[i1,k1];
                                }
                            }
                        }
                    }
                    else
                    {
                        for(i1=0; i1<=i-1; i1++)
                        {
                            for(k1=0; k1<=kmax-1; k1++)
                            {
                                for(j1=0; j1<=j-1; j1++)
                                {
                                    ty[i1,k1] = ty[i1,k1]+a[i1,j1]*x0[j1,k1];
                                    tyt[j1,k1] = tyt[j1,k1]+a[i1,j1]*x0[i1,k1];
                                }
                            }
                        }
                    }
                    for(k=1; k<=kmax; k++)
                    {
                        
                        //
                        // Consider two cases: square matrix, and non-square matrix
                        //
                        if( i!=j )
                        {
                            
                            //
                            // Multiplication
                            //
                            sparse.sparsemm(s, x0, k, ref y);
                            sparse.sparsemtm(s, x1, k, ref yt);
                            
                            //
                            // Check for MM-result
                            //
                            for(i1=0; i1<=i-1; i1++)
                            {
                                for(j1=0; j1<=k-1; j1++)
                                {
                                    if( (double)(Math.Abs(y[i1,j1]-ty[i1,j1]))>=(double)(eps) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                            
                            //
                            // Check for MTM-result
                            //
                            for(i1=0; i1<=j-1; i1++)
                            {
                                for(j1=0; j1<=k-1; j1++)
                                {
                                    if( (double)(Math.Abs(yt[i1,j1]-tyt[i1,j1]))>=(double)(eps) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                        else
                        {
                            sparse.sparsemm(s, x0, k, ref y);
                            sparse.sparsemtm(s, x0, k, ref yt);
                            sparse.sparsemm2(s, x0, k, ref y0, ref yt0);
                            
                            //
                            // Check for MM2-result
                            //
                            for(i1=0; i1<=i-1; i1++)
                            {
                                for(j1=0; j1<=k-1; j1++)
                                {
                                    if( (double)(Math.Abs(y0[i1,j1]-ty[i1,j1]))>=(double)(eps) || (double)(Math.Abs(yt0[i1,j1]-tyt[i1,j1]))>=(double)(eps) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                            
                            //
                            // Check for MV- and MTM-result by help MV2
                            //
                            for(i1=0; i1<=i-1; i1++)
                            {
                                for(j1=0; j1<=k-1; j1++)
                                {
                                    if( (double)(Math.Abs(y0[i1,j1]-y[i1,j1]))>(double)(eps) || (double)(Math.Abs(yt0[i1,j1]-yt[i1,j1]))>(double)(eps) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing multyplication for simmetric sparse matrix with narrow
        dense matrix

          -- ALGLIB PROJECT --
             Copyright 14.10.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool linearfunctionssmmtest()
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            int m = 0;
            int k = 0;
            int i = 0;
            int j = 0;
            int i1 = 0;
            int j1 = 0;
            int k1 = 0;
            double lb = 0;
            double rb = 0;
            double[,] a = new double[0,0];
            double[,] x0 = new double[0,0];
            double[,] x1 = new double[0,0];
            double[,] ty = new double[0,0];
            double[,] tyt = new double[0,0];
            double[,] y = new double[0,0];
            double[,] yt = new double[0,0];
            double eps = 0;

            
            //
            // Accuracy
            //
            eps = 1000*math.machineepsilon;
            
            //
            // Size of the matrix (m*m)
            //
            m = 32;
            k = 32;
            
            //
            // Left and right borders, limiting matrix values
            //
            lb = -10;
            rb = 10;
            
            //
            // Test linear algebra functions for:
            // a) sparse matrix converted to CRS from Hash-Table
            // b) sparse matrix initially created as CRS
            //
            for(i=1; i<=m-1; i++)
            {
                for(j=1; j<=k-1; j++)
                {
                    
                    //
                    // Prepare test problem
                    //
                    createrandom(i, i, -1, -1, -1, -1, ref a, s);
                    
                    //
                    // Initialize temporaries
                    //
                    ty = new double[i, j];
                    tyt = new double[i, j];
                    x0 = new double[i, j];
                    x1 = new double[i, j];
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            ty[i1,j1] = 0;
                            tyt[i1,j1] = 0;
                            x0[i1,j1] = (rb-lb)*math.randomreal()+lb;
                            x1[i1,j1] = (rb-lb)*math.randomreal()+lb;
                        }
                    }
                    
                    //
                    // Searching true result for upper and lower triangles
                    // of the matrix
                    //
                    for(k1=0; k1<=j-1; k1++)
                    {
                        for(i1=0; i1<=i-1; i1++)
                        {
                            for(j1=i1; j1<=i-1; j1++)
                            {
                                ty[i1,k1] = ty[i1,k1]+a[i1,j1]*x0[j1,k1];
                                if( i1!=j1 )
                                {
                                    ty[j1,k1] = ty[j1,k1]+a[i1,j1]*x0[i1,k1];
                                }
                            }
                        }
                    }
                    for(k1=0; k1<=j-1; k1++)
                    {
                        for(i1=0; i1<=i-1; i1++)
                        {
                            for(j1=0; j1<=i1; j1++)
                            {
                                tyt[i1,k1] = tyt[i1,k1]+a[i1,j1]*x1[j1,k1];
                                if( i1!=j1 )
                                {
                                    tyt[j1,k1] = tyt[j1,k1]+a[i1,j1]*x1[i1,k1];
                                }
                            }
                        }
                    }
                    
                    //
                    // Multiplication
                    //
                    sparse.sparsesmm(s, true, x0, j, ref y);
                    sparse.sparsesmm(s, false, x1, j, ref yt);
                    
                    //
                    // Check for SMM-result
                    //
                    for(k1=0; k1<=j-1; k1++)
                    {
                        for(i1=0; i1<=i-1; i1++)
                        {
                            if( (double)(Math.Abs(y[i1,k1]-ty[i1,k1]))>=(double)(eps) || (double)(Math.Abs(yt[i1,k1]-tyt[i1,k1]))>=(double)(eps) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for basic test SparseCopy

          -- ALGLIB PROJECT --
             Copyright 14.10.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool basiccopyfunctest(bool silent)
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            sparse.sparsematrix ss = new sparse.sparsematrix();
            sparse.sparsematrix sss = new sparse.sparsematrix();
            int n = 0;
            int m = 0;
            int[] ner = new int[0];
            int i = 0;
            int j = 0;
            int i1 = 0;
            int j1 = 0;
            double[,] a = new double[0,0];
            double a0 = 0;
            double a1 = 0;

            n = 30;
            m = 30;
            for(i=1; i<=m-1; i++)
            {
                for(j=1; j<=n-1; j++)
                {
                    sparse.sparsecreate(i, j, 1, s);
                    a = new double[i, j];
                    ner = new int[i];
                    for(i1=0; i1<=i-1; i1++)
                    {
                        if( i1<=j-3 )
                        {
                            ner[i1] = 2;
                        }
                        else
                        {
                            if( j-3<i1 && i1<=j-2 )
                            {
                                ner[i1] = 1;
                            }
                            else
                            {
                                ner[i1] = 0;
                            }
                        }
                    }
                    sparse.sparsecreatecrs(i, j, ner, sss);
                    
                    //
                    // Checking for Matrix with hash table type
                    //
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            if( j1>i1 && j1<=i1+2 )
                            {
                                a[i1,j1] = i1+j1+1;
                                sparse.sparseset(s, i1, j1, a[i1,j1]);
                                sparse.sparseadd(s, i1, j1, 0);
                                sparse.sparseset(sss, i1, j1, a[i1,j1]);
                            }
                            else
                            {
                                a[i1,j1] = 0;
                                sparse.sparseset(s, i1, j1, a[i1,j1]);
                                sparse.sparseadd(s, i1, j1, 0);
                            }
                            
                            //
                            // Check for SparseCreate
                            //
                            sparse.sparsecopy(s, ss);
                            a0 = sparse.sparseget(s, i1, j1);
                            a1 = sparse.sparseget(ss, i1, j1);
                            if( (double)(a0)!=(double)(a1) )
                            {
                                if( !silent )
                                {
                                    System.Console.Write("BasicCopyFuncTest::Report::SparseGet");
                                    System.Console.WriteLine();
                                    System.Console.Write("S::[");
                                    System.Console.Write("{0,0:d}",i1);
                                    System.Console.Write(",");
                                    System.Console.Write("{0,0:d}",j1);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F5}",a0);
                                    System.Console.WriteLine();
                                    System.Console.Write("SS::[");
                                    System.Console.Write("{0,0:d}",i1);
                                    System.Console.Write(",");
                                    System.Console.Write("{0,0:d}",j1);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F5}",a1);
                                    System.Console.WriteLine();
                                    System.Console.Write("          TEST FAILED.");
                                    System.Console.WriteLine();
                                }
                                result = true;
                                return result;
                            }
                        }
                    }
                    
                    //
                    // Check for SparseCreateCRS
                    //
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            sparse.sparsecopy(sss, ss);
                            a0 = sparse.sparseget(sss, i1, j1);
                            a1 = sparse.sparseget(ss, i1, j1);
                            if( (double)(a0)!=(double)(a1) )
                            {
                                if( !silent )
                                {
                                    System.Console.Write("BasicCopyFuncTest::Report::SparseGet");
                                    System.Console.WriteLine();
                                    System.Console.Write("S::[");
                                    System.Console.Write("{0,0:d}",i1);
                                    System.Console.Write(",");
                                    System.Console.Write("{0,0:d}",j1);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F5}",a0);
                                    System.Console.WriteLine();
                                    System.Console.Write("SS::[");
                                    System.Console.Write("{0,0:d}",i1);
                                    System.Console.Write(",");
                                    System.Console.Write("{0,0:d}",j1);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F5}",a1);
                                    System.Console.WriteLine();
                                    System.Console.Write("          TEST FAILED.");
                                    System.Console.WriteLine();
                                }
                                result = true;
                                return result;
                            }
                        }
                    }
                    
                    //
                    // Check for Matrix with CRS type
                    //
                    sparse.sparseconverttocrs(s);
                    sparse.sparsecopy(s, ss);
                    for(i1=0; i1<=i-1; i1++)
                    {
                        for(j1=0; j1<=j-1; j1++)
                        {
                            a0 = sparse.sparseget(s, i1, j1);
                            a1 = sparse.sparseget(ss, i1, j1);
                            if( (double)(a0)!=(double)(a1) )
                            {
                                if( !silent )
                                {
                                    System.Console.Write("BasicCopyFuncTest::Report::SparseGet");
                                    System.Console.WriteLine();
                                    System.Console.Write("S::[");
                                    System.Console.Write("{0,0:d}",i1);
                                    System.Console.Write(",");
                                    System.Console.Write("{0,0:d}",j1);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F5}",a0);
                                    System.Console.WriteLine();
                                    System.Console.Write("SS::[");
                                    System.Console.Write("{0,0:d}",i1);
                                    System.Console.Write(",");
                                    System.Console.Write("{0,0:d}",j1);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F5}",a1);
                                    System.Console.WriteLine();
                                    System.Console.Write("          TEST FAILED.");
                                    System.Console.WriteLine();
                                }
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            if( !silent )
            {
                System.Console.Write("          TEST IS PASSED.");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing SparseCopy

          -- ALGLIB PROJECT --
             Copyright 14.10.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool copyfunctest(bool silent)
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            sparse.sparsematrix ss = new sparse.sparsematrix();
            int n = 0;
            int m = 0;
            int mtype = 0;
            int i = 0;
            int j = 0;
            int i1 = 0;
            int j1 = 0;
            double lb = 0;
            double rb = 0;
            double[,] a = new double[0,0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] ty = new double[0];
            double[] tyt = new double[0];
            double[] y = new double[0];
            double[] yt = new double[0];
            double[] y0 = new double[0];
            double[] yt0 = new double[0];
            double[] cpy = new double[0];
            double[] cpyt = new double[0];
            double[] cpy0 = new double[0];
            double[] cpyt0 = new double[0];
            double eps = 0;
            double a0 = 0;
            double a1 = 0;

            
            //
            // Accuracy
            //
            eps = 1000*math.machineepsilon;
            
            //
            // Size of the matrix (m*n)
            //
            n = 30;
            m = 30;
            
            //
            // Left and right borders, limiting matrix values
            //
            lb = -10;
            rb = 10;
            
            //
            // Test linear algebra functions for:
            // a) sparse matrix converted to CRS from Hash-Table
            // b) sparse matrix initially created as CRS
            //
            for(i=1; i<=m-1; i++)
            {
                for(j=1; j<=n-1; j++)
                {
                    for(mtype=0; mtype<=1; mtype++)
                    {
                        
                        //
                        // Prepare test problem
                        //
                        createrandom(i, j, -1, mtype, -1, -1, ref a, s);
                        sparse.sparsecopy(s, ss);
                        
                        //
                        // Initialize temporaries
                        //
                        ty = new double[i];
                        tyt = new double[j];
                        for(i1=0; i1<=i-1; i1++)
                        {
                            ty[i1] = 0;
                        }
                        for(i1=0; i1<=j-1; i1++)
                        {
                            tyt[i1] = 0;
                        }
                        x0 = new double[j];
                        x1 = new double[i];
                        for(i1=0; i1<=j-1; i1++)
                        {
                            x0[i1] = (rb-lb)*math.randomreal()+lb;
                        }
                        for(i1=0; i1<=i-1; i1++)
                        {
                            x1[i1] = (rb-lb)*math.randomreal()+lb;
                        }
                        
                        //
                        // Consider two cases: square matrix, and non-square matrix
                        //
                        if( i!=j )
                        {
                            
                            //
                            // Searching true result
                            //
                            for(i1=0; i1<=i-1; i1++)
                            {
                                for(j1=0; j1<=j-1; j1++)
                                {
                                    ty[i1] = ty[i1]+a[i1,j1]*x0[j1];
                                    tyt[j1] = tyt[j1]+a[i1,j1]*x1[i1];
                                }
                            }
                            
                            //
                            // Multiplication
                            //
                            sparse.sparsemv(s, x0, ref y);
                            sparse.sparsemtv(s, x1, ref yt);
                            sparse.sparsemv(ss, x0, ref cpy);
                            sparse.sparsemtv(ss, x1, ref cpyt);
                            
                            //
                            // Check for MV-result
                            //
                            for(i1=0; i1<=i-1; i1++)
                            {
                                if( ((double)(Math.Abs(y[i1]-ty[i1]))>=(double)(eps) || (double)(Math.Abs(cpy[i1]-ty[i1]))>=(double)(eps)) || (double)(cpy[i1]-y[i1])!=(double)(0) )
                                {
                                    if( !silent )
                                    {
                                        System.Console.Write("CopyFuncTest::Report::RES_MV");
                                        System.Console.WriteLine();
                                        System.Console.Write("Y[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",y[i1]);
                                        System.Console.Write("; tY[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",ty[i1]);
                                        System.Console.WriteLine();
                                        System.Console.Write("cpY[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",cpy[i1]);
                                        System.Console.Write(";");
                                        System.Console.WriteLine();
                                        System.Console.Write("          TEST FAILED.");
                                        System.Console.WriteLine();
                                    }
                                    result = true;
                                    return result;
                                }
                            }
                            
                            //
                            // Check for MTV-result
                            //
                            for(i1=0; i1<=j-1; i1++)
                            {
                                if( ((double)(Math.Abs(yt[i1]-tyt[i1]))>=(double)(eps) || (double)(Math.Abs(cpyt[i1]-tyt[i1]))>=(double)(eps)) || (double)(cpyt[i1]-yt[i1])!=(double)(0) )
                                {
                                    if( !silent )
                                    {
                                        System.Console.Write("CopyFuncTest::Report::RES_MTV");
                                        System.Console.WriteLine();
                                        System.Console.Write("Yt[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",yt[i1]);
                                        System.Console.Write("; tYt[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",tyt[i1]);
                                        System.Console.WriteLine();
                                        System.Console.Write("cpYt[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",cpyt[i1]);
                                        System.Console.Write(";");
                                        System.Console.WriteLine();
                                        System.Console.Write("          TEST FAILED.");
                                        System.Console.WriteLine();
                                    }
                                    result = true;
                                    return result;
                                }
                            }
                            sparse.sparsecopy(s, ss);
                            for(i1=0; i1<=i-1; i1++)
                            {
                                for(j1=0; j1<=j-1; j1++)
                                {
                                    a0 = sparse.sparseget(s, i1, j1);
                                    a1 = sparse.sparseget(ss, i1, j1);
                                    if( (double)(a0)!=(double)(a1) )
                                    {
                                        if( !silent )
                                        {
                                            System.Console.Write("CopyFuncTest::Report::SparseGet");
                                            System.Console.WriteLine();
                                            System.Console.Write("S::[");
                                            System.Console.Write("{0,0:d}",i1);
                                            System.Console.Write(",");
                                            System.Console.Write("{0,0:d}",j1);
                                            System.Console.Write("]=");
                                            System.Console.Write("{0,0:F5}",a0);
                                            System.Console.WriteLine();
                                            System.Console.Write("SS::[");
                                            System.Console.Write("{0,0:d}",i1);
                                            System.Console.Write(",");
                                            System.Console.Write("{0,0:d}",j1);
                                            System.Console.Write("]=");
                                            System.Console.Write("{0,0:F5}",a1);
                                            System.Console.WriteLine();
                                            System.Console.Write("          TEST FAILED.");
                                            System.Console.WriteLine();
                                        }
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                        else
                        {
                            
                            //
                            // Searching true result
                            //
                            for(i1=0; i1<=i-1; i1++)
                            {
                                for(j1=0; j1<=j-1; j1++)
                                {
                                    ty[i1] = ty[i1]+a[i1,j1]*x0[j1];
                                    tyt[j1] = tyt[j1]+a[i1,j1]*x0[i1];
                                }
                            }
                            
                            //
                            // Multiplication
                            //
                            sparse.sparsemv(s, x0, ref y);
                            sparse.sparsemtv(s, x0, ref yt);
                            sparse.sparsemv2(s, x0, ref y0, ref yt0);
                            sparse.sparsemv(ss, x0, ref cpy);
                            sparse.sparsemtv(ss, x0, ref cpyt);
                            sparse.sparsemv2(ss, x0, ref cpy0, ref cpyt0);
                            
                            //
                            // Check for MV2-result
                            //
                            for(i1=0; i1<=i-1; i1++)
                            {
                                if( (((((double)(Math.Abs(y0[i1]-ty[i1]))>=(double)(eps) || (double)(Math.Abs(yt0[i1]-tyt[i1]))>=(double)(eps)) || (double)(Math.Abs(cpy0[i1]-ty[i1]))>=(double)(eps)) || (double)(Math.Abs(cpyt0[i1]-tyt[i1]))>=(double)(eps)) || (double)(cpy0[i1]-y0[i1])!=(double)(0)) || (double)(cpyt0[i1]-yt0[i1])!=(double)(0) )
                                {
                                    if( !silent )
                                    {
                                        System.Console.Write("CopyFuncTest::Report::RES_MV2");
                                        System.Console.WriteLine();
                                        System.Console.Write("Y0[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",y0[i1]);
                                        System.Console.Write("; tY[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",ty[i1]);
                                        System.Console.WriteLine();
                                        System.Console.Write("Yt0[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",yt0[i1]);
                                        System.Console.Write("; tYt[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",tyt[i1]);
                                        System.Console.WriteLine();
                                        System.Console.Write("cpY0[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",cpy0[i1]);
                                        System.Console.Write(";");
                                        System.Console.WriteLine();
                                        System.Console.Write("cpYt0[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",cpyt0[i1]);
                                        System.Console.Write(";");
                                        System.Console.WriteLine();
                                        System.Console.Write("          TEST FAILED.");
                                        System.Console.WriteLine();
                                    }
                                    result = true;
                                    return result;
                                }
                            }
                            
                            //
                            // Check for MV- and MTV-result by help MV2
                            //
                            for(i1=0; i1<=i-1; i1++)
                            {
                                if( (((double)(Math.Abs(y0[i1]-y[i1]))>(double)(eps) || (double)(Math.Abs(yt0[i1]-yt[i1]))>(double)(eps)) || (double)(Math.Abs(cpy0[i1]-cpy[i1]))>(double)(eps)) || (double)(Math.Abs(cpyt0[i1]-cpyt[i1]))>(double)(eps) )
                                {
                                    if( !silent )
                                    {
                                        System.Console.Write("CopyFuncTest::Report::RES_MV_MVT");
                                        System.Console.WriteLine();
                                        System.Console.Write("Y0[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",y0[i1]);
                                        System.Console.Write("; Y[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",y[i1]);
                                        System.Console.WriteLine();
                                        System.Console.Write("Yt0[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",yt0[i1]);
                                        System.Console.Write("; Yt[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",yt[i1]);
                                        System.Console.WriteLine();
                                        System.Console.Write("cpY0[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",cpy0[i1]);
                                        System.Console.Write("; cpY[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",cpy[i1]);
                                        System.Console.WriteLine();
                                        System.Console.Write("cpYt0[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",cpyt0[i1]);
                                        System.Console.Write("; cpYt[");
                                        System.Console.Write("{0,0:d}",i1);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",cpyt[i1]);
                                        System.Console.WriteLine();
                                        System.Console.Write("          TEST FAILED.");
                                        System.Console.WriteLine();
                                    }
                                    result = true;
                                    return result;
                                }
                            }
                            sparse.sparsecopy(s, ss);
                            for(i1=0; i1<=i-1; i1++)
                            {
                                for(j1=0; j1<=j-1; j1++)
                                {
                                    a0 = sparse.sparseget(s, i1, j1);
                                    a1 = sparse.sparseget(ss, i1, j1);
                                    if( (double)(a0)!=(double)(a1) )
                                    {
                                        if( !silent )
                                        {
                                            System.Console.Write("CopyFuncTest::Report::SparseGet");
                                            System.Console.WriteLine();
                                            System.Console.Write("S::[");
                                            System.Console.Write("{0,0:d}",i1);
                                            System.Console.Write(",");
                                            System.Console.Write("{0,0:d}",j1);
                                            System.Console.Write("]=");
                                            System.Console.Write("{0,0:F5}",a0);
                                            System.Console.WriteLine();
                                            System.Console.Write("SS::[");
                                            System.Console.Write("{0,0:d}",i1);
                                            System.Console.Write(",");
                                            System.Console.Write("{0,0:d}",j1);
                                            System.Console.Write("]=");
                                            System.Console.Write("{0,0:F5}",a1);
                                            System.Console.WriteLine();
                                            System.Console.Write("          TEST FAILED.");
                                            System.Console.WriteLine();
                                        }
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if( !silent )
            {
                System.Console.Write("          TEST IS PASSED.");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function initializes sparse matrix generator, which is used to generate
        a set of matrices with sequentially increasing sparsity.

        PARAMETERS:
            M, N        -   matrix size. If M=0, then matrix is square N*N.
                            N and M must be small enough to store N*M dense matrix.
            MatKind     -   matrix properties:
                            * 0     -   general sparse (no structure)
                            * 1     -   general sparse, but diagonal is always present and non-zero
                            * 2     -   diagonally dominant, SPD
            Triangle    -   triangle being returned:
                            * +1    -   upper triangle
                            * -1    -   lower triangle
                            *  0    -   full matrix is returned
                            
        OUTPUT PARAMETERS:
            G           -   generator
            A           -   matrix A in dense format
            SA          -   matrix A in sparse format (hash-table storage)
        *************************************************************************/
        private static void initgenerator(int m,
            int n,
            int matkind,
            int triangle,
            sparsegenerator g)
        {
            g.n = n;
            g.m = m;
            g.matkind = matkind;
            g.triangle = triangle;
            hqrnd.hqrndrandomize(g.rs);
            g.rcs.ia = new int[5+1];
            g.rcs.ra = new double[1+1];
            g.rcs.stage = -1;
        }


        private static bool generatenext(sparsegenerator g,
            ref double[,] da,
            sparse.sparsematrix sa)
        {
            bool result = new bool();
            int n = 0;
            int m = 0;
            int nz = 0;
            int nzd = 0;
            double pnz = 0;
            int i = 0;
            int j = 0;
            double v = 0;

            da = new double[0,0];

            
            //
            // Reverse communication preparations
            // I know it looks ugly, but it works the same way
            // anywhere from C++ to Python.
            //
            // This code initializes locals by:
            // * random values determined during code
            //   generation - on first subroutine call
            // * values from previous call - on subsequent calls
            //
            if( g.rcs.stage>=0 )
            {
                n = g.rcs.ia[0];
                m = g.rcs.ia[1];
                nz = g.rcs.ia[2];
                nzd = g.rcs.ia[3];
                i = g.rcs.ia[4];
                j = g.rcs.ia[5];
                pnz = g.rcs.ra[0];
                v = g.rcs.ra[1];
            }
            else
            {
                n = -983;
                m = -989;
                nz = -834;
                nzd = 900;
                i = -287;
                j = 364;
                pnz = 214;
                v = -338;
            }
            if( g.rcs.stage==0 )
            {
                goto lbl_0;
            }
            if( g.rcs.stage==1 )
            {
                goto lbl_1;
            }
            
            //
            // Routine body
            //
            n = g.n;
            if( g.m==0 )
            {
                m = n;
            }
            else
            {
                m = g.m;
            }
            alglib.ap.assert(m>0 && n>0, "GenerateNext: incorrect N/M");
            
            //
            // Generate general sparse matrix
            //
            if( g.matkind!=0 )
            {
                goto lbl_2;
            }
            nz = n*m;
        lbl_4:
            if( false )
            {
                goto lbl_5;
            }
            
            //
            // Generate dense N*N matrix where probability of element
            // being non-zero is PNZ.
            //
            pnz = (double)nz/(double)(n*m);
            g.bufa = new double[m, n];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (double)(hqrnd.hqrnduniformr(g.rs))<=(double)(pnz) )
                    {
                        g.bufa[i,j] = hqrnd.hqrnduniformr(g.rs)-0.5;
                    }
                    else
                    {
                        g.bufa[i,j] = 0.0;
                    }
                }
            }
            
            //
            // Output matrix and RComm
            //
            da = new double[m, n];
            sparse.sparsecreate(m, n, (int)Math.Round(pnz*m*n), sa);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j<=i && g.triangle<=0) || (j>=i && g.triangle>=0) )
                    {
                        da[i,j] = g.bufa[i,j];
                        sparse.sparseset(sa, i, j, g.bufa[i,j]);
                    }
                    else
                    {
                        da[i,j] = 0.0;
                    }
                }
            }
            g.rcs.stage = 0;
            goto lbl_rcomm;
        lbl_0:
            
            //
            // Increase problem sparcity and try one more time. 
            // Stop after testing NZ=0.
            //
            if( nz==0 )
            {
                goto lbl_5;
            }
            nz = nz/2;
            goto lbl_4;
        lbl_5:
            result = false;
            return result;
        lbl_2:
            
            //
            // Generate general sparse matrix with non-zero diagonal
            //
            if( g.matkind!=1 )
            {
                goto lbl_6;
            }
            alglib.ap.assert(n==m, "GenerateNext: non-square matrix for MatKind=1");
            nz = n*n-n;
        lbl_8:
            if( false )
            {
                goto lbl_9;
            }
            
            //
            // Generate dense N*N matrix where probability of non-diagonal element
            // being non-zero is PNZ.
            //
            if( n>1 )
            {
                pnz = (double)nz/(double)(n*n-n);
            }
            else
            {
                pnz = 1;
            }
            g.bufa = new double[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( i==j )
                    {
                        do
                        {
                            g.bufa[i,i] = hqrnd.hqrnduniformr(g.rs)-0.5;
                        }
                        while( (double)(g.bufa[i,i])==(double)(0) );
                        g.bufa[i,i] = g.bufa[i,i]+1.5*Math.Sign(g.bufa[i,i]);
                        continue;
                    }
                    if( (double)(hqrnd.hqrnduniformr(g.rs))<=(double)(pnz) )
                    {
                        g.bufa[i,j] = hqrnd.hqrnduniformr(g.rs)-0.5;
                    }
                    else
                    {
                        g.bufa[i,j] = 0.0;
                    }
                }
            }
            
            //
            // Output matrix and RComm
            //
            da = new double[n, n];
            sparse.sparsecreate(n, n, (int)Math.Round(pnz*(n*n-n)+n), sa);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( (j<=i && g.triangle<=0) || (j>=i && g.triangle>=0) )
                    {
                        da[i,j] = g.bufa[i,j];
                        sparse.sparseset(sa, i, j, g.bufa[i,j]);
                    }
                    else
                    {
                        da[i,j] = 0.0;
                    }
                }
            }
            g.rcs.stage = 1;
            goto lbl_rcomm;
        lbl_1:
            
            //
            // Increase problem sparcity and try one more time. 
            // Stop after testing NZ=0.
            //
            if( nz==0 )
            {
                goto lbl_9;
            }
            nz = nz/2;
            goto lbl_8;
        lbl_9:
            result = false;
            return result;
        lbl_6:
            alglib.ap.assert(false);
            result = false;
            return result;
            
            //
            // Saving state
            //
        lbl_rcomm:
            result = true;
            g.rcs.ia[0] = n;
            g.rcs.ia[1] = m;
            g.rcs.ia[2] = nz;
            g.rcs.ia[3] = nzd;
            g.rcs.ia[4] = i;
            g.rcs.ia[5] = j;
            g.rcs.ra[0] = pnz;
            g.rcs.ra[1] = v;
            return result;
        }


        /*************************************************************************
        This function creates random sparse matrix with some prescribed pattern.

        INPUT PARAMETERS:
            M       -   number of rows
            N       -   number of columns
            PKind   -   sparsity pattern:
                        *-1 = pattern is chosen at random as well as P0/P1
                        * 0 = matrix with up to P0 non-zero elements at random locations
                              (however, actual number of non-zero elements can be
                              less than P0, and in fact can be zero)
                        * 1 = band matrix with P0 non-zero elements below diagonal
                              and P1 non-zero element above diagonal
                        * 2 = matrix with random number of contiguous non-zero 
                              elements in the each row
            CKind   -   creation type:
                        *-1 = CKind is chosen at random
                        * 0 = matrix is created in Hash-Table format and converted
                              to CRS representation
                        * 1 = matrix is created in CRS format

        OUTPUT PARAMETERS:
            DA      -   dense representation of A, array[M,N]
            SA      -   sparse representation of A, in CRS format

          -- ALGLIB PROJECT --
             Copyright 31.10.2011 by Bochkanov Sergey
        *************************************************************************/
        private static void createrandom(int m,
            int n,
            int pkind,
            int ckind,
            int p0,
            int p1,
            ref double[,] da,
            sparse.sparsematrix sa)
        {
            int maxpkind = 0;
            int maxckind = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            int[] c0 = new int[0];
            int[] c1 = new int[0];
            int[] rowsizes = new int[0];

            da = new double[0,0];

            maxpkind = 2;
            maxckind = 1;
            alglib.ap.assert(m>=1, "CreateRandom: incorrect parameters");
            alglib.ap.assert(n>=1, "CreateRandom: incorrect parameters");
            alglib.ap.assert(pkind>=-1 && pkind<=maxpkind, "CreateRandom: incorrect parameters");
            alglib.ap.assert(ckind>=-1 && ckind<=maxckind, "CreateRandom: incorrect parameters");
            if( pkind==-1 )
            {
                pkind = math.randominteger(maxpkind+1);
                if( pkind==0 )
                {
                    p0 = math.randominteger(m*n);
                }
                if( pkind==1 )
                {
                    p0 = math.randominteger(Math.Min(m, n));
                    p1 = math.randominteger(Math.Min(m, n));
                }
            }
            if( ckind==-1 )
            {
                ckind = math.randominteger(maxckind+1);
            }
            if( pkind==0 )
            {
                
                //
                // Matrix with elements at random locations
                //
                da = new double[m, n];
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        da[i,j] = 0;
                    }
                }
                if( ckind==0 )
                {
                    
                    //
                    // Create matrix in Hash format, convert to CRS
                    //
                    sparse.sparsecreate(m, n, 1, sa);
                    for(k=0; k<=p0-1; k++)
                    {
                        i = math.randominteger(m);
                        j = math.randominteger(n);
                        v = (double)(math.randominteger(17)-8)/(double)8;
                        if( (double)(math.randomreal())>(double)(0.5) )
                        {
                            da[i,j] = v;
                            sparse.sparseset(sa, i, j, v);
                        }
                        else
                        {
                            da[i,j] = da[i,j]+v;
                            sparse.sparseadd(sa, i, j, v);
                        }
                    }
                    sparse.sparseconverttocrs(sa);
                    return;
                }
                if( ckind==1 )
                {
                    
                    //
                    // Create matrix in CRS format
                    //
                    for(k=0; k<=p0-1; k++)
                    {
                        i = math.randominteger(m);
                        j = math.randominteger(n);
                        v = (double)(math.randominteger(17)-8)/(double)8;
                        da[i,j] = v;
                    }
                    rowsizes = new int[m];
                    for(i=0; i<=m-1; i++)
                    {
                        rowsizes[i] = 0;
                        for(j=0; j<=n-1; j++)
                        {
                            if( (double)(da[i,j])!=(double)(0) )
                            {
                                rowsizes[i] = rowsizes[i]+1;
                            }
                        }
                    }
                    sparse.sparsecreatecrs(m, n, rowsizes, sa);
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (double)(da[i,j])!=(double)(0) )
                            {
                                sparse.sparseset(sa, i, j, da[i,j]);
                            }
                        }
                    }
                    return;
                }
                alglib.ap.assert(false, "CreateRandom: internal error");
            }
            if( pkind==1 )
            {
                
                //
                // Band matrix
                //
                da = new double[m, n];
                rowsizes = new int[m];
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        da[i,j] = 0;
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=Math.Max(i-p0, 0); j<=Math.Min(i+p1, n-1); j++)
                    {
                        do
                        {
                            da[i,j] = (double)(math.randominteger(17)-8)/(double)8;
                        }
                        while( (double)(da[i,j])==(double)(0) );
                    }
                    rowsizes[i] = Math.Max(Math.Min(i+p1, n-1)-Math.Max(i-p0, 0)+1, 0);
                }
                if( ckind==0 )
                {
                    sparse.sparsecreate(m, n, 1, sa);
                }
                if( ckind==1 )
                {
                    sparse.sparsecreatecrs(m, n, rowsizes, sa);
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (double)(da[i,j])!=(double)(0) )
                        {
                            sparse.sparseset(sa, i, j, da[i,j]);
                        }
                    }
                }
                sparse.sparseconverttocrs(sa);
                return;
            }
            if( pkind==2 )
            {
                
                //
                // Matrix with one contiguous sequence of non-zero elements per row
                //
                da = new double[m, n];
                rowsizes = new int[m];
                c0 = new int[m];
                c1 = new int[m];
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        da[i,j] = 0;
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    c0[i] = math.randominteger(n);
                    c1[i] = c0[i]+math.randominteger(n-c0[i]+1);
                    rowsizes[i] = c1[i]-c0[i];
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=c0[i]; j<=c1[i]-1; j++)
                    {
                        do
                        {
                            da[i,j] = (double)(math.randominteger(17)-8)/(double)8;
                        }
                        while( (double)(da[i,j])==(double)(0) );
                    }
                }
                if( ckind==0 )
                {
                    sparse.sparsecreate(m, n, 1, sa);
                }
                if( ckind==1 )
                {
                    sparse.sparsecreatecrs(m, n, rowsizes, sa);
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (double)(da[i,j])!=(double)(0) )
                        {
                            sparse.sparseset(sa, i, j, da[i,j]);
                        }
                    }
                }
                sparse.sparseconverttocrs(sa);
                return;
            }
        }


        /*************************************************************************
        This function does test for SparseEnumerate function.

          -- ALGLIB PROJECT --
             Copyright 14.03.2012 by Bochkanov Sergey
        *************************************************************************/
        private static bool enumeratetest()
        {
            bool result = new bool();
            sparse.sparsematrix spa = new sparse.sparsematrix();
            double[,] a = new double[0,0];
            bool[,] ta = new bool[0,0];
            int m = 0;
            int n = 0;
            double r = 0;
            double v = 0;
            int ne = 0;
            int t0 = 0;
            int t1 = 0;
            int counter = 0;
            int c = 0;
            int hashcrs = 0;
            int i = 0;
            int j = 0;

            r = 10.5;
            for(m=1; m<=30; m++)
            {
                for(n=1; n<=30; n++)
                {
                    ne = 0;
                    
                    //
                    // Create matrix with non-zero elements inside the region:
                    // 0<=I<S.M and 0<=J<S.N
                    //
                    a = new double[m, n];
                    ta = new bool[m, n];
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0;
                            ta[i,j] = false;
                        }
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            c = math.randominteger(2);
                            if( c==0 )
                            {
                                a[i,j] = 0;
                            }
                            else
                            {
                                a[i,j] = r*(2*math.randomreal()-1);
                                
                                //
                                // Number of non-zero elements
                                //
                                ne = ne+1;
                            }
                        }
                    }
                    for(hashcrs=0; hashcrs<=1; hashcrs++)
                    {
                        sparse.sparsecreate(m, n, m*n, spa);
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                sparse.sparseset(spa, i, j, a[i,j]);
                            }
                        }
                        if( hashcrs==1 )
                        {
                            sparse.sparseconverttocrs(spa);
                        }
                        t0 = 0;
                        t1 = 0;
                        counter = 0;
                        while( sparse.sparseenumerate(spa, ref t0, ref t1, ref i, ref j, ref v) )
                        {
                            ta[i,j] = true;
                            counter = counter+1;
                            if( (double)(v)!=(double)(a[i,j]) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        
                        //
                        // Check that all non-zero elements was enumerated
                        //
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( ta[i,j] && (double)(a[i,j])==(double)(0) )
                                {
                                    result = true;
                                    return result;
                                }
                            }
                        }
                        if( ne!=counter )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function does test for SparseRewriteExisting function.

          -- ALGLIB PROJECT --
             Copyright 14.03.2012 by Bochkanov Sergey
        *************************************************************************/
        private static bool rewriteexistingtest()
        {
            bool result = new bool();
            sparse.sparsematrix spa = new sparse.sparsematrix();
            double spaval = 0;
            double[,] a = new double[0,0];
            bool[,] ta = new bool[0,0];
            int m = 0;
            int n = 0;
            int c = 0;
            int ne = 0;
            int nr = 0;
            double r = 0;
            double v = 0;
            int hashcrs = 0;
            int i = 0;
            int j = 0;

            r = 20.0;
            for(m=1; m<=30; m++)
            {
                for(n=1; n<=30; n++)
                {
                    a = new double[m, n];
                    ta = new bool[m, n];
                    for(hashcrs=0; hashcrs<=1; hashcrs++)
                    {
                        v = r*(2*math.randomreal()-1);
                        
                        //
                        // Creating and filling of the matrix
                        //
                        ne = 0;
                        sparse.sparsecreate(m, n, m*n, spa);
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                c = math.randominteger(2);
                                if( c==0 )
                                {
                                    a[i,j] = 0;
                                }
                                if( c==1 )
                                {
                                    do
                                    {
                                        a[i,j] = r*(2*math.randomreal()-1);
                                    }
                                    while( (double)(a[i,j])==(double)(0) );
                                    sparse.sparseset(spa, i, j, a[i,j]);
                                    ne = ne+1;
                                }
                                ta[i,j] = false;
                            }
                        }
                        if( hashcrs==1 )
                        {
                            sparse.sparseconverttocrs(spa);
                        }
                        
                        //
                        // Rewrite some elements
                        //
                        nr = 0;
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                c = math.randominteger(2);
                                if( c==1 )
                                {
                                    ta[i,j] = sparse.sparserewriteexisting(spa, i, j, v);
                                    if( ta[i,j] )
                                    {
                                        a[i,j] = v;
                                        nr = nr+1;
                                    }
                                }
                            }
                        }
                        
                        //
                        // Now we have to be sure, that all changes had made correctly
                        //
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( ta[i,j] )
                                {
                                    spaval = sparse.sparseget(spa, i, j);
                                    nr = nr-1;
                                    if( (double)(spaval)!=(double)(v) || (double)(spaval)!=(double)(a[i,j]) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                        if( nr!=0 )
                        {
                            result = true;
                            return result;
                        }
                        
                        //
                        // Rewrite all elements
                        //
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ta[i,j] = sparse.sparserewriteexisting(spa, i, j, v);
                                if( ta[i,j] )
                                {
                                    a[i,j] = v;
                                }
                            }
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( ta[i,j] )
                                {
                                    ne = ne-1;
                                }
                            }
                        }
                        if( ne!=0 )
                        {
                            result = true;
                            return result;
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                spaval = sparse.sparseget(spa, i, j);
                                if( ta[i,j] )
                                {
                                    if( (double)(spaval)!=(double)(v) || (double)(spaval)!=(double)(a[i,j]) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                                else
                                {
                                    if( (double)(spaval)!=(double)(0) || (double)(spaval)!=(double)(a[i,j]) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Test  for  SparseGetRow/GetCompressedRow  function.   It  creates  random
        dense and sparse matrices;  then  get every  row from  sparse matrix  and
        compares it with every row in dense matrix.

        On failure sets error flag, on success leaves it unchanged.

          -- ALGLIB PROJECT --
             Copyright 23.07.2012 by Bochkanov Sergey
        *************************************************************************/
        private static void testgetrow(ref bool err)
        {
            sparse.sparsematrix s = new sparse.sparsematrix();
            double[,] a = new double[0,0];
            int m = 0;
            int n = 0;
            int msize = 0;
            int nsize = 0;
            int nz = 0;
            double[] vals = new double[0];
            double[] mrow = new double[0];
            int[] colidx = new int[0];
            bool[] wasreturned = new bool[0];
            int mtype = 0;
            int i = 0;
            int j = 0;

            msize = 15;
            nsize = 15;
            for(mtype=1; mtype<=2; mtype++)
            {
                for(m=1; m<=msize; m++)
                {
                    for(n=1; n<=nsize; n++)
                    {
                        
                        //
                        // Skip nonrectangular SKS matrices - not supported
                        //
                        if( mtype==2 && m!=n )
                        {
                            continue;
                        }
                        
                        //
                        // Create "reference" and sparse matrices
                        //
                        a = new double[m, n];
                        sparse.sparsecreate(m, n, 1, s);
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( math.randominteger(5)==3 )
                                {
                                    a[i,j] = 2*math.randomreal()-1;
                                    sparse.sparseset(s, i, j, a[i,j]);
                                }
                                else
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        
                        //
                        // Choose matrix type to test
                        //
                        if( mtype==1 )
                        {
                            sparse.sparseconverttocrs(s);
                        }
                        else
                        {
                            sparse.sparseconverttosks(s);
                        }
                        
                        //
                        // Test SparseGetRow()
                        //
                        for(i=0; i<=m-1; i++)
                        {
                            sparse.sparsegetrow(s, i, ref mrow);
                            for(j=0; j<=n-1; j++)
                            {
                                if( (double)(mrow[j])!=(double)(a[i,j]) || (double)(mrow[j])!=(double)(sparse.sparseget(s, i, j)) )
                                {
                                    apserv.seterrorflag(ref err, true);
                                    return;
                                }
                            }
                        }
                        
                        //
                        // Test SparseGetCompressedRow()
                        //
                        wasreturned = new bool[n];
                        for(i=0; i<=m-1; i++)
                        {
                            sparse.sparsegetcompressedrow(s, i, ref colidx, ref vals, ref nz);
                            if( nz<0 || nz>n )
                            {
                                apserv.seterrorflag(ref err, true);
                                return;
                            }
                            for(j=0; j<=n-1; j++)
                            {
                                wasreturned[j] = false;
                            }
                            for(j=0; j<=nz-1; j++)
                            {
                                if( colidx[j]<0 || colidx[j]>n )
                                {
                                    apserv.seterrorflag(ref err, true);
                                    return;
                                }
                                apserv.seterrorflag(ref err, j>0 && colidx[j]<=colidx[j-1]);
                                apserv.seterrorflag(ref err, (double)(vals[j])!=(double)(a[i,colidx[j]]) || (double)(vals[j])!=(double)(sparse.sparseget(s, i, colidx[j])));
                                wasreturned[colidx[j]] = true;
                            }
                            for(j=0; j<=n-1; j++)
                            {
                                apserv.seterrorflag(ref err, (double)(a[i,j])!=(double)(0) && !wasreturned[j]);
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Test for SparseConvert functions(isn't tested ConvertToCRS function). The
        function  create random  dense and sparse  matrices  in CRS  format. Then
        convert  sparse matrix  to some  format  by CONVERT_TO/COPY_TO  functions,
        then it does  some modification in matrices and compares that marices are
        identical.

        NOTE:
            Result of the function assigned to variable CopyErrors in unit test.

          -- ALGLIB PROJECT --
             Copyright 23.07.2012 by Bochkanov Sergey
        *************************************************************************/
        private static bool testconvertsm()
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            sparse.sparsematrix cs = new sparse.sparsematrix();
            double[,] a = new double[0,0];
            int m = 0;
            int n = 0;
            int msize = 0;
            int nsize = 0;
            int[] ner = new int[0];
            double tmp = 0;
            int i = 0;
            int j = 0;
            int vartf = 0;

            msize = 15;
            nsize = 15;
            for(m=1; m<=msize; m++)
            {
                for(n=1; n<=nsize; n++)
                {
                    for(vartf=0; vartf<=2; vartf++)
                    {
                        a = new double[m, n];
                        ner = new int[m];
                        for(i=0; i<=m-1; i++)
                        {
                            ner[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                if( math.randominteger(5)==3 )
                                {
                                    ner[i] = ner[i]+1;
                                    a[i,j] = 2*math.randomreal()-1;
                                }
                                else
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        
                        //
                        // Create sparse matrix
                        //
                        sparse.sparsecreatecrs(m, n, ner, s);
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (double)(a[i,j])!=(double)(0) )
                                {
                                    a[i,j] = 2*math.randomreal()-1;
                                    sparse.sparseset(s, i, j, a[i,j]);
                                }
                            }
                        }
                        
                        //
                        // Set matrix type(we have to be sure that all formats
                        // converted correctly)
                        //
                        i = math.randominteger(2);
                        if( i==0 )
                        {
                            sparse.sparseconverttohash(s);
                        }
                        if( i==1 )
                        {
                            sparse.sparseconverttocrs(s);
                        }
                        
                        //
                        // Start test
                        //
                        if( vartf==0 )
                        {
                            sparse.sparseconverttohash(s);
                            sparse.sparsecopy(s, cs);
                        }
                        if( vartf==1 )
                        {
                            sparse.sparsecopytohash(s, cs);
                        }
                        if( vartf==2 )
                        {
                            sparse.sparsecopytocrs(s, cs);
                        }
                        
                        //
                        // Change some elements in row
                        //
                        if( vartf!=2 )
                        {
                            for(i=0; i<=m-1; i++)
                            {
                                tmp = 2*math.randomreal()-1;
                                j = math.randominteger(n);
                                a[i,j] = tmp;
                                sparse.sparseset(cs, i, j, tmp);
                                tmp = 2*math.randomreal()-1;
                                j = math.randominteger(n);
                                a[i,j] = a[i,j]+tmp;
                                sparse.sparseadd(cs, i, j, tmp);
                            }
                        }
                        
                        //
                        // Check that A is identical to S
                        //
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( (double)(a[i,j])!=(double)(sparse.sparseget(cs, i, j)) )
                                {
                                    result = true;
                                    return result;
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Test for  check/get  type functions.  The function  create sparse matrix,
        converts it to desired type then check this type.

        NOTE:
            Result of the function assigned to variable BasicErrors in unit test.

          -- ALGLIB PROJECT --
             Copyright 23.07.2012 by Bochkanov Sergey
        *************************************************************************/
        private static bool testgcmatrixtype()
        {
            bool result = new bool();
            sparse.sparsematrix s = new sparse.sparsematrix();
            sparse.sparsematrix cs = new sparse.sparsematrix();
            int m = 0;
            int n = 0;
            int msize = 0;
            int nsize = 0;

            msize = 5;
            nsize = 5;
            for(m=1; m<=msize; m++)
            {
                for(n=1; n<=nsize; n++)
                {
                    sparse.sparsecreate(m, n, 1, s);
                    sparse.sparseconverttocrs(s);
                    if( (sparse.sparseishash(s) || !sparse.sparseiscrs(s)) || sparse.sparsegetmatrixtype(s)!=1 )
                    {
                        result = true;
                        return result;
                    }
                    sparse.sparseconverttohash(s);
                    if( (!sparse.sparseishash(s) || sparse.sparseiscrs(s)) || sparse.sparsegetmatrixtype(s)!=0 )
                    {
                        result = true;
                        return result;
                    }
                    sparse.sparsecopytocrs(s, cs);
                    if( (sparse.sparseishash(cs) || !sparse.sparseiscrs(cs)) || sparse.sparsegetmatrixtype(cs)!=1 )
                    {
                        result = true;
                        return result;
                    }
                    sparse.sparsecopytohash(cs, s);
                    if( (!sparse.sparseishash(s) || sparse.sparseiscrs(s)) || sparse.sparsegetmatrixtype(s)!=0 )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            result = false;
            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 largemn = 0;
            int i = 0;
            int j = 0;
            complex vc = 0;
            double vr = 0;
            bool waserrors = new bool();
            bool dspderr = new bool();
            bool sspderr = new bool();
            bool hpderr = new bool();
            bool rerr = new bool();
            bool cerr = new bool();
            bool properr = new bool();
            bool dspdupderr = new bool();
            double threshold = 0;
            int i_ = 0;

            rerr = false;
            dspderr = false;
            sspderr = false;
            cerr = false;
            hpderr = false;
            properr = false;
            dspdupderr = false;
            waserrors = false;
            maxmn = 4*ablas.ablasblocksize(ra)+1;
            largemn = 256;
            threshold = 1000*math.machineepsilon*maxmn;
            
            //
            // Sparse Cholesky
            //
            sspderr = sparserealcholeskytest();
            
            //
            // Cholesky updates
            //
            testdensecholeskyupdates(ref dspdupderr);
            
            //
            // test LU:
            // * first, test on small-scale matrices
            // * then, perform several large-scale tests
            //
            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);
            }
            for(m=largemn-1; m<=largemn+1; m++)
            {
                for(n=largemn-1; n<=largemn+1; n++)
                {
                    
                    //
                    // 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
                //
                // Test SPDMatrixCholesky:
                // 1. it must leave upper (lower) part unchanged
                // 2. max(A-L*L^H) must be small
                //
                // After testing SPDMatrixCholesky() we compare results
                // returned by SparseCholeskyX() against ones returned
                // by SPDMatrixCholesky().
                //
                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_];
                    }
                }
                if( trfac.spdmatrixcholesky(ref ral, n, false) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( j>i )
                            {
                                dspderr = dspderr || (double)(ral[i,j])!=(double)(i);
                            }
                            else
                            {
                                vr = 0.0;
                                for(i_=0; i_<=j;i_++)
                                {
                                    vr += ral[i,i_]*ral[j,i_];
                                }
                                dspderr = dspderr || (double)(Math.Abs(ra[i,j]-vr))>(double)(threshold);
                            }
                        }
                    }
                }
                else
                {
                    dspderr = 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 )
                            {
                                dspderr = dspderr || (double)(rau[i,j])!=(double)(j);
                            }
                            else
                            {
                                vr = 0.0;
                                for(i_=0; i_<=i;i_++)
                                {
                                    vr += rau[i_,i]*rau[i_,j];
                                }
                                dspderr = dspderr || (double)(Math.Abs(ra[i,j]-vr))>(double)(threshold);
                            }
                        }
                    }
                }
                else
                {
                    dspderr = true;
                }
                
                //
                // Check algorithms on negative definite matrices -
                // correct error code must be returned.
                //
                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.0;
                        ca[i,j] = 0.0;
                    }
                    ra[i,i] = 1.0;
                    ca[i,i] = 1.0;
                }
                ra[n/2,n/2] = -1.0;
                ca[n/2,n/2] = -1.0;
                apserv.seterrorflag(ref dspderr, trfac.spdmatrixcholesky(ref ra, n, (double)(math.randomreal())>(double)(0.5)));
                apserv.seterrorflag(ref hpderr, trfac.hpdmatrixcholesky(ref ca, n, (double)(math.randomreal())>(double)(0.5)));
            }
            
            //
            // report
            //
            waserrors = (((((rerr || dspderr) || sspderr) || cerr) || hpderr) || properr) || dspdupderr;
            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 (dense)                            ");
                if( dspderr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* SPD (sparse)                           ");
                if( sspderr )
                {
                    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();
                }
                System.Console.Write("TESTING UPDATED FACTORIZATIONS");
                System.Console.WriteLine();
                System.Console.Write("* SPD (dense)                            ");
                if( dspdupderr )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testtrfac(bool silent)
        {
            return testtrfac(silent);
        }


        /*************************************************************************
        Function for testing sparse real Cholesky.
        Returns True on errors, False on success.

          -- ALGLIB PROJECT --
             Copyright 16.01.1014 by Bochkanov Sergey
        *************************************************************************/
        public static bool sparserealcholeskytest()
        {
            bool result = new bool();
            int n = 0;
            int nz = 0;
            double pnz = 0;
            double[,] a = new double[0,0];
            double[,] a1 = new double[0,0];
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            int t0 = 0;
            int t1 = 0;
            bool isupper = new bool();
            double offscale = 0;
            double tol = 0;
            sparse.sparsematrix sa = new sparse.sparsematrix();
            sparse.sparsematrix sa1 = new sparse.sparsematrix();
            sparse.sparsematrix sc = new sparse.sparsematrix();
            sparse.sparsebuffers sbuf = new sparse.sparsebuffers();
            int[] p0 = new int[0];
            int[] p1 = new int[0];
            bool[] b1 = new bool[0];
            int cfmt = 0;
            int cord = 0;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int maxfmt = 0;
            int maxord = 0;
            int minord = 0;

            result = false;
            hqrnd.hqrndrandomize(rs);
            
            //
            // Settings
            //
            maxfmt = 2;
            maxord = 0;
            minord = -2;
            offscale = 1.0E-3;
            tol = 1.0E-8;
            
            //
            // SparseCholeskyX test: performed for matrices
            // of all sizes in 1..20 and all sparcity percentages.
            //
            for(n=1; n<=20; n++)
            {
                nz = n*n-n;
                while( true )
                {
                    
                    //
                    // Generate symmetric N*N matrix where probability of non-diagonal element
                    // being non-zero is PNZ. Off-diagonal elements are set to very
                    // small values, so positive definiteness is guaranteed.
                    //
                    if( n>1 )
                    {
                        pnz = (double)nz/(double)(n*n-n);
                    }
                    else
                    {
                        pnz = 1.0;
                    }
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=i; j++)
                        {
                            if( i==j )
                            {
                                a[i,i] = 1+hqrnd.hqrnduniformr(rs);
                                continue;
                            }
                            if( (double)(hqrnd.hqrnduniformr(rs))<=(double)(pnz) )
                            {
                                a[i,j] = offscale*(hqrnd.hqrnduniformr(rs)-0.5);
                                a[j,i] = a[i,j];
                            }
                            else
                            {
                                a[i,j] = 0.0;
                                a[j,i] = 0.0;
                            }
                        }
                    }
                    
                    //
                    // Problem statement
                    //
                    isupper = (double)(math.randomreal())>(double)(0.5);
                    cfmt = math.randominteger(maxfmt+1);
                    cord = math.randominteger(maxord+1-minord)+minord;
                    
                    //
                    // Create matrix is hash-based storage format, convert it to random storage format.
                    //
                    sparse.sparsecreate(n, n, 0, sa);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (j<=i && !isupper) || (j>=i && isupper) )
                            {
                                sparse.sparseset(sa, i, j, a[i,j]);
                            }
                        }
                    }
                    sparse.sparseconvertto(sa, hqrnd.hqrnduniformi(rs, maxfmt+1));
                    
                    //
                    // Perform sparse Cholesky and make several tests:
                    // * correctness of P0 and P1 (they are correct permutations and one is inverse of another)
                    // * format of SC matches CFmt
                    // * SC has correct size (exactly N*N)
                    // * check that correct triangle is returned
                    //
                    if( !trfac.sparsecholeskyx(sa, n, isupper, ref p0, ref p1, cord, math.randominteger(3), cfmt, sbuf, sc) )
                    {
                        apserv.seterrorflag(ref result, true);
                        return result;
                    }
                    apserv.seterrorflag(ref result, alglib.ap.len(p0)<n);
                    apserv.seterrorflag(ref result, alglib.ap.len(p1)<n);
                    if( result )
                    {
                        return result;
                    }
                    b1 = new bool[n];
                    for(i=0; i<=n-1; i++)
                    {
                        b1[i] = false;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref result, p0[i]<0);
                        apserv.seterrorflag(ref result, p1[i]<0);
                        apserv.seterrorflag(ref result, p0[i]>=n);
                        apserv.seterrorflag(ref result, p1[i]>=n);
                        if( result )
                        {
                            return result;
                        }
                        apserv.seterrorflag(ref result, b1[p0[i]]);
                        b1[p0[i]] = true;
                        apserv.seterrorflag(ref result, p1[p0[i]]!=i);
                    }
                    apserv.seterrorflag(ref result, sparse.sparsegetmatrixtype(sc)!=cfmt);
                    apserv.seterrorflag(ref result, sparse.sparsegetncols(sc)!=n);
                    apserv.seterrorflag(ref result, sparse.sparsegetnrows(sc)!=n);
                    t0 = 0;
                    t1 = 0;
                    while( sparse.sparseenumerate(sc, ref t0, ref t1, ref i, ref j, ref v) )
                    {
                        apserv.seterrorflag(ref result, j<i && isupper);
                        apserv.seterrorflag(ref result, j>i && !isupper);
                    }
                    
                    //
                    // Now, test correctness of Cholesky decomposition itself.
                    // We calculate U'*U (or L*L') and check at against permutation
                    // of A given by P0.
                    //
                    // NOTE: we expect that only one triangle of SC is filled,
                    //       and another one is exactly zero.
                    //
                    if( isupper )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(k=0; k<=n-1; k++)
                                {
                                    v = v+sparse.sparseget(sc, k, j)*sparse.sparseget(sc, k, i);
                                }
                                apserv.seterrorflag(ref result, (double)(Math.Abs(a[p0[i],p0[j]]-v))>(double)(tol));
                            }
                        }
                    }
                    else
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(k=0; k<=n-1; k++)
                                {
                                    v = v+sparse.sparseget(sc, j, k)*sparse.sparseget(sc, i, k);
                                }
                                apserv.seterrorflag(ref result, (double)(Math.Abs(a[p0[i],p0[j]]-v))>(double)(tol));
                            }
                        }
                    }
                    
                    //
                    // Increase problem sparcity and try one more time. 
                    // Stop after testing NZ=0.
                    //
                    if( nz==0 )
                    {
                        break;
                    }
                    nz = nz/2;
                }
            }
            
            //
            // SparseCholeskySkyline test: performed for matrices
            // of all sizes in 1..20 and all sparcity percentages.
            //
            for(n=1; n<=20; n++)
            {
                nz = n*n-n;
                while( true )
                {
                    
                    //
                    // Choose IsUpper - main triangle to work with.
                    //
                    // Generate A - symmetric N*N matrix where probability of non-diagonal
                    // element being non-zero is PNZ. Off-diagonal elements are set to
                    // very small values, so positive definiteness is guaranteed. Full matrix
                    // is generated.
                    //
                    // Additionally, we create A1 - same as A, but one of the triangles is
                    // asymmetrically spoiled. If IsUpper is True, we spoil lower one, or vice versa.
                    //
                    isupper = (double)(math.randomreal())>(double)(0.5);
                    if( n>1 )
                    {
                        pnz = (double)nz/(double)(n*n-n);
                    }
                    else
                    {
                        pnz = 1.0;
                    }
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=i; j++)
                        {
                            if( i==j )
                            {
                                a[i,i] = 1+hqrnd.hqrnduniformr(rs);
                                continue;
                            }
                            if( (double)(hqrnd.hqrnduniformr(rs))<=(double)(pnz) )
                            {
                                a[i,j] = offscale*(hqrnd.hqrnduniformr(rs)-0.5);
                                a[j,i] = a[i,j];
                            }
                            else
                            {
                                a[i,j] = 0.0;
                                a[j,i] = 0.0;
                            }
                        }
                    }
                    a1 = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (j<=i && !isupper) || (j>=i && isupper) )
                            {
                                
                                //
                                // Copy one triangle
                                //
                                a1[i,j] = a[i,j];
                            }
                            else
                            {
                                
                                //
                                // Form another sparse pattern in different triangle.
                                //
                                if( (double)(hqrnd.hqrnduniformr(rs))<=(double)(pnz) )
                                {
                                    a1[i,j] = offscale*(hqrnd.hqrnduniformr(rs)-0.5);
                                }
                                else
                                {
                                    a1[i,j] = 0.0;
                                }
                            }
                        }
                    }
                    
                    //
                    // Create copies of A and A1 in hash-based storage format.
                    // Only one triangle of A is copied, but A1 is copied fully.
                    // Convert them to SKS
                    //
                    sparse.sparsecreate(n, n, 0, sa);
                    sparse.sparsecreate(n, n, 0, sa1);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (j<=i && !isupper) || (j>=i && isupper) )
                            {
                                sparse.sparseset(sa, i, j, a[i,j]);
                            }
                            sparse.sparseset(sa1, i, j, a1[i,j]);
                        }
                    }
                    sparse.sparseconverttosks(sa);
                    sparse.sparseconverttosks(sa1);
                    
                    //
                    // Call SparseCholeskySkyline() for SA and make several tests:
                    // * check that it is still SKS
                    // * check that it has correct size (exactly N*N)
                    // * check that correct triangle is returned (and another one is unchanged - zero)
                    // * check that it is correct Cholesky decomposition.
                    //   We calculate U'*U (or L*L') and check at against A. We expect
                    //   that only one triangle of SA is filled, and another one is
                    //   exactly zero.
                    //
                    if( !trfac.sparsecholeskyskyline(sa, n, isupper) )
                    {
                        apserv.seterrorflag(ref result, true);
                        return result;
                    }
                    apserv.seterrorflag(ref result, !sparse.sparseissks(sa));
                    apserv.seterrorflag(ref result, sparse.sparsegetncols(sa)!=n);
                    apserv.seterrorflag(ref result, sparse.sparsegetnrows(sa)!=n);
                    t0 = 0;
                    t1 = 0;
                    while( sparse.sparseenumerate(sa, ref t0, ref t1, ref i, ref j, ref v) )
                    {
                        apserv.seterrorflag(ref result, j<i && isupper);
                        apserv.seterrorflag(ref result, j>i && !isupper);
                    }
                    if( isupper )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(k=0; k<=n-1; k++)
                                {
                                    v = v+sparse.sparseget(sa, k, j)*sparse.sparseget(sa, k, i);
                                }
                                apserv.seterrorflag(ref result, (double)(Math.Abs(a[i,j]-v))>(double)(tol));
                            }
                        }
                    }
                    else
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(k=0; k<=n-1; k++)
                                {
                                    v = v+sparse.sparseget(sa, j, k)*sparse.sparseget(sa, i, k);
                                }
                                apserv.seterrorflag(ref result, (double)(Math.Abs(a[i,j]-v))>(double)(tol));
                            }
                        }
                    }
                    
                    //
                    // Call SparseCholeskySkyline() for SA1 and make several tests:
                    // * check that it is still SKS
                    // * check that it has correct size (exactly N*N)
                    // * check that factorized triangle matches contents of SA,
                    //   and another triangle was unchanged (matches contents of A1).
                    //
                    if( !trfac.sparsecholeskyskyline(sa1, n, isupper) )
                    {
                        apserv.seterrorflag(ref result, true);
                        return result;
                    }
                    apserv.seterrorflag(ref result, !sparse.sparseissks(sa1));
                    apserv.seterrorflag(ref result, sparse.sparsegetncols(sa1)!=n);
                    apserv.seterrorflag(ref result, sparse.sparsegetnrows(sa1)!=n);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (j<=i && !isupper) || (j>=i && isupper) )
                            {
                                apserv.seterrorflag(ref result, (double)(Math.Abs(sparse.sparseget(sa1, i, j)-sparse.sparseget(sa, i, j)))>(double)(10*math.machineepsilon));
                            }
                            else
                            {
                                apserv.seterrorflag(ref result, (double)(Math.Abs(sparse.sparseget(sa1, i, j)-a1[i,j]))>(double)(10*math.machineepsilon));
                            }
                        }
                    }
                    
                    //
                    // Increase problem sparcity and try one more time. 
                    // Stop after testing NZ=0.
                    //
                    if( nz==0 )
                    {
                        break;
                    }
                    nz = nz/2;
                }
            }
            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 = true;
                    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 = true;
                    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 = true;
                    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 = true;
                    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);
                }
            }
        }


        /*************************************************************************
        Function for testing dense Cholesky updates
        Sets error flag to True on errors, does not change it on success.

          -- ALGLIB PROJECT --
             Copyright 16.01.1014 by Bochkanov Sergey
        *************************************************************************/
        private static void testdensecholeskyupdates(ref bool spdupderrorflag)
        {
            int n = 0;
            double pfix = 0;
            double[,] a0 = new double[0,0];
            double[,] a1 = new double[0,0];
            double[] u = new double[0];
            bool[] fix = new bool[0];
            int i = 0;
            int j = 0;
            bool isupper = new bool();
            double tol = 0;
            double[] bufr = new double[0];
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();

            hqrnd.hqrndrandomize(rs);
            
            //
            // Settings
            //
            tol = 1.0E-8;
            
            //
            // Test rank-1 updates
            //
            // For each matrix size in 1..30 select sparse update vector with probability of element
            // being non-zero equal to 1/2.
            //
            for(n=1; n<=30; n++)
            {
                
                //
                // Generate two matrices A0=A1, fill one triangle with SPD matrix,
                // another one with trash. Prepare vector U.
                //
                isupper = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                matgen.spdmatrixrndcond(n, 1.0E4, ref a0);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i && isupper) || (j>i && !isupper) )
                        {
                            a0[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                        }
                    }
                }
                a1 = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a1[i,j] = a0[i,j];
                    }
                }
                u = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    if( (double)(hqrnd.hqrnduniformr(rs))<=(double)(0.5) )
                    {
                        u[i] = hqrnd.hqrnduniformr(rs)-0.5;
                    }
                    else
                    {
                        u[i] = 0;
                    }
                }
                
                //
                // Factorize and compare:
                // * A0 is factorized as follows: first with full Cholesky, then
                //   we call SPDMatrixCholeskyUpdateAdd1
                // * A1 is transformed explicitly before factorization with full Cholesky
                //
                // We randomly test either SPDMatrixCholeskyUpdateFix() or its
                // buffered version, SPDMatrixCholeskyUpdateFixBuf()
                //
                apserv.seterrorflag(ref spdupderrorflag, !trfac.spdmatrixcholesky(ref a0, n, isupper));
                if( spdupderrorflag )
                {
                    return;
                }
                if( (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5) )
                {
                    trfac.spdmatrixcholeskyupdateadd1(a0, n, isupper, u);
                }
                else
                {
                    trfac.spdmatrixcholeskyupdateadd1buf(a0, n, isupper, u, ref bufr);
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j>=i && isupper) || (j<=i && !isupper) )
                        {
                            a1[i,j] = a1[i,j]+u[i]*u[j];
                        }
                    }
                }
                apserv.seterrorflag(ref spdupderrorflag, !trfac.spdmatrixcholesky(ref a1, n, isupper));
                if( spdupderrorflag )
                {
                    return;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        apserv.seterrorflag(ref spdupderrorflag, (double)(Math.Abs(a0[i,j]-a1[i,j]))>(double)(tol));
                    }
                }
            }
            
            //
            // Test variable fixing functions.
            //
            // For each matrix size in 1..30 select PFix - probability of each variable being fixed,
            // and perform test.
            //
            for(n=1; n<=30; n++)
            {
                
                //
                // Generate two matrices A0=A1, fill one triangle with SPD matrix,
                // another one with trash. Prepare vector Fix.
                //
                pfix = (double)hqrnd.hqrnduniformi(rs, n+1)/(double)n;
                isupper = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                matgen.spdmatrixrndcond(n, 1.0E4, ref a0);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i && isupper) || (j>i && !isupper) )
                        {
                            a0[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                        }
                    }
                }
                a1 = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a1[i,j] = a0[i,j];
                    }
                }
                fix = new bool[n];
                for(i=0; i<=n-1; i++)
                {
                    fix[i] = (double)(hqrnd.hqrnduniformr(rs))<=(double)(pfix);
                }
                
                //
                // Factorize and compare:
                // * A0 is factorized as follows: first with full Cholesky, then
                //   variables are fixed with SPDMatrixCholeskyUpdateFix
                // * A1 is fixed explicitly before factorization with full Cholesky
                //
                // We randomly test either SPDMatrixCholeskyUpdateFix() or its
                // buffered version, SPDMatrixCholeskyUpdateFixBuf()
                //
                apserv.seterrorflag(ref spdupderrorflag, !trfac.spdmatrixcholesky(ref a0, n, isupper));
                if( spdupderrorflag )
                {
                    return;
                }
                if( (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5) )
                {
                    trfac.spdmatrixcholeskyupdatefixbuf(a0, n, isupper, fix, ref bufr);
                }
                else
                {
                    trfac.spdmatrixcholeskyupdatefix(a0, n, isupper, fix);
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j>=i && isupper) || (j<=i && !isupper) )
                        {
                            if( fix[i] || fix[j] )
                            {
                                if( i==j )
                                {
                                    a1[i,j] = 1;
                                }
                                else
                                {
                                    a1[i,j] = 0;
                                }
                            }
                        }
                    }
                }
                apserv.seterrorflag(ref spdupderrorflag, !trfac.spdmatrixcholesky(ref a1, n, isupper));
                if( spdupderrorflag )
                {
                    return;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        apserv.seterrorflag(ref spdupderrorflag, (double)(Math.Abs(a0[i,j]-a1[i,j]))>(double)(tol));
                    }
                }
            }
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testtrlinsolve(bool silent)
        {
            return testtrlinsolve(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testsafesolve(bool silent)
        {
            return testsafesolve(silent);
        }


        /*************************************************************************
        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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testrcond(bool silent)
        {
            return testrcond(silent);
        }


        /*************************************************************************
        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 largen = 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;
            largen = 256;
            passcount = 1;
            threshold = 10000*math.machineepsilon;
            rcondtol = 0.01;
            rtrerrors = false;
            ctrerrors = false;
            rerrors = false;
            cerrors = false;
            spderrors = false;
            hpderrors = false;
            testrtrinv(1, maxrn, passcount, threshold, ref rtrerrors);
            testctrinv(1, maxcn, passcount, threshold, ref ctrerrors);
            testrinv(1, maxrn, passcount, threshold, ref rerrors);
            testspdinv(1, maxrn, passcount, threshold, ref spderrors);
            testcinv(1, maxcn, passcount, threshold, ref cerrors);
            testhpdinv(1, maxcn, passcount, threshold, ref hpderrors);
            testrtrinv(largen, largen, passcount, threshold, ref rtrerrors);
            testctrinv(largen, largen, passcount, threshold, ref ctrerrors);
            testrinv(largen, largen, passcount, threshold, ref rerrors);
            testspdinv(largen, largen, passcount, threshold, ref spderrors);
            testcinv(largen, largen, passcount, threshold, ref cerrors);
            testhpdinv(largen, largen, 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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testmatinv(bool silent)
        {
            return testmatinv(silent);
        }


        /*************************************************************************
        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 minn,
            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=minn; 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 minn,
            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=minn; 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 minn,
            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=minn; 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 minn,
            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=minn; 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 minn,
            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=minn; 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 minn,
            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=minn; 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 matrix
        *************************************************************************/
        private static void cunset2d(ref complex[,] x)
        {
            x = new complex[1, 1];
            x[0,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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testlda(bool silent)
        {
            return testlda(silent);
        }


        /*************************************************************************
        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];

            alglib.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];

            alglib.ap.assert(axis>=0 && axis<nfeatures, "GenDeg1Set: wrong Axis!");
            alglib.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 testmlpbaseunit
    {
        public static bool testmlpbase(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            int passcount = 0;
            int maxn = 0;
            int maxhid = 0;
            int sizemin = 0;
            int sizemax = 0;
            int nf = 0;
            int nl = 0;
            int nhid1 = 0;
            int nhid2 = 0;
            int nkind = 0;
            mlpbase.multilayerperceptron network = new mlpbase.multilayerperceptron();
            mlpbase.multilayerperceptron network2 = new mlpbase.multilayerperceptron();
            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 errerrors = new bool();

            waserrors = false;
            inferrors = false;
            procerrors = false;
            graderrors = false;
            hesserrors = false;
            errerrors = false;
            passcount = 5;
            maxn = 3;
            maxhid = 3;
            
            //
            // Special tests
            //
            spectests(ref inferrors, ref procerrors, ref graderrors, ref hesserrors, ref errerrors);
            
            //
            // General multilayer network tests.
            // These tests are performed with small dataset, whose size is in [0,10].
            // We test correctness of functions on small sets, but do not test code
            // which splits large dataset into smaller chunks.
            //
            sizemin = 0;
            sizemax = 10;
            for(nf=1; nf<=maxn; nf++)
            {
                for(nl=1; nl<=maxn; nl++)
                {
                    for(nhid1=0; nhid1<=maxhid; nhid1++)
                    {
                        for(nhid2=0; nhid2<=maxhid; 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, sizemin, sizemax, ref graderrors);
                                testhessian(nkind, nf, nhid1, nhid2, nl, passcount, ref hesserrors);
                                testerr(nkind, nf, nhid1, nhid2, nl, passcount, sizemin, sizemax, ref errerrors);
                            }
                        }
                    }
                }
            }
            
            //
            // Special tests on large datasets: test ability to correctly split
            // work into smaller chunks.
            //
            nf = 2;
            nhid1 = 30;
            nhid2 = 30;
            nl = 2;
            sizemin = 1000;
            sizemax = 1000;
            testerr(0, nf, nhid1, nhid2, nl, 1, sizemin, sizemax, ref errerrors);
            testgradient(0, nf, nhid1, nhid2, nl, 1, sizemin, sizemax, ref graderrors);
            
            //
            // Test for MLPGradBatch____Subset()
            //
            graderrors = graderrors || testmlpgbsubset();
            
            //
            // Final report
            //
            waserrors = (((inferrors || procerrors) || graderrors) || hesserrors) || errerrors;
            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("ERROR FUNCTIONS:                         ");
                if( !errerrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testmlpbase(bool silent)
        {
            return testmlpbase(silent);
        }


        /*************************************************************************
        This function compares vectors G0 and G1 and returns
            ||G0-G1||/max(||G0||,||G1||,S)

        For zero G0, G1 and S (all three quantities are zero) it returns zero.
        *************************************************************************/
        private static double vectordiff(double[] g0,
            double[] g1,
            int n,
            double s)
        {
            double result = 0;
            int i = 0;
            double norm0 = 0;
            double norm1 = 0;
            double diff = 0;

            norm0 = 0;
            norm1 = 0;
            diff = 0;
            for(i=0; i<=n-1; i++)
            {
                norm0 = norm0+math.sqr(g0[i]);
                norm1 = norm1+math.sqr(g1[i]);
                diff = diff+math.sqr(g0[i]-g1[i]);
            }
            norm0 = Math.Sqrt(norm0);
            norm1 = Math.Sqrt(norm1);
            diff = Math.Sqrt(diff);
            if( ((double)(norm0)!=(double)(0) || (double)(norm1)!=(double)(0)) || (double)(s)!=(double)(0) )
            {
                diff = diff/Math.Max(Math.Max(norm0, norm1), s);
            }
            else
            {
                diff = 0;
            }
            result = diff;
            return result;
        }


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

        This function creates network with desired structure.

        Network is created using one of the three methods:
        a) straightforward 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 at random.
        *************************************************************************/
        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();

            alglib.ap.assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0, "CreateNetwork error");
            alglib.ap.assert(nhid1!=0 || nhid2==0, "CreateNetwork error");
            alglib.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;
            err = ((err || mlpbase.mlpgetinputscount(network)!=nin) || mlpbase.mlpgetoutputscount(network)!=nout) || mlpbase.mlpgetweightscount(network)!=wcount;
            
            //
            // 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();
            sparse.sparsematrix sparsexy = new sparse.sparsematrix();
            double[,] densexy = new double[0,0];
            int npoints = 0;
            int subnp = 0;
            bool iscls = new bool();
            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;
            int j = 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[] p0 = new double[0];
            double[] p1 = new double[0];
            int pcount = 0;
            double v = 0;
            int i_ = 0;

            alglib.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);
            iscls = mlpbase.mlpissoftmax(network);
            
            //
            // Initialize arrays
            //
            x1 = new double[nin];
            x2 = new double[nin];
            y1 = new double[nout];
            y2 = new double[nout];
            
            //
            // Initialize sets
            //
            npoints = math.randominteger(11)+10;
            if( iscls )
            {
                densexy = new double[npoints, nin+1];
                sparse.sparsecreate(npoints, nin+1, npoints, sparsexy);
            }
            else
            {
                densexy = new double[npoints, nin+nout];
                sparse.sparsecreate(npoints, nin+nout, npoints, sparsexy);
            }
            sparse.sparseconverttocrs(sparsexy);
            
            //
            // 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);
                apserv.seterrorflag(ref err, (double)(vectordiff(y1, y2, nout, 1.0))!=(double)(0.0));
                
                //
                // Same inputs on original network leads to same outputs
                // on copy created:
                // * using MLPCopy
                // * using MLPCopyShared
                //
                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;
                }
                for(i=0; i<=nout-1; i++)
                {
                    y2[i] = 2*math.randomreal()-1;
                }
                unsetnetwork(network2);
                mlpbase.mlpcopy(network, network2);
                mlpbase.mlpprocess(network, x1, ref y1);
                mlpbase.mlpprocess(network2, x2, ref y2);
                apserv.seterrorflag(ref err, (double)(vectordiff(y1, y2, nout, 1.0))!=(double)(0.0));
                for(i=0; i<=nout-1; i++)
                {
                    y2[i] = 2*math.randomreal()-1;
                }
                unsetnetwork(network2);
                mlpbase.mlpcopyshared(network, network2);
                mlpbase.mlpprocess(network, x1, ref y1);
                mlpbase.mlpprocess(network2, x2, ref y2);
                apserv.seterrorflag(ref err, (double)(vectordiff(y1, y2, nout, 1.0))!=(double)(0.0));
                
                //
                // Additionally we tests functions for copying of tunable
                // parameters by:
                // * copying network using MLPCopy
                // * randomizing tunable parameters with MLPRandomizeFull()
                // * copying tunable parameters with:
                //   a) MLPCopyTunableParameters
                //   b) combination of MLPExportTunableParameters and
                //      MLPImportTunableParameters - we export parameters
                //      to P1, copy PCount elements to P2, then test import.
                //
                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;
                }
                for(i=0; i<=nout-1; i++)
                {
                    y2[i] = 2*math.randomreal()-1;
                }
                unsetnetwork(network2);
                mlpbase.mlpcopy(network, network2);
                mlpbase.mlprandomizefull(network2);
                mlpbase.mlpcopytunableparameters(network, network2);
                mlpbase.mlpprocess(network, x1, ref y1);
                mlpbase.mlpprocess(network2, x2, ref y2);
                apserv.seterrorflag(ref err, (double)(vectordiff(y1, y2, nout, 1.0))!=(double)(0.0));
                for(i=0; i<=nout-1; i++)
                {
                    y2[i] = 2*math.randomreal()-1;
                }
                unsetnetwork(network2);
                mlpbase.mlpcopy(network, network2);
                mlpbase.mlprandomizefull(network2);
                mlpbase.mlpexporttunableparameters(network, ref p0, ref pcount);
                p1 = new double[pcount];
                for(i=0; i<=pcount-1; i++)
                {
                    p1[i] = p0[i];
                }
                mlpbase.mlpimporttunableparameters(network2, p1);
                mlpbase.mlpprocess(network, x1, ref y1);
                mlpbase.mlpprocess(network2, x2, ref y2);
                apserv.seterrorflag(ref err, (double)(vectordiff(y1, y2, nout, 1.0))!=(double)(0.0));
                
                //
                // 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));
                    }
                }
                
                //
                // Comperison MLPInitPreprocessor results with
                // MLPInitPreprocessorSparse results
                //
                sparse.sparseconverttohash(sparsexy);
                if( iscls )
                {
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=nin-1; j++)
                        {
                            densexy[i,j] = 2*math.randomreal()-1;
                            sparse.sparseset(sparsexy, i, j, densexy[i,j]);
                        }
                        densexy[i,nin] = math.randominteger(nout);
                        sparse.sparseset(sparsexy, i, j, densexy[i,nin]);
                    }
                }
                else
                {
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=nin+nout-1; j++)
                        {
                            densexy[i,j] = 2*math.randomreal()-1;
                            sparse.sparseset(sparsexy, i, j, densexy[i,j]);
                        }
                    }
                }
                sparse.sparseconverttocrs(sparsexy);
                mlpbase.mlpcopy(network, network2);
                mlpbase.mlpinitpreprocessor(network, densexy, npoints);
                mlpbase.mlpinitpreprocessorsparse(network2, sparsexy, npoints);
                subnp = math.randominteger(npoints);
                for(i=0; i<=subnp-1; i++)
                {
                    for(j=0; j<=nin-1; j++)
                    {
                        x1[j] = 2*math.randomreal()-1;
                    }
                    mlpbase.mlpprocess(network, x1, ref y1);
                    mlpbase.mlpprocess(network2, x1, ref y2);
                    for(j=0; j<=nout-1; j++)
                    {
                        err = err || (double)(Math.Abs(y1[j]-y2[j]))>(double)(1.0E-6);
                    }
                }
            }
        }


        /*************************************************************************
        Gradient functions test
        *************************************************************************/
        private static void testgradient(int nkind,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int passcount,
            int sizemin,
            int sizemax,
            ref bool err)
        {
            mlpbase.multilayerperceptron network = new mlpbase.multilayerperceptron();
            sparse.sparsematrix sparsexy = new sparse.sparsematrix();
            sparse.sparsematrix sparsexy2 = new sparse.sparsematrix();
            int n1 = 0;
            int n2 = 0;
            int wcount = 0;
            double h = 0;
            double etol = 0;
            double escale = 0;
            double gscale = 0;
            double nonstricttolerance = 0;
            double a1 = 0;
            double a2 = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int ssize = 0;
            int subsetsize = 0;
            int rowsize = 0;
            double[,] xy = new double[0,0];
            double[,] xy2 = new double[0,0];
            double[] grad1 = new double[0];
            double[] grad2 = new double[0];
            double[] gradsp = 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];
            int[] idx = new int[0];
            double v = 0;
            double e = 0;
            double e1 = 0;
            double e2 = 0;
            double esp = 0;
            double v1 = 0;
            double v2 = 0;
            double v3 = 0;
            double v4 = 0;
            double wprev = 0;
            double referencee = 0;
            double[] referenceg = new double[0];
            int i_ = 0;
            int i1_ = 0;

            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 = 1.0E-2;
            escale = 1.0E-2;
            gscale = 1.0E-2;
            nonstricttolerance = 0.01;
            
            //
            // Initialize
            //
            x = new double[nin];
            x1 = new double[nin];
            x2 = new double[nin];
            y = new double[nout];
            y1 = new double[nout];
            y2 = new double[nout];
            referenceg = new double[wcount];
            grad1 = new double[wcount];
            grad2 = new double[wcount];
            
            //
            // Process
            //
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Randomize network, then re-randomaze weights manually.
                //
                // NOTE: weights magnitude is chosen to be small, about 0.1,
                //       which allows us to avoid oversaturated network.
                //       In 10% of cases we use zero weights.
                //
                mlpbase.mlprandomizefull(network);
                if( (double)(math.randomreal())<=(double)(0.1) )
                {
                    for(i=0; i<=wcount-1; i++)
                    {
                        network.weights[i] = 0.0;
                    }
                }
                else
                {
                    for(i=0; i<=wcount-1; i++)
                    {
                        network.weights[i] = 0.2*math.randomreal()-0.1;
                    }
                }
                
                //
                // Test MLPError(), MLPErrorSparse(), MLPGrad() for single-element dataset:
                // * generate input X, output Y, combine them in dataset XY
                // * calculate "reference" error on dataset manually (call MLPProcess and evaluate sum-of-squared errors)
                // * calculate "reference" gradient by performing numerical differentiation of "reference" error
                //   using 4-point differentiation formula
                // * test error/gradient returned by MLPGrad(), MLPError(), MLPErrorSparse()
                //
                xy = new double[1, nin+nout];
                sparse.sparsecreate(1, nin+nout, nin+nout, sparsexy);
                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_];
                }
                for(i=0; i<=nin-1; i++)
                {
                    sparse.sparseset(sparsexy, 0, i, x[i]);
                }
                if( mlpbase.mlpissoftmax(network) )
                {
                    for(i=0; i<=nout-1; i++)
                    {
                        y[i] = 0;
                    }
                    xy[0,nin] = math.randominteger(nout);
                    sparse.sparseset(sparsexy, 0, nin, xy[0,nin]);
                    y[(int)Math.Round(xy[0,nin])] = 1;
                }
                else
                {
                    for(i=0; i<=nout-1; i++)
                    {
                        y[i] = 4*math.randomreal()-2;
                        sparse.sparseset(sparsexy, 0, nin+i, y[i]);
                    }
                    i1_ = (0) - (nin);
                    for(i_=nin; i_<=nin+nout-1;i_++)
                    {
                        xy[0,i_] = y[i_+i1_];
                    }
                }
                sparse.sparseconverttocrs(sparsexy);
                mlpbase.mlpprocess(network, x, ref y2);
                for(i_=0; i_<=nout-1;i_++)
                {
                    y2[i_] = y2[i_] - y[i_];
                }
                referencee = 0.0;
                for(i_=0; i_<=nout-1;i_++)
                {
                    referencee += y2[i_]*y2[i_];
                }
                referencee = referencee/2;
                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;
                    referenceg[i] = (v1-8*v2+8*v3-v4)/(12*h);
                }
                mlpbase.mlpgrad(network, x, y, ref e, ref grad2);
                apserv.seterrorflagdiff(ref err, e, referencee, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlperror(network, xy, 1), referencee, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlperrorsparse(network, sparsexy, 1), referencee, etol, escale);
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, grad2, wcount, gscale))>(double)(etol));
                
                //
                // Test MLPErrorN(), MLPGradN() for single-element dataset:
                // * generate input X, output Y, combine them in dataset XY
                // * calculate "reference" error on dataset manually (call MLPProcess and evaluate sum-of-squared errors)
                // * calculate "reference" gradient by performing numerical differentiation of "reference" error
                // * test error/gradient returned by MLPGradN(), MLPErrorN()
                //
                // NOTE: because we use inexact 2-point formula, we perform gradient test with NonStrictTolerance
                //
                xy = new double[1, nin+nout];
                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.mlpprocess(network, x, ref y2);
                referencee = 0;
                if( nkind!=1 )
                {
                    for(i=0; i<=nout-1; i++)
                    {
                        referencee = referencee+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) )
                            {
                                referencee = referencee+y[i]*Math.Log(math.maxrealnumber);
                            }
                            else
                            {
                                referencee = referencee+y[i]*Math.Log(y[i]/y2[i]);
                            }
                        }
                    }
                }
                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);
                    }
                    referenceg[i] = v;
                }
                mlpbase.mlpgradn(network, x, y, ref e, ref grad2);
                apserv.seterrorflagdiff(ref err, e, referencee, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlperrorn(network, xy, 1), referencee, etol, escale);
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, grad2, wcount, gscale))>(double)(nonstricttolerance));
                
                //
                // Test that gradient calculation functions automatically allocate
                // space for gradient, if needed.
                //
                // NOTE: we perform test with empty dataset.
                //
                sparse.sparsecreate(1, nin+nout, 0, sparsexy);
                sparse.sparseconverttocrs(sparsexy);
                grad1 = new double[1];
                mlpbase.mlpgradbatch(network, xy, 0, ref e1, ref grad1);
                apserv.seterrorflag(ref err, alglib.ap.len(grad1)!=wcount);
                grad1 = new double[1];
                mlpbase.mlpgradbatchsparse(network, sparsexy, 0, ref e1, ref grad1);
                apserv.seterrorflag(ref err, alglib.ap.len(grad1)!=wcount);
                grad1 = new double[1];
                mlpbase.mlpgradbatchsubset(network, xy, 0, idx, 0, ref e1, ref grad1);
                apserv.seterrorflag(ref err, alglib.ap.len(grad1)!=wcount);
                grad1 = new double[1];
                mlpbase.mlpgradbatchsparsesubset(network, sparsexy, 0, idx, 0, ref e1, ref grad1);
                apserv.seterrorflag(ref err, alglib.ap.len(grad1)!=wcount);
                
                //
                // Test MLPError(), MLPErrorSparse(), MLPGradBatch(), MLPGradBatchSparse() for many-element dataset:
                // * generate random dataset XY
                // * calculate "reference" error/gradient using MLPGrad(), which was tested in previous
                //   section and is assumed to work correctly
                // * test results returned by MLPGradBatch/MLPGradBatchSparse against reference ones
                //
                // NOTE: about 10% of tests are performed with zero SSize
                //
                ssize = sizemin+math.randominteger(sizemax-sizemin+1);
                xy = new double[Math.Max(ssize, 1), nin+nout];
                sparse.sparsecreate(Math.Max(ssize, 1), nin+nout, ssize*(nin+nout), sparsexy);
                for(i=0; i<=wcount-1; i++)
                {
                    referenceg[i] = 0;
                }
                referencee = 0;
                for(i=0; i<=ssize-1; i++)
                {
                    for(j=0; j<=nin-1; j++)
                    {
                        x1[j] = 4*math.randomreal()-2;
                        sparse.sparseset(sparsexy, i, j, x1[j]);
                    }
                    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);
                        sparse.sparseset(sparsexy, i, nin, xy[i,nin]);
                        y1[(int)Math.Round(xy[i,nin])] = 1;
                    }
                    else
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 4*math.randomreal()-2;
                            sparse.sparseset(sparsexy, i, nin+j, y1[j]);
                        }
                        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);
                    referencee = referencee+v;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        referenceg[i_] = referenceg[i_] + grad2[i_];
                    }
                }
                sparse.sparseconverttocrs(sparsexy);
                e2 = mlpbase.mlperror(network, xy, ssize);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                e2 = mlpbase.mlperrorsparse(network, sparsexy, ssize);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                mlpbase.mlpgradbatch(network, xy, ssize, ref e2, ref grad2);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, grad2, wcount, gscale))>(double)(etol));
                mlpbase.mlpgradbatchsparse(network, sparsexy, ssize, ref esp, ref gradsp);
                apserv.seterrorflagdiff(ref err, esp, referencee, etol, escale);
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, gradsp, wcount, gscale))>(double)(etol));
                
                //
                // Test MLPErrorSubset(), MLPGradBatchSubset(), MLPErrorSparseSubset(), MLPGradBatchSparseSubset()
                // for many-element dataset with different types of subsets:
                // * generate random dataset XY
                // * "reference" error/gradient are calculated with MLPGradBatch(),
                //   which was tested in previous section and is assumed to work correctly
                // * we perform tests for different subsets:
                //   * SubsetSize<0 - subset is a full dataset
                //   * SubsetSize=0 - subset is empty
                //   * SubsetSize>0 - random subset
                //
                ssize = sizemin+math.randominteger(sizemax-sizemin+1);
                xy = new double[Math.Max(ssize, 1), nin+nout];
                sparse.sparsecreate(Math.Max(ssize, 1), nin+nout, ssize*(nin+nout), sparsexy);
                for(i=0; i<=ssize-1; i++)
                {
                    for(j=0; j<=nin-1; j++)
                    {
                        x1[j] = 4*math.randomreal()-2;
                        sparse.sparseset(sparsexy, i, j, x1[j]);
                    }
                    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);
                        sparse.sparseset(sparsexy, i, nin, xy[i,nin]);
                        y1[(int)Math.Round(xy[i,nin])] = 1;
                    }
                    else
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 4*math.randomreal()-2;
                            sparse.sparseset(sparsexy, i, nin+j, y1[j]);
                        }
                        i1_ = (0) - (nin);
                        for(i_=nin; i_<=nin+nout-1;i_++)
                        {
                            xy[i,i_] = y1[i_+i1_];
                        }
                    }
                }
                sparse.sparseconverttocrs(sparsexy);
                if( ssize>0 )
                {
                    subsetsize = 1+math.randominteger(10);
                    xy2 = new double[subsetsize, nin+nout];
                    idx = new int[subsetsize];
                    sparse.sparsecreate(subsetsize, nin+nout, subsetsize*(nin+nout), sparsexy2);
                    if( mlpbase.mlpissoftmax(network) )
                    {
                        rowsize = nin+1;
                    }
                    else
                    {
                        rowsize = nin+nout;
                    }
                    for(i=0; i<=subsetsize-1; i++)
                    {
                        k = math.randominteger(ssize);
                        idx[i] = k;
                        for(j=0; j<=rowsize-1; j++)
                        {
                            xy2[i,j] = xy[k,j];
                            sparse.sparseset(sparsexy2, i, j, sparse.sparseget(sparsexy, k, j));
                        }
                    }
                    sparse.sparseconverttocrs(sparsexy2);
                }
                else
                {
                    subsetsize = 0;
                    xy2 = new double[0, 0];
                    idx = new int[0];
                    sparse.sparsecreate(1, nin+nout, 0, sparsexy2);
                    sparse.sparseconverttocrs(sparsexy2);
                }
                mlpbase.mlpgradbatch(network, xy, ssize, ref referencee, ref referenceg);
                e2 = mlpbase.mlperrorsubset(network, xy, ssize, idx, -1);
                esp = mlpbase.mlperrorsparsesubset(network, sparsexy, ssize, idx, -1);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                apserv.seterrorflagdiff(ref err, esp, referencee, etol, escale);
                mlpbase.mlpgradbatchsubset(network, xy, ssize, idx, -1, ref e2, ref grad2);
                mlpbase.mlpgradbatchsparsesubset(network, sparsexy, ssize, idx, -1, ref esp, ref gradsp);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                apserv.seterrorflagdiff(ref err, esp, referencee, etol, escale);
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, grad2, wcount, gscale))>(double)(etol));
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, gradsp, wcount, gscale))>(double)(etol));
                mlpbase.mlpgradbatch(network, xy, 0, ref referencee, ref referenceg);
                e2 = mlpbase.mlperrorsubset(network, xy, ssize, idx, 0);
                esp = mlpbase.mlperrorsparsesubset(network, sparsexy, ssize, idx, 0);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                apserv.seterrorflagdiff(ref err, esp, referencee, etol, escale);
                mlpbase.mlpgradbatchsubset(network, xy, ssize, idx, 0, ref e2, ref grad2);
                mlpbase.mlpgradbatchsparsesubset(network, sparsexy, ssize, idx, 0, ref esp, ref gradsp);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                apserv.seterrorflagdiff(ref err, esp, referencee, etol, escale);
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, grad2, wcount, gscale))>(double)(etol));
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, gradsp, wcount, gscale))>(double)(etol));
                mlpbase.mlpgradbatch(network, xy2, subsetsize, ref referencee, ref referenceg);
                e2 = mlpbase.mlperrorsubset(network, xy, ssize, idx, subsetsize);
                esp = mlpbase.mlperrorsparsesubset(network, sparsexy, ssize, idx, subsetsize);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                apserv.seterrorflagdiff(ref err, esp, referencee, etol, escale);
                mlpbase.mlpgradbatchsubset(network, xy, ssize, idx, subsetsize, ref e2, ref grad2);
                mlpbase.mlpgradbatchsparsesubset(network, sparsexy, ssize, idx, subsetsize, ref esp, ref gradsp);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                apserv.seterrorflagdiff(ref err, esp, referencee, etol, escale);
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, grad2, wcount, gscale))>(double)(etol));
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, gradsp, wcount, gscale))>(double)(etol));
                
                //
                // Test MLPGradNBatch() for many-element dataset:
                // * generate random dataset XY
                // * calculate "reference" error/gradient using MLPGrad(), which was tested in previous
                //   section and is assumed to work correctly
                // * test results returned by MLPGradNBatch against reference ones
                //
                ssize = sizemin+math.randominteger(sizemax-sizemin+1);
                xy = new double[ssize, nin+nout];
                for(i=0; i<=wcount-1; i++)
                {
                    referenceg[i] = 0;
                }
                referencee = 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);
                    referencee = referencee+v;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        referenceg[i_] = referenceg[i_] + grad2[i_];
                    }
                }
                mlpbase.mlpgradnbatch(network, xy, ssize, ref e2, ref grad2);
                apserv.seterrorflagdiff(ref err, e2, referencee, etol, escale);
                apserv.seterrorflag(ref err, (double)(vectordiff(referenceg, grad2, wcount, gscale))>(double)(etol));
            }
        }


        /*************************************************************************
        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;

            alglib.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);
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Error functions (other than MLPError and MLPErrorN) test.

        Network of type NKind is created, with  NIn  inputs,  NHid1*NHid2   hidden
        layers (one layer if NHid2=0), NOut outputs. PassCount  random  passes  is
        performed. Dataset has random size in [SizeMin,SizeMax].
        *************************************************************************/
        private static void testerr(int nkind,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int passcount,
            int sizemin,
            int sizemax,
            ref bool err)
        {
            mlpbase.multilayerperceptron network = new mlpbase.multilayerperceptron();
            sparse.sparsematrix sparsexy = new sparse.sparsematrix();
            int n1 = 0;
            int n2 = 0;
            int wcount = 0;
            double etol = 0;
            double escale = 0;
            double a1 = 0;
            double a2 = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int ssize = 0;
            int subsetsize = 0;
            double[,] xy = new double[0,0];
            double[] y = new double[0];
            double[] x1 = new double[0];
            double[] y1 = new double[0];
            int[] idx = new int[0];
            int[] dummy = new int[0];
            double refrmserror = 0;
            double refclserror = 0;
            double refrelclserror = 0;
            double refavgce = 0;
            double refavgerror = 0;
            double refavgrelerror = 0;
            int avgrelcnt = 0;
            mlpbase.modelerrors allerrors = new mlpbase.modelerrors();
            int nnmax = 0;
            int dsmax = 0;
            double relclstolerance = 0;
            int i_ = 0;
            int i1_ = 0;

            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);
            etol = 1.0E-4;
            escale = 1.0E-2;
            
            //
            // Initialize
            //
            x1 = new double[nin];
            y = new double[nout];
            y1 = new double[nout];
            
            //
            // Process
            //
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Randomize network, then re-randomaze weights manually.
                //
                // NOTE: weights magnitude is chosen to be small, about 0.1,
                //       which allows us to avoid oversaturated network.
                //       In 10% of cases we use zero weights.
                //
                mlpbase.mlprandomizefull(network);
                if( (double)(math.randomreal())<=(double)(0.1) )
                {
                    for(i=0; i<=wcount-1; i++)
                    {
                        network.weights[i] = 0.0;
                    }
                }
                else
                {
                    for(i=0; i<=wcount-1; i++)
                    {
                        network.weights[i] = 0.2*math.randomreal()-0.1;
                    }
                }
                
                //
                // Generate random dataset.
                // Calculate reference errors.
                //
                // NOTE: about 10% of tests are performed with zero SSize
                //
                ssize = sizemin+math.randominteger(sizemax-sizemin+1);
                if( mlpbase.mlpissoftmax(network) )
                {
                    xy = new double[Math.Max(ssize, 1), nin+1];
                    sparse.sparsecreate(Math.Max(ssize, 1), nin+1, 0, sparsexy);
                }
                else
                {
                    xy = new double[Math.Max(ssize, 1), nin+nout];
                    sparse.sparsecreate(Math.Max(ssize, 1), nin+nout, 0, sparsexy);
                }
                refrmserror = 0.0;
                refclserror = 0.0;
                refavgce = 0.0;
                refavgerror = 0.0;
                refavgrelerror = 0.0;
                avgrelcnt = 0;
                for(i=0; i<=ssize-1; i++)
                {
                    
                    //
                    // Fill I-th row
                    //
                    for(j=0; j<=nin-1; j++)
                    {
                        x1[j] = 4*math.randomreal()-2;
                        sparse.sparseset(sparsexy, i, j, x1[j]);
                    }
                    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);
                        sparse.sparseset(sparsexy, i, nin, xy[i,nin]);
                        y1[(int)Math.Round(xy[i,nin])] = 1;
                    }
                    else
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 4*math.randomreal()-2;
                            sparse.sparseset(sparsexy, i, nin+j, y1[j]);
                        }
                        i1_ = (0) - (nin);
                        for(i_=nin; i_<=nin+nout-1;i_++)
                        {
                            xy[i,i_] = y1[i_+i1_];
                        }
                    }
                    
                    //
                    // Process
                    //
                    mlpbase.mlpprocess(network, x1, ref y);
                    
                    //
                    // Update reference errors
                    //
                    nnmax = 0;
                    if( mlpbase.mlpissoftmax(network) )
                    {
                        if( (double)(y[(int)Math.Round(xy[i,nin])])>(double)(0) )
                        {
                            refavgce = refavgce+Math.Log(1/y[(int)Math.Round(xy[i,nin])]);
                        }
                        else
                        {
                            refavgce = refavgce+Math.Log(math.maxrealnumber);
                        }
                    }
                    if( mlpbase.mlpissoftmax(network) )
                    {
                        dsmax = (int)Math.Round(xy[i,nin]);
                    }
                    else
                    {
                        dsmax = 0;
                    }
                    for(j=0; j<=nout-1; j++)
                    {
                        refrmserror = refrmserror+math.sqr(y[j]-y1[j]);
                        refavgerror = refavgerror+Math.Abs(y[j]-y1[j]);
                        if( (double)(y1[j])!=(double)(0) )
                        {
                            refavgrelerror = refavgrelerror+Math.Abs(y[j]-y1[j])/Math.Abs(y1[j]);
                            avgrelcnt = avgrelcnt+1;
                        }
                        if( (double)(y[j])>(double)(y[nnmax]) )
                        {
                            nnmax = j;
                        }
                        if( !mlpbase.mlpissoftmax(network) && (double)(y1[j])>(double)(y1[dsmax]) )
                        {
                            dsmax = j;
                        }
                    }
                    if( nnmax!=dsmax )
                    {
                        refclserror = refclserror+1;
                    }
                }
                sparse.sparseconverttocrs(sparsexy);
                if( ssize>0 )
                {
                    refrmserror = Math.Sqrt(refrmserror/(ssize*nout));
                    refavgerror = refavgerror/(ssize*nout);
                    refrelclserror = refclserror/ssize;
                    refavgce = refavgce/(ssize*Math.Log(2));
                }
                else
                {
                    refrelclserror = 0.0;
                }
                if( avgrelcnt>0 )
                {
                    refavgrelerror = refavgrelerror/avgrelcnt;
                }
                
                //
                // Test "continuous" errors on full dataset
                //
                apserv.seterrorflagdiff(ref err, mlpbase.mlprmserror(network, xy, ssize), refrmserror, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlpavgce(network, xy, ssize), refavgce, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlpavgerror(network, xy, ssize), refavgerror, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlpavgrelerror(network, xy, ssize), refavgrelerror, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlprmserrorsparse(network, sparsexy, ssize), refrmserror, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlpavgcesparse(network, sparsexy, ssize), refavgce, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlpavgerrorsparse(network, sparsexy, ssize), refavgerror, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpbase.mlpavgrelerrorsparse(network, sparsexy, ssize), refavgrelerror, etol, escale);
                mlpbase.mlpallerrorssubset(network, xy, ssize, dummy, -1, allerrors);
                apserv.seterrorflagdiff(ref err, allerrors.avgce, refavgce, etol, escale);
                apserv.seterrorflagdiff(ref err, allerrors.rmserror, refrmserror, etol, escale);
                apserv.seterrorflagdiff(ref err, allerrors.avgerror, refavgerror, etol, escale);
                apserv.seterrorflagdiff(ref err, allerrors.avgrelerror, refavgrelerror, etol, escale);
                mlpbase.mlpallerrorssparsesubset(network, sparsexy, ssize, dummy, -1, allerrors);
                apserv.seterrorflagdiff(ref err, allerrors.avgce, refavgce, etol, escale);
                apserv.seterrorflagdiff(ref err, allerrors.rmserror, refrmserror, etol, escale);
                apserv.seterrorflagdiff(ref err, allerrors.avgerror, refavgerror, etol, escale);
                apserv.seterrorflagdiff(ref err, allerrors.avgrelerror, refavgrelerror, etol, escale);
                
                //
                // Test errors on dataset given by subset.
                // We perform only limited test for RMS error, assuming that either all errors
                // are calculated correctly (subject to subset given by Idx) - or none of them.
                //
                if( ssize>0 )
                {
                    subsetsize = math.randominteger(10);
                }
                else
                {
                    subsetsize = 0;
                }
                idx = new int[subsetsize];
                refrmserror = 0.0;
                for(i=0; i<=subsetsize-1; i++)
                {
                    k = math.randominteger(ssize);
                    idx[i] = k;
                    for(i_=0; i_<=nin-1;i_++)
                    {
                        x1[i_] = xy[k,i_];
                    }
                    if( mlpbase.mlpissoftmax(network) )
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 0;
                        }
                        y1[(int)Math.Round(xy[k,nin])] = 1;
                    }
                    else
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = xy[k,nin+j];
                        }
                    }
                    mlpbase.mlpprocess(network, x1, ref y);
                    for(j=0; j<=nout-1; j++)
                    {
                        refrmserror = refrmserror+math.sqr(y[j]-y1[j]);
                    }
                }
                if( subsetsize>0 )
                {
                    refrmserror = Math.Sqrt(refrmserror/(subsetsize*nout));
                }
                mlpbase.mlpallerrorssubset(network, xy, ssize, idx, subsetsize, allerrors);
                apserv.seterrorflagdiff(ref err, allerrors.rmserror, refrmserror, etol, escale);
                mlpbase.mlpallerrorssparsesubset(network, sparsexy, ssize, idx, subsetsize, allerrors);
                apserv.seterrorflagdiff(ref err, allerrors.rmserror, refrmserror, etol, escale);
                
                //
                // Test "discontinuous" error function.
                // Even slight changes in the network output may force these functions
                // to change by 1. So, we test them with relaxed criteria, corresponding to
                // difference in classification of two samples.
                //
                if( ssize>0 )
                {
                    relclstolerance = 2.5/ssize;
                    apserv.seterrorflag(ref err, (double)(Math.Abs(mlpbase.mlpclserror(network, xy, ssize)-refclserror))>(double)(ssize*relclstolerance));
                    apserv.seterrorflag(ref err, (double)(Math.Abs(mlpbase.mlprelclserror(network, xy, ssize)-refrelclserror))>(double)(relclstolerance));
                    apserv.seterrorflag(ref err, (double)(Math.Abs(mlpbase.mlprelclserrorsparse(network, sparsexy, ssize)-refrelclserror))>(double)(relclstolerance));
                }
            }
        }


        /*************************************************************************
        Special tests
        *************************************************************************/
        private static void spectests(ref bool inferrors,
            ref bool procerrors,
            ref bool graderrors,
            ref bool hesserrors,
            ref bool errerrors)
        {
            mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron();
            double[,] xy = new double[0,0];
            double f = 0;
            double[] g = new double[0];
            int i = 0;

            
            //
            // Special test for overflow in TanH:
            // * create 1x1x1 linear network
            // * create dataset with 1 item: [x, y] = [0, 1]
            // * set network weights to [10000000, 10000000, 10000000, 10000000]
            // * check that error function is finite
            // * check that gradient is finite
            //
            mlpbase.mlpcreate1(1, 1, 1, net);
            xy = new double[1, 2];
            xy[0,0] = 0;
            xy[0,1] = 1.0;
            for(i=0; i<=mlpbase.mlpgetweightscount(net)-1; i++)
            {
                net.weights[i] = 10000000.0;
            }
            mlpbase.mlpgradbatch(net, xy, 1, ref f, ref g);
            apserv.seterrorflag(ref graderrors, !math.isfinite(f));
            apserv.seterrorflag(ref graderrors, !math.isfinite(mlpbase.mlperror(net, xy, 1)));
            for(i=0; i<=mlpbase.mlpgetweightscount(net)-1; i++)
            {
                apserv.seterrorflag(ref graderrors, !math.isfinite(g[i]));
            }
            
            //
            // Special test for overflow in SOFTMAX layer:
            // * create 1x1x2 classifier network
            // * create dataset with 1 item: [x, y] = [0, 1]
            // * set network weights to [10000000, 10000000, 10000000, 10000000]
            // * check that error function is finite
            // * check that gradient is finite
            //
            mlpbase.mlpcreatec1(1, 1, 2, net);
            xy = new double[1, 2];
            xy[0,0] = 0;
            xy[0,1] = 1;
            for(i=0; i<=mlpbase.mlpgetweightscount(net)-1; i++)
            {
                net.weights[i] = 10000000.0;
            }
            mlpbase.mlpgradbatch(net, xy, 1, ref f, ref g);
            apserv.seterrorflag(ref graderrors, !math.isfinite(f));
            apserv.seterrorflag(ref graderrors, !math.isfinite(mlpbase.mlperror(net, xy, 1)));
            for(i=0; i<=mlpbase.mlpgetweightscount(net)-1; i++)
            {
                apserv.seterrorflag(ref graderrors, !math.isfinite(g[i]));
            }
        }


        /*************************************************************************
        The function test functions MLPGradBatchMasked and MLPGradBatchSparseMasked.
        *************************************************************************/
        private static bool testmlpgbsubset()
        {
            bool result = new bool();
            mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron();
            double[,] a = new double[0,0];
            double[,] parta = new double[0,0];
            sparse.sparsematrix sa = new sparse.sparsematrix();
            sparse.sparsematrix partsa = new sparse.sparsematrix();
            int[] idx = new int[0];
            double e1 = 0;
            double e2 = 0;
            double[] grad1 = new double[0];
            double[] grad2 = new double[0];
            int nin = 0;
            int nout = 0;
            int w = 0;
            int wcount = 0;
            int nhid1 = 0;
            int nhid2 = 0;
            int nkind = 0;
            double a1 = 0;
            double a2 = 0;
            int n1 = 0;
            int n2 = 0;
            int ssize = 0;
            int maxssize = 0;
            int sbsize = 0;
            int nvar = 0;
            int variant = 0;
            int i = 0;
            int j = 0;
            int i_ = 0;

            
            //
            // Variant:
            //  * 1 - there are all rows;
            //  * 2 - there are no one rows;
            //  * 3 - there are some random rows.
            //
            nvar = 3;
            maxssize = 96;
            for(ssize=0; ssize<=maxssize; ssize++)
            {
                idx = new int[ssize];
                nkind = math.randominteger(4);
                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());
                }
                nin = math.randominteger(20)+1;
                nhid1 = math.randominteger(5);
                if( nhid1==0 )
                {
                    nhid2 = 0;
                }
                else
                {
                    nhid2 = math.randominteger(5);
                }
                nout = math.randominteger(20)+2;
                createnetwork(net, nkind, a1, a2, nin, nhid1, nhid2, nout);
                mlpbase.mlpproperties(net, ref n1, ref n2, ref wcount);
                if( mlpbase.mlpissoftmax(net) )
                {
                    w = nin+1;
                    if( ssize>0 )
                    {
                        a = new double[ssize, w];
                        sparse.sparsecreate(ssize, w, ssize*w, sa);
                    }
                    else
                    {
                        a = new double[0, 0];
                        sparse.sparsecreate(1, 1, 0, sa);
                    }
                    for(i=0; i<=ssize-1; i++)
                    {
                        for(j=0; j<=w-1; j++)
                        {
                            a[i,j] = 2*math.randomreal()-1;
                            sparse.sparseset(sa, i, j, a[i,j]);
                        }
                    }
                    for(i=0; i<=ssize-1; i++)
                    {
                        a[i,nin] = math.randominteger(nout);
                        sparse.sparseset(sa, i, nin, a[i,nin]);
                    }
                }
                else
                {
                    w = nin+nout;
                    if( ssize>0 )
                    {
                        a = new double[ssize, w];
                        sparse.sparsecreate(ssize, w, ssize*w, sa);
                    }
                    else
                    {
                        a = new double[0, 0];
                        sparse.sparsecreate(1, 1, 0, sa);
                    }
                    for(i=0; i<=ssize-1; i++)
                    {
                        for(j=0; j<=w-1; j++)
                        {
                            a[i,j] = 2*math.randomreal()-1;
                            sparse.sparseset(sa, i, j, a[i,j]);
                        }
                    }
                }
                sparse.sparseconverttocrs(sa);
                for(variant=1; variant<=nvar; variant++)
                {
                    sbsize = -1;
                    if( variant==1 )
                    {
                        sbsize = ssize;
                        for(i=0; i<=sbsize-1; i++)
                        {
                            idx[i] = i;
                        }
                    }
                    if( variant==2 )
                    {
                        sbsize = 0;
                    }
                    if( variant==3 )
                    {
                        if( ssize==0 )
                        {
                            sbsize = 0;
                        }
                        else
                        {
                            sbsize = math.randominteger(ssize);
                        }
                        for(i=0; i<=sbsize-1; i++)
                        {
                            idx[i] = math.randominteger(ssize);
                        }
                    }
                    alglib.ap.assert(sbsize>=0, "mlpbase test: integrity check failed");
                    if( sbsize!=0 )
                    {
                        parta = new double[sbsize, w];
                        sparse.sparsecreate(sbsize, w, sbsize*w, partsa);
                    }
                    else
                    {
                        parta = new double[0, 0];
                        sparse.sparsecreate(1, 1, 0, partsa);
                    }
                    for(i=0; i<=sbsize-1; i++)
                    {
                        for(i_=0; i_<=w-1;i_++)
                        {
                            parta[i,i_] = a[idx[i],i_];
                        }
                        for(j=0; j<=w-1; j++)
                        {
                            sparse.sparseset(partsa, i, j, parta[i,j]);
                        }
                    }
                    sparse.sparseconverttocrs(partsa);
                    mlpbase.mlpgradbatch(net, parta, sbsize, ref e1, ref grad1);
                    mlpbase.mlpgradbatchsubset(net, a, ssize, idx, sbsize, ref e2, ref grad2);
                    
                    //
                    // Test for dense matrix
                    //
                    if( (double)(Math.Abs(e1-e2))>(double)(1.0E-6) )
                    {
                        result = true;
                        return result;
                    }
                    for(i=0; i<=wcount-1; i++)
                    {
                        if( (double)(Math.Abs(grad1[i]-grad2[i]))>(double)(1.0E-6) )
                        {
                            result = true;
                            return result;
                        }
                    }
                    
                    //
                    // Test for sparse matrix
                    //
                    mlpbase.mlpgradbatchsparse(net, partsa, sbsize, ref e1, ref grad1);
                    mlpbase.mlpgradbatchsparsesubset(net, sa, ssize, idx, sbsize, ref e2, ref grad2);
                    if( (double)(Math.Abs(e1-e2))>(double)(1.0E-6) )
                    {
                        result = true;
                        return result;
                    }
                    for(i=0; i<=wcount-1; i++)
                    {
                        if( (double)(Math.Abs(grad1[i]-grad2[i]))>(double)(1.0E-6) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            result = false;
            return result;
        }


    }
    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++)
            {
                alglib.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++)
            {
                alglib.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-1.0*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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testxblas(bool silent)
        {
            return testxblas(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testdensesolver(bool silent)
        {
            return testdensesolver(silent);
        }


        /*************************************************************************
        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 rmatrixchecksolutionmfast(double[,] xe,
            int n,
            int m,
            double threshold,
            int info,
            double[,] xs)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info<=0 )
            {
                result = false;
            }
            else
            {
                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 are correct solution.
        Returns True on success.
        *************************************************************************/
        private static bool rmatrixchecksolutionfast(double[,] xe,
            int n,
            double threshold,
            int info,
            double[] xs)
        {
            bool result = new bool();
            int i = 0;

            result = true;
            if( info<=0 )
            {
                result = false;
            }
            else
            {
                for(i=0; i<=n-1; i++)
                {
                    result = result && (double)(Math.Abs(xe[i,0]-xs[i]))<=(double)(threshold);
                }
            }
            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 rmatrixchecksingularmfast(int n,
            int m,
            int info,
            double[,] xs)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info!=-3 )
            {
                result = false;
            }
            else
            {
                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 indicate singular matrix.
        Returns True on success.
        *************************************************************************/
        private static bool rmatrixchecksingularfast(int n,
            int info,
            double[] xs)
        {
            bool result = new bool();
            int i = 0;

            result = true;
            if( info!=-3 )
            {
                result = false;
            }
            else
            {
                for(i=0; i<=n-1; i++)
                {
                    result = result && (double)(xs[i])==(double)(0);
                }
            }
            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 cmatrixchecksolutionmfast(complex[,] xe,
            int n,
            int m,
            double threshold,
            int info,
            complex[,] xs)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info<=0 )
            {
                result = false;
                return result;
            }
            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 are correct solution.
        Returns True on success.
        *************************************************************************/
        private static bool cmatrixchecksolutionfast(complex[,] xe,
            int n,
            double threshold,
            int info,
            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 = cmatrixchecksolutionmfast(xe, n, 1, threshold, info, 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;
                return result;
            }
            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 cmatrixchecksingularmfast(int n,
            int m,
            int info,
            complex[,] xs)
        {
            bool result = new bool();
            int i = 0;
            int j = 0;

            result = true;
            if( info!=-3 )
            {
                result = false;
                return result;
            }
            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;
        }


        /*************************************************************************
        Checks whether solver results indicate singular matrix.
        Returns True on success.
        *************************************************************************/
        private static bool cmatrixchecksingularfast(int n,
            int info,
            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 = cmatrixchecksingularmfast(n, 1, info, 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;
                        x = new double[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x[i,j] = b[i,j];
                            }
                        }
                        densesolver.rmatrixsolvemfast(a, n, x, m, ref info);
                        rerrors = rerrors || !rmatrixchecksolutionmfast(xe, n, m, threshold, info, 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;
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.rmatrixsolvefast(a, n, bv, ref info);
                        rerrors = rerrors || !rmatrixchecksolutionfast(xe, n, threshold, info, bv);
                        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;
                        x = new double[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x[i,j] = b[i,j];
                            }
                        }
                        densesolver.rmatrixlusolvemfast(lua, p, n, x, m, ref info);
                        apserv.seterrorflag(ref rerrors, !rmatrixchecksolutionmfast(xe, n, m, threshold, info, 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;
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            xv[i_] = b[i_,0];
                        }
                        densesolver.rmatrixlusolvefast(lua, p, n, xv, ref info);
                        apserv.seterrorflag(ref rerrors, !rmatrixchecksolutionfast(xe, n, threshold, info, 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 (TaskKind=0)
                        //    * with zero columns (TaskKind=1)
                        //    * with zero rows (TaskKind=2)
                        //    * with equal rows/columns (TaskKind=2 or 3)
                        // 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 RMatrixSolveMFast(); performed only for matrices
                            // with zero rows or columns
                            //
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                x = new double[n, m];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=m-1; j++)
                                    {
                                        x[i,j] = b[i,j];
                                    }
                                }
                                densesolver.rmatrixsolvemfast(a, n, x, m, ref info);
                                rerrors = rerrors || !rmatrixchecksingularmfast(n, m, info, 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 RMatrixSolveFast(); performed only for matrices
                            // with zero rows or columns
                            //
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                bv = new double[n];
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    bv[i_] = b[i_,0];
                                }
                                densesolver.rmatrixsolvefast(a, n, bv, ref info);
                                apserv.seterrorflag(ref rerrors, !rmatrixchecksingularfast(n, info, bv));
                            }
                            
                            //
                            // 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 RMatrixLUSolveMFast(); performed only for matrices
                            // with zero rows or columns
                            //
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                x = new double[n, m];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=m-1; j++)
                                    {
                                        x[i,j] = b[i,j];
                                    }
                                }
                                densesolver.rmatrixlusolvemfast(lua, p, n, x, m, ref info);
                                apserv.seterrorflag(ref rerrors, !rmatrixchecksingularmfast(n, m, info, 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 RMatrixLUSolveFast(); performed only for matrices
                            // with zero rows or columns
                            //
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                bv = new double[n];
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    bv[i_] = b[i_,0];
                                }
                                densesolver.rmatrixlusolvefast(lua, p, n, bv, ref info);
                                apserv.seterrorflag(ref rerrors, !rmatrixchecksingularfast(n, info, bv));
                            }
                            
                            //
                            // 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;
                        x = new double[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x[i,j] = b[i,j];
                            }
                        }
                        densesolver.spdmatrixsolvemfast(a, n, isupper, x, m, ref info);
                        apserv.seterrorflag(ref spderrors, !rmatrixchecksolutionmfast(xe, n, m, threshold, info, 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;
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.spdmatrixsolvefast(a, n, isupper, bv, ref info);
                        apserv.seterrorflag(ref spderrors, !rmatrixchecksolutionfast(xe, n, threshold, info, bv));
                        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;
                        x = new double[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x[i,j] = b[i,j];
                            }
                        }
                        densesolver.spdmatrixcholeskysolvemfast(cha, n, isupper, x, m, ref info);
                        apserv.seterrorflag(ref spderrors, !rmatrixchecksolutionmfast(xe, n, m, threshold, info, 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);
                        info = 0;
                        bv = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.spdmatrixcholeskysolvefast(cha, n, isupper, bv, ref info);
                        apserv.seterrorflag(ref spderrors, !rmatrixchecksolutionfast(xe, n, threshold, info, bv));
                        
                        //
                        // ********************************************************
                        // 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 SPDMatrixSolveMFast()
                            //
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                x = new double[n, m];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=m-1; j++)
                                    {
                                        x[i,j] = b[i,j];
                                    }
                                }
                                densesolver.spdmatrixsolvemfast(a, n, isupper, x, m, ref info);
                                apserv.seterrorflag(ref spderrors, !rmatrixchecksingularmfast(n, m, info, 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);
                            
                            //
                            // Test SPDMatrixSolveFast()
                            //
                            info = 0;
                            bv = new double[n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                bv[i_] = b[i_,0];
                            }
                            densesolver.spdmatrixsolvefast(a, n, isupper, bv, ref info);
                            apserv.seterrorflag(ref spderrors, !rmatrixchecksingular(n, info, rep, bv));
                            
                            //
                            // '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() (and fast version)
                                //
                                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);
                                if( (taskkind==0 || taskkind==1) || taskkind==2 )
                                {
                                    info = 0;
                                    x = new double[n, m];
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=0; j<=m-1; j++)
                                        {
                                            x[i,j] = b[i,j];
                                        }
                                    }
                                    densesolver.spdmatrixcholeskysolvemfast(a, n, isupper, x, m, ref info);
                                    apserv.seterrorflag(ref spderrors, !rmatrixchecksingularmfast(n, m, info, 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);
                                if( (taskkind==0 || taskkind==1) || taskkind==2 )
                                {
                                    info = 0;
                                    bv = new double[n];
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        bv[i_] = b[i_,0];
                                    }
                                    densesolver.spdmatrixcholeskysolvefast(a, n, isupper, bv, ref info);
                                    apserv.seterrorflag(ref spderrors, !rmatrixchecksingularfast(n, info, bv));
                                }
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        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;
                        x = new complex[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x[i,j] = b[i,j];
                            }
                        }
                        densesolver.cmatrixsolvemfast(a, n, x, m, ref info);
                        cerrors = cerrors || !cmatrixchecksolutionmfast(xe, n, m, threshold, info, 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;
                        bv = new complex[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.cmatrixsolvefast(a, n, bv, ref info);
                        cerrors = cerrors || !cmatrixchecksolutionfast(xe, n, threshold, info, bv);
                        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;
                        x = new complex[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x[i,j] = b[i,j];
                            }
                        }
                        densesolver.cmatrixlusolvemfast(lua, p, n, x, m, ref info);
                        apserv.seterrorflag(ref cerrors, !cmatrixchecksolutionmfast(xe, n, m, threshold, info, 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;
                        bv = new complex[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.cmatrixlusolvefast(lua, p, n, bv, ref info);
                        apserv.seterrorflag(ref cerrors, !cmatrixchecksolutionfast(xe, n, threshold, info, bv));
                        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 CMatrixSolveMFast(); performed only for matrices
                            // with zero rows or columns
                            //
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                x = new complex[n, m];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=m-1; j++)
                                    {
                                        x[i,j] = b[i,j];
                                    }
                                }
                                densesolver.cmatrixsolvemfast(a, n, x, m, ref info);
                                apserv.seterrorflag(ref cerrors, !cmatrixchecksingularmfast(n, m, info, 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);
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                bv = new complex[n];
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    bv[i_] = b[i_,0];
                                }
                                densesolver.cmatrixsolvefast(a, n, bv, ref info);
                                apserv.seterrorflag(ref cerrors, !cmatrixchecksingularfast(n, info, bv));
                            }
                            
                            //
                            // 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 CMatrixLUSolveMFast()
                            //
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                x = new complex[n, m];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=m-1; j++)
                                    {
                                        x[i,j] = b[i,j];
                                    }
                                }
                                densesolver.cmatrixlusolvemfast(lua, p, n, x, m, ref info);
                                apserv.seterrorflag(ref cerrors, !cmatrixchecksingularmfast(n, m, info, 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 CMatrixLUSolveFast()
                            //
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                bv = new complex[n];
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    bv[i_] = b[i_,0];
                                }
                                densesolver.cmatrixlusolvefast(lua, p, n, bv, ref info);
                                apserv.seterrorflag(ref cerrors, !cmatrixchecksingularfast(n, info, bv));
                            }
                            
                            //
                            // 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;
                        x = new complex[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x[i,j] = b[i,j];
                            }
                        }
                        densesolver.hpdmatrixsolvemfast(a, n, isupper, x, m, ref info);
                        apserv.seterrorflag(ref hpderrors, !cmatrixchecksolutionmfast(xe, n, m, threshold, info, 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;
                        bv = new complex[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.hpdmatrixsolvefast(a, n, isupper, bv, ref info);
                        hpderrors = hpderrors || !cmatrixchecksolution(xe, n, threshold, info, rep, bv);
                        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;
                        x = new complex[n, m];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                x[i,j] = b[i,j];
                            }
                        }
                        densesolver.hpdmatrixcholeskysolvemfast(cha, n, isupper, x, m, ref info);
                        apserv.seterrorflag(ref hpderrors, !cmatrixchecksolutionmfast(xe, n, m, threshold, info, 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);
                        info = 0;
                        bv = new complex[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            bv[i_] = b[i_,0];
                        }
                        densesolver.hpdmatrixcholeskysolvefast(cha, n, isupper, bv, ref info);
                        apserv.seterrorflag(ref hpderrors, !cmatrixchecksolutionfast(xe, n, threshold, info, bv));
                        
                        //
                        // ********************************************************
                        // 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() (and fast version)
                            //
                            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);
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                x = new complex[n, m];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=m-1; j++)
                                    {
                                        x[i,j] = b[i,j];
                                    }
                                }
                                densesolver.hpdmatrixsolvemfast(a, n, isupper, x, m, ref info);
                                apserv.seterrorflag(ref hpderrors, !cmatrixchecksingularmfast(n, m, info, 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);
                            if( (taskkind==0 || taskkind==1) || taskkind==2 )
                            {
                                info = 0;
                                bv = new complex[n];
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    bv[i_] = b[i_,0];
                                }
                                densesolver.hpdmatrixsolvefast(a, n, isupper, bv, ref info);
                                apserv.seterrorflag(ref hpderrors, !cmatrixchecksingularfast(n, info, bv));
                            }
                            
                            //
                            // '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 SPDMatrixCholeskySolveM()/fast
                                //
                                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);
                                if( (taskkind==0 || taskkind==1) || taskkind==2 )
                                {
                                    info = 0;
                                    x = new complex[n, m];
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=0; j<=m-1; j++)
                                        {
                                            x[i,j] = b[i,j];
                                        }
                                    }
                                    densesolver.hpdmatrixcholeskysolvemfast(cha, n, isupper, x, m, ref info);
                                    apserv.seterrorflag(ref hpderrors, !cmatrixchecksingularmfast(n, m, info, x));
                                }
                                
                                //
                                // Test HPDMatrixCholeskySolve() (fast)
                                //
                                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);
                                if( (taskkind==0 || taskkind==1) || taskkind==2 )
                                {
                                    bv = new complex[n];
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        bv[i_] = b[i_,0];
                                    }
                                    densesolver.hpdmatrixcholeskysolvefast(cha, n, isupper, bv, ref info);
                                    apserv.seterrorflag(ref hpderrors, !cmatrixchecksingularfast(n, info, bv));
                                }
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        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 testoptservunit
    {
        public static bool testoptserv(bool silent)
        {
            bool result = new bool();
            bool precerrors = new bool();
            bool wereerrors = new bool();

            precerrors = false;
            testprec(ref precerrors);
            
            //
            // report
            //
            wereerrors = precerrors;
            if( !silent )
            {
                System.Console.Write("TESTING OPTSERV");
                System.Console.WriteLine();
                System.Console.Write("TESTS:                                    ");
                System.Console.WriteLine();
                System.Console.Write("* PRECONDITIONERS                         ");
                if( precerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                if( wereerrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !wereerrors;
            return result;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testoptserv(bool silent)
        {
            return testoptserv(silent);
        }


        /*************************************************************************
        This function checks preconditioning functions

        On failure sets error flag.
        *************************************************************************/
        private static void testprec(ref bool wereerrors)
        {
            int n = 0;
            int k = 0;
            int i = 0;
            int j = 0;
            int i0 = 0;
            int j0 = 0;
            int j1 = 0;
            double v = 0;
            double rho = 0;
            double theta = 0;
            double tolg = 0;
            double[,] va = new double[0,0];
            double[] vc = new double[0];
            double[] vd = new double[0];
            double[] vb = new double[0];
            double[] s0 = new double[0];
            double[] s1 = new double[0];
            double[] s2 = new double[0];
            double[] g = new double[0];
            optserv.precbuflbfgs buf = new optserv.precbuflbfgs();
            optserv.precbuflowrank lowrankbuf = new optserv.precbuflowrank();
            double[] norms = new double[0];
            double[,] sk = new double[0,0];
            double[,] yk = new double[0,0];
            double[,] bk = new double[0,0];
            double[] bksk = new double[0];
            double[] tmp = new double[0];
            matinv.matinvreport rep = new matinv.matinvreport();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            
            //
            // Test for inexact L-BFGS preconditioner.
            //
            // We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V.
            // Different K's, from 0 to N, are tried. We test preconditioner
            // code which uses compact L-BFGS update against reference implementation
            // which uses non-compact BFGS scheme.
            //
            // For each K we perform two tests: first for KxN non-zero matrix V,
            // second one for NxN matrix V with last N-K rows set to zero. Last test
            // checks algorithm's ability to handle zero updates.
            //
            tolg = 1.0E-9;
            for(n=1; n<=10; n++)
            {
                for(k=0; k<=n; k++)
                {
                    
                    //
                    // Prepare problem:
                    // * VD, VC, VA, with VC/VA reordered by ascending of VC[i]*norm(VA[i,...])^2
                    // * trial vector S (copies are stored to S0,S1,S2)
                    //
                    vd = new double[n];
                    s0 = new double[n];
                    s1 = new double[n];
                    s2 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        vd[i] = Math.Exp(hqrnd.hqrndnormal(rs));
                        s0[i] = hqrnd.hqrndnormal(rs);
                        s1[i] = s0[i];
                        s2[i] = s0[i];
                    }
                    matgen.rmatrixrndcond(n, 1.0E2, ref va);
                    apserv.rvectorsetlengthatleast(ref vc, n);
                    for(i=0; i<=k-1; i++)
                    {
                        vc[i] = Math.Exp(hqrnd.hqrndnormal(rs));
                    }
                    for(i=k; i<=n-1; i++)
                    {
                        vc[i] = 0;
                        for(j=0; j<=n-1; j++)
                        {
                            va[i,j] = 0.0;
                        }
                    }
                    norms = new double[k];
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += va[i,i_]*va[i,i_];
                        }
                        norms[i] = v*vc[i];
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        for(j=0; j<=k-2; j++)
                        {
                            if( (double)(norms[j])>(double)(norms[j+1]) )
                            {
                                
                                //
                                // Swap elements J and J+1
                                //
                                v = norms[j];
                                norms[j] = norms[j+1];
                                norms[j+1] = v;
                                v = vc[j];
                                vc[j] = vc[j+1];
                                vc[j+1] = v;
                                for(j0=0; j0<=n-1; j0++)
                                {
                                    v = va[j,j0];
                                    va[j,j0] = va[j+1,j0];
                                    va[j+1,j0] = v;
                                }
                            }
                        }
                    }
                    
                    //
                    // Generate reference model and apply it to S2:
                    // * generate approximate Hessian Bk
                    // * calculate inv(Bk)
                    // * calculate inv(Bk)*S2, store to S2
                    //
                    apserv.rmatrixsetlengthatleast(ref sk, k, n);
                    apserv.rmatrixsetlengthatleast(ref yk, k, n);
                    bk = new double[n, n];
                    bksk = new double[n];
                    tmp = new double[n];
                    for(i=0; i<=k-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            sk[i,i_] = va[i,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += va[i,i_]*sk[i,i_];
                        }
                        v = v*vc[i];
                        for(j=0; j<=n-1; j++)
                        {
                            yk[i,j] = vd[j]*sk[i,j]+va[i,j]*v;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( i==j )
                            {
                                bk[i,i] = vd[i];
                            }
                            else
                            {
                                bk[i,j] = 0.0;
                            }
                        }
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        theta = 0.0;
                        for(j0=0; j0<=n-1; j0++)
                        {
                            bksk[j0] = 0;
                            for(j1=0; j1<=n-1; j1++)
                            {
                                theta = theta+sk[i,j0]*bk[j0,j1]*sk[i,j1];
                                bksk[j0] = bksk[j0]+bk[j0,j1]*sk[i,j1];
                            }
                        }
                        theta = 1/theta;
                        rho = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            rho += sk[i,i_]*yk[i,i_];
                        }
                        rho = 1/rho;
                        for(j0=0; j0<=n-1; j0++)
                        {
                            for(j1=0; j1<=n-1; j1++)
                            {
                                bk[j0,j1] = bk[j0,j1]+rho*yk[i,j0]*yk[i,j1];
                            }
                        }
                        for(j0=0; j0<=n-1; j0++)
                        {
                            for(j1=0; j1<=n-1; j1++)
                            {
                                bk[j0,j1] = bk[j0,j1]-theta*bksk[j0]*bksk[j1];
                            }
                        }
                    }
                    matinv.rmatrixinverse(ref bk, n, ref j0, rep);
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += bk[i,i_]*s2[i_];
                        }
                        tmp[i] = v;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        s2[i] = tmp[i];
                    }
                    
                    //
                    // First test for non-zero V:
                    // * apply preconditioner to X0
                    // * compare reference model against implementation being tested
                    //
                    optserv.inexactlbfgspreconditioner(s0, n, vd, vc, va, k, buf);
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(s2[i]-s0[i]))>(double)(tolg));
                    }
                    
                    //
                    // Second test - N-K zero rows appended to V and rows are
                    // randomly reordered. Doing so should not change result,
                    // algorithm must be able to order rows according to second derivative
                    // and skip zero updates.
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        i0 = i+hqrnd.hqrnduniformi(rs, n-i);
                        v = vc[i];
                        vc[i] = vc[i0];
                        vc[i0] = v;
                        for(j=0; j<=n-1; j++)
                        {
                            v = va[i,j];
                            va[i,j] = va[i0,j];
                            va[i0,j] = v;
                        }
                    }
                    optserv.inexactlbfgspreconditioner(s1, n, vd, vc, va, n, buf);
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(s2[i]-s1[i]))>(double)(tolg));
                    }
                }
            }
            
            //
            // Test for exact low-rank preconditioner.
            //
            // We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V.
            // Different K's, from 0 to N, are tried. We test preconditioner
            // code which uses Woodbury update against reference implementation
            // which performs straightforward matrix inversion.
            //
            // For each K we perform two tests: first for KxN non-zero matrix V,
            // second one for NxN matrix V with randomly appended N-K zero rows.
            // Last test checks algorithm's ability to handle zero updates.
            //
            tolg = 1.0E-9;
            for(n=1; n<=10; n++)
            {
                for(k=0; k<=n; k++)
                {
                    
                    //
                    // Prepare problem:
                    // * VD, VC, VA
                    // * trial vector S (copies are stored to S0,S1,S2)
                    //
                    vd = new double[n];
                    s0 = new double[n];
                    s1 = new double[n];
                    s2 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        vd[i] = Math.Exp(hqrnd.hqrndnormal(rs));
                        s0[i] = hqrnd.hqrndnormal(rs);
                        s1[i] = s0[i];
                        s2[i] = s0[i];
                    }
                    matgen.rmatrixrndcond(n, 1.0E2, ref va);
                    apserv.rvectorsetlengthatleast(ref vc, n);
                    for(i=0; i<=k-1; i++)
                    {
                        vc[i] = Math.Exp(hqrnd.hqrndnormal(rs));
                    }
                    for(i=k; i<=n-1; i++)
                    {
                        vc[i] = 0;
                        for(j=0; j<=n-1; j++)
                        {
                            va[i,j] = 0.0;
                        }
                    }
                    
                    //
                    // Generate reference model and apply it to S2
                    //
                    bk = new double[n, n];
                    tmp = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( i==j )
                            {
                                v = vd[i];
                            }
                            else
                            {
                                v = 0.0;
                            }
                            for(j1=0; j1<=k-1; j1++)
                            {
                                v = v+va[j1,i]*vc[j1]*va[j1,j];
                            }
                            bk[i,j] = v;
                        }
                    }
                    matinv.rmatrixinverse(ref bk, n, ref j, rep);
                    alglib.ap.assert(j>0);
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(j=0; j<=n-1; j++)
                        {
                            v = v+bk[i,j]*s2[j];
                        }
                        tmp[i] = v;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        s2[i] = tmp[i];
                    }
                    
                    //
                    // First test for non-zero V:
                    // * apply preconditioner to X0
                    // * compare reference model against implementation being tested
                    //
                    optserv.preparelowrankpreconditioner(vd, vc, va, n, k, lowrankbuf);
                    optserv.applylowrankpreconditioner(s0, lowrankbuf);
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(s2[i]-s0[i]))>(double)(tolg));
                    }
                    
                    //
                    // Second test - N-K zero rows appended to V and rows are
                    // randomly reordered. Doing so should not change result,
                    // algorithm must be able to order rows according to second derivative
                    // and skip zero updates.
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        i0 = i+hqrnd.hqrnduniformi(rs, n-i);
                        v = vc[i];
                        vc[i] = vc[i0];
                        vc[i0] = v;
                        for(j=0; j<=n-1; j++)
                        {
                            v = va[i,j];
                            va[i,j] = va[i0,j];
                            va[i0,j] = v;
                        }
                    }
                    optserv.preparelowrankpreconditioner(vd, vc, va, n, n, lowrankbuf);
                    optserv.applylowrankpreconditioner(s1, lowrankbuf);
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(s2[i]-s1[i]))>(double)(tolg));
                    }
                }
            }
        }


    }
    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();
            bool lserrors = new bool();
            double eps = 0;
            double v = 0;
            double v1 = 0;
            double v2 = 0;
            double[] tmp0 = new double[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;
            lserrors = 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);
                }
            }
            
            //
            // Test linear least squares:
            // * try N=1..5, M=N..2*N
            //                           [ B ]
            // * generate MxN matrix A = [   ], where (M-N)xN submatrix B contains
            //                           [ C ]
            //   random values from [-1,+1], and NxN submatrix C is diagonally dominant
            //   (diagonal of C is equal to 1.0, and magnitude of off-diagonal elements
            //   is smaller than 0.01). Such matrix is guaranteed to be non-degenerate.
            // * generate random known solution xe, set right part b=A*xe
            // * check that results of FBLSSolveLS agree with xe
            //
            eps = 1.0E-6;
            for(n=1; n<=5; n++)
            {
                for(m=n; m<=2*n; m++)
                {
                    a = new double[m, n];
                    for(i=0; i<=m-n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 2*math.randomreal()-1;
                        }
                    }
                    for(i=m-n; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0.01*(2*math.randomreal()-1);
                        }
                        a[i,i-(m-n)] = 1.0;
                    }
                    xe = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        xe[i] = 2*math.randomreal()-1;
                    }
                    b = new double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*xe[i_];
                        }
                        b[i] = v;
                    }
                    fbls.fblssolvels(ref a, ref b, m, n, ref tmp0, ref tmp1, ref tmp2);
                    for(i=0; i<=n-1; i++)
                    {
                        lserrors = lserrors || (double)(Math.Abs(b[i]-xe[i]))>(double)(eps);
                    }
                }
            }
            
            //
            // report
            //
            waserrors = cgerrors || lserrors;
            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();
                }
                System.Console.Write("LS ERRORS:                               ");
                if( lserrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testfbls(bool silent)
        {
            return testfbls(silent);
        }


    }
    public class testcqmodelsunit
    {
        public static bool testcqmodels(bool silent)
        {
            bool result = new bool();
            bool eval0errors = new bool();
            bool eval1errors = new bool();
            bool eval2errors = new bool();
            bool newton0errors = new bool();
            bool newton1errors = new bool();
            bool newton2errors = new bool();
            bool waserrors = new bool();
            cqmodels.convexquadraticmodel s = new cqmodels.convexquadraticmodel();
            int nkind = 0;
            int kmax = 0;
            int n = 0;
            int k = 0;
            int i = 0;
            int pass = 0;
            int j = 0;
            double alpha = 0;
            double theta = 0;
            double tau = 0;
            double v = 0;
            double v2 = 0;
            double h = 0;
            double f0 = 0;
            double mkind = 0;
            double xtadx2 = 0;
            double noise = 0;
            double[,] a = new double[0,0];
            double[,] q = new double[0,0];
            double[] b = new double[0];
            double[] r = new double[0];
            double[] x = new double[0];
            double[] x0 = new double[0];
            double[] xc = new double[0];
            double[] d = new double[0];
            double[] ge = new double[0];
            double[] gt = new double[0];
            double[] tmp0 = new double[0];
            double[] adx = new double[0];
            double[] adxe = new double[0];
            bool[] activeset = new bool[0];
            int i_ = 0;

            waserrors = false;
            
            //
            // Eval0 test: unconstrained model evaluation
            //
            eval0errors = false;
            for(n=1; n<=5; n++)
            {
                for(k=0; k<=2*n; k++)
                {
                    
                    //
                    // Allocate place
                    //
                    a = new double[n, n];
                    b = new double[n];
                    x = new double[n];
                    d = new double[n];
                    ge = new double[n];
                    gt = new double[n];
                    if( k>0 )
                    {
                        q = new double[k, n];
                        r = new double[k];
                    }
                    
                    //
                    // Generate problem
                    //
                    alpha = math.randomreal()+1.0;
                    theta = math.randomreal()+1.0;
                    tau = math.randomreal()*math.randominteger(2);
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 10*(1+math.randomreal());
                        b[i] = 2*math.randomreal()-1;
                        d[i] = math.randomreal()+1;
                        for(j=i+1; j<=n-1; j++)
                        {
                            v = 0.1*math.randomreal()-0.05;
                            a[i,j] = v;
                            a[j,i] = v;
                        }
                        for(j=0; j<=k-1; j++)
                        {
                            q[j,i] = 2*math.randomreal()-1;
                        }
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        r[i] = 2*math.randomreal()-1;
                    }
                    
                    //
                    // Build model
                    //
                    cqmodels.cqminit(n, s);
                    cqmodels.cqmseta(s, a, (double)(math.randomreal())>(double)(0.5), alpha);
                    cqmodels.cqmsetb(s, b);
                    cqmodels.cqmsetq(s, q, r, k, theta);
                    cqmodels.cqmsetd(s, d, tau);
                    
                    //
                    // Evaluate and compare:
                    // * X          -   random point
                    // * GE         -   "exact" gradient
                    // * XTADX2     -   x'*(alpha*A+tau*D)*x/2
                    // * ADXE       -   (alpha*A+tau*D)*x
                    // * V          -   model value at X
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                        ge[i] = 0.0;
                    }
                    v = 0.0;
                    xtadx2 = 0.0;
                    adxe = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        adxe[i] = 0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+x[i]*b[i];
                        ge[i] = ge[i]+b[i];
                        v = v+0.5*math.sqr(x[i])*tau*d[i];
                        ge[i] = ge[i]+x[i]*tau*d[i];
                        adxe[i] = adxe[i]+x[i]*tau*d[i];
                        xtadx2 = xtadx2+0.5*math.sqr(x[i])*tau*d[i];
                        for(j=0; j<=n-1; j++)
                        {
                            v = v+0.5*alpha*x[i]*a[i,j]*x[j];
                            ge[i] = ge[i]+alpha*a[i,j]*x[j];
                            adxe[i] = adxe[i]+alpha*a[i,j]*x[j];
                            xtadx2 = xtadx2+0.5*alpha*x[i]*a[i,j]*x[j];
                        }
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v2 = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v2 += q[i,i_]*x[i_];
                        }
                        v = v+0.5*theta*math.sqr(v2-r[i]);
                        for(j=0; j<=n-1; j++)
                        {
                            ge[j] = ge[j]+theta*(v2-r[i])*q[i,j];
                        }
                    }
                    v2 = cqmodels.cqmeval(s, x);
                    eval0errors = eval0errors || (double)(Math.Abs(v-v2))>(double)(10000*math.machineepsilon);
                    cqmodels.cqmevalx(s, x, ref v2, ref noise);
                    eval0errors = eval0errors || (double)(Math.Abs(v-v2))>(double)(10000*math.machineepsilon);
                    eval0errors = (eval0errors || (double)(noise)<(double)(0)) || (double)(noise)>(double)(10000*math.machineepsilon);
                    v2 = cqmodels.cqmxtadx2(s, x);
                    eval0errors = eval0errors || (double)(Math.Abs(xtadx2-v2))>(double)(10000*math.machineepsilon);
                    cqmodels.cqmgradunconstrained(s, x, ref gt);
                    for(i=0; i<=n-1; i++)
                    {
                        eval0errors = eval0errors || (double)(Math.Abs(ge[i]-gt[i]))>(double)(10000*math.machineepsilon);
                    }
                    cqmodels.cqmadx(s, x, ref adx);
                    for(i=0; i<=n-1; i++)
                    {
                        eval0errors = eval0errors || (double)(Math.Abs(adx[i]-adxe[i]))>(double)(10000*math.machineepsilon);
                    }
                }
            }
            waserrors = waserrors || eval0errors;
            
            //
            // Eval1 test: constrained model evaluation
            //
            eval1errors = false;
            for(n=1; n<=5; n++)
            {
                for(k=0; k<=2*n; k++)
                {
                    
                    //
                    // Allocate place
                    //
                    a = new double[n, n];
                    b = new double[n];
                    x = new double[n];
                    xc = new double[n];
                    activeset = new bool[n];
                    if( k>0 )
                    {
                        q = new double[k, n];
                        r = new double[k];
                    }
                    
                    //
                    // Generate problem
                    //
                    alpha = math.randomreal()+1.0;
                    theta = math.randomreal()+1.0;
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 10*(1+math.randomreal());
                        b[i] = 2*math.randomreal()-1;
                        xc[i] = 2*math.randomreal()-1;
                        activeset[i] = (double)(math.randomreal())>(double)(0.5);
                        for(j=i+1; j<=n-1; j++)
                        {
                            v = 0.1*math.randomreal()-0.05;
                            a[i,j] = v;
                            a[j,i] = v;
                        }
                        for(j=0; j<=k-1; j++)
                        {
                            q[j,i] = 2*math.randomreal()-1;
                        }
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        r[i] = 2*math.randomreal()-1;
                    }
                    
                    //
                    // Build model, evaluate at random point X, compare
                    //
                    cqmodels.cqminit(n, s);
                    cqmodels.cqmseta(s, a, (double)(math.randomreal())>(double)(0.5), alpha);
                    cqmodels.cqmsetb(s, b);
                    cqmodels.cqmsetq(s, q, r, k, theta);
                    cqmodels.cqmsetactiveset(s, xc, activeset);
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                        if( !activeset[i] )
                        {
                            xc[i] = x[i];
                        }
                    }
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+xc[i]*b[i];
                        for(j=0; j<=n-1; j++)
                        {
                            v = v+0.5*alpha*xc[i]*a[i,j]*xc[j];
                        }
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v2 = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v2 += q[i,i_]*xc[i_];
                        }
                        v = v+0.5*theta*math.sqr(v2-r[i]);
                    }
                    eval1errors = eval1errors || (double)(Math.Abs(v-cqmodels.cqmeval(s, xc)))>(double)(10000*math.machineepsilon);
                    eval1errors = eval1errors || (double)(Math.Abs(v-cqmodels.cqmdebugconstrainedevalt(s, x)))>(double)(10000*math.machineepsilon);
                    eval1errors = eval1errors || (double)(Math.Abs(v-cqmodels.cqmdebugconstrainedevale(s, x)))>(double)(10000*math.machineepsilon);
                }
            }
            waserrors = waserrors || eval1errors;
            
            //
            // Eval2 test: we generate empty problem and apply sequence of random transformations,
            // re-evaluating and re-checking model after each modification.
            //
            // The purpose of such test is to ensure that our caching strategy works correctly.
            //
            eval2errors = false;
            for(n=1; n<=5; n++)
            {
                kmax = 2*n;
                a = new double[n, n];
                b = new double[n];
                d = new double[n];
                x = new double[n];
                xc = new double[n];
                q = new double[kmax, n];
                r = new double[kmax];
                activeset = new bool[n];
                tmp0 = new double[n];
                alpha = 0.0;
                theta = 0.0;
                k = 0;
                tau = 1.0+math.randomreal();
                for(i=0; i<=n-1; i++)
                {
                    d[i] = 1.0;
                    b[i] = 0.0;
                    xc[i] = 2*math.randomreal()-1;
                    for(j=0; j<=n-1; j++)
                    {
                        a[i,j] = 0;
                    }
                }
                cqmodels.cqminit(n, s);
                cqmodels.cqmsetd(s, d, tau);
                for(pass=1; pass<=100; pass++)
                {
                    
                    //
                    // Select random modification type, apply modification.
                    //
                    // MKind is a random integer in [0,7] - number of specific 
                    // modification to apply.
                    //
                    mkind = math.randominteger(8);
                    if( (double)(mkind)==(double)(0) )
                    {
                        
                        //
                        // Set non-zero D
                        //
                        tau = 1.0+math.randomreal();
                        for(i=0; i<=n-1; i++)
                        {
                            d[i] = 2*math.randomreal()+1;
                        }
                        cqmodels.cqmsetd(s, d, tau);
                    }
                    else
                    {
                        if( (double)(mkind)==(double)(1) )
                        {
                            
                            //
                            // Set zero D.
                            // In case Alpha=0, set non-zero A.
                            //
                            if( (double)(alpha)==(double)(0) )
                            {
                                alpha = 1.0+math.randomreal();
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i+1; j<=n-1; j++)
                                    {
                                        a[i,j] = 0.2*math.randomreal()-0.1;
                                        a[j,i] = a[i,j];
                                    }
                                }
                                for(i=0; i<=n-1; i++)
                                {
                                    a[i,i] = 4+2*math.randomreal();
                                }
                                cqmodels.cqmseta(s, a, (double)(math.randomreal())>(double)(0.5), alpha);
                            }
                            tau = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                d[i] = 0;
                            }
                            cqmodels.cqmsetd(s, d, 0.0);
                        }
                        else
                        {
                            if( (double)(mkind)==(double)(2) )
                            {
                                
                                //
                                // Set non-zero A
                                //
                                alpha = 1.0+math.randomreal();
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i+1; j<=n-1; j++)
                                    {
                                        a[i,j] = 0.2*math.randomreal()-0.1;
                                        a[j,i] = a[i,j];
                                    }
                                }
                                for(i=0; i<=n-1; i++)
                                {
                                    a[i,i] = 4+2*math.randomreal();
                                }
                                cqmodels.cqmseta(s, a, (double)(math.randomreal())>(double)(0.5), alpha);
                            }
                            else
                            {
                                if( (double)(mkind)==(double)(3) )
                                {
                                    
                                    //
                                    // Set zero A.
                                    // In case Tau=0, set non-zero D.
                                    //
                                    if( (double)(tau)==(double)(0) )
                                    {
                                        tau = 1.0+math.randomreal();
                                        for(i=0; i<=n-1; i++)
                                        {
                                            d[i] = 2*math.randomreal()+1;
                                        }
                                        cqmodels.cqmsetd(s, d, tau);
                                    }
                                    alpha = 0.0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=0; j<=n-1; j++)
                                        {
                                            a[i,j] = 0;
                                        }
                                    }
                                    cqmodels.cqmseta(s, a, (double)(math.randomreal())>(double)(0.5), alpha);
                                }
                                else
                                {
                                    if( (double)(mkind)==(double)(4) )
                                    {
                                        
                                        //
                                        // Set B.
                                        //
                                        for(i=0; i<=n-1; i++)
                                        {
                                            b[i] = 2*math.randomreal()-1;
                                        }
                                        cqmodels.cqmsetb(s, b);
                                    }
                                    else
                                    {
                                        if( (double)(mkind)==(double)(5) )
                                        {
                                            
                                            //
                                            // Set Q.
                                            //
                                            k = math.randominteger(kmax+1);
                                            theta = 1.0+math.randomreal();
                                            for(i=0; i<=k-1; i++)
                                            {
                                                r[i] = 2*math.randomreal()-1;
                                                for(j=0; j<=n-1; j++)
                                                {
                                                    q[i,j] = 2*math.randomreal()-1;
                                                }
                                            }
                                            cqmodels.cqmsetq(s, q, r, k, theta);
                                        }
                                        else
                                        {
                                            if( (double)(mkind)==(double)(6) )
                                            {
                                                
                                                //
                                                // Set active set
                                                //
                                                for(i=0; i<=n-1; i++)
                                                {
                                                    activeset[i] = (double)(math.randomreal())>(double)(0.5);
                                                    xc[i] = 2*math.randomreal()-1;
                                                }
                                                cqmodels.cqmsetactiveset(s, xc, activeset);
                                            }
                                            else
                                            {
                                                if( (double)(mkind)==(double)(7) )
                                                {
                                                    
                                                    //
                                                    // Rewrite main diagonal
                                                    //
                                                    if( (double)(alpha)==(double)(0) )
                                                    {
                                                        alpha = 1.0;
                                                    }
                                                    for(i=0; i<=n-1; i++)
                                                    {
                                                        tmp0[i] = 1+math.randomreal();
                                                        a[i,i] = tmp0[i]/alpha;
                                                    }
                                                    cqmodels.cqmrewritedensediagonal(s, tmp0);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    
                    //
                    // generate random point with respect to constraints,
                    // test model at this point
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                        if( activeset[i] )
                        {
                            x[i] = xc[i];
                        }
                    }
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+x[i]*b[i];
                    }
                    if( (double)(tau)>(double)(0) )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            v = v+0.5*tau*d[i]*math.sqr(x[i]);
                        }
                    }
                    if( (double)(alpha)>(double)(0) )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                v = v+0.5*alpha*x[i]*a[i,j]*x[j];
                            }
                        }
                    }
                    if( (double)(theta)>(double)(0) )
                    {
                        for(i=0; i<=k-1; i++)
                        {
                            v2 = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v2 += q[i,i_]*x[i_];
                            }
                            v = v+0.5*theta*math.sqr(v2-r[i]);
                        }
                    }
                    v2 = cqmodels.cqmeval(s, x);
                    eval2errors = eval2errors || (double)(Math.Abs(v-v2))>(double)(10000*math.machineepsilon);
                    v2 = cqmodels.cqmdebugconstrainedevalt(s, x);
                    eval2errors = eval2errors || (double)(Math.Abs(v-v2))>(double)(10000*math.machineepsilon);
                    v2 = cqmodels.cqmdebugconstrainedevale(s, x);
                    eval2errors = eval2errors || (double)(Math.Abs(v-v2))>(double)(10000*math.machineepsilon);
                }
            }
            waserrors = waserrors || eval2errors;
            
            //
            // Newton0 test: unconstrained optimization
            //
            newton0errors = false;
            for(n=1; n<=5; n++)
            {
                for(k=0; k<=2*n; k++)
                {
                    
                    //
                    // Allocate place
                    //
                    a = new double[n, n];
                    b = new double[n];
                    x = new double[n];
                    x0 = new double[n];
                    if( k>0 )
                    {
                        q = new double[k, n];
                        r = new double[k];
                    }
                    
                    //
                    // Generate problem with known solution x0:
                    //   min f(x),
                    //   f(x) = 0.5*(x-x0)'*A*(x-x0)
                    //        = 0.5*x'*A*x + (-x0'*A)*x + 0.5*x0'*A*x0'
                    //
                    alpha = math.randomreal()+1.0;
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 2*math.randomreal()-1;
                        a[i,i] = 10*(1+math.randomreal());
                        for(j=i+1; j<=n-1; j++)
                        {
                            v = 0.1*math.randomreal()-0.05;
                            a[i,j] = v;
                            a[j,i] = v;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*x0[i_];
                        }
                        b[i] = -(alpha*v);
                    }
                    theta = math.randomreal()+1.0;
                    for(i=0; i<=k-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            q[i,j] = 2*math.randomreal()-1;
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += q[i,i_]*x0[i_];
                        }
                        r[i] = v;
                    }
                    
                    //
                    // Build model, evaluate at random point X, compare
                    //
                    cqmodels.cqminit(n, s);
                    cqmodels.cqmseta(s, a, (double)(math.randomreal())>(double)(0.5), alpha);
                    cqmodels.cqmsetb(s, b);
                    cqmodels.cqmsetq(s, q, r, k, theta);
                    cqmodels.cqmconstrainedoptimum(s, ref x);
                    for(i=0; i<=n-1; i++)
                    {
                        newton0errors = newton0errors || (double)(Math.Abs(x[i]-x0[i]))>(double)(1.0E6*math.machineepsilon);
                    }
                }
            }
            waserrors = waserrors || newton0errors;
            
            //
            // Newton1 test: constrained optimization
            //
            newton1errors = false;
            h = 1.0E-3;
            for(n=1; n<=5; n++)
            {
                for(k=0; k<=2*n; k++)
                {
                    
                    //
                    // Allocate place
                    //
                    a = new double[n, n];
                    b = new double[n];
                    x = new double[n];
                    xc = new double[n];
                    activeset = new bool[n];
                    if( k>0 )
                    {
                        q = new double[k, n];
                        r = new double[k];
                    }
                    
                    //
                    // Generate test problem with unknown solution.
                    //
                    alpha = math.randomreal()+1.0;
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 10*(1+math.randomreal());
                        b[i] = 2*math.randomreal()-1;
                        xc[i] = 2*math.randomreal()-1;
                        activeset[i] = (double)(math.randomreal())>(double)(0.5);
                        for(j=i+1; j<=n-1; j++)
                        {
                            v = 0.1*math.randomreal()-0.05;
                            a[i,j] = v;
                            a[j,i] = v;
                        }
                    }
                    theta = math.randomreal()+1.0;
                    for(i=0; i<=k-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            q[i,j] = 2*math.randomreal()-1;
                        }
                        r[i] = 2*math.randomreal()-1;
                    }
                    
                    //
                    // Build model, find solution
                    //
                    cqmodels.cqminit(n, s);
                    cqmodels.cqmseta(s, a, (double)(math.randomreal())>(double)(0.5), alpha);
                    cqmodels.cqmsetb(s, b);
                    cqmodels.cqmsetq(s, q, r, k, theta);
                    cqmodels.cqmsetactiveset(s, xc, activeset);
                    if( cqmodels.cqmconstrainedoptimum(s, ref x) )
                    {
                        
                        //
                        // Check that constraints are satisfied,
                        // and that solution is true optimum
                        //
                        f0 = cqmodels.cqmeval(s, x);
                        for(i=0; i<=n-1; i++)
                        {
                            newton1errors = newton1errors || (activeset[i] && (double)(x[i])!=(double)(xc[i]));
                            if( !activeset[i] )
                            {
                                v = x[i];
                                x[i] = v+h;
                                v2 = cqmodels.cqmeval(s, x);
                                newton1errors = newton1errors || (double)(v2)<(double)(f0);
                                x[i] = v-h;
                                v2 = cqmodels.cqmeval(s, x);
                                newton1errors = newton1errors || (double)(v2)<(double)(f0);
                                x[i] = v;
                            }
                        }
                    }
                    else
                    {
                        newton1errors = true;
                    }
                }
            }
            waserrors = waserrors || newton1errors;
            
            //
            // Newton2 test: we test ability to work with diagonal matrices, including
            // very large ones (up to 100.000 elements). This test checks that:
            // a) we can work with Alpha=0, i.e. when we have strictly diagonal A
            // b) diagonal problems are handled efficiently, i.e. algorithm will
            //    successfully solve problem with N=100.000
            //
            // Test problem:
            // * diagonal term D and rank-K term Q
            // * known solution X0,
            // * about 50% of constraints are active and equal to components of X0
            //
            newton2errors = false;
            for(nkind=0; nkind<=5; nkind++)
            {
                for(k=0; k<=5; k++)
                {
                    n = (int)Math.Round(Math.Pow(n, nkind));
                    
                    //
                    // generate problem
                    //
                    d = new double[n];
                    b = new double[n];
                    x = new double[n];
                    x0 = new double[n];
                    activeset = new bool[n];
                    if( k>0 )
                    {
                        q = new double[k, n];
                        r = new double[k];
                    }
                    tau = 1+math.randomreal();
                    theta = 1+math.randomreal();
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 2*math.randomreal()-1;
                        d[i] = 1+math.randomreal();
                        b[i] = -(x0[i]*tau*d[i]);
                        activeset[i] = (double)(math.randomreal())>(double)(0.5);
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(j=0; j<=n-1; j++)
                        {
                            q[i,j] = 2*math.randomreal()-1;
                            v = v+q[i,j]*x0[j];
                        }
                        r[i] = v;
                    }
                    
                    //
                    // Solve, test
                    //
                    cqmodels.cqminit(n, s);
                    cqmodels.cqmsetb(s, b);
                    cqmodels.cqmsetd(s, d, tau);
                    cqmodels.cqmsetq(s, q, r, k, theta);
                    cqmodels.cqmsetactiveset(s, x0, activeset);
                    if( cqmodels.cqmconstrainedoptimum(s, ref x) )
                    {
                        
                        //
                        // Check that constraints are satisfied,
                        // and that solution is true optimum
                        //
                        f0 = cqmodels.cqmeval(s, x);
                        for(i=0; i<=n-1; i++)
                        {
                            newton2errors = newton2errors || (activeset[i] && (double)(x[i])!=(double)(x0[i]));
                            newton2errors = newton2errors || (!activeset[i] && (double)(Math.Abs(x[i]-x0[i]))>(double)(1000*math.machineepsilon));
                        }
                        
                        //
                        // Check that constrained evaluation at some point gives correct results
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            if( activeset[i] )
                            {
                                x[i] = x0[i];
                            }
                            else
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                        }
                        v = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            v = v+0.5*tau*d[i]*math.sqr(x[i])+x[i]*b[i];
                        }
                        for(i=0; i<=k-1; i++)
                        {
                            v2 = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v2 += q[i,i_]*x[i_];
                            }
                            v = v+0.5*theta*math.sqr(v2-r[i]);
                        }
                        v2 = cqmodels.cqmeval(s, x);
                        newton2errors = (newton2errors || !math.isfinite(v2)) || (double)(Math.Abs(v-v2))>(double)(10000*math.machineepsilon);
                        v2 = cqmodels.cqmdebugconstrainedevalt(s, x);
                        newton2errors = (newton2errors || !math.isfinite(v2)) || (double)(Math.Abs(v-v2))>(double)(10000*math.machineepsilon);
                        v2 = cqmodels.cqmdebugconstrainedevale(s, x);
                        newton2errors = (newton2errors || !math.isfinite(v2)) || (double)(Math.Abs(v-v2))>(double)(10000*math.machineepsilon);
                    }
                    else
                    {
                        newton2errors = true;
                    }
                }
            }
            waserrors = waserrors || newton2errors;
            
            //
            // report
            //
            if( !silent )
            {
                System.Console.Write("TESTING CONVEX QUADRATIC MODELS");
                System.Console.WriteLine();
                System.Console.Write("Eval0 test:                               ");
                if( eval0errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Eval1 test:                               ");
                if( eval1errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Eval2 test:                               ");
                if( eval2errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Newton0 test:                             ");
                if( newton0errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Newton1 test:                             ");
                if( newton1errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Newton2 test:                             ");
                if( newton2errors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testcqmodels(bool silent)
        {
            return testcqmodels(silent);
        }


    }
    public class testsnnlsunit
    {
        public static bool testsnnls(bool silent)
        {
            bool result = new bool();
            bool test0errors = new bool();
            bool test1errors = new bool();
            bool test2errors = new bool();
            bool testnewtonerrors = new bool();
            bool waserrors = new bool();
            double eps = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            int ns = 0;
            int nd = 0;
            int nr = 0;
            double[,] densea = new double[0,0];
            double[,] effectivea = new double[0,0];
            bool[] isconstrained = new bool[0];
            double[] g = new double[0];
            double[] b = new double[0];
            double[] x = new double[0];
            double[] xs = new double[0];
            snnls.snnlssolver s = new snnls.snnlssolver();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            double rho = 0;
            double xtol = 0;
            int nmax = 0;
            int i_ = 0;

            test0errors = false;
            test1errors = false;
            test2errors = false;
            testnewtonerrors = false;
            waserrors = false;
            hqrnd.hqrndrandomize(rs);
            nmax = 10;
            
            //
            // Test 2 (comes first because it is very basic):
            // * NS=0
            // * ND in [1,NMAX]
            // * NR=ND
            // * DenseA is diagonal with positive entries
            // * B is random
            // * random constraints
            // Exact solution is known and can be tested
            //
            eps = 1.0E-12;
            for(nd=1; nd<=nmax; nd++)
            {
                
                //
                // Generate problem
                //
                ns = 0;
                nr = nd;
                densea = new double[nd, nd];
                b = new double[nd];
                isconstrained = new bool[nd];
                for(i=0; i<=nd-1; i++)
                {
                    for(j=0; j<=nd-1; j++)
                    {
                        densea[i,j] = 0;
                    }
                    densea[i,i] = 1+hqrnd.hqrnduniformi(rs, 2);
                    b[i] = (1+hqrnd.hqrnduniformi(rs, 2))*(2*hqrnd.hqrnduniformi(rs, 2)-1);
                    isconstrained[i] = (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5);
                }
                
                //
                // Solve with SNNLS solver
                //
                snnls.snnlsinit(0, 0, 0, s);
                snnls.snnlssetproblem(s, densea, b, 0, nd, nd);
                for(i=0; i<=nd-1; i++)
                {
                    if( !isconstrained[i] )
                    {
                        snnls.snnlsdropnnc(s, i);
                    }
                }
                snnls.snnlssolve(s, ref x);
                
                //
                // Check
                //
                for(i=0; i<=nd-1; i++)
                {
                    if( isconstrained[i] )
                    {
                        apserv.seterrorflag(ref test2errors, (double)(Math.Abs(x[i]-Math.Max(b[i]/densea[i,i], 0.0)))>(double)(eps));
                        apserv.seterrorflag(ref test2errors, (double)(x[i])<(double)(0.0));
                    }
                    else
                    {
                        apserv.seterrorflag(ref test2errors, (double)(Math.Abs(x[i]-b[i]/densea[i,i]))>(double)(eps));
                    }
                }
            }
            
            //
            // Test 0:
            // * NS in [0,NMAX]
            // * ND in [0,NMAX]
            // * NR in [NS,NS+ND+NMAX]
            // * NS+ND>0, NR>0
            // * about 50% of variables are constrained
            // * we check that constrained gradient is small at the solution
            //
            eps = 1.0E-5;
            for(ns=0; ns<=nmax; ns++)
            {
                for(nd=0; nd<=nmax; nd++)
                {
                    for(nr=ns; nr<=ns+nd+nmax; nr++)
                    {
                        
                        //
                        // Skip NS+ND=0, NR=0
                        //
                        if( ns+nd==0 )
                        {
                            continue;
                        }
                        if( nr==0 )
                        {
                            continue;
                        }
                        
                        //
                        // Generate problem:
                        // * DenseA, array[NR,ND]
                        // * EffectiveA, array[NR,NS+ND]
                        // * B, array[NR]
                        // * IsConstrained, array[NS+ND]
                        //
                        if( nd>0 )
                        {
                            densea = new double[nr, nd];
                            for(i=0; i<=nr-1; i++)
                            {
                                for(j=0; j<=nd-1; j++)
                                {
                                    densea[i,j] = 2*math.randomreal()-1;
                                }
                            }
                        }
                        effectivea = new double[nr, ns+nd];
                        for(i=0; i<=nr-1; i++)
                        {
                            for(j=0; j<=ns+nd-1; j++)
                            {
                                effectivea[i,j] = 0.0;
                            }
                        }
                        for(i=0; i<=ns-1; i++)
                        {
                            effectivea[i,i] = 1.0;
                        }
                        for(i=0; i<=nr-1; i++)
                        {
                            for(j=0; j<=nd-1; j++)
                            {
                                effectivea[i,ns+j] = densea[i,j];
                            }
                        }
                        b = new double[nr];
                        for(i=0; i<=nr-1; i++)
                        {
                            b[i] = 2*math.randomreal()-1;
                        }
                        isconstrained = new bool[ns+nd];
                        for(i=0; i<=ns+nd-1; i++)
                        {
                            isconstrained[i] = (double)(math.randomreal())>(double)(0.5);
                        }
                        
                        //
                        // Solve with SNNLS solver
                        //
                        snnls.snnlsinit(0, 0, 0, s);
                        snnls.snnlssetproblem(s, densea, b, ns, nd, nr);
                        for(i=0; i<=ns+nd-1; i++)
                        {
                            if( !isconstrained[i] )
                            {
                                snnls.snnlsdropnnc(s, i);
                            }
                        }
                        snnls.snnlssolve(s, ref x);
                        
                        //
                        // Check non-negativity
                        //
                        for(i=0; i<=ns+nd-1; i++)
                        {
                            apserv.seterrorflag(ref test0errors, isconstrained[i] && (double)(x[i])<(double)(0));
                        }
                        
                        //
                        // Calculate gradient A'*A*x-b'*A.
                        // Check projected gradient (each component must be less than Eps).
                        //
                        g = new double[ns+nd];
                        for(i=0; i<=ns+nd-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=nr-1;i_++)
                            {
                                v += b[i_]*effectivea[i_,i];
                            }
                            g[i] = -v;
                        }
                        for(i=0; i<=nr-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=ns+nd-1;i_++)
                            {
                                v += effectivea[i,i_]*x[i_];
                            }
                            for(i_=0; i_<=ns+nd-1;i_++)
                            {
                                g[i_] = g[i_] + v*effectivea[i,i_];
                            }
                        }
                        for(i=0; i<=ns+nd-1; i++)
                        {
                            if( !isconstrained[i] || (double)(x[i])>(double)(0) )
                            {
                                apserv.seterrorflag(ref test0errors, (double)(Math.Abs(g[i]))>(double)(eps));
                            }
                            else
                            {
                                apserv.seterrorflag(ref test0errors, (double)(g[i])<(double)(-eps));
                            }
                        }
                    }
                }
            }
            
            //
            // Test 1: ability of the solver to take very short steps.
            //
            // We solve problem similar to one solver in test 0, but with
            // progressively decreased magnitude of variables. We generate
            // problem with already-known solution and compare results against it.
            //
            xtol = 1.0E6*math.machineepsilon;
            for(ns=0; ns<=nmax; ns++)
            {
                for(nd=0; nd<=nmax; nd++)
                {
                    for(nr=ns; nr<=ns+nd+nmax; nr++)
                    {
                        for(k=0; k<=20; k++)
                        {
                            
                            //
                            // Skip NS+ND=0, NR=0
                            //
                            // Skip degenerate problems (NR<NS+ND) - important for this particular test.
                            //
                            if( ns+nd==0 )
                            {
                                continue;
                            }
                            if( nr==0 )
                            {
                                continue;
                            }
                            if( nr<ns+nd )
                            {
                                continue;
                            }
                            
                            //
                            // Generate problem:
                            // * DenseA, array[NR,ND]
                            // * EffectiveA, array[NR,NS+ND]
                            // * B, array[NR]
                            // * IsConstrained, array[NS+ND]
                            //
                            rho = Math.Pow(10, -k);
                            if( nd>0 )
                            {
                                densea = new double[nr, nd];
                                for(i=0; i<=nr-1; i++)
                                {
                                    for(j=0; j<=nd-1; j++)
                                    {
                                        densea[i,j] = 2*hqrnd.hqrnduniformr(rs)-1;
                                    }
                                }
                            }
                            effectivea = new double[nr, ns+nd];
                            for(i=0; i<=nr-1; i++)
                            {
                                for(j=0; j<=ns+nd-1; j++)
                                {
                                    effectivea[i,j] = 0.0;
                                }
                            }
                            for(i=0; i<=ns-1; i++)
                            {
                                effectivea[i,i] = 1.0;
                            }
                            for(i=0; i<=nr-1; i++)
                            {
                                for(j=0; j<=nd-1; j++)
                                {
                                    effectivea[i,ns+j] = densea[i,j];
                                }
                            }
                            xs = new double[ns+nd];
                            isconstrained = new bool[ns+nd];
                            for(i=0; i<=ns+nd-1; i++)
                            {
                                xs[i] = rho*(hqrnd.hqrnduniformr(rs)-0.5);
                                isconstrained[i] = (double)(xs[i])>(double)(0.0);
                            }
                            b = new double[nr];
                            for(i=0; i<=nr-1; i++)
                            {
                                v = 0.0;
                                for(j=0; j<=ns+nd-1; j++)
                                {
                                    v = v+effectivea[i,j]*xs[j];
                                }
                                b[i] = v;
                            }
                            
                            //
                            // Solve with SNNLS solver
                            //
                            snnls.snnlsinit(0, 0, 0, s);
                            snnls.snnlssetproblem(s, densea, b, ns, nd, nr);
                            for(i=0; i<=ns+nd-1; i++)
                            {
                                if( !isconstrained[i] )
                                {
                                    snnls.snnlsdropnnc(s, i);
                                }
                            }
                            snnls.snnlssolve(s, ref x);
                            
                            //
                            // Check non-negativity
                            //
                            for(i=0; i<=ns+nd-1; i++)
                            {
                                apserv.seterrorflag(ref test1errors, isconstrained[i] && (double)(x[i])<(double)(0));
                            }
                            
                            //
                            // Compare with true solution
                            //
                            for(i=0; i<=ns+nd-1; i++)
                            {
                                apserv.seterrorflag(ref test1errors, (double)(Math.Abs(xs[i]-x[i]))>(double)(rho*xtol));
                            }
                        }
                    }
                }
            }
            
            //
            // Test for Newton phase:
            // * NS in [0,NMAX]
            // * ND in [0,NMAX]
            // * NR in [NS,NS+ND+NMAX]
            // * NS+ND>0, NR>0
            // * all variables are unconstrained
            // * S.DebugMaxNewton is set to 1, S.RefinementIts is set to 1,
            //   i.e. algorithm is terminated after one Newton iteration, and no
            //   iterative refinement is used.
            // * we test that gradient is small at solution, i.e. one Newton iteration
            //   on unconstrained problem is enough to find solution. In case of buggy
            //   Newton solver one iteration won't move us to the solution - it may
            //   decrease function value, but won't find exact solution.
            //
            // This test is intended to catch subtle bugs in the Newton solver which
            // do NOT prevent algorithm from converging to the solution, but slow it
            // down (convergence becomes linear or even slower).
            //
            eps = 1.0E-4;
            for(ns=0; ns<=nmax; ns++)
            {
                for(nd=0; nd<=nmax; nd++)
                {
                    for(nr=ns; nr<=ns+nd+nmax; nr++)
                    {
                        
                        //
                        // Skip NS+ND=0, NR=0
                        //
                        if( ns+nd==0 )
                        {
                            continue;
                        }
                        if( nr==0 )
                        {
                            continue;
                        }
                        
                        //
                        // Generate problem:
                        // * DenseA, array[NR,ND]
                        // * EffectiveA, array[NR,NS+ND]
                        // * B, array[NR]
                        // * IsConstrained, array[NS+ND]
                        //
                        if( nd>0 )
                        {
                            densea = new double[nr, nd];
                            for(i=0; i<=nr-1; i++)
                            {
                                for(j=0; j<=nd-1; j++)
                                {
                                    densea[i,j] = hqrnd.hqrndnormal(rs);
                                }
                            }
                        }
                        effectivea = new double[nr, ns+nd];
                        for(i=0; i<=nr-1; i++)
                        {
                            for(j=0; j<=ns+nd-1; j++)
                            {
                                effectivea[i,j] = 0.0;
                            }
                        }
                        for(i=0; i<=ns-1; i++)
                        {
                            effectivea[i,i] = 1.0;
                        }
                        for(i=0; i<=nr-1; i++)
                        {
                            for(j=0; j<=nd-1; j++)
                            {
                                effectivea[i,ns+j] = densea[i,j];
                            }
                        }
                        b = new double[nr];
                        for(i=0; i<=nr-1; i++)
                        {
                            b[i] = hqrnd.hqrndnormal(rs);
                        }
                        
                        //
                        // Solve with SNNLS solver
                        //
                        snnls.snnlsinit(0, 0, 0, s);
                        snnls.snnlssetproblem(s, densea, b, ns, nd, nr);
                        for(i=0; i<=ns+nd-1; i++)
                        {
                            snnls.snnlsdropnnc(s, i);
                        }
                        s.debugmaxinnerits = 1;
                        snnls.snnlssolve(s, ref x);
                        
                        //
                        // Calculate gradient A'*A*x-b'*A.
                        // Check projected gradient (each component must be less than Eps).
                        //
                        g = new double[ns+nd];
                        for(i=0; i<=ns+nd-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=nr-1;i_++)
                            {
                                v += b[i_]*effectivea[i_,i];
                            }
                            g[i] = -v;
                        }
                        for(i=0; i<=nr-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=ns+nd-1;i_++)
                            {
                                v += effectivea[i,i_]*x[i_];
                            }
                            for(i_=0; i_<=ns+nd-1;i_++)
                            {
                                g[i_] = g[i_] + v*effectivea[i,i_];
                            }
                        }
                        for(i=0; i<=ns+nd-1; i++)
                        {
                            apserv.seterrorflag(ref testnewtonerrors, (double)(Math.Abs(g[i]))>(double)(eps));
                        }
                    }
                }
            }
            
            //
            // report
            //
            waserrors = ((test0errors || test1errors) || test2errors) || testnewtonerrors;
            if( !silent )
            {
                System.Console.Write("TESTING SPECIAL NNLS SOLVER");
                System.Console.WriteLine();
                System.Console.Write("TEST 0:                                   ");
                if( test0errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("TEST 1:                                   ");
                if( test1errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("TEST 2:                                   ");
                if( test2errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("NEWTON PHASE:                             ");
                if( testnewtonerrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testsnnls(bool silent)
        {
            return testsnnls(silent);
        }


    }
    public class testsactivesetsunit
    {
        public static bool testsactivesets(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();

            waserrors = false;
            
            //
            // report
            //
            waserrors = waserrors;
            if( !silent )
            {
                System.Console.Write("TESTING ACTIVE SETS");
                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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testsactivesets(bool silent)
        {
            return testsactivesets(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testlinmin(bool silent)
        {
            return testlinmin(silent);
        }


    }
    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();
            bool graderrors = 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);
                }
            }
            
            //
            //  Test for MinCGGradientCheck
            //
            graderrors = gradientchecktest();
            
            //
            // end
            //
            waserrors = (((((((referror || eqerror) || linerror1) || linerror2) || converror) || othererrors) || restartserror) || precerror) || graderrors;
            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();
                }
                System.Console.Write("TEST FOR VERIFICATION OF THE GRADIENT:    ");
                if( graderrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testmincg(bool silent)
        {
            return testmincg(silent);
        }


        /*************************************************************************
        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[] b = new double[0];
            double[] h = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] xlast = new double[0];
            double[,] fulla = new double[0,0];
            double fprev = 0;
            double xprev = 0;
            double stpmax = 0;
            int i = 0;
            int j = 0;
            int k = 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();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int spoiliteration = 0;
            int stopiteration = 0;
            int spoilvar = 0;
            double spoilval = 0;
            int pass = 0;
            double ss = 0;
            int callidx = 0;
            int stopcallidx = 0;
            int maxits = 0;
            bool terminationrequested = new bool();
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            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);
                        lastscaledstep = 0;
                        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
                        //
                        vc = 1.0;
                        vm = 1.0E+8;
                        if( ckind==1 )
                        {
                            vc = 1.0E+6;
                        }
                        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);
                    }
                }
            }
            
            //
            // Test integrity checks for NAN/INF:
            // * algorithm solves optimization problem, which is normal for some time (quadratic)
            // * after 5-th step we choose random component of gradient and consistently spoil
            //   it by NAN or INF.
            // * we check that correct termination code is returned (-8)
            //
            n = 100;
            for(pass=1; pass<=10; pass++)
            {
                spoiliteration = 5;
                stopiteration = 8;
                if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                {
                    
                    //
                    // Gradient can be spoiled by +INF, -INF, NAN
                    //
                    spoilvar = hqrnd.hqrnduniformi(rs, n);
                    i = hqrnd.hqrnduniformi(rs, 3);
                    spoilval = Double.NaN;
                    if( i==0 )
                    {
                        spoilval = Double.NegativeInfinity;
                    }
                    if( i==1 )
                    {
                        spoilval = Double.PositiveInfinity;
                    }
                }
                else
                {
                    
                    //
                    // Function value can be spoiled only by NAN
                    // (+INF can be recognized as legitimate value during optimization)
                    //
                    spoilvar = -1;
                    spoilval = Double.NaN;
                }
                matgen.spdmatrixrndcond(n, 1.0E5, ref fulla);
                b = new double[n];
                x0 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    b[i] = hqrnd.hqrndnormal(rs);
                    x0[i] = hqrnd.hqrndnormal(rs);
                }
                mincg.mincgcreate(n, x0, state);
                mincg.mincgsetcond(state, 0.0, 0.0, 0.0, stopiteration);
                mincg.mincgsetxrep(state, true);
                k = -1;
                while( mincg.mincgiteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+b[i]*state.x[i];
                            state.g[i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.f = state.f+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.g[i] = state.g[i]+fulla[i,j]*state.x[j];
                            }
                        }
                        if( k>=spoiliteration )
                        {
                            if( spoilvar<0 )
                            {
                                state.f = spoilval;
                            }
                            else
                            {
                                state.g[spoilvar] = spoilval;
                            }
                        }
                        continue;
                    }
                    if( state.xupdated )
                    {
                        apserv.inc(ref k);
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                mincg.mincgresults(state, ref x1, rep);
                apserv.seterrorflag(ref err, rep.terminationtype!=-8);
            }
            
            //
            // Check algorithm ability to handle request for termination:
            // * to terminate with correct return code = 8
            // * to return point which was "current" at the moment of termination
            //
            for(pass=1; pass<=50; pass++)
            {
                n = 3;
                ss = 100;
                x = new double[n];
                xlast = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 6+math.randomreal();
                }
                stopcallidx = math.randominteger(20);
                maxits = 25;
                mincg.mincgcreate(n, x, state);
                mincg.mincgsetcond(state, 0, 0, 0, maxits);
                mincg.mincgsetxrep(state, true);
                callidx = 0;
                terminationrequested = false;
                for(i_=0; i_<=n-1;i_++)
                {
                    xlast[i_] = x[i_];
                }
                while( mincg.mincgiteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = ss*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*ss*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[2]-state.x[0])*-1;
                        state.g[1] = 2*state.x[1];
                        state.g[2] = 2*(state.x[2]-state.x[0]);
                        if( callidx==stopcallidx )
                        {
                            mincg.mincgrequesttermination(state);
                            terminationrequested = true;
                        }
                        apserv.inc(ref callidx);
                        continue;
                    }
                    if( state.xupdated )
                    {
                        if( !terminationrequested )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xlast[i_] = state.x[i_];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                mincg.mincgresults(state, ref x, rep);
                apserv.seterrorflag(ref err, rep.terminationtype!=8);
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref err, (double)(x[i])!=(double)(xlast[i]));
                }
            }
        }


        /*************************************************************************
        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;
                }
            }
        }


        /*************************************************************************
        This function tests, that gradient verified correctly.
        *************************************************************************/
        private static bool gradientchecktest()
        {
            bool result = new bool();
            mincg.mincgstate state = new mincg.mincgstate();
            mincg.mincgreport rep = new mincg.mincgreport();
            int n = 0;
            double a = 0;
            double b = 0;
            double c = 0;
            double d = 0;
            double x0 = 0;
            double x1 = 0;
            double x2 = 0;
            double[] x = new double[0];
            double teststep = 0;
            double noise = 0;
            int nbrcomp = 0;
            int func = 0;
            int pass = 0;
            int passcount = 0;
            int i = 0;

            passcount = 35;
            teststep = 0.01;
            n = 3;
            x = new double[n];
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Prepare test's parameters
                //
                func = math.randominteger(3)+1;
                nbrcomp = math.randominteger(n);
                noise = 2*math.randominteger(2)-1;
                
                //
                // Prepare function's parameters
                //
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 5*apserv.randomnormal();
                }
                a = 5*math.randomreal()+1;
                b = 5*math.randomreal()+1;
                c = 5*math.randomreal()+1;
                d = 5*math.randomreal()+1;
                x0 = 5*(2*math.randomreal()-1);
                x1 = 5*(2*math.randomreal()-1);
                x2 = 5*(2*math.randomreal()-1);
                mincg.mincgcreate(n, x, state);
                mincg.mincgsetcond(state, 0, 0, 0, 0);
                mincg.mincgsetgradientcheck(state, teststep);
                
                //
                // Check that the criterion passes a derivative if it is correct
                //
                while( mincg.mincgiteration(state) )
                {
                    if( state.needfg )
                    {
                        funcderiv(a, b, c, d, x0, x1, x2, state.x, func, ref state.f, ref state.g);
                    }
                }
                mincg.mincgresults(state, ref x, rep);
                
                //
                // Check that error code does not equal to -7 and parameter .VarIdx
                // equal to -1.
                //
                if( rep.terminationtype==-7 || rep.varidx!=-1 )
                {
                    result = true;
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 5*apserv.randomnormal();
                }
                mincg.mincgrestartfrom(state, x);
                
                //
                // Check that the criterion does not miss a derivative if
                // it is incorrect
                //
                while( mincg.mincgiteration(state) )
                {
                    if( state.needfg )
                    {
                        funcderiv(a, b, c, d, x0, x1, x2, state.x, func, ref state.f, ref state.g);
                        state.g[nbrcomp] = state.g[nbrcomp]+noise;
                    }
                }
                mincg.mincgresults(state, ref x, rep);
                
                //
                // Check that error code equal to -7 and parameter .VarIdx
                // equal to number of incorrect component.
                //
                if( rep.terminationtype!=-7 || rep.varidx!=nbrcomp )
                {
                    result = true;
                    return result;
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function return function value and it derivatives. Function dimension
        is 3.
            Function's list:
                * funcType=1:
                    F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*(Z-Z0)^2+D;
                * funcType=2:
                    F(X)=A*sin(X-X0)^2+B*sin(Y-Y0)^2+C*sin(Z-Z0)^2+D;
                * funcType=3:
                    F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*((Z-Z0)-(X-X0))^2+D.
        *************************************************************************/
        private static void funcderiv(double a,
            double b,
            double c,
            double d,
            double x0,
            double x1,
            double x2,
            double[] x,
            int functype,
            ref double f,
            ref double[] g)
        {
            alglib.ap.assert(((math.isfinite(a) && math.isfinite(b)) && math.isfinite(c)) && math.isfinite(d), "FuncDeriv: A, B, C or D contains NaN or Infinite.");
            alglib.ap.assert((math.isfinite(x0) && math.isfinite(x1)) && math.isfinite(x2), "FuncDeriv: X0, X1 or X2 contains NaN or Infinite.");
            alglib.ap.assert(functype>=1 && functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).");
            if( functype==1 )
            {
                f = a*math.sqr(x[0]-x0)+b*math.sqr(x[1]-x1)+c*math.sqr(x[2]-x2)+d;
                g[0] = 2*a*(x[0]-x0);
                g[1] = 2*b*(x[1]-x1);
                g[2] = 2*c*(x[2]-x2);
                return;
            }
            if( functype==2 )
            {
                f = a*math.sqr(Math.Sin(x[0]-x0))+b*math.sqr(Math.Sin(x[1]-x1))+c*math.sqr(Math.Sin(x[2]-x2))+d;
                g[0] = 2*a*Math.Sin(x[0]-x0)*Math.Cos(x[0]-x0);
                g[1] = 2*b*Math.Sin(x[1]-x1)*Math.Cos(x[1]-x1);
                g[2] = 2*c*Math.Sin(x[2]-x2)*Math.Cos(x[2]-x2);
                return;
            }
            if( functype==3 )
            {
                f = a*math.sqr(x[0]-x0)+b*math.sqr(x[1]-x1)+c*math.sqr(x[2]-x2-(x[0]-x0))+d;
                g[0] = 2*a*(x[0]-x0)+2*c*(x[0]-x[2]-x0+x2);
                g[1] = 2*b*(x[1]-x1);
                g[2] = 2*c*(x[2]-x[0]-x2+x0);
                return;
            }
        }


    }
    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();
            bool graderrors = new bool();
            bool bugs = new bool();

            waserrors = false;
            feasibilityerrors = false;
            othererrors = false;
            precerrors = false;
            interrors = false;
            converrors = false;
            graderrors = false;
            bugs = false;
            testfeasibility(ref feasibilityerrors, ref converrors, ref interrors);
            testother(ref othererrors);
            testconv(ref converrors);
            testbugs(ref bugs);
            testpreconditioning(ref precerrors);
            testgradientcheck(ref graderrors);
            
            //
            // end
            //
            waserrors = (((((feasibilityerrors || othererrors) || converrors) || interrors) || precerrors) || graderrors) || bugs;
            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();
                }
                System.Console.Write("TEST FOR VERIFICATION OF THE GRADIENT:    ");
                if( graderrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("FIXED BUGS:                               ");
                if( bugs )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testminbleic(bool silent)
        {
            return testminbleic(silent);
        }


        /*************************************************************************
        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[] xc = new double[0];
            double[] xs = new double[0];
            double[] svdw = new double[0];
            double[,] c = new double[0,0];
            double[,] svdu = new double[0,0];
            double[,] svdvt = new double[0,0];
            int[] ct = new int[0];
            minbleic.minbleicstate state = new minbleic.minbleicstate();
            double epsx = 0;
            double epsg = 0;
            double epsfeas = 0;
            double weakepsg = 0;
            minbleic.minbleicreport rep = new minbleic.minbleicreport();
            int dkind = 0;
            double diffstep = 0;
            int i_ = 0;

            nmax = 5;
            epsg = 1.0E-8;
            weakepsg = 1.0E-4;
            epsx = 1.0E-4;
            epsfeas = 1.0E-6;
            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.minbleicsetcond(state, weakepsg, 0.0, 0.0, 0);
                                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)(epsfeas);
                                
                                //
                                // 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)(weakepsg);
                                }
                            }
                        }
                    }
                }
                
                //
                // 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]
                // * equality constraints are checked for being well conditioned
                // * 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.
                                //
                                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;
                                }
                                do
                                {
                                    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;
                                    }
                                    apserv.seterrorflag(ref feaserr, !svd.rmatrixsvd(c, k, n, 0, 0, 0, ref svdw, ref svdu, ref svdvt));
                                }
                                while( !((double)(svdw[0])>(double)(0) && (double)(svdw[k-1])>(double)(0.001*svdw[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.minbleicsetcond(state, weakepsg, 0.0, 0.0, 0);
                                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)(epsx);
                                }
                                
                                //
                                // 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.minbleicsetcond(state, weakepsg, 0.0, 0.0, 0);
                                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
                                //
                                v = 0.0;
                                for(i=0; i<=n-1; i++)
                                {
                                    if( (double)(x[i])>(double)(0) && (double)(x[i])<(double)(1) )
                                    {
                                        v = v+math.sqr(p*Math.Pow(x[i]-x0[i], p-1));
                                    }
                                    feaserr = feaserr || (double)(x[i])<(double)(0.0);
                                    feaserr = feaserr || (double)(x[i])>(double)(1.0);
                                }
                                converr = converr || (double)(Math.Sqrt(v))>(double)(weakepsg);
                            }
                        }
                    }
                }
                
                //
                // Same as previous problem, but with minor modifications:
                // * some bound constraints are 0<=x[i]<=1, some are Ci=x[i]=Ci
                // * 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++)
                                {
                                    if( (double)(math.randomreal())>(double)(0.5) )
                                    {
                                        bl[i] = 0;
                                        bu[i] = 1;
                                    }
                                    else
                                    {
                                        bl[i] = math.randomreal();
                                        bu[i] = bl[i];
                                    }
                                    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.minbleicsetcond(state, weakepsg, 0.0, 0.0, 0);
                                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)(bl[i]);
                                        feaserr = feaserr || (double)(state.x[i])>(double)(bu[i]);
                                    }
                                }
                                minbleic.minbleicresults(state, ref x, rep);
                                if( rep.terminationtype<=0 )
                                {
                                    converr = true;
                                    return;
                                }
                                
                                //
                                // * compare solution with analytic one
                                // * check feasibility
                                //
                                v = 0.0;
                                for(i=0; i<=n-1; i++)
                                {
                                    if( (double)(x[i])>(double)(bl[i]) && (double)(x[i])<(double)(bu[i]) )
                                    {
                                        v = v+math.sqr(p*Math.Pow(x[i]-x0[i], p-1));
                                    }
                                    feaserr = feaserr || (double)(x[i])<(double)(bl[i]);
                                    feaserr = feaserr || (double)(x[i])>(double)(bu[i]);
                                }
                                converr = converr || (double)(Math.Sqrt(v))>(double)(weakepsg);
                            }
                        }
                    }
                }
                
                //
                // 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.minbleicsetcond(state, weakepsg, 0.0, 0.0, 0);
                            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
                            //
                            v = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                if( (double)(x[i])>(double)(0.02) && (double)(x[i])<(double)(0.98) )
                                {
                                    v = v+math.sqr(p*Math.Pow(x[i]-x0[i], p-1));
                                }
                                feaserr = feaserr || (double)(x[i])<(double)(0.0-epsfeas);
                                feaserr = feaserr || (double)(x[i])>(double)(1.0+epsfeas);
                            }
                            converr = converr || (double)(Math.Sqrt(v))>(double)(weakepsg);
                        }
                    }
                }
                
                //
                // Feasibility problem:
                // * bound constraints 0<=x[i]<=1
                // * starting point xs with xs[i] in [-1,+2]
                // * random point xc from [0,1] is used to generate K<=N
                //   random linear equality/inequality constraints of the form
                //   (c,x-xc)=0.0 (or, alternatively, >= or <=), where
                //   c is a random vector.
                // * 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
                // * we do not know analytic form of the solution, and, if fact, we do not
                //   check for solution correctness. We just check that algorithm converges
                //   to the feasible points.
                //
                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;
                                x0 = new double[n];
                                xc = new double[n];
                                xs = new double[n];
                                c = new double[k, n+1];
                                ct = new int[k];
                                bl = new double[n];
                                bu = new double[n];
                                for(i=0; i<=n-1; i++)
                                {
                                    x0[i] = 3*math.randomreal()-1;
                                    xs[i] = 3*math.randomreal()-1;
                                    xc[i] = 0.1+0.8*math.randomreal();
                                    bl[i] = 0;
                                    bu[i] = 1;
                                }
                                for(i=0; i<=k-1; i++)
                                {
                                    c[i,n] = 0;
                                    for(j=0; j<=n-1; j++)
                                    {
                                        c[i,j] = 2*math.randomreal()-1;
                                        c[i,n] = c[i,n]+c[i,j]*xc[j];
                                    }
                                    ct[i] = math.randominteger(3)-1;
                                }
                                
                                //
                                // Create and optimize
                                //
                                minbleic.minbleiccreate(n, xs, state);
                                minbleic.minbleicsetbc(state, bl, bu);
                                minbleic.minbleicsetlc(state, c, ct, k);
                                minbleic.minbleicsetcond(state, weakepsg, 0.0, 0.0, 0);
                                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;
                                }
                                
                                //
                                // Check feasibility
                                //
                                for(i=0; i<=n-1; i++)
                                {
                                    feaserr = feaserr || (double)(x[i])<(double)(0.0);
                                    feaserr = feaserr || (double)(x[i])>(double)(1.0);
                                }
                                for(i=0; i<=k-1; i++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += c[i,i_]*x[i_];
                                    }
                                    v = v-c[i,n];
                                    if( ct[i]==0 )
                                    {
                                        feaserr = feaserr || (double)(Math.Abs(v))>(double)(epsfeas);
                                    }
                                    if( ct[i]<0 )
                                    {
                                        feaserr = feaserr || (double)(v)>(double)(epsfeas);
                                    }
                                    if( ct[i]>0 )
                                    {
                                        feaserr = feaserr || (double)(v)<(double)(-epsfeas);
                                    }
                                }
                            }
                        }
                    }
                }
                
                //
                // 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.minbleicsetcond(state, weakepsg, 0.0, 0.0, 0);
                            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.minbleicsetcond(state, weakepsg, 0.0, 0.0, 0);
                                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;
            int j = 0;
            int k = 0;
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] x = new double[0];
            double[] xf = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] b = 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];
            double[,] fulla = 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 epsx = 0;
            double epsg = 0;
            double eps = 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;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int spoiliteration = 0;
            int stopiteration = 0;
            int spoilvar = 0;
            double spoilval = 0;
            double ss = 0;
            int stopcallidx = 0;
            int callidx = 0;
            int maxits = 0;
            bool terminationrequested = new bool();
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            nmax = 5;
            epsx = 1.0E-4;
            epsg = 1.0E-8;
            passcount = 10;
            
            //
            // Try to reproduce bug 570 (optimizer hangs on problems where it is required
            // to perform very small step - less than 1E-50 - in order to activate constraints).
            //
            // The problem being solved is:
            //
            //     min x[0]+x[1]+...+x[n-1]
            //
            // subject to
            //
            //     x[i]>=0, for i=0..n-1
            //
            // with initial point
            //
            //     x[0] = 1.0E-100, x[1]=x[2]=...=0.5
            //
            // We try to reproduce this problem in different settings:
            // * boundary-only constraints - we test that completion code is positive,
            //   and all x[] are EXACTLY zero
            // * boundary constraints posed as general linear ones - we test that
            //   completion code is positive, and all x[] are APPROXIMATELY zero.
            //
            n = 10;
            x = new double[n];
            bl = new double[n];
            bu = new double[n];
            c = new double[n, n+1];
            ct = new int[n];
            for(i=0; i<=n-1; i++)
            {
                x[i] = 0.5;
                bl[i] = 0.0;
                bu[i] = Double.PositiveInfinity;
                ct[i] = 1;
                for(j=0; j<=n; j++)
                {
                    c[i,j] = 0.0;
                }
                c[i,i] = 1.0;
            }
            x[0] = 1.0E-100;
            minbleic.minbleiccreate(n, x, state);
            minbleic.minbleicsetbc(state, bl, bu);
            minbleic.minbleicsetcond(state, 0, 0, 0, 2*n);
            while( minbleic.minbleiciteration(state) )
            {
                if( state.needfg )
                {
                    state.f = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+state.x[i];
                        state.g[i] = 1.0;
                    }
                }
            }
            minbleic.minbleicresults(state, ref xf, rep);
            apserv.seterrorflag(ref err, rep.terminationtype<=0);
            if( rep.terminationtype>0 )
            {
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref err, (double)(xf[i])!=(double)(0));
                }
            }
            minbleic.minbleiccreate(n, x, state);
            minbleic.minbleicsetlc(state, c, ct, n);
            minbleic.minbleicsetcond(state, 1.0E-64, 0, 0, 10);
            while( minbleic.minbleiciteration(state) )
            {
                if( state.needfg )
                {
                    state.f = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+state.x[i];
                        state.g[i] = 1.0;
                    }
                }
            }
            minbleic.minbleicresults(state, ref xf, rep);
            apserv.seterrorflag(ref err, rep.terminationtype<=0);
            if( rep.terminationtype>0 )
            {
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref err, (double)(Math.Abs(xf[i]))>(double)(1.0E-10));
                }
            }
            
            //
            // Test reports:
            // * first value must be starting point
            // * last value must be last point
            //
            for(pass=1; pass<=passcount; pass++)
            {
                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.minbleicsetcond(state, 1.0E-64, 0, 0, 10);
                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)
            //
            for(pass=1; pass<=passcount; pass++)
            {
                n = 10;
                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.minbleicsetcond(state, 1.0E-6, 0, epsx, 0);
                    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.
            //
            for(pass=1; pass<=passcount; pass++)
            {
                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.minbleicsetcond(state, 1.0E-6, 0, epsx, 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
            //
            for(pass=1; pass<=passcount; pass++)
            {
                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.minbleicsetcond(state, epsg, 0, epsx, 0);
                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
            //
            for(pass=1; pass<=passcount; pass++)
            {
                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.minbleicsetcond(state, epsg, 0, epsx, 0);
                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)(epsx);
            }
            
            //
            // 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 stringent stopping conditions
            // * and we test that in the extremum stopping conditions are
            //   satisfied subject to the current scaling coefficients.
            //
            for(pass=1; pass<=passcount; pass++)
            {
                tmpeps = 1.0E-5;
                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(10)*(2*math.randomreal()-1));
                                s[i] = Math.Exp(Math.Log(10)*(2*math.randomreal()-1));
                                h[i] = Math.Exp(Math.Log(10)*(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.minbleicsetcond(state, tmpeps, 0, 0, 0);
                            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.sqr(state.x[i]);
                                        state.g[i] = 2*a[i]*state.x[i];
                                    }
                                }
                            }
                            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]*2*a[i]*x[i]);
                            }
                            v = Math.Sqrt(v);
                            apserv.seterrorflag(ref 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+4, M is either 1.0E8, 1.0E20 or +INF
            // (we try different combinations)
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(ckind=0; ckind<=1; ckind++)
                {
                    for(mkind=0; mkind<=2; mkind++)
                    {
                        
                        //
                        // Choose c and M
                        //
                        vc = 1;
                        vm = 1;
                        if( ckind==0 )
                        {
                            vc = 1.0;
                        }
                        if( ckind==1 )
                        {
                            vc = 1.0E+4;
                        }
                        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.minbleicsetcond(state, epsg, 0, 0, 0);
                        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;
                                    state.g[0] = 0;
                                }
                            }
                        }
                        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);
                    }
                }
            }
            
            //
            // Test behaviour on noisy functions.
            //
            // Consider following problem:
            // * f(x,y) = (x+1)^2 + (y+1)^2 + 10000*MachineEpsilon*RandomReal()
            // * boundary constraints x>=0, y>=0
            // * starting point (x0,y0)=(10*MachineEpsilon,1.0)
            //
            // Such problem contains small numerical noise. Without noise its
            // solution is (xs,ys)=(0,0), which is easy to find. However, presence
            // of the noise makes it hard to solve:
            // * noisy f(x,y) is monotonically decreasing only when we perform
            //   steps orders of magnitude larger than 10000*MachineEpsilon
            // * at small scales f(x,y) is non-monotonic and non-convex
            // * however, our first step must be done towards
            //   (x1,y1) = (0,1-some_small_value), and length of such step is
            //   many times SMALLER than 10000*MachineEpsilon
            // * second step, from (x1,y1) to (xs,ys), will be large enough to
            //   ignore numerical noise, so the only problem is to perform
            //   first step
            //
            // Naive implementation of BLEIC should fail sometimes (sometimes -
            // due to non-deterministic nature of noise) on such problem. However,
            // our improved implementation should solve it correctly. We test
            // several variations of inner stopping criteria.
            //
            for(pass=1; pass<=passcount; pass++)
            {
                eps = 1.0E-9;
                x = new double[2];
                bl = new double[2];
                bu = new double[2];
                x[0] = 10*math.machineepsilon;
                x[1] = 1.0;
                bl[0] = 0.0;
                bu[0] = Double.PositiveInfinity;
                bl[1] = 0.0;
                bu[1] = Double.PositiveInfinity;
                for(ckind=0; ckind<=2; ckind++)
                {
                    minbleic.minbleiccreate(2, x, state);
                    minbleic.minbleicsetbc(state, bl, bu);
                    if( ckind==0 )
                    {
                        minbleic.minbleicsetcond(state, eps, 0, 0, 0);
                    }
                    if( ckind==1 )
                    {
                        minbleic.minbleicsetcond(state, 0, eps, 0, 0);
                    }
                    if( ckind==2 )
                    {
                        minbleic.minbleicsetcond(state, 0, 0, eps, 0);
                    }
                    while( minbleic.minbleiciteration(state) )
                    {
                        if( state.needfg )
                        {
                            state.f = math.sqr(state.x[0]+1)+math.sqr(state.x[1]+1)+10000*math.machineepsilon*math.randomreal();
                            state.g[0] = 2*(state.x[0]+1);
                            state.g[1] = 2*(state.x[1]+1);
                        }
                    }
                    minbleic.minbleicresults(state, ref xf, rep);
                    if( (rep.terminationtype<=0 || (double)(xf[0])!=(double)(0)) || (double)(xf[1])!=(double)(0) )
                    {
                        err = true;
                        return;
                    }
                }
            }
            
            //
            // Deterministic variation of the previous problem.
            //
            // Consider following problem:
            // * boundary constraints x>=0, y>=0
            // * starting point (x0,y0)=(10*MachineEpsilon,1.0)
            //            / (x+1)^2 + (y+1)^2,        for (x,y)<>(x0,y0)
            // * f(x,y) = |
            //            \ (x+1)^2 + (y+1)^2 - 0.1,  for (x,y)=(x0,y0)
            //
            // Such problem contains deterministic numerical noise (-0.1 at
            // starting point). Without noise its solution is easy to find.
            // However, presence of the noise makes it hard to solve:
            // * our first step must be done towards (x1,y1) = (0,1-some_small_value),
            //   but such step will increase function valye by approximately 0.1  -
            //   instead of decreasing it.
            //
            // Naive implementation of BLEIC should fail on such problem. However,
            // our improved implementation should solve it correctly. We test
            // several variations of inner stopping criteria.
            //
            for(pass=1; pass<=passcount; pass++)
            {
                eps = 1.0E-9;
                x = new double[2];
                bl = new double[2];
                bu = new double[2];
                x[0] = 10*math.machineepsilon;
                x[1] = 1.0;
                bl[0] = 0.0;
                bu[0] = Double.PositiveInfinity;
                bl[1] = 0.0;
                bu[1] = Double.PositiveInfinity;
                for(ckind=0; ckind<=2; ckind++)
                {
                    minbleic.minbleiccreate(2, x, state);
                    minbleic.minbleicsetbc(state, bl, bu);
                    if( ckind==0 )
                    {
                        minbleic.minbleicsetcond(state, eps, 0, 0, 0);
                    }
                    if( ckind==1 )
                    {
                        minbleic.minbleicsetcond(state, 0, eps, 0, 0);
                    }
                    if( ckind==2 )
                    {
                        minbleic.minbleicsetcond(state, 0, 0, eps, 0);
                    }
                    while( minbleic.minbleiciteration(state) )
                    {
                        if( state.needfg )
                        {
                            state.f = math.sqr(state.x[0]+1)+math.sqr(state.x[1]+1);
                            if( (double)(state.x[0])==(double)(x[0]) && (double)(state.x[1])==(double)(x[1]) )
                            {
                                state.f = state.f-0.1;
                            }
                            state.g[0] = 2*(state.x[0]+1);
                            state.g[1] = 2*(state.x[1]+1);
                        }
                    }
                    minbleic.minbleicresults(state, ref xf, rep);
                    if( (rep.terminationtype<=0 || (double)(xf[0])!=(double)(0)) || (double)(xf[1])!=(double)(0) )
                    {
                        err = true;
                        return;
                    }
                }
            }
            
            //
            // Test integrity checks for NAN/INF:
            // * algorithm solves optimization problem, which is normal for some time (quadratic)
            // * after 5-th step we choose random component of gradient and consistently spoil
            //   it by NAN or INF.
            // * we check that correct termination code is returned (-8)
            //
            n = 100;
            for(pass=1; pass<=10; pass++)
            {
                spoiliteration = 5;
                stopiteration = 8;
                if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                {
                    
                    //
                    // Gradient can be spoiled by +INF, -INF, NAN
                    //
                    spoilvar = hqrnd.hqrnduniformi(rs, n);
                    i = hqrnd.hqrnduniformi(rs, 3);
                    spoilval = Double.NaN;
                    if( i==0 )
                    {
                        spoilval = Double.NegativeInfinity;
                    }
                    if( i==1 )
                    {
                        spoilval = Double.PositiveInfinity;
                    }
                }
                else
                {
                    
                    //
                    // Function value can be spoiled only by NAN
                    // (+INF can be recognized as legitimate value during optimization)
                    //
                    spoilvar = -1;
                    spoilval = Double.NaN;
                }
                matgen.spdmatrixrndcond(n, 1.0E5, ref fulla);
                b = new double[n];
                x0 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    b[i] = hqrnd.hqrndnormal(rs);
                    x0[i] = hqrnd.hqrndnormal(rs);
                }
                minbleic.minbleiccreate(n, x0, state);
                minbleic.minbleicsetcond(state, 0.0, 0.0, 0.0, stopiteration);
                minbleic.minbleicsetxrep(state, true);
                k = -1;
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+b[i]*state.x[i];
                            state.g[i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.f = state.f+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.g[i] = state.g[i]+fulla[i,j]*state.x[j];
                            }
                        }
                        if( k>=spoiliteration )
                        {
                            if( spoilvar<0 )
                            {
                                state.f = spoilval;
                            }
                            else
                            {
                                state.g[spoilvar] = spoilval;
                            }
                        }
                        continue;
                    }
                    if( state.xupdated )
                    {
                        apserv.inc(ref k);
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minbleic.minbleicresults(state, ref x1, rep);
                apserv.seterrorflag(ref err, rep.terminationtype!=-8);
            }
            
            //
            // Check algorithm ability to handle request for termination:
            // * to terminate with correct return code = 8
            // * to return point which was "current" at the moment of termination
            //
            // NOTE: we solve problem with "corrupted" preconditioner which makes it hard
            //       to converge in less than StopCallIdx iterations
            //
            for(pass=1; pass<=50; pass++)
            {
                n = 3;
                ss = 100;
                x = new double[n];
                xlast = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 6+math.randomreal();
                }
                s = new double[3];
                s[0] = 0.00001;
                s[1] = 0.00001;
                s[2] = 10000.0;
                stopcallidx = math.randominteger(20);
                maxits = 25;
                minbleic.minbleiccreate(n, x, state);
                minbleic.minbleicsetcond(state, 0, 0, 0, maxits);
                minbleic.minbleicsetxrep(state, true);
                minbleic.minbleicsetprecdiag(state, s);
                callidx = 0;
                terminationrequested = false;
                for(i_=0; i_<=n-1;i_++)
                {
                    xlast[i_] = x[i_];
                }
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = ss*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*ss*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[2]-state.x[0])*-1;
                        state.g[1] = 2*state.x[1];
                        state.g[2] = 2*(state.x[2]-state.x[0]);
                        if( callidx==stopcallidx )
                        {
                            minbleic.minbleicrequesttermination(state);
                            terminationrequested = true;
                        }
                        apserv.inc(ref callidx);
                        continue;
                    }
                    if( state.xupdated )
                    {
                        if( !terminationrequested )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xlast[i_] = state.x[i_];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minbleic.minbleicresults(state, ref x, rep);
                apserv.seterrorflag(ref err, rep.terminationtype!=8);
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref err, (double)(x[i])!=(double)(xlast[i]));
                }
            }
        }


        /*************************************************************************
        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[] b = new double[0];
            double[] tmp = new double[0];
            double[] g = new double[0];
            double[] xf = new double[0];
            double[] xs0 = new double[0];
            double[] xs1 = new double[0];
            double[,] a = new double[0,0];
            double[,] c = new double[0,0];
            double[,] ce = new double[0,0];
            int[] ct = new int[0];
            bool[] nonnegative = new bool[0];
            minbleic.minbleicstate state = new minbleic.minbleicstate();
            double epsg = 0;
            double epsfeas = 0;
            double tol = 0;
            minbleic.minbleicreport rep = new minbleic.minbleicreport();
            snnls.snnlssolver nnls = new snnls.snnlssolver();
            int m = 0;
            int n = 0;
            int k = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            double vv = 0;
            int preckind = 0;
            int akind = 0;
            int shiftkind = 0;
            int bscale = 0;
            double tolconstr = 0;
            double f0 = 0;
            double f1 = 0;
            int ccnt = 0;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            epsg = 1.0E-8;
            epsfeas = 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.minbleicsetcond(state, epsg, 0.0, 0.0, 0);
                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.minbleicsetcond(state, epsg, 0.0, 0.0, 0);
                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.minbleicsetcond(state, epsg, 0.0, 0.0, 0);
                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;
                }
            }
            
            //
            // Degenerate optimization problem with excessive constraints.
            //
            // * N=3..10, M=N div 3, K = 2*N
            // * f(x) = 0.5*|A*x-b|^2, where A is MxN random matrix, b is Mx1 random vector
            // * bound constraint:
            //   a) Ci=x[i]=Ci  for i=0..M-1
            //   b) 0<=x[i]<=1  for i=M..N-1
            // * linear constraints (for fixed feasible xf and random ai):
            //   a) ai*x  = ai*xf                   for i=0..M-1
            //   b) ai*x <= ai*xf+random(0.1,1.0)   for i=M..K-1
            // * preconditioner is chosen at random (we just want to be
            //   sure that preconditioning won't prevent us from detecting
            //   infeasible point):
            //   a) unit preconditioner
            //   b) random diagonal-based preconditioner
            //   c) random scale-based preconditioner
            // * we choose two random initial points from interior of the area
            //   given by bound constraints.
            //
            // We do not know analytic solution of this problem, and we do not need
            // to solve it :) we just perform two restarts from two different initial
            // points and check that both solutions give approximately same function
            // value.
            //
            for(preckind=0; preckind<=2; preckind++)
            {
                for(n=3; n<=10; n++)
                {
                    
                    //
                    // Generate problem
                    //
                    m = n/3;
                    k = 2*n;
                    bl = new double[n];
                    bu = new double[n];
                    x = new double[n];
                    xs0 = new double[n];
                    xs1 = new double[n];
                    xf = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        if( i<m )
                        {
                            v = math.randomreal();
                            bl[i] = v;
                            bu[i] = v;
                            xf[i] = v;
                            xs0[i] = v;
                            xs1[i] = v;
                        }
                        else
                        {
                            bl[i] = 0;
                            bu[i] = 1;
                            xf[i] = math.randomreal();
                            xs0[i] = math.randomreal();
                            xs1[i] = math.randomreal();
                        }
                        x[i] = math.randomreal();
                    }
                    c = new double[k, n+1];
                    ct = new int[k];
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0;
                        for(j=0; j<=n-1; j++)
                        {
                            c[i,j] = apserv.randomnormal();
                            v = v+math.sqr(c[i,j]);
                        }
                        if( (double)(v)>(double)(0) )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                c[i,j] = c[i,j]/Math.Sqrt(v);
                            }
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += c[i,i_]*xf[i_];
                        }
                        c[i,n] = v;
                        if( i<m )
                        {
                            ct[i] = 0;
                        }
                        else
                        {
                            ct[i] = -1;
                            c[i,n] = c[i,n]+0.1+0.9*math.randomreal();
                        }
                    }
                    a = new double[m, n+1];
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n; j++)
                        {
                            a[i,j] = math.randomreal();
                        }
                    }
                    
                    //
                    // Create and optimize
                    //
                    minbleic.minbleiccreate(n, x, state);
                    minbleic.minbleicsetbc(state, bl, bu);
                    minbleic.minbleicsetlc(state, c, ct, k);
                    minbleic.minbleicsetcond(state, epsg, 0.0, 0.0, 0);
                    setrandompreconditioner(state, n, preckind);
                    
                    //
                    // Solve problem 0:
                    // * restart from XS0
                    // * solve
                    // * check convergence/feasibility
                    // * calculate F0 - function value at solution
                    //
                    minbleic.minbleicrestartfrom(state, xs0);
                    while( minbleic.minbleiciteration(state) )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.g[i] = 0;
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*state.x[i_];
                            }
                            v = v-a[i,n];
                            state.f = state.f+0.5*math.sqr(v);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                state.g[i_] = state.g[i_] + v*a[i,i_];
                            }
                        }
                    }
                    minbleic.minbleicresults(state, ref x, rep);
                    if( rep.terminationtype<=0 )
                    {
                        err = true;
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        if( (double)(x[i])<(double)(bl[i]) || (double)(x[i])>(double)(bu[i]) )
                        {
                            err = true;
                            return;
                        }
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += c[i,i_]*x[i_];
                        }
                        v = v-c[i,n];
                        if( ct[i]==0 && (double)(Math.Abs(v))>(double)(epsfeas) )
                        {
                            err = true;
                            return;
                        }
                        if( ct[i]<0 && (double)(v)>(double)(epsfeas) )
                        {
                            err = true;
                            return;
                        }
                    }
                    f0 = 0;
                    for(i=0; i<=m-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*state.x[i_];
                        }
                        v = v-a[i,n];
                        f0 = f0+0.5*math.sqr(v);
                    }
                    
                    //
                    // Solve problem 1:
                    // * restart from XS1
                    // * solve
                    // * check convergence/feasibility
                    // * calculate F1 - function value at solution
                    //
                    minbleic.minbleicrestartfrom(state, xs1);
                    while( minbleic.minbleiciteration(state) )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.g[i] = 0;
                        }
                        for(i=0; i<=m-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*state.x[i_];
                            }
                            v = v-a[i,n];
                            state.f = state.f+0.5*math.sqr(v);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                state.g[i_] = state.g[i_] + v*a[i,i_];
                            }
                        }
                    }
                    minbleic.minbleicresults(state, ref x, rep);
                    if( rep.terminationtype<=0 )
                    {
                        err = true;
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        if( (double)(x[i])<(double)(bl[i]) || (double)(x[i])>(double)(bu[i]) )
                        {
                            err = true;
                            return;
                        }
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += c[i,i_]*x[i_];
                        }
                        v = v-c[i,n];
                        if( ct[i]==0 && (double)(Math.Abs(v))>(double)(epsfeas) )
                        {
                            err = true;
                            return;
                        }
                        if( ct[i]<0 && (double)(v)>(double)(epsfeas) )
                        {
                            err = true;
                            return;
                        }
                    }
                    f1 = 0;
                    for(i=0; i<=m-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*state.x[i_];
                        }
                        v = v-a[i,n];
                        f1 = f1+0.5*math.sqr(v);
                    }
                    
                    //
                    // compare F0 and F1
                    //
                    apserv.seterrorflag(ref err, (double)(Math.Abs(f0-f1))>(double)(1.0E-4));
                }
            }
            
            //
            // Convex/nonconvex optimization problem with excessive
            // (degenerate constraints):
            //
            // * N=2..8
            // * f = 0.5*x'*A*x+b'*x
            // * b has normally distributed entries with scale 10^BScale
            // * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank
            // * box constraints: x[i] in [-1,+1]
            // * 2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
            //   where v_k is one of 2^N vertices of feasible hypercube, v_shift is
            //   a shift parameter:
            //   * with zero v_shift such constraints are degenerate (each vertex has
            //     N box constraints and one "redundant" linear constraint)
            //   * with positive v_shift linear constraint is always inactive
            //   * with small (about machine epsilon) but negative v_shift,
            //     constraint is close to degenerate - but not exactly
            //
            // We check that constrained gradient is close to zero at solution.
            // Box constraint is considered active if distance to boundary is less
            // than TolConstr.
            //
            // NOTE: TolConstr must be large enough so it won't conflict with
            //       perturbation introduced by v_shift
            //
            tolconstr = 1.0E-8;
            for(n=2; n<=8; n++)
            {
                for(akind=0; akind<=3; akind++)
                {
                    for(shiftkind=-5; shiftkind<=1; shiftkind++)
                    {
                        for(bscale=0; bscale>=-2; bscale--)
                        {
                            
                            //
                            // Generate A, B and initial point
                            //
                            a = new double[n, n];
                            b = new double[n];
                            x = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                b[i] = Math.Pow(10, bscale)*hqrnd.hqrndnormal(rs);
                                x[i] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0.0;
                                }
                            }
                            if( akind==1 )
                            {
                                
                                //
                                // Dense well conditioned SPD
                                //
                                matgen.spdmatrixrndcond(n, 50.0, ref a);
                            }
                            if( akind==2 )
                            {
                                
                                //
                                // Dense well conditioned indefinite
                                //
                                matgen.smatrixrndcond(n, 50.0, ref a);
                            }
                            if( akind==3 )
                            {
                                
                                //
                                // Low rank
                                //
                                tmp = new double[n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 0.0;
                                    }
                                }
                                for(k=1; k<=Math.Min(3, n-1); k++)
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        tmp[i] = hqrnd.hqrndnormal(rs);
                                    }
                                    v = hqrnd.hqrndnormal(rs);
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=0; j<=n-1; j++)
                                        {
                                            a[i,j] = a[i,j]+v*tmp[i]*tmp[j];
                                        }
                                    }
                                }
                            }
                            
                            //
                            // Generate constraints
                            //
                            bl = new double[n];
                            bu = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                bl[i] = -1.0;
                                bu[i] = 1.0;
                            }
                            ccnt = (int)Math.Round(Math.Pow(2, n));
                            c = new double[ccnt, n+1];
                            ct = new int[ccnt];
                            for(i=0; i<=ccnt-1; i++)
                            {
                                ct[i] = -1;
                                k = i;
                                c[i,n] = Math.Sign(shiftkind)*Math.Pow(10, Math.Abs(shiftkind))*math.machineepsilon;
                                for(j=0; j<=n-1; j++)
                                {
                                    c[i,j] = 2*(k%2)-1;
                                    c[i,n] = c[i,n]+c[i,j]*c[i,j];
                                    k = k/2;
                                }
                            }
                            
                            //
                            // Create and optimize
                            //
                            minbleic.minbleiccreate(n, x, state);
                            minbleic.minbleicsetbc(state, bl, bu);
                            minbleic.minbleicsetlc(state, c, ct, ccnt);
                            minbleic.minbleicsetcond(state, 1.0E-9, 0.0, 0.0, 0);
                            while( minbleic.minbleiciteration(state) )
                            {
                                alglib.ap.assert(state.needfg);
                                state.f = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    state.f = state.f+state.x[i]*b[i];
                                    state.g[i] = b[i];
                                }
                                for(i=0; i<=n-1; i++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += a[i,i_]*state.x[i_];
                                    }
                                    state.f = state.f+0.5*state.x[i]*v;
                                    state.g[i] = state.g[i]+v;
                                }
                            }
                            minbleic.minbleicresults(state, ref xs0, rep);
                            apserv.seterrorflag(ref err, rep.terminationtype<=0);
                            if( err )
                            {
                                return;
                            }
                            
                            //
                            // Evaluate gradient at solution and test
                            //
                            vv = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*xs0[i_];
                                }
                                v = v+b[i];
                                if( (double)(xs0[i])<=(double)(bl[i]+tolconstr) && (double)(v)>(double)(0) )
                                {
                                    v = 0.0;
                                }
                                if( (double)(xs0[i])>=(double)(bu[i]-tolconstr) && (double)(v)<(double)(0) )
                                {
                                    v = 0.0;
                                }
                                vv = vv+math.sqr(v);
                            }
                            vv = Math.Sqrt(vv);
                            apserv.seterrorflag(ref err, (double)(vv)>(double)(1.0E-5));
                        }
                    }
                }
            }
            
            //
            // Convex/nonconvex optimization problem with combination of
            // box and linear constraints:
            //
            // * N=2..8
            // * f = 0.5*x'*A*x+b'*x
            // * b has normally distributed entries with scale 10^BScale
            // * several kinds of A are tried: zero, well conditioned SPD,
            //   well conditioned indefinite, low rank
            // * box constraints: x[i] in [-1,+1]
            // * initial point x0 = [0 0 ... 0 0]
            // * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
            //   random mix of equality/inequality constraints is tried.
            //   x0 is guaranteed to be feasible.
            //
            // We check that constrained gradient is close to zero at solution.
            // Inequality constraint is considered active if distance to boundary
            // is less than TolConstr. We use nonnegative least squares solver
            // in order to compute constrained gradient.
            //
            tolconstr = 1.0E-8;
            for(n=2; n<=8; n++)
            {
                for(akind=0; akind<=3; akind++)
                {
                    for(bscale=0; bscale>=-2; bscale--)
                    {
                        
                        //
                        // Generate A, B and initial point
                        //
                        a = new double[n, n];
                        b = new double[n];
                        x = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            b[i] = Math.Pow(10, bscale)*hqrnd.hqrndnormal(rs);
                            x[i] = 0.0;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a[i,j] = 0.0;
                            }
                        }
                        if( akind==1 )
                        {
                            
                            //
                            // Dense well conditioned SPD
                            //
                            matgen.spdmatrixrndcond(n, 50.0, ref a);
                        }
                        if( akind==2 )
                        {
                            
                            //
                            // Dense well conditioned indefinite
                            //
                            matgen.smatrixrndcond(n, 50.0, ref a);
                        }
                        if( akind==3 )
                        {
                            
                            //
                            // Low rank
                            //
                            tmp = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0.0;
                                }
                            }
                            for(k=1; k<=Math.Min(3, n-1); k++)
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    tmp[i] = hqrnd.hqrndnormal(rs);
                                }
                                v = hqrnd.hqrndnormal(rs);
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = a[i,j]+v*tmp[i]*tmp[j];
                                    }
                                }
                            }
                        }
                        
                        //
                        // Generate constraints
                        //
                        bl = new double[n];
                        bu = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            bl[i] = -1.0;
                            bu[i] = 1.0;
                        }
                        ccnt = Math.Min(3, n-1);
                        c = new double[ccnt, n+1];
                        ct = new int[ccnt];
                        for(i=0; i<=ccnt-1; i++)
                        {
                            ct[i] = hqrnd.hqrnduniformi(rs, 3)-1;
                            c[i,n] = 0.0;
                            for(j=0; j<=n-1; j++)
                            {
                                c[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                        }
                        
                        //
                        // Create and optimize
                        //
                        minbleic.minbleiccreate(n, x, state);
                        minbleic.minbleicsetbc(state, bl, bu);
                        minbleic.minbleicsetlc(state, c, ct, ccnt);
                        minbleic.minbleicsetcond(state, 1.0E-9, 0.0, 0.0, 0);
                        while( minbleic.minbleiciteration(state) )
                        {
                            alglib.ap.assert(state.needfg);
                            state.f = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+state.x[i]*b[i];
                                state.g[i] = b[i];
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*state.x[i_];
                                }
                                state.f = state.f+0.5*state.x[i]*v;
                                state.g[i] = state.g[i]+v;
                            }
                        }
                        minbleic.minbleicresults(state, ref xs0, rep);
                        apserv.seterrorflag(ref err, rep.terminationtype<=0);
                        if( err )
                        {
                            return;
                        }
                        
                        //
                        // 1. evaluate unconstrained gradient at solution
                        //
                        // 2. calculate constrained gradient (NNLS solver is used
                        //    to evaluate gradient subject to active constraints).
                        //    In order to do this we form CE matrix, matrix of active
                        //    constraints (columns store constraint vectors). Then
                        //    we try to approximate gradient vector by columns of CE,
                        //    subject to non-negativity restriction placed on variables
                        //    corresponding to inequality constraints.
                        //
                        //    Residual from such regression is a constrained gradient vector.
                        //
                        g = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*xs0[i_];
                            }
                            g[i] = v+b[i];
                        }
                        ce = new double[n, n+ccnt];
                        nonnegative = new bool[n+ccnt];
                        k = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref err, (double)(xs0[i])<(double)(bl[i]));
                            apserv.seterrorflag(ref err, (double)(xs0[i])>(double)(bu[i]));
                            if( (double)(xs0[i])<=(double)(bl[i]+tolconstr) )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = 0.0;
                                }
                                ce[i,k] = 1.0;
                                nonnegative[k] = true;
                                apserv.inc(ref k);
                                continue;
                            }
                            if( (double)(xs0[i])>=(double)(bu[i]-tolconstr) )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = 0.0;
                                }
                                ce[i,k] = -1.0;
                                nonnegative[k] = true;
                                apserv.inc(ref k);
                                continue;
                            }
                        }
                        for(i=0; i<=ccnt-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += c[i,i_]*xs0[i_];
                            }
                            v = v-c[i,n];
                            apserv.seterrorflag(ref err, ct[i]==0 && (double)(Math.Abs(v))>(double)(tolconstr));
                            apserv.seterrorflag(ref err, ct[i]>0 && (double)(v)<(double)(-tolconstr));
                            apserv.seterrorflag(ref err, ct[i]<0 && (double)(v)>(double)(tolconstr));
                            if( ct[i]==0 )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = c[i,j];
                                }
                                nonnegative[k] = false;
                                apserv.inc(ref k);
                                continue;
                            }
                            if( (ct[i]>0 && (double)(v)<=(double)(tolconstr)) || (ct[i]<0 && (double)(v)>=(double)(-tolconstr)) )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = Math.Sign(ct[i])*c[i,j];
                                }
                                nonnegative[k] = true;
                                apserv.inc(ref k);
                                continue;
                            }
                        }
                        snnls.snnlsinit(0, 0, 0, nnls);
                        snnls.snnlssetproblem(nnls, ce, g, 0, k, n);
                        for(i=0; i<=k-1; i++)
                        {
                            if( !nonnegative[i] )
                            {
                                snnls.snnlsdropnnc(nnls, i);
                            }
                        }
                        snnls.snnlssolve(nnls, ref tmp);
                        for(i=0; i<=k-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                g[j] = g[j]-tmp[i]*ce[j,i];
                            }
                        }
                        vv = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vv += g[i_]*g[i_];
                        }
                        vv = Math.Sqrt(vv);
                        apserv.seterrorflag(ref err, (double)(vv)>(double)(1.0E-5));
                    }
                }
            }
        }


        /*************************************************************************
        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 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)*x[i])^2, i=0..N-1)
            // * 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 = 100;
            epsg = 1.0E-8;
            for(n=10; n<=10; n++)
            {
                for(ckind=0; ckind<=3; ckind++)
                {
                    fk = 1;
                    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);
                    minbleic.minbleicsetcond(state, epsg, 0.0, 0.0, 0);
                    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(6*math.randomreal()-3);
                }
                minbleic.minbleicsetprecdiag(state, p);
            }
            else
            {
                minbleic.minbleicsetprecdefault(state);
            }
        }


        /*************************************************************************
        This function tests, that gradient verified correctly.
        *************************************************************************/
        private static void testgradientcheck(ref bool testg)
        {
            minbleic.minbleicstate state = new minbleic.minbleicstate();
            minbleic.minbleicreport rep = new minbleic.minbleicreport();
            int n = 0;
            double a = 0;
            double b = 0;
            double c = 0;
            double d = 0;
            double x0 = 0;
            double x1 = 0;
            double x2 = 0;
            double[] x = new double[0];
            double[] bl = new double[0];
            double[] bu = new double[0];
            int infcomp = 0;
            double teststep = 0;
            double noise = 0;
            int nbrcomp = 0;
            double spp = 0;
            int func = 0;
            int pass = 0;
            int passcount = 0;
            int i = 0;

            testg = new bool();

            passcount = 35;
            spp = 1.0;
            teststep = 0.01;
            n = 3;
            x = new double[n];
            bl = new double[n];
            bu = new double[n];
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Prepare test's parameters
                //
                func = math.randominteger(3)+1;
                nbrcomp = math.randominteger(n);
                noise = 10*(2*math.randominteger(2)-1);
                
                //
                // Prepare function's parameters
                //
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 5*apserv.randomnormal();
                }
                a = 5*math.randomreal()+1;
                b = 5*math.randomreal()+1;
                c = 5*math.randomreal()+1;
                d = 5*math.randomreal()+1;
                x0 = 5*(2*math.randomreal()-1);
                x1 = 5*(2*math.randomreal()-1);
                x2 = 5*(2*math.randomreal()-1);
                
                //
                // Prepare boundary parameters
                //
                for(i=0; i<=n-1; i++)
                {
                    bl[i] = math.randomreal()-spp;
                    bu[i] = math.randomreal()+spp-1;
                }
                infcomp = math.randominteger(n+1);
                if( infcomp<n )
                {
                    bl[infcomp] = Double.NegativeInfinity;
                }
                infcomp = math.randominteger(n+1);
                if( infcomp<n )
                {
                    bu[infcomp] = Double.PositiveInfinity;
                }
                minbleic.minbleiccreate(n, x, state);
                minbleic.minbleicsetgradientcheck(state, teststep);
                minbleic.minbleicsetbc(state, bl, bu);
                
                //
                // Check that the criterion passes a derivative if it is correct
                //
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        
                        //
                        // Check that .X within the boundaries
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            if( (math.isfinite(bl[i]) && (double)(state.x[i])<(double)(bl[i])) || (math.isfinite(bu[i]) && (double)(state.x[i])>(double)(bu[i])) )
                            {
                                testg = true;
                                return;
                            }
                        }
                        funcderiv(a, b, c, d, x0, x1, x2, state.x, func, ref state.f, ref state.g);
                    }
                }
                minbleic.minbleicresults(state, ref x, rep);
                
                //
                // Check that error code does not equal to -7 and parameter .VarIdx
                // equal to -1.
                //
                if( rep.terminationtype==-7 || rep.varidx!=-1 )
                {
                    testg = true;
                    return;
                }
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 5*apserv.randomnormal();
                }
                minbleic.minbleicrestartfrom(state, x);
                
                //
                // Check that the criterion does not miss a derivative if
                // it is incorrect
                //
                while( minbleic.minbleiciteration(state) )
                {
                    if( state.needfg )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            if( (math.isfinite(bl[i]) && (double)(state.x[i])<(double)(bl[i])) || (math.isfinite(bu[i]) && (double)(state.x[i])>(double)(bu[i])) )
                            {
                                testg = true;
                                return;
                            }
                        }
                        funcderiv(a, b, c, d, x0, x1, x2, state.x, func, ref state.f, ref state.g);
                        state.g[nbrcomp] = state.g[nbrcomp]+noise;
                    }
                }
                minbleic.minbleicresults(state, ref x, rep);
                
                //
                // Check that error code equal to -7 and parameter .VarIdx
                // equal to number of incorrect component.
                //
                if( rep.terminationtype!=-7 || rep.varidx!=nbrcomp )
                {
                    testg = true;
                    return;
                }
            }
            testg = false;
        }


        /*************************************************************************
        This function tests problems which caused bugs in the past.

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testbugs(ref bool err)
        {
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] x = new double[0];
            double[] x1 = new double[0];
            double[] h = new double[0];
            double[] prior = new double[0];
            double[] w = new double[0];
            double[,] a = new double[0,0];
            double[,] c = new double[0,0];
            double[,] xy = new double[0,0];
            int[] ct = new int[0];
            minbleic.minbleicstate state = new minbleic.minbleicstate();
            minbleic.minbleicreport rep = new minbleic.minbleicreport();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int pass = 0;
            double tolx = 0;
            double regterm = 0;
            int n = 0;
            int ckind = 0;
            int i_ = 0;
            int i1_ = 0;

            hqrnd.hqrndrandomize(rs);
            
            //
            // Reproduce situation: optimizer sometimes hangs when starts with
            // gradient orthogonal to the only linear constraint. In most cases
            // it is solved successfully, but sometimes leads to infinite loop
            // in one of the early optimizer versions.
            //
            // The problem is:
            // * f(x)= x'*x + c'*x
            // * linear constraint c'*x=0
            // * initial point is x=0
            // * there are two ways to choose coefficient vector c:
            //   * its components can be long binary fractions
            //   * or they can be either 0 or 1
            //   both ways test different scenarios for accumulation of rounding errors
            //
            // If test fails, it usually hangs
            //
            tolx = 1.0E-10;
            for(pass=1; pass<=10; pass++)
            {
                for(ckind=0; ckind<=1; ckind++)
                {
                    for(n=2; n<=10; n++)
                    {
                        x = new double[n];
                        c = new double[1, n+1];
                        ct = new int[1];
                        for(i=0; i<=n-1; i++)
                        {
                            x[i] = 0.0;
                            if( ckind==0 )
                            {
                                c[0,i] = Math.Sqrt(hqrnd.hqrnduniformr(rs));
                            }
                            else
                            {
                                c[0,i] = hqrnd.hqrnduniformi(rs, 2);
                            }
                        }
                        c[0,n] = 0.0;
                        ct[0] = 0;
                        minbleic.minbleiccreate(n, x, state);
                        minbleic.minbleicsetlc(state, c, ct, 1);
                        minbleic.minbleicsetcond(state, 0.0, 0.0, 0.0, 99);
                        while( minbleic.minbleiciteration(state) )
                        {
                            alglib.ap.assert(state.needfg);
                            if( state.needfg )
                            {
                                state.f = 0.0;
                                for(i=0; i<=n-1; i++)
                                {
                                    state.f = state.f+math.sqr(state.x[i])+state.x[i]*c[0,i];
                                    state.g[i] = 2*state.x[i]+c[0,i];
                                }
                            }
                        }
                        minbleic.minbleicresultsbuf(state, ref x1, rep);
                        apserv.seterrorflag(ref err, rep.terminationtype<=0);
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref err, (double)(Math.Abs(x1[i]))>(double)(tolx));
                        }
                    }
                }
            }
            
            //
            // Reproduce optimization problem which caused bugs (optimizer hangs)
            // when BLEIC was used from MCPD unit. We perform test on specific
            // 9-dimensional problem, no need to try general-case methods.
            //
            // This test hangs if bug is present. Thus, we do not test completion
            // code returned by optimizer - we just test that it was returned :)
            //
            tolx = 1.0E-8;
            regterm = 1.0E-8;
            for(pass=1; pass<=1000; pass++)
            {
                
                //
                // Prepare constraints:
                // * [0,1] box constraints on all variables
                // * 5 linear constraints, first one is random equality;
                //   second one is random inequality; other ones are "sum-to-one" constraints
                //   for x0-x2, x3-x5, x6-x8.
                //
                bl = new double[9];
                bu = new double[9];
                for(i=0; i<=9-1; i++)
                {
                    bl[i] = 0.0;
                    bu[i] = 1.0;
                }
                c = new double[5, 10];
                ct = new int[5];
                for(i=0; i<=1; i++)
                {
                    c[i,9] = 0;
                    for(j=0; j<=9-1; j++)
                    {
                        c[i,j] = 2*math.randomreal()-1;
                        c[i,9] = c[i,9]+c[i,j]*((double)1/(double)9);
                    }
                }
                ct[0] = 0;
                ct[1] = 1;
                c[1,9] = c[1,9]-0.1;
                for(i=0; i<=3-1; i++)
                {
                    for(k=0; k<=9-1; k++)
                    {
                        c[2+i,k] = 0;
                    }
                    for(k=0; k<=3-1; k++)
                    {
                        c[2+i,k*3+i] = 1;
                    }
                    c[2+i,9] = 1.0;
                    ct[2+i] = 0;
                }
                
                //
                // Prepare weights
                //
                w = new double[3];
                for(i=0; i<=alglib.ap.len(w)-1; i++)
                {
                    w[i] = 1.0;
                }
                
                //
                // Prepare preconditioner H
                //
                h = new double[9];
                for(i=0; i<=alglib.ap.len(h)-1; i++)
                {
                    h[i] = 1.0;
                }
                
                //
                // Prepare prior value for regularization
                //
                prior = new double[9];
                for(i=0; i<=alglib.ap.len(prior)-1; i++)
                {
                    prior[i] = 0;
                }
                prior[0] = 1.0;
                prior[4] = 1.0;
                prior[8] = 1.0;
                
                //
                // Prepare dataset XY
                //
                xy = new double[6, 3];
                for(i=0; i<=alglib.ap.rows(xy)-1; i++)
                {
                    for(j=0; j<=alglib.ap.cols(xy)-1; j++)
                    {
                        xy[i,j] = math.randomreal();
                    }
                }
                
                //
                // Optimize
                //
                x = new double[9];
                for(i=0; i<=9-1; i++)
                {
                    x[i] = (double)1/(double)9;
                }
                minbleic.minbleiccreate(9, x, state);
                minbleic.minbleicsetbc(state, bl, bu);
                minbleic.minbleicsetlc(state, c, ct, 5);
                minbleic.minbleicsetcond(state, 0.0, 0.0, tolx, 0);
                minbleic.minbleicsetprecdiag(state, h);
                while( minbleic.minbleiciteration(state) )
                {
                    alglib.ap.assert(state.needfg);
                    if( state.needfg )
                    {
                        
                        //
                        // Calculate regularization term
                        //
                        state.f = 0.0;
                        for(i=0; i<=9-1; i++)
                        {
                            state.f = state.f+regterm*math.sqr(state.x[i]-prior[i]);
                            state.g[i] = 2*regterm*(state.x[i]-prior[i]);
                        }
                        
                        //
                        // calculate prediction error/gradient for K-th pair
                        //
                        for(k=0; k<=alglib.ap.rows(xy)-2; k++)
                        {
                            for(i=0; i<=3-1; i++)
                            {
                                i1_ = (0)-(i*3);
                                v = 0.0;
                                for(i_=i*3; i_<=i*3+3-1;i_++)
                                {
                                    v += state.x[i_]*xy[k,i_+i1_];
                                }
                                state.f = state.f+math.sqr(w[i]*(v-xy[k+1,i]));
                                for(j=0; j<=3-1; j++)
                                {
                                    state.g[i*3+j] = state.g[i*3+j]+2*w[i]*w[i]*(v-xy[k+1,i])*xy[k,j];
                                }
                            }
                        }
                    }
                }
                minbleic.minbleicresultsbuf(state, ref x, rep);
            }
        }


        /*************************************************************************
        This function return function value and it derivatives. Function dimension
        is 3.
            Function's list:
                * funcType=1:
                    F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*(Z-Z0)^2+D;
                * funcType=2:
                    F(X)=A*sin(X-X0)^2+B*sin(Y-Y0)^2+C*sin(Z-Z0)^2+D;
                * funcType=3:
                    F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*((Z-Z0)-(X-X0))^2+D.
        *************************************************************************/
        private static void funcderiv(double a,
            double b,
            double c,
            double d,
            double x0,
            double x1,
            double x2,
            double[] x,
            int functype,
            ref double f,
            ref double[] g)
        {
            alglib.ap.assert(((math.isfinite(a) && math.isfinite(b)) && math.isfinite(c)) && math.isfinite(d), "FuncDeriv: A, B, C or D contains NaN or Infinite.");
            alglib.ap.assert((math.isfinite(x0) && math.isfinite(x1)) && math.isfinite(x2), "FuncDeriv: X0, X1 or X2 contains NaN or Infinite.");
            alglib.ap.assert(functype>=1 && functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).");
            if( functype==1 )
            {
                f = a*math.sqr(x[0]-x0)+b*math.sqr(x[1]-x1)+c*math.sqr(x[2]-x2)+d;
                g[0] = 2*a*(x[0]-x0);
                g[1] = 2*b*(x[1]-x1);
                g[2] = 2*c*(x[2]-x2);
                return;
            }
            if( functype==2 )
            {
                f = a*math.sqr(Math.Sin(x[0]-x0))+b*math.sqr(Math.Sin(x[1]-x1))+c*math.sqr(Math.Sin(x[2]-x2))+d;
                g[0] = 2*a*Math.Sin(x[0]-x0)*Math.Cos(x[0]-x0);
                g[1] = 2*b*Math.Sin(x[1]-x1)*Math.Cos(x[1]-x1);
                g[2] = 2*c*Math.Sin(x[2]-x2)*Math.Cos(x[2]-x2);
                return;
            }
            if( functype==3 )
            {
                f = a*math.sqr(x[0]-x0)+b*math.sqr(x[1]-x1)+c*math.sqr(x[2]-x2-(x[0]-x0))+d;
                g[0] = 2*a*(x[0]-x0)+2*c*(x[0]-x[2]-x0+x2);
                g[1] = 2*b*(x[1]-x1);
                g[2] = 2*c*(x[2]-x[0]-x2+x0);
                return;
            }
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testmcpd(bool silent)
        {
            return testmcpd(silent);
        }


        /*************************************************************************
        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<=alglib.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<=alglib.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, alglib.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<=alglib.ap.rows(xy)-1; i++)
                        {
                            for(j=0; j<=alglib.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
                        //
                        alglib.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, alglib.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
                        //
                        alglib.ap.assert(n>=3, "TestEC: expectation failed");
                        jc = math.randominteger(n-1);
                        vc = 0.95;
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, alglib.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, alglib.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, alglib.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, alglib.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, alglib.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.
                        //
                        alglib.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, alglib.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<=alglib.ap.rows(xy)-1; i++)
                        {
                            for(j=0; j<=alglib.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
                        //
                        alglib.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, alglib.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
                        //
                        alglib.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, alglib.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, alglib.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, alglib.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, alglib.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, alglib.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.
                        //
                        alglib.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, alglib.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<=alglib.ap.rows(xy)-1; i++)
                        {
                            for(j=0; j<=alglib.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).
                        //
                        alglib.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, alglib.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
                        //
                        alglib.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, alglib.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, alglib.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, alglib.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, alglib.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, alglib.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, alglib.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<=alglib.ap.rows(xy)-1; i++)
                    {
                        for(j=0; j<=alglib.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, alglib.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 testmlpeunit
    {
        public static bool testmlpe(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool inferrors = new bool();
            bool procerrors = new bool();
            bool errerrors = new bool();
            int passcount = 0;
            int maxn = 0;
            int maxhid = 0;
            int nf = 0;
            int nl = 0;
            int nhid1 = 0;
            int nhid2 = 0;
            int ec = 0;
            int nkind = 0;
            int sizemin = 0;
            int sizemax = 0;

            waserrors = false;
            inferrors = false;
            procerrors = false;
            errerrors = false;
            passcount = 5;
            maxn = 3;
            maxhid = 3;
            
            //
            // General MLP ensembles tests
            // These tests are performed with small dataset, whose size is in [0,10].
            // We test correctness of functions on small sets, but do not test code
            // which splits large dataset into smaller chunks.
            //
            sizemin = 0;
            sizemax = 10;
            for(nf=1; nf<=maxn; nf++)
            {
                for(nl=1; nl<=maxn; nl++)
                {
                    for(nhid1=0; nhid1<=maxhid; nhid1++)
                    {
                        for(nhid2=0; nhid2<=maxhid; 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);
                                    testerr(nkind, nf, nhid1, nhid2, nl, ec, passcount, sizemin, sizemax, ref errerrors);
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // Special tests on large datasets: test ability to correctly split
            // work into smaller chunks.
            //
            nf = 2;
            nhid1 = 10;
            nhid2 = 10;
            nl = 2;
            ec = 10;
            sizemin = 1000;
            sizemax = 1000;
            testerr(0, nf, nhid1, nhid2, nl, ec, 1, sizemin, sizemax, ref errerrors);
            
            //
            // Final report
            //
            waserrors = (inferrors || procerrors) || errerrors;
            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("ERROR FUNCTIONS:                         ");
                if( !errerrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testmlpe(bool silent)
        {
            return testmlpe(silent);
        }


        /*************************************************************************
        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)
        {
            alglib.ap.assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0, "CreateNetwork error");
            alglib.ap.assert(nhid1!=0 || nhid2==0, "CreateNetwork error");
            alglib.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 rkind = 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[] 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:
            // * Pass is a number of repeated test
            // * RKind is a "replication kind":
            //   * RKind=0 means that we work with original ensemble
            //   * RKind=1 means that we work with replica created with MLPECopy()
            //   * RKind=2 means that we work with replica created with serialization/unserialization
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(rkind=0; rkind<=2; rkind++)
                {
                    
                    //
                    // Create network, pass through replication in order to test that replicated network works correctly.
                    //
                    createensemble(ensemble, nkind, a1, a2, nin, nhid1, nhid2, nout, ec);
                    if( rkind==1 )
                    {
                        mlpe.mlpecopy(ensemble, ensemble2);
                        unsetensemble(ensemble);
                        mlpe.mlpecopy(ensemble2, ensemble);
                        unsetensemble(ensemble2);
                    }
                    if( rkind==2 )
                    {
                        {
                            //
                            // 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();
                            mlpe.mlpealloc(_local_serializer, ensemble);
                            _local_serializer.sstart_str();
                            mlpe.mlpeserialize(_local_serializer, ensemble);
                            _local_serializer.stop();
                            _local_str = _local_serializer.get_string();
                            
                            _local_serializer = new serializer();
                            _local_serializer.ustart_str(_local_str);
                            mlpe.mlpeunserialize(_local_serializer, ensemble2);
                            _local_serializer.stop();
                        }
                        unsetensemble(ensemble);
                        {
                            //
                            // 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();
                            mlpe.mlpealloc(_local_serializer, ensemble2);
                            _local_serializer.sstart_str();
                            mlpe.mlpeserialize(_local_serializer, ensemble2);
                            _local_serializer.stop();
                            _local_str = _local_serializer.get_string();
                            
                            _local_serializer = new serializer();
                            _local_serializer.ustart_str(_local_str);
                            mlpe.mlpeunserialize(_local_serializer, ensemble);
                            _local_serializer.stop();
                        }
                        unsetensemble(ensemble2);
                    }
                    
                    //
                    // 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
                    //
                    {
                        //
                        // 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();
                        mlpe.mlpealloc(_local_serializer, ensemble);
                        _local_serializer.sstart_str();
                        mlpe.mlpeserialize(_local_serializer, ensemble);
                        _local_serializer.stop();
                        _local_str = _local_serializer.get_string();
                        
                        _local_serializer = new serializer();
                        _local_serializer.ustart_str(_local_str);
                        mlpe.mlpeunserialize(_local_serializer, ensemble2);
                        _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;
                    }
                    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));
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Error functions

        Ensemble of type NKind is created, with  NIn  inputs,  NHid1*NHid2  hidden
        layers (one layer if NHid2=0), NOut outputs. PassCount  random  passes  is
        performed. Dataset has random size in [SizeMin,SizeMax].
        *************************************************************************/
        private static void testerr(int nkind,
            int nin,
            int nhid1,
            int nhid2,
            int nout,
            int ec,
            int passcount,
            int sizemin,
            int sizemax,
            ref bool err)
        {
            mlpe.mlpensemble ensemble = new mlpe.mlpensemble();
            sparse.sparsematrix sparsexy = new sparse.sparsematrix();
            sparse.sparsematrix sparsexy2 = new sparse.sparsematrix();
            int n1 = 0;
            int n2 = 0;
            int wcount = 0;
            double etol = 0;
            double escale = 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[,] xy2 = new double[0,0];
            double[] y = new double[0];
            double[] x1 = new double[0];
            double[] y1 = new double[0];
            int[] idx = new int[0];
            int[] dummy = new int[0];
            double refrmserror = 0;
            double refclserror = 0;
            double refrelclserror = 0;
            double refavgce = 0;
            double refavgerror = 0;
            double refavgrelerror = 0;
            int avgrelcnt = 0;
            mlpbase.modelerrors allerrors = new mlpbase.modelerrors();
            int nnmax = 0;
            int dsmax = 0;
            int i_ = 0;
            int i1_ = 0;

            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());
            }
            createensemble(ensemble, nkind, a1, a2, nin, nhid1, nhid2, nout, ec);
            mlpbase.mlpproperties(ensemble.network, ref n1, ref n2, ref wcount);
            etol = 1.0E-4;
            escale = 1.0E-2;
            
            //
            // Initialize
            //
            x1 = new double[nin];
            y = new double[nout];
            y1 = new double[nout];
            
            //
            // Process
            //
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Randomize Ensemble, then re-randomaze weights manually.
                //
                // NOTE: weights magnitude is chosen to be small, about 0.1,
                //       which allows us to avoid oversaturated Ensemble.
                //       In 10% of cases we use zero weights.
                //
                mlpe.mlperandomize(ensemble);
                if( (double)(math.randomreal())<=(double)(0.1) )
                {
                    for(i=0; i<=wcount*ec-1; i++)
                    {
                        ensemble.weights[i] = 0.0;
                    }
                }
                else
                {
                    for(i=0; i<=wcount*ec-1; i++)
                    {
                        ensemble.weights[i] = 0.2*math.randomreal()-0.1;
                    }
                }
                
                //
                // Generate random dataset.
                // Calculate reference errors.
                //
                // NOTE: about 10% of tests are performed with zero SSize
                //
                ssize = sizemin+math.randominteger(sizemax-sizemin+1);
                if( mlpe.mlpeissoftmax(ensemble) )
                {
                    xy = new double[Math.Max(ssize, 1), nin+1];
                    sparse.sparsecreate(Math.Max(ssize, 1), nin+1, 0, sparsexy);
                }
                else
                {
                    xy = new double[Math.Max(ssize, 1), nin+nout];
                    sparse.sparsecreate(Math.Max(ssize, 1), nin+nout, 0, sparsexy);
                }
                refrmserror = 0.0;
                refclserror = 0.0;
                refavgce = 0.0;
                refavgerror = 0.0;
                refavgrelerror = 0.0;
                avgrelcnt = 0;
                for(i=0; i<=ssize-1; i++)
                {
                    
                    //
                    // Fill I-th row
                    //
                    for(j=0; j<=nin-1; j++)
                    {
                        x1[j] = 4*math.randomreal()-2;
                        sparse.sparseset(sparsexy, i, j, x1[j]);
                    }
                    for(i_=0; i_<=nin-1;i_++)
                    {
                        xy[i,i_] = x1[i_];
                    }
                    if( mlpe.mlpeissoftmax(ensemble) )
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 0;
                        }
                        xy[i,nin] = math.randominteger(nout);
                        sparse.sparseset(sparsexy, i, nin, xy[i,nin]);
                        y1[(int)Math.Round(xy[i,nin])] = 1;
                    }
                    else
                    {
                        for(j=0; j<=nout-1; j++)
                        {
                            y1[j] = 4*math.randomreal()-2;
                            sparse.sparseset(sparsexy, i, nin+j, y1[j]);
                        }
                        i1_ = (0) - (nin);
                        for(i_=nin; i_<=nin+nout-1;i_++)
                        {
                            xy[i,i_] = y1[i_+i1_];
                        }
                    }
                    
                    //
                    // Process
                    //
                    mlpe.mlpeprocess(ensemble, x1, ref y);
                    
                    //
                    // Update reference errors
                    //
                    nnmax = 0;
                    if( mlpe.mlpeissoftmax(ensemble) )
                    {
                        if( (double)(y[(int)Math.Round(xy[i,nin])])>(double)(0) )
                        {
                            refavgce = refavgce+Math.Log(1/y[(int)Math.Round(xy[i,nin])]);
                        }
                        else
                        {
                            refavgce = refavgce+Math.Log(math.maxrealnumber);
                        }
                    }
                    if( mlpe.mlpeissoftmax(ensemble) )
                    {
                        dsmax = (int)Math.Round(xy[i,nin]);
                    }
                    else
                    {
                        dsmax = 0;
                    }
                    for(j=0; j<=nout-1; j++)
                    {
                        refrmserror = refrmserror+math.sqr(y[j]-y1[j]);
                        refavgerror = refavgerror+Math.Abs(y[j]-y1[j]);
                        if( (double)(y1[j])!=(double)(0) )
                        {
                            refavgrelerror = refavgrelerror+Math.Abs(y[j]-y1[j])/Math.Abs(y1[j]);
                            avgrelcnt = avgrelcnt+1;
                        }
                        if( (double)(y[j])>(double)(y[nnmax]) )
                        {
                            nnmax = j;
                        }
                        if( !mlpe.mlpeissoftmax(ensemble) && (double)(y1[j])>(double)(y1[dsmax]) )
                        {
                            dsmax = j;
                        }
                    }
                    if( nnmax!=dsmax )
                    {
                        refclserror = refclserror+1;
                    }
                }
                sparse.sparseconverttocrs(sparsexy);
                if( ssize>0 )
                {
                    refrmserror = Math.Sqrt(refrmserror/(ssize*nout));
                    refavgerror = refavgerror/(ssize*nout);
                    refrelclserror = refclserror/ssize;
                    refavgce = refavgce/(ssize*Math.Log(2));
                }
                else
                {
                    refrelclserror = 0.0;
                }
                if( avgrelcnt>0 )
                {
                    refavgrelerror = refavgrelerror/avgrelcnt;
                }
                
                //
                // Test "continuous" errors on full dataset
                //
                apserv.seterrorflagdiff(ref err, mlpe.mlpermserror(ensemble, xy, ssize), refrmserror, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpe.mlpeavgce(ensemble, xy, ssize), refavgce, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpe.mlpeavgerror(ensemble, xy, ssize), refavgerror, etol, escale);
                apserv.seterrorflagdiff(ref err, mlpe.mlpeavgrelerror(ensemble, xy, ssize), refavgrelerror, etol, escale);
            }
        }


    }
    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();
            bool graderrors = 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;
            referror = 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 = (((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;
            
            //
            //  Test for MinLBFGSGradientCheck
            //
            graderrors = gradientchecktest();
            
            //
            // end
            //
            waserrors = (((((((referror || nonconverror) || eqerror) || converror) || crashtest) || othererrors) || restartserror) || precerror) || graderrors;
            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();
                }
                System.Console.Write("TEST FOR VERIFICATION OF THE GRADIENT:    ");
                if( graderrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testminlbfgs(bool silent)
        {
            return testminlbfgs(silent);
        }


        /*************************************************************************
        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[] b = new double[0];
            double[] s = new double[0];
            double[] h = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] xlast = new double[0];
            double[,] fulla = new double[0,0];
            bool hasxlast = new bool();
            double lastscaledstep = 0;
            int i = 0;
            int j = 0;
            int k = 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;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int spoiliteration = 0;
            int stopiteration = 0;
            int spoilvar = 0;
            double spoilval = 0;
            double ss = 0;
            bool terminationrequested = new bool();
            int pass = 0;
            int stopcallidx = 0;
            int callidx = 0;
            int maxits = 0;
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            
            //
            // 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;
                    lastscaledstep = math.maxrealnumber;
                    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
                    //
                    vc = 1;
                    vm = 1;
                    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);
                }
            }
            
            //
            // Test integrity checks for NAN/INF:
            // * algorithm solves optimization problem, which is normal for some time (quadratic)
            // * after 5-th step we choose random component of gradient and consistently spoil
            //   it by NAN or INF.
            // * we check that correct termination code is returned (-8)
            //
            n = 100;
            for(pass=1; pass<=10; pass++)
            {
                spoiliteration = 5;
                stopiteration = 8;
                if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                {
                    
                    //
                    // Gradient can be spoiled by +INF, -INF, NAN
                    //
                    spoilvar = hqrnd.hqrnduniformi(rs, n);
                    i = hqrnd.hqrnduniformi(rs, 3);
                    spoilval = Double.NaN;
                    if( i==0 )
                    {
                        spoilval = Double.NegativeInfinity;
                    }
                    if( i==1 )
                    {
                        spoilval = Double.PositiveInfinity;
                    }
                }
                else
                {
                    
                    //
                    // Function value can be spoiled only by NAN
                    // (+INF can be recognized as legitimate value during optimization)
                    //
                    spoilvar = -1;
                    spoilval = Double.NaN;
                }
                matgen.spdmatrixrndcond(n, 1.0E5, ref fulla);
                b = new double[n];
                x0 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    b[i] = hqrnd.hqrndnormal(rs);
                    x0[i] = hqrnd.hqrndnormal(rs);
                }
                minlbfgs.minlbfgscreate(n, 1, x0, state);
                minlbfgs.minlbfgssetcond(state, 0.0, 0.0, 0.0, stopiteration);
                minlbfgs.minlbfgssetxrep(state, true);
                k = -1;
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+b[i]*state.x[i];
                            state.g[i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.f = state.f+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.g[i] = state.g[i]+fulla[i,j]*state.x[j];
                            }
                        }
                        if( k>=spoiliteration )
                        {
                            if( spoilvar<0 )
                            {
                                state.f = spoilval;
                            }
                            else
                            {
                                state.g[spoilvar] = spoilval;
                            }
                        }
                        continue;
                    }
                    if( state.xupdated )
                    {
                        apserv.inc(ref k);
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minlbfgs.minlbfgsresults(state, ref x1, rep);
                apserv.seterrorflag(ref err, rep.terminationtype!=-8);
            }
            
            //
            // Check algorithm ability to handle request for termination:
            // * to terminate with correct return code = 8
            // * to return point which was "current" at the moment of termination
            //
            for(pass=1; pass<=50; pass++)
            {
                n = 3;
                ss = 100;
                x = new double[n];
                xlast = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 6+math.randomreal();
                }
                stopcallidx = math.randominteger(20);
                maxits = 25;
                minlbfgs.minlbfgscreate(n, 1, x, state);
                minlbfgs.minlbfgssetcond(state, 0, 0, 0, maxits);
                minlbfgs.minlbfgssetxrep(state, true);
                callidx = 0;
                terminationrequested = false;
                for(i_=0; i_<=n-1;i_++)
                {
                    xlast[i_] = x[i_];
                }
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    if( state.needfg )
                    {
                        state.f = ss*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*ss*(Math.Exp(state.x[0])-2)*Math.Exp(state.x[0])+2*(state.x[2]-state.x[0])*-1;
                        state.g[1] = 2*state.x[1];
                        state.g[2] = 2*(state.x[2]-state.x[0]);
                        if( callidx==stopcallidx )
                        {
                            minlbfgs.minlbfgsrequesttermination(state);
                            terminationrequested = true;
                        }
                        apserv.inc(ref callidx);
                        continue;
                    }
                    if( state.xupdated )
                    {
                        if( !terminationrequested )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xlast[i_] = state.x[i_];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                apserv.seterrorflag(ref err, rep.terminationtype!=8);
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref err, (double)(x[i])!=(double)(xlast[i]));
                }
            }
        }


        /*************************************************************************
        This function tests, that gradient verified correctly.
        *************************************************************************/
        private static bool gradientchecktest()
        {
            bool result = new bool();
            minlbfgs.minlbfgsstate state = new minlbfgs.minlbfgsstate();
            minlbfgs.minlbfgsreport rep = new minlbfgs.minlbfgsreport();
            int m = 0;
            int n = 0;
            double a = 0;
            double b = 0;
            double c = 0;
            double d = 0;
            double x0 = 0;
            double x1 = 0;
            double x2 = 0;
            double[] x = new double[0];
            double teststep = 0;
            double noise = 0;
            int nbrcomp = 0;
            int func = 0;
            int pass = 0;
            int passcount = 0;
            int i = 0;

            passcount = 35;
            teststep = 0.01;
            n = 3;
            m = 2;
            x = new double[n];
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Prepare test's parameters
                //
                func = math.randominteger(3)+1;
                nbrcomp = math.randominteger(n);
                noise = 10*(2*math.randominteger(2)-1);
                
                //
                // Prepare function's parameters
                //
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 5*apserv.randomnormal();
                }
                a = 5*math.randomreal()+1;
                b = 5*math.randomreal()+1;
                c = 5*math.randomreal()+1;
                d = 5*math.randomreal()+1;
                x0 = 5*(2*math.randomreal()-1);
                x1 = 5*(2*math.randomreal()-1);
                x2 = 5*(2*math.randomreal()-1);
                minlbfgs.minlbfgscreate(n, m, x, state);
                minlbfgs.minlbfgssetcond(state, 0, 0, 0, 0);
                minlbfgs.minlbfgssetgradientcheck(state, teststep);
                
                //
                // Check that the criterion passes a derivative if it is correct
                //
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    if( state.needfg )
                    {
                        funcderiv(a, b, c, d, x0, x1, x2, state.x, func, ref state.f, ref state.g);
                    }
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                
                //
                // Check that error code does not equal to -7 and parameter .VarIdx
                // equal to -1.
                //
                if( rep.terminationtype==-7 || rep.varidx!=-1 )
                {
                    result = true;
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 5*apserv.randomnormal();
                }
                minlbfgs.minlbfgsrestartfrom(state, x);
                
                //
                // Check that the criterion does not miss a derivative if
                // it is incorrect
                //
                while( minlbfgs.minlbfgsiteration(state) )
                {
                    if( state.needfg )
                    {
                        funcderiv(a, b, c, d, x0, x1, x2, state.x, func, ref state.f, ref state.g);
                        state.g[nbrcomp] = state.g[nbrcomp]+noise;
                    }
                }
                minlbfgs.minlbfgsresults(state, ref x, rep);
                
                //
                // Check that error code equal to -7 and parameter .VarIdx
                // equal to number of incorrect component.
                //
                if( rep.terminationtype!=-7 || rep.varidx!=nbrcomp )
                {
                    result = true;
                    return result;
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function return function value and it derivatives. Function dimension
        is 3.
            Function's list:
                * funcType=1:
                    F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*(Z-Z0)^2+D;
                * funcType=2:
                    F(X)=A*sin(X-X0)^2+B*sin(Y-Y0)^2+C*sin(Z-Z0)^2+D;
                * funcType=3:
                    F(X)=A*(X-X0)^2+B*(Y-Y0)^2+C*((Z-Z0)-(X-X0))^2+D.
        *************************************************************************/
        private static void funcderiv(double a,
            double b,
            double c,
            double d,
            double x0,
            double x1,
            double x2,
            double[] x,
            int functype,
            ref double f,
            ref double[] g)
        {
            alglib.ap.assert(((math.isfinite(a) && math.isfinite(b)) && math.isfinite(c)) && math.isfinite(d), "FuncDeriv: A, B, C or D contains NaN or Infinite.");
            alglib.ap.assert((math.isfinite(x0) && math.isfinite(x1)) && math.isfinite(x2), "FuncDeriv: X0, X1 or X2 contains NaN or Infinite.");
            alglib.ap.assert(functype>=1 && functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).");
            if( functype==1 )
            {
                f = a*math.sqr(x[0]-x0)+b*math.sqr(x[1]-x1)+c*math.sqr(x[2]-x2)+d;
                g[0] = 2*a*(x[0]-x0);
                g[1] = 2*b*(x[1]-x1);
                g[2] = 2*c*(x[2]-x2);
                return;
            }
            if( functype==2 )
            {
                f = a*math.sqr(Math.Sin(x[0]-x0))+b*math.sqr(Math.Sin(x[1]-x1))+c*math.sqr(Math.Sin(x[2]-x2))+d;
                g[0] = 2*a*Math.Sin(x[0]-x0)*Math.Cos(x[0]-x0);
                g[1] = 2*b*Math.Sin(x[1]-x1)*Math.Cos(x[1]-x1);
                g[2] = 2*c*Math.Sin(x[2]-x2)*Math.Cos(x[2]-x2);
                return;
            }
            if( functype==3 )
            {
                f = a*math.sqr(x[0]-x0)+b*math.sqr(x[1]-x1)+c*math.sqr(x[2]-x2-(x[0]-x0))+d;
                g[0] = 2*a*(x[0]-x0)+2*c*(x[0]-x[2]-x0+x2);
                g[1] = 2*b*(x[1]-x1);
                g[2] = 2*c*(x[2]-x[0]-x2+x0);
                return;
            }
        }


    }
    public class testmlptrainunit
    {
        public static bool testmlptrain(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            int maxn = 0;
            int maxhid = 0;
            int info = 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();
            double[,] xy = new double[0,0];
            double[,] valxy = new double[0,0];
            bool trnerrors = new bool();
            bool mlpcverrorerr = new bool();
            bool mlptrainregrerr = new bool();
            bool mlptrainclasserr = new bool();
            bool mlprestartserr = new bool();
            bool mlpxorregrerr = new bool();
            bool mlpxorclserr = new bool();
            bool mlptrainenserrors = new bool();

            waserrors = false;
            trnerrors = false;
            mlpcverrorerr = false;
            mlptrainregrerr = false;
            mlptrainclasserr = false;
            mlprestartserr = false;
            mlpxorregrerr = false;
            mlpxorclserr = false;
            mlptrainenserrors = false;
            maxn = 4;
            maxhid = 4;
            
            //
            // 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 early stopping
            //
            trnerrors = trnerrors || testmlptraines();
            
            //
            // Test for function MLPFoldCV()
            //
            mlpcverrorerr = testmlpcverror();
            
            //
            // Test for training functions
            //
            mlptrainregrerr = testmlptrainregr() || testmlpzeroweights();
            mlptrainclasserr = testmlptrainclass();
            mlprestartserr = testmlprestarts();
            mlpxorregrerr = testmlpxorregr();
            mlpxorclserr = testmlpxorcls();
            
            //
            // Training for ensembles
            //
            mlptrainenserrors = (testmlptrainens() || testmlptrainensregr()) || testmlptrainenscls();
            
            //
            // Final report
            //
            waserrors = ((((((trnerrors || mlptrainregrerr) || mlptrainclasserr) || mlprestartserr) || mlpxorregrerr) || mlpxorclserr) || mlpcverrorerr) || mlptrainenserrors;
            if( !silent )
            {
                System.Console.Write("MLP TEST");
                System.Console.WriteLine();
                System.Console.Write("CROSS-VALIDATION ERRORS:                 ");
                if( !mlpcverrorerr )
                {
                    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();
                }
                System.Console.Write("TRAIN -LM -LBFGS FOR REGRESSION:         ");
                if( mlptrainregrerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("TRAIN -LM -LBFGS FOR CLASSIFIER:         ");
                if( mlptrainclasserr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("PARAMETER RESTARTS IN TRAIN -LBFGS:      ");
                if( mlprestartserr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("TRAINIG WITH TRAINER FOR REGRESSION:     ");
                if( mlpxorregrerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("TRAINIG WITH TRAINER FOR CLASSIFIER:     ");
                if( mlpxorclserr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("TRAINING ENSEMBLES:                      ");
                if( mlptrainenserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testmlptrain(bool silent)
        {
            return testmlptrain(silent);
        }


        /*************************************************************************
        This function tests MLPTrainES().

        It returns True in case of errors, False when no errors were detected
        *************************************************************************/
        private static bool testmlptraines()
        {
            bool result = new bool();
            int pass = 0;
            int passcount = 0;
            mlpbase.multilayerperceptron network = new mlpbase.multilayerperceptron();
            double[,] trnxy = new double[0,0];
            double[,] valxy = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            int n = 0;
            int i = 0;
            int j = 0;
            int nrestarts = 0;
            int info = 0;
            mlptrain.mlpreport rep = new mlptrain.mlpreport();

            result = false;
            
            //
            // First test checks that MLPTrainES() - when training set is equal to the validation
            // set, MLPTrainES() behaves just like a "normal" training algorithm.
            //
            // Test sequence:
            // * generate training set - 100 random points from 2D square [-1,+1]*[-1,+1]
            // * generate network with 2 inputs, no hidden layers, nonlinear output layer,
            //   use its outputs as target values for the test set
            // * randomize network
            // * train with MLPTrainES(), using original set as both training and validation set
            // * trained network must reproduce training set with good precision
            //
            // NOTE: it is important to test algorithm on nonlinear network because linear
            //       problems converge too fast. Slow convergence is important to detect
            //       some kinds of bugs.
            //
            // NOTE: it is important to have NRestarts at least equal to 5, because with just
            //       one restart algorithm fails test about once in several thousands of passes.
            //
            passcount = 10;
            nrestarts = 5;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Create network, generate training/validation sets
                //
                mlpbase.mlpcreater0(2, 1, -2.0, 2.0, network);
                mlpbase.mlprandomize(network);
                n = 100;
                trnxy = new double[n, 3];
                valxy = new double[n, 3];
                x = new double[2];
                y = new double[1];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=1; j++)
                    {
                        trnxy[i,j] = 2*math.randomreal()-1;
                        valxy[i,j] = trnxy[i,j];
                        x[j] = trnxy[i,j];
                    }
                    mlpbase.mlpprocess(network, x, ref y);
                    trnxy[i,2] = y[0];
                    valxy[i,2] = y[0];
                }
                mlpbase.mlprandomize(network);
                mlptrain.mlptraines(network, trnxy, n, valxy, n, 0.0001, nrestarts, ref info, rep);
                if( info<=0 )
                {
                    result = true;
                    return result;
                }
                if( (double)(Math.Sqrt(mlpbase.mlperror(network, valxy, n)/n))>(double)(0.01) )
                {
                    result = true;
                    return result;
                }
            }
            return result;
        }


        /*************************************************************************
        This  function   tests   MLPTrainLM,  MLPTrainLBFGS   and  MLPTrainNetwork
        functions  for regression.  It check that  train functions  work correctly.
        Test use Create1 with 10 neurons.
        Test function is f(x,y)=X^2+cos(3*Pi*y).
        *************************************************************************/
        private static bool testmlptrainregr()
        {
            bool result = new bool();
            mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron();
            mlptrain.mlptrainer trainer = new mlptrain.mlptrainer();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            int info = 0;
            double[,] xy = new double[0,0];
            sparse.sparsematrix sm = new sparse.sparsematrix();
            double[] x = new double[0];
            double[] y = new double[0];
            int n = 0;
            int sn = 0;
            int nneurons = 0;
            double vdecay = 0;
            double averr = 0;
            double st = 0;
            double eps = 0;
            double traineps = 0;
            int nneedrest = 0;
            int trainits = 0;
            int shift = 0;
            int i = 0;
            int j = 0;
            int vtrain = 0;

            eps = 0.01;
            vdecay = 0.001;
            nneurons = 10;
            nneedrest = 5;
            traineps = 1.0E-3;
            trainits = 0;
            sn = 5;
            n = sn*sn;
            st = (double)2/(double)(sn-1);
            sparse.sparsecreate(n, 3, n*3, sm);
            xy = new double[n, 3];
            x = new double[2];
            for(vtrain=0; vtrain<=3; vtrain++)
            {
                averr = 0;
                
                //
                // Create a train set(uniformly distributed set of points).
                //
                for(i=0; i<=sn-1; i++)
                {
                    for(j=0; j<=sn-1; j++)
                    {
                        shift = i*sn+j;
                        xy[shift,0] = i*st-1.0;
                        xy[shift,1] = j*st-1.0;
                        xy[shift,2] = xy[shift,0]*xy[shift,0]+Math.Cos(3*Math.PI*xy[shift,1]);
                    }
                }
                
                //
                // Create and train a neural network
                //
                mlpbase.mlpcreate1(2, nneurons, 1, net);
                if( vtrain==0 )
                {
                    mlptrain.mlptrainlm(net, xy, n, vdecay, nneedrest, ref info, rep);
                }
                if( vtrain==1 )
                {
                    mlptrain.mlptrainlbfgs(net, xy, n, vdecay, nneedrest, traineps, trainits, ref info, rep);
                }
                
                //
                // Train with trainer, using:
                //  * dense matrix;
                //
                if( vtrain==2 )
                {
                    mlptrain.mlpcreatetrainer(2, 1, trainer);
                    mlptrain.mlpsetdataset(trainer, xy, n);
                    mlptrain.mlpsetdecay(trainer, vdecay);
                    mlptrain.mlpsetcond(trainer, traineps, trainits);
                    mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                }
                
                //
                //  * sparse matrix.
                //
                if( vtrain==3 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=2; j++)
                        {
                            sparse.sparseset(sm, i, j, xy[i,j]);
                        }
                    }
                    mlptrain.mlpcreatetrainer(2, 1, trainer);
                    mlptrain.mlpsetsparsedataset(trainer, sm, n);
                    mlptrain.mlpsetdecay(trainer, vdecay);
                    mlptrain.mlpsetcond(trainer, traineps, trainits);
                    mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                }
                
                //
                // Check that network is trained correctly
                //
                for(i=0; i<=n-1; i++)
                {
                    x[0] = xy[i,0];
                    x[1] = xy[i,1];
                    mlpbase.mlpprocess(net, x, ref y);
                    
                    //
                    // Calculate average error
                    //
                    averr = averr+Math.Abs(y[0]-xy[i,2]);
                }
                if( (double)(averr/n)>(double)(eps) )
                {
                    result = true;
                    return result;
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This  function tests  MLPTrainNetwork/MLPStartTraining/MLPContinueTraining
        functions for  regression.  It check  that train  functions work correctly.
        Test use Create1 with 2 neurons.
        Test function is XOR(x,y).
        *************************************************************************/
        private static bool testmlpxorregr()
        {
            bool result = new bool();
            mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron();
            mlptrain.mlptrainer trainer = new mlptrain.mlptrainer();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            double[,] xy = new double[0,0];
            sparse.sparsematrix sm = new sparse.sparsematrix();
            double[] x = new double[0];
            double[] y = new double[0];
            int n = 0;
            int sn = 0;
            int nneurons = 0;
            double vdecay = 0;
            double averr = 0;
            double eps = 0;
            int numxp = 0;
            double traineps = 0;
            int nneedrest = 0;
            int trainits = 0;
            int shift = 0;
            int i = 0;
            int j = 0;
            int vtrain = 0;
            int xp = 0;

            eps = 0.01;
            numxp = 15;
            vdecay = 0.001;
            nneurons = 3;
            nneedrest = 1;
            traineps = 1.0E-4;
            trainits = 0;
            sn = 2;
            n = sn*sn;
            sparse.sparsecreate(n, 3, n*3, sm);
            xy = new double[n, 3];
            x = new double[2];
            for(xp=1; xp<=numxp; xp++)
            {
                for(vtrain=0; vtrain<=3; vtrain++)
                {
                    averr = 0;
                    
                    //
                    // Create a train set(uniformly distributed set of points).
                    //
                    for(i=0; i<=sn-1; i++)
                    {
                        for(j=0; j<=sn-1; j++)
                        {
                            shift = i*sn+j;
                            xy[shift,0] = i;
                            xy[shift,1] = j;
                            if( (double)(xy[shift,0])==(double)(xy[shift,1]) )
                            {
                                xy[shift,2] = 0;
                            }
                            else
                            {
                                xy[shift,2] = 1;
                            }
                        }
                    }
                    
                    //
                    // Create and train a neural network
                    //
                    mlpbase.mlpcreate1(2, nneurons, 1, net);
                    
                    //
                    // Train with trainer, using:
                    //  * dense matrix;
                    //
                    if( vtrain==0 )
                    {
                        mlptrain.mlpcreatetrainer(2, 1, trainer);
                        mlptrain.mlpsetdataset(trainer, xy, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                    }
                    if( vtrain==1 )
                    {
                        mlptrain.mlpcreatetrainer(2, 1, trainer);
                        mlptrain.mlpsetdataset(trainer, xy, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlpstarttraining(trainer, net, true);
                        while( mlptrain.mlpcontinuetraining(trainer, net) )
                        {
                        }
                    }
                    
                    //
                    //  * sparse matrix.
                    //
                    if( vtrain==2 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=2; j++)
                            {
                                sparse.sparseset(sm, i, j, xy[i,j]);
                            }
                        }
                        mlptrain.mlpcreatetrainer(2, 1, trainer);
                        mlptrain.mlpsetsparsedataset(trainer, sm, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                    }
                    if( vtrain==3 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=2; j++)
                            {
                                sparse.sparseset(sm, i, j, xy[i,j]);
                            }
                        }
                        mlptrain.mlpcreatetrainer(2, 1, trainer);
                        mlptrain.mlpsetsparsedataset(trainer, sm, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlpstarttraining(trainer, net, true);
                        while( mlptrain.mlpcontinuetraining(trainer, net) )
                        {
                        }
                    }
                    
                    //
                    // Check that network is trained correctly
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[0] = xy[i,0];
                        x[1] = xy[i,1];
                        mlpbase.mlpprocess(net, x, ref y);
                        
                        //
                        // Calculate average error
                        //
                        averr = averr+Math.Abs(y[0]-xy[i,2]);
                    }
                    if( (double)(averr/n)>(double)(eps) )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This  function   tests   MLPTrainLM,  MLPTrainLBFGS  and   MLPTrainNetwork
        functions for classification problems. It check that train  functions work
        correctly  when  is used CreateC1  function.  Here  the network  tries  to
        distinguish positive from negative numbers.
        *************************************************************************/
        private static bool testmlptrainclass()
        {
            bool result = new bool();
            mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron();
            mlptrain.mlptrainer trainer = new mlptrain.mlptrainer();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            int info = 0;
            double[,] xy = new double[0,0];
            sparse.sparsematrix sm = new sparse.sparsematrix();
            double[] x = new double[0];
            double[] y = new double[0];
            int n = 0;
            double vdecay = 0;
            double traineps = 0;
            int nneedrest = 0;
            int trainits = 0;
            double tmp = 0;
            double mnc = 0;
            double mxc = 0;
            int nxp = 0;
            int i = 0;
            int rndind = 0;
            int vtrain = 0;
            int xp = 0;

            mnc = 10;
            mxc = 11;
            nxp = 15;
            vdecay = 0.001;
            nneedrest = 10;
            traineps = 1.0E-4;
            trainits = 0;
            n = 100;
            sparse.sparsecreate(n, 2, n*2, sm);
            x = new double[1];
            xy = new double[n, 2];
            for(xp=1; xp<=nxp; xp++)
            {
                for(vtrain=0; vtrain<=3; vtrain++)
                {
                    
                    //
                    // Initialization:
                    //  * create negative part of the set;
                    //
                    for(i=0; i<=n/2-1; i++)
                    {
                        xy[i,0] = -(1*((mxc-mnc)*math.randomreal()+mnc));
                        xy[i,1] = 0;
                    }
                    
                    //
                    //  * create positive part of the set;
                    //
                    for(i=n/2; i<=n-1; i++)
                    {
                        xy[i,0] = (mxc-mnc)*math.randomreal()+mnc;
                        xy[i,1] = 1;
                    }
                    
                    //
                    //  * mix two parts.
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        do
                        {
                            rndind = math.randominteger(n);
                        }
                        while( rndind==i );
                        tmp = xy[i,0];
                        xy[i,0] = xy[rndind,0];
                        xy[rndind,0] = tmp;
                        tmp = xy[i,1];
                        xy[i,1] = xy[rndind,1];
                        xy[rndind,1] = tmp;
                    }
                    
                    //
                    // Create and train a neural network
                    //
                    mlpbase.mlpcreatec0(1, 2, net);
                    if( vtrain==0 )
                    {
                        mlptrain.mlptrainlm(net, xy, n, vdecay, nneedrest, ref info, rep);
                    }
                    if( vtrain==1 )
                    {
                        mlptrain.mlptrainlbfgs(net, xy, n, vdecay, nneedrest, traineps, trainits, ref info, rep);
                    }
                    
                    //
                    // Train with trainer, using:
                    //  * dense matrix;
                    //
                    if( vtrain==2 )
                    {
                        mlptrain.mlpcreatetrainercls(1, 2, trainer);
                        mlptrain.mlpsetdataset(trainer, xy, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                    }
                    
                    //
                    //  * sparse matrix.
                    //
                    if( vtrain==3 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            sparse.sparseset(sm, i, 0, xy[i,0]);
                            sparse.sparseset(sm, i, 1, xy[i,1]);
                        }
                        mlptrain.mlpcreatetrainercls(1, 2, trainer);
                        mlptrain.mlpsetsparsedataset(trainer, sm, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                    }
                    
                    //
                    // Test on training set
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[0] = xy[i,0];
                        mlpbase.mlpprocess(net, x, ref y);
                        
                        //
                        // Negative number has to be negative and
                        // positive number has to be positive.
                        //
                        if( (((double)(x[0])<(double)(0) && (double)(y[0])<(double)(0.95)) && (double)(y[1])>(double)(0.05)) || (((double)(x[0])>=(double)(0) && (double)(y[0])>(double)(0.05)) && (double)(y[1])<(double)(0.95)) )
                        {
                            result = true;
                            return result;
                        }
                    }
                    
                    //
                    // Test on random set
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[0] = Math.Pow(-1, math.randominteger(2))*((mxc-mnc)*math.randomreal()+mnc);
                        mlpbase.mlpprocess(net, x, ref y);
                        if( (((double)(x[0])<(double)(0) && (double)(y[0])<(double)(0.95)) && (double)(y[1])>(double)(0.05)) || (((double)(x[0])>=(double)(0) && (double)(y[0])>(double)(0.05)) && (double)(y[1])<(double)(0.95)) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function tests   MLPTrainNetwork/MLPStartTraining/MLPContinueTraining
        functions for classification problems. It check that train  functions work
        correctly  when  is used CreateC1  function.  Here  the network  tries  to
        distinguish positive from negative numbers.
        *************************************************************************/
        private static bool testmlpxorcls()
        {
            bool result = new bool();
            mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron();
            mlptrain.mlptrainer trainer = new mlptrain.mlptrainer();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            double[,] xy = new double[0,0];
            sparse.sparsematrix sm = new sparse.sparsematrix();
            double[] x = new double[0];
            double[] y = new double[0];
            int n = 0;
            int nin = 0;
            int nout = 0;
            int wcount = 0;
            double e = 0;
            double ebest = 0;
            double v = 0;
            double[] wbest = new double[0];
            double vdecay = 0;
            double traineps = 0;
            int nneurons = 0;
            int nneedrest = 0;
            int trainits = 0;
            int nxp = 0;
            int i = 0;
            int vtrain = 0;
            int xp = 0;
            int i_ = 0;

            nxp = 15;
            nneurons = 3;
            vdecay = 0.001;
            nneedrest = 3;
            traineps = 1.0E-4;
            trainits = 0;
            n = 4;
            sparse.sparsecreate(n, 3, n*3, sm);
            x = new double[2];
            xy = new double[n, 3];
            
            //
            // Initialization:
            //
            xy[0,0] = 0;
            xy[0,1] = 0;
            xy[0,2] = 0;
            xy[1,0] = 0;
            xy[1,1] = 1;
            xy[1,2] = 1;
            xy[2,0] = 1;
            xy[2,1] = 0;
            xy[2,2] = 1;
            xy[3,0] = 1;
            xy[3,1] = 1;
            xy[3,2] = 0;
            
            //
            // Create a neural network
            //
            mlpbase.mlpcreatec1(2, nneurons, 2, net);
            mlpbase.mlpproperties(net, ref nin, ref nout, ref wcount);
            wbest = new double[wcount];
            
            //
            // Test
            //
            for(xp=1; xp<=nxp; xp++)
            {
                for(vtrain=0; vtrain<=3; vtrain++)
                {
                    
                    //
                    // Train with trainer, using:
                    //  * dense matrix;
                    //
                    if( vtrain==0 )
                    {
                        mlptrain.mlpcreatetrainercls(2, 2, trainer);
                        mlptrain.mlpsetdataset(trainer, xy, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                    }
                    if( vtrain==1 )
                    {
                        mlptrain.mlpcreatetrainercls(2, 2, trainer);
                        mlptrain.mlpsetdataset(trainer, xy, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        ebest = math.maxrealnumber;
                        for(i=1; i<=nneedrest; i++)
                        {
                            mlptrain.mlpstarttraining(trainer, net, true);
                            while( mlptrain.mlpcontinuetraining(trainer, net) )
                            {
                            }
                            v = 0.0;
                            for(i_=0; i_<=wcount-1;i_++)
                            {
                                v += net.weights[i_]*net.weights[i_];
                            }
                            e = mlpbase.mlperror(net, xy, n)+0.5*vdecay*v;
                            
                            //
                            // Compare with the best answer.
                            //
                            if( (double)(e)<(double)(ebest) )
                            {
                                for(i_=0; i_<=wcount-1;i_++)
                                {
                                    wbest[i_] = net.weights[i_];
                                }
                                ebest = e;
                            }
                        }
                        
                        //
                        // The best result
                        //
                        for(i_=0; i_<=wcount-1;i_++)
                        {
                            net.weights[i_] = wbest[i_];
                        }
                    }
                    
                    //
                    //  * sparse matrix.
                    //
                    if( vtrain==2 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            sparse.sparseset(sm, i, 0, xy[i,0]);
                            sparse.sparseset(sm, i, 1, xy[i,1]);
                            sparse.sparseset(sm, i, 2, xy[i,2]);
                        }
                        mlptrain.mlpcreatetrainercls(2, 2, trainer);
                        mlptrain.mlpsetsparsedataset(trainer, sm, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                    }
                    if( vtrain==3 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            sparse.sparseset(sm, i, 0, xy[i,0]);
                            sparse.sparseset(sm, i, 1, xy[i,1]);
                            sparse.sparseset(sm, i, 2, xy[i,2]);
                        }
                        mlptrain.mlpcreatetrainercls(2, 2, trainer);
                        mlptrain.mlpsetsparsedataset(trainer, sm, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        ebest = math.maxrealnumber;
                        for(i=1; i<=nneedrest; i++)
                        {
                            mlptrain.mlpstarttraining(trainer, net, true);
                            while( mlptrain.mlpcontinuetraining(trainer, net) )
                            {
                            }
                            v = 0.0;
                            for(i_=0; i_<=wcount-1;i_++)
                            {
                                v += net.weights[i_]*net.weights[i_];
                            }
                            e = mlpbase.mlperror(net, xy, n)+0.5*vdecay*v;
                            
                            //
                            // Compare with the best answer.
                            //
                            if( (double)(e)<(double)(ebest) )
                            {
                                for(i_=0; i_<=wcount-1;i_++)
                                {
                                    wbest[i_] = net.weights[i_];
                                }
                                ebest = e;
                            }
                        }
                        
                        //
                        // The best result
                        //
                        for(i_=0; i_<=wcount-1;i_++)
                        {
                            net.weights[i_] = wbest[i_];
                        }
                    }
                    
                    //
                    // Test on training set
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        x[0] = xy[i,0];
                        x[1] = xy[i,1];
                        mlpbase.mlpprocess(net, x, ref y);
                        if( (((double)(x[0])==(double)(x[1]) && (double)(y[0])<(double)(0.95)) && (double)(y[1])>(double)(0.05)) || (((double)(x[0])!=(double)(x[1]) && (double)(y[0])>(double)(0.05)) && (double)(y[1])<(double)(0.95)) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        The  test  check,  that  all weights are zero after training with trainer
        using empty dataset(either zero size or is't used MLPSetDataSet function).
        Test  on  regression and classification problems given by dense or sparse
        matrix.

        NOTE: Result of the function is written in MLPTrainRegrErr variable in
              unit test.
        *************************************************************************/
        private static bool testmlpzeroweights()
        {
            bool result = new bool();
            mlptrain.mlptrainer trainer = new mlptrain.mlptrainer();
            mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            int nin = 0;
            int nout = 0;
            int wcount = 0;
            int mxnin = 0;
            int mxnout = 0;
            double vdecay = 0;
            double traineps = 0;
            int trainits = 0;
            int nneedrest = 0;
            double[,] dds = new double[0,0];
            sparse.sparsematrix sds = new sparse.sparsematrix();
            bool iscls = new bool();
            bool issparse = new bool();
            int c = 0;
            int n = 0;
            int mnn = 0;
            int mxn = 0;
            int xp = 0;
            int nxp = 0;

            mxn = 20;
            mnn = 10;
            mxnin = 10;
            mxnout = 10;
            vdecay = 1.0E-3;
            nneedrest = 1;
            traineps = 1.0E-3;
            trainits = 0;
            sparse.sparsecreate(1, 1, 0, sds);
            sparse.sparseconverttocrs(sds);
            nxp = 10;
            for(xp=1; xp<=nxp; xp++)
            {
                c = math.randominteger(2);
                iscls = c==1;
                c = math.randominteger(2);
                issparse = c==1;
                
                //
                // Create trainer and network
                //
                if( !iscls )
                {
                    
                    //
                    // Regression
                    //
                    nin = math.randominteger(mxnin)+1;
                    nout = math.randominteger(mxnout)+1;
                    mlptrain.mlpcreatetrainer(nin, nout, trainer);
                    mlpbase.mlpcreate0(nin, nout, net);
                }
                else
                {
                    
                    //
                    // Classification
                    //
                    nin = math.randominteger(mxnin)+1;
                    nout = math.randominteger(mxnout)+2;
                    mlptrain.mlpcreatetrainercls(nin, nout, trainer);
                    mlpbase.mlpcreatec0(nin, nout, net);
                }
                n = math.randominteger(2)-1;
                if( n==0 )
                {
                    if( !issparse )
                    {
                        mlptrain.mlpsetdataset(trainer, dds, n);
                    }
                    else
                    {
                        mlptrain.mlpsetsparsedataset(trainer, sds, n);
                    }
                }
                mlptrain.mlpsetdecay(trainer, vdecay);
                mlptrain.mlpsetcond(trainer, traineps, trainits);
                c = math.randominteger(2);
                if( c==0 )
                {
                    mlptrain.mlpstarttraining(trainer, net, true);
                    while( mlptrain.mlpcontinuetraining(trainer, net) )
                    {
                    }
                }
                if( c==1 )
                {
                    mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                }
                
                //
                // Check weights
                //
                mlpbase.mlpproperties(net, ref nin, ref nout, ref wcount);
                for(c=0; c<=wcount-1; c++)
                {
                    if( (double)(net.weights[c])!=(double)(0) )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function tests that increasing numbers of restarts lead to statistical
        improvement quality of solution.
        Neural network created by Create1(10 neurons) and trained by MLPTrainLBFGS.

        TEST's DISCRIPTION:

        Net0   -   network trained with one restart (denoted as R1)
        Net1   -   network trained with more than one restart (denoted as Rn)

        We must refuse hypothesis that R1 equivalent to Rn.
        Here Mean = N/2, Sigma = Sqrt(N)/2.
               _
              | 0   -   R1 worse than Rn;
         ri = |
              |_1   -   Rn same or worse then R1.
            
        If Sum(ri)<Mean-5*Sigma then hypothesis is refused and test is passed.
        In another case if Mean-5*Sigma<=Sum(ri)<=Mean+5*Sigma then hypothesis
        is't refused and test is broken; and if Mean+5*Sigma<Sum(ri) then test
        broken too hard!
        *************************************************************************/
        private static bool testmlprestarts()
        {
            bool result = new bool();
            mlpbase.multilayerperceptron net0 = new mlpbase.multilayerperceptron();
            mlpbase.multilayerperceptron net1 = new mlpbase.multilayerperceptron();
            mlptrain.mlptrainer trainer = new mlptrain.mlptrainer();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            int info = 0;
            sparse.sparsematrix sm = new sparse.sparsematrix();
            double[,] xy = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            int n = 0;
            int nneurons = 0;
            double vdecay = 0;
            int wcount0 = 0;
            int wcount1 = 0;
            int nin = 0;
            int nout = 0;
            double avval = 0;
            double e0 = 0;
            double e1 = 0;
            double mean = 0;
            double numsigma = 0;
            int numxp = 0;
            double traineps = 0;
            int nneedrest = 0;
            int trainits = 0;
            int i = 0;
            int vtrain = 0;
            int xp = 0;
            int i_ = 0;

            vdecay = 0.001;
            nneurons = 4;
            nneedrest = 3;
            traineps = 0.00;
            trainits = 2;
            n = 20;
            numxp = 400;
            xy = new double[n, 2];
            x = new double[1];
            sparse.sparsecreate(n, 2, n*2, sm);
            mean = numxp/2.0;
            numsigma = 5.0*Math.Sqrt(numxp)/2.0;
            for(vtrain=0; vtrain<=2; vtrain++)
            {
                avval = 0;
                for(xp=1; xp<=numxp; xp++)
                {
                    
                    //
                    // Create a train set
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        xy[i,0] = 2*math.randomreal()-1;
                        xy[i,1] = 2*math.randomreal()-1;
                    }
                    
                    //
                    // Create and train a neural network
                    //
                    mlpbase.mlpcreate1(1, nneurons, 1, net0);
                    mlpbase.mlpcreate1(1, nneurons, 1, net1);
                    if( vtrain==0 )
                    {
                        mlptrain.mlptrainlbfgs(net0, xy, n, vdecay, 1, traineps, trainits, ref info, rep);
                        mlptrain.mlptrainlbfgs(net1, xy, n, vdecay, nneedrest, traineps, trainits, ref info, rep);
                    }
                    if( vtrain==1 )
                    {
                        mlptrain.mlpcreatetrainer(1, 1, trainer);
                        mlptrain.mlpsetdataset(trainer, xy, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlptrainnetwork(trainer, net0, 1, rep);
                        mlptrain.mlptrainnetwork(trainer, net1, nneedrest, rep);
                    }
                    if( vtrain==2 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            sparse.sparseset(sm, i, 0, xy[i,0]);
                            sparse.sparseset(sm, i, 1, xy[i,1]);
                        }
                        mlptrain.mlpcreatetrainer(1, 1, trainer);
                        mlptrain.mlpsetsparsedataset(trainer, sm, n);
                        mlptrain.mlpsetdecay(trainer, vdecay);
                        mlptrain.mlpsetcond(trainer, traineps, trainits);
                        mlptrain.mlptrainnetwork(trainer, net0, 1, rep);
                        mlptrain.mlptrainnetwork(trainer, net1, nneedrest, rep);
                    }
                    
                    //
                    // Calculate errors for...
                    //
                    // ...for Net0, trained with 1 restart.
                    //
                    mlpbase.mlpproperties(net0, ref nin, ref nout, ref wcount0);
                    e0 = 0.0;
                    for(i_=0; i_<=wcount0-1;i_++)
                    {
                        e0 += net0.weights[i_]*net0.weights[i_];
                    }
                    e0 = mlpbase.mlperrorn(net0, xy, n)+0.5*vdecay*e0;
                    
                    //
                    // ...for Net1, trained with NNeedRest>1 restarts.
                    //
                    mlpbase.mlpproperties(net1, ref nin, ref nout, ref wcount1);
                    e1 = 0.0;
                    for(i_=0; i_<=wcount1-1;i_++)
                    {
                        e1 += net1.weights[i_]*net1.weights[i_];
                    }
                    e1 = mlpbase.mlperrorn(net1, xy, n)+0.5*vdecay*e1;
                    if( (double)(e0)<=(double)(e1) )
                    {
                        avval = avval+1;
                    }
                }
                if( (double)(mean-numsigma)<(double)(avval) )
                {
                    result = true;
                    return result;
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        The function test function MLPKFoldCV.
        *************************************************************************/
        private static bool testmlpcverror()
        {
            bool result = new bool();
            mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron();
            mlptrain.mlptrainer trainer = new mlptrain.mlptrainer();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            mlptrain.mlpreport cvrep = new mlptrain.mlpreport();
            int nin = 0;
            int nout = 0;
            int nneurons = 0;
            int rowsz = 0;
            double decay = 0;
            double wstep = 0;
            int maxits = 0;
            int foldscount = 0;
            int nneedrest = 0;
            sparse.sparsematrix sptrainingset = new sparse.sparsematrix();
            double[,] trainingset = new double[0,0];
            double[,] testset = new double[0,0];
            int npoints = 0;
            int ntstpoints = 0;
            double mean = 0;
            double numsigma = 0;
            double diffms = 0;
            double tstrelclserror = 0;
            double tstavgce = 0;
            double tstrmserror = 0;
            double tstavgerror = 0;
            double tstavgrelerror = 0;
            int r0 = 0;
            int r1 = 0;
            int r2 = 0;
            int r3 = 0;
            int r4 = 0;
            int ntest = 0;
            int xp = 0;
            int nxp = 0;
            bool isregr = new bool();
            int issparse = 0;
            int i = 0;
            int j = 0;

            decay = 1.0E-6;
            wstep = 0.0;
            foldscount = 5;
            nneedrest = 1;
            ntest = math.randominteger(4);
            nxp = 1000;
            maxits = 50;
            nin = 1;
            nout = 1;
            npoints = 5;
            ntstpoints = 100;
            isregr = true;
            nneurons = 3;
            if( ntest==1 )
            {
                nxp = 1000;
                maxits = 50;
                nin = 1;
                nout = 10;
                npoints = 5;
                ntstpoints = 100;
                isregr = true;
            }
            if( ntest==2 )
            {
                nxp = 1000;
                maxits = 50;
                nin = 10;
                nout = 1;
                npoints = 20;
                ntstpoints = 100;
                isregr = true;
            }
            if( ntest==3 )
            {
                nxp = 2000;
                maxits = 10;
                nin = 1;
                nneurons = 3;
                nout = 3;
                npoints = 10;
                ntstpoints = 100;
                isregr = false;
            }
            mean = nxp/2.0;
            numsigma = 5.0*Math.Sqrt(nxp)/2.0;
            diffms = mean-numsigma;
            issparse = math.randominteger(2);
            if( isregr )
            {
                mlpbase.mlpcreate0(nin, nout, net);
                mlptrain.mlpcreatetrainer(nin, nout, trainer);
            }
            else
            {
                mlpbase.mlpcreatec1(nin, nneurons, nout, net);
                mlptrain.mlpcreatetrainercls(nin, nout, trainer);
            }
            mlptrain.mlpsetcond(trainer, wstep, maxits);
            mlptrain.mlpsetdecay(trainer, decay);
            if( isregr )
            {
                rowsz = nin+nout;
            }
            else
            {
                rowsz = nin+1;
            }
            r0 = 0;
            r1 = 0;
            r2 = 0;
            r3 = 0;
            r4 = 0;
            for(xp=1; xp<=nxp; xp++)
            {
                
                //
                // Dense matrix
                //
                if( issparse==0 )
                {
                    apserv.rmatrixsetlengthatleast(ref trainingset, npoints, rowsz);
                    
                    //
                    // Create training set
                    //
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=nin-1; j++)
                        {
                            trainingset[i,j] = 2*math.randomreal()-1;
                        }
                    }
                    if( isregr )
                    {
                        for(i=0; i<=npoints-1; i++)
                        {
                            for(j=nin; j<=rowsz-1; j++)
                            {
                                trainingset[i,j] = 2*math.randomreal()+1;
                            }
                        }
                    }
                    else
                    {
                        for(i=0; i<=npoints-1; i++)
                        {
                            for(j=nin; j<=rowsz-1; j++)
                            {
                                trainingset[i,j] = math.randominteger(nout);
                            }
                        }
                    }
                    mlptrain.mlpsetdataset(trainer, trainingset, npoints);
                }
                
                //
                // Sparse matrix
                //
                if( issparse==1 )
                {
                    sparse.sparsecreate(npoints, rowsz, npoints*rowsz, sptrainingset);
                    
                    //
                    // Create training set
                    //
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=nin-1; j++)
                        {
                            sparse.sparseset(sptrainingset, i, j, 2*math.randomreal()-1);
                        }
                    }
                    if( isregr )
                    {
                        for(i=0; i<=npoints-1; i++)
                        {
                            for(j=nin; j<=rowsz-1; j++)
                            {
                                sparse.sparseset(sptrainingset, i, j, 2*math.randomreal()+1);
                            }
                        }
                    }
                    else
                    {
                        for(i=0; i<=npoints-1; i++)
                        {
                            for(j=nin; j<=rowsz-1; j++)
                            {
                                sparse.sparseset(sptrainingset, i, j, math.randominteger(nout));
                            }
                        }
                    }
                    sparse.sparseconverttocrs(sptrainingset);
                    mlptrain.mlpsetsparsedataset(trainer, sptrainingset, npoints);
                }
                apserv.rmatrixsetlengthatleast(ref testset, ntstpoints, rowsz);
                
                //
                // Create test set
                //
                for(i=0; i<=ntstpoints-1; i++)
                {
                    for(j=0; j<=nin-1; j++)
                    {
                        testset[i,j] = 2*math.randomreal()-1;
                    }
                }
                if( isregr )
                {
                    for(i=0; i<=ntstpoints-1; i++)
                    {
                        for(j=nin; j<=rowsz-1; j++)
                        {
                            testset[i,j] = 2*math.randomreal()+1;
                        }
                    }
                }
                else
                {
                    for(i=0; i<=ntstpoints-1; i++)
                    {
                        for(j=nin; j<=rowsz-1; j++)
                        {
                            testset[i,j] = math.randominteger(nout);
                        }
                    }
                }
                mlptrain.mlptrainnetwork(trainer, net, nneedrest, rep);
                tstrelclserror = 0;
                tstavgce = 0;
                tstrmserror = 0;
                tstavgerror = 0;
                tstavgrelerror = 0;
                if( !isregr )
                {
                    tstrelclserror = mlpbase.mlprelclserror(net, testset, ntstpoints);
                    tstavgce = mlpbase.mlpavgce(net, testset, ntstpoints);
                }
                tstrmserror = mlpbase.mlprmserror(net, testset, ntstpoints);
                tstavgerror = mlpbase.mlpavgerror(net, testset, ntstpoints);
                tstavgrelerror = mlpbase.mlpavgrelerror(net, testset, ntstpoints);
                
                //
                // Cross-validation
                //
                mlptrain.mlpkfoldcv(trainer, net, nneedrest, foldscount, cvrep);
                if( !isregr )
                {
                    if( (double)(Math.Abs(tstrelclserror-rep.relclserror))<(double)(Math.Abs(tstrelclserror-cvrep.relclserror)) )
                    {
                        r0 = r0+1;
                    }
                    if( (double)(Math.Abs(tstavgce-rep.avgce))<(double)(Math.Abs(tstavgce-cvrep.avgce)) )
                    {
                        r1 = r1+1;
                    }
                }
                if( (double)(Math.Abs(tstrmserror-rep.rmserror))<(double)(Math.Abs(tstrmserror-cvrep.rmserror)) )
                {
                    r2 = r2+1;
                }
                if( (double)(Math.Abs(tstavgerror-rep.avgerror))<(double)(Math.Abs(tstavgerror-cvrep.avgerror)) )
                {
                    r3 = r3+1;
                }
                if( (double)(Math.Abs(tstavgrelerror-rep.avgrelerror))<(double)(Math.Abs(tstavgrelerror-cvrep.avgrelerror)) )
                {
                    r4 = r4+1;
                }
            }
            if( !isregr )
            {
                if( (double)(diffms)<=(double)(r0) || (double)(diffms)<=(double)(r1) )
                {
                    result = true;
                    return result;
                }
            }
            if( ((double)(diffms)<=(double)(r2) || (double)(diffms)<=(double)(r3)) || (double)(diffms)<=(double)(r4) )
            {
                result = true;
                return result;
            }
            
            //
            // Test FoldCV when  no dataset  was specified with
            // MLPSetDataset/SetSparseDataset(), or subset with
            // only one point  was  given.
            //
            // NPoints values:
            //  * -1 - don't set dataset with using MLPSetDataset..;
            //  *  0 - zero dataset;
            //  *  1 - dataset with one point.
            //
            for(npoints=-1; npoints<=1; npoints++)
            {
                if( isregr )
                {
                    mlptrain.mlpcreatetrainer(nin, nout, trainer);
                }
                else
                {
                    mlptrain.mlpcreatetrainercls(nin, nout, trainer);
                }
                if( npoints>-1 )
                {
                    if( issparse==0 )
                    {
                        mlptrain.mlpsetdataset(trainer, trainingset, npoints);
                    }
                    if( issparse==1 )
                    {
                        mlptrain.mlpsetsparsedataset(trainer, sptrainingset, npoints);
                    }
                }
                mlptrain.mlpkfoldcv(trainer, net, nneedrest, foldscount, cvrep);
                if( (((((((double)(cvrep.relclserror)!=(double)(0) || (double)(cvrep.avgce)!=(double)(0)) || (double)(cvrep.rmserror)!=(double)(0)) || (double)(cvrep.avgerror)!=(double)(0)) || (double)(cvrep.avgrelerror)!=(double)(0)) || cvrep.ngrad!=0) || cvrep.nhess!=0) || cvrep.ncholesky!=0 )
                {
                    result = true;
                    return result;
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        The  function  tests  functions  for  training  ensembles:  MLPEBaggingLM,
        MLPEBaggingLBFGS.
        *************************************************************************/
        private static bool testmlptrainens()
        {
            bool result = new bool();
            mlpe.mlpensemble ensemble = new mlpe.mlpensemble();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            mlptrain.mlpcvreport oobrep = new mlptrain.mlpcvreport();
            int info = 0;
            double[,] xy = new double[0,0];
            int nin = 0;
            int nout = 0;
            int npoints = 0;
            int nhid = 0;
            int algtype = 0;
            int tasktype = 0;
            int pass = 0;
            double e = 0;
            int nless = 0;
            int nall = 0;
            int nclasses = 0;
            int i = 0;
            int j = 0;

            result = false;
            
            //
            // 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, nin+nout];
                            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, 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 )
                        {
                            mlptrain.mlpebagginglm(ensemble, xy, npoints, 0.001, 1, ref info, rep, oobrep);
                        }
                        else
                        {
                            mlptrain.mlpebagginglbfgs(ensemble, xy, npoints, 0.001, 1, 0.01, 0, ref info, rep, oobrep);
                        }
                        if( info<0 )
                        {
                            result = true;
                        }
                        else
                        {
                            if( (double)(mlpe.mlpermserror(ensemble, xy, npoints))<(double)(e) )
                            {
                                nless = nless+1;
                            }
                        }
                        nall = nall+1;
                    }
                }
            }
            result = result || (double)(nall-nless)>(double)(0.3*nall);
            return result;
        }


        /*************************************************************************
        Testing  for  functions  MLPETrainES and MLPTrainEnsembleES  on regression
        problems. Returns TRUE for errors, FALSE for success.
        *************************************************************************/
        private static bool testmlptrainensregr()
        {
            bool result = new bool();
            mlptrain.mlptrainer trainer = new mlptrain.mlptrainer();
            mlpe.mlpensemble netens = new mlpe.mlpensemble();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            mlpbase.modelerrors repx = new mlpbase.modelerrors();
            int info = 0;
            sparse.sparsematrix xytrainsp = new sparse.sparsematrix();
            double[,] xytrain = new double[0,0];
            double[,] xytest = new double[0,0];
            int nin = 0;
            int nout = 0;
            int nneurons = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double decay = 0;
            double wstep = 0;
            int maxits = 0;
            int nneedrest = 0;
            int enssize = 0;
            double mnval = 0;
            double mxval = 0;
            int ntrain = 0;
            int ntest = 0;
            double avgerr = 0;
            int issparse = 0;
            int withtrainer = 0;
            double eps = 0;
            int xp = 0;
            int i = 0;
            int j = 0;
            int i_ = 0;

            result = false;
            
            //
            // This test checks ability to train ensemble on simple regression
            // problem "f(x0,x1,x2,...) = x0 + x1 + x2 + ...".
            //
            eps = 5.0E-2;
            mnval = -1;
            mxval = 1;
            ntrain = 40;
            ntest = 20;
            decay = 1.0E-3;
            wstep = 1.0E-3;
            maxits = 20;
            nneedrest = 1;
            nneurons = 20;
            nout = 1;
            enssize = 100;
            for(xp=1; xp<=2; xp++)
            {
                nin = math.randominteger(3)+1;
                apserv.rvectorsetlengthatleast(ref x, nin);
                mlptrain.mlpcreatetrainer(nin, nout, trainer);
                mlptrain.mlpsetdecay(trainer, decay);
                mlptrain.mlpsetcond(trainer, wstep, maxits);
                apserv.rmatrixsetlengthatleast(ref xytrain, ntrain, nin+nout);
                apserv.rmatrixsetlengthatleast(ref xytest, ntest, nin+nout);
                withtrainer = math.randominteger(2);
                issparse = 0;
                if( withtrainer==0 )
                {
                    issparse = 0;
                }
                if( withtrainer==1 )
                {
                    issparse = math.randominteger(2);
                }
                
                //
                // Training set
                //
                for(i=0; i<=ntrain-1; i++)
                {
                    for(j=0; j<=nin-1; j++)
                    {
                        xytrain[i,j] = (mxval-mnval)*math.randomreal()+mnval;
                    }
                    xytrain[i,nin] = 0;
                    for(j=0; j<=nin-1; j++)
                    {
                        xytrain[i,nin] = xytrain[i,nin]+xytrain[i,j];
                    }
                }
                if( withtrainer==1 )
                {
                    
                    //
                    // Dense matrix
                    //
                    if( issparse==0 )
                    {
                        mlptrain.mlpsetdataset(trainer, xytrain, ntrain);
                    }
                    
                    //
                    // Sparse matrix
                    //
                    if( issparse==1 )
                    {
                        sparse.sparsecreate(ntrain, nin+nout, ntrain*(nin+nout), xytrainsp);
                        
                        //
                        // Just copy dense matrix to sparse matrix(using SparseGet() is too expensive).
                        //
                        for(i=0; i<=ntrain-1; i++)
                        {
                            for(j=0; j<=nin+nout-1; j++)
                            {
                                sparse.sparseset(xytrainsp, i, j, xytrain[i,j]);
                            }
                        }
                        sparse.sparseconverttocrs(xytrainsp);
                        mlptrain.mlpsetsparsedataset(trainer, xytrainsp, ntrain);
                    }
                }
                
                //
                // Test set
                //
                for(i=0; i<=ntest-1; i++)
                {
                    for(j=0; j<=nin-1; j++)
                    {
                        xytest[i,j] = (mxval-mnval)*math.randomreal()+mnval;
                    }
                    xytest[i,nin] = 0;
                    for(j=0; j<=nin-1; j++)
                    {
                        xytest[i,nin] = xytest[i,nin]+xytest[i,j];
                    }
                }
                
                //
                // Create ensemble
                //
                mlpe.mlpecreate1(nin, nneurons, nout, enssize, netens);
                
                //
                // Train ensembles:
                //  * without trainer;
                //
                if( withtrainer==0 )
                {
                    mlptrain.mlpetraines(netens, xytrain, ntrain, decay, nneedrest, ref info, rep);
                }
                
                //
                //  * with trainer.
                //
                if( withtrainer==1 )
                {
                    mlptrain.mlptrainensemblees(trainer, netens, nneedrest, rep);
                }
                
                //
                // Test that Rep contains correct error values
                //
                mlpe.mlpeallerrorsx(netens, xytrain, xytrainsp, ntrain, 0, netens.network.dummyidx, 0, ntrain, 0, netens.network.buf, repx);
                apserv.seterrorflagdiff(ref result, rep.relclserror, repx.relclserror, 1.0E-4, 1.0E-2);
                apserv.seterrorflagdiff(ref result, rep.avgce, repx.avgce, 1.0E-4, 1.0E-2);
                apserv.seterrorflagdiff(ref result, rep.rmserror, repx.rmserror, 1.0E-4, 1.0E-2);
                apserv.seterrorflagdiff(ref result, rep.avgerror, repx.avgerror, 1.0E-4, 1.0E-2);
                apserv.seterrorflagdiff(ref result, rep.avgrelerror, repx.avgrelerror, 1.0E-4, 1.0E-2);
                
                //
                // Test that network fits data well. Calculate average error:
                //  * on training dataset;
                //  * on test dataset. (here we reduce the accuracy
                //    requirements - average error is compared with 2*Eps).
                //
                avgerr = 0;
                for(i=0; i<=ntrain-1; i++)
                {
                    if( issparse==0 )
                    {
                        for(i_=0; i_<=nin-1;i_++)
                        {
                            x[i_] = xytrain[i,i_];
                        }
                    }
                    if( issparse==1 )
                    {
                        sparse.sparsegetrow(xytrainsp, i, ref x);
                    }
                    mlpe.mlpeprocess(netens, x, ref y);
                    avgerr = avgerr+Math.Abs(y[0]-xytrain[i,nin]);
                }
                avgerr = avgerr/ntrain;
                apserv.seterrorflag(ref result, (double)(avgerr)>(double)(eps));
                avgerr = 0;
                for(i=0; i<=ntest-1; i++)
                {
                    for(i_=0; i_<=nin-1;i_++)
                    {
                        x[i_] = xytest[i,i_];
                    }
                    mlpe.mlpeprocess(netens, x, ref y);
                    avgerr = avgerr+Math.Abs(y[0]-xytest[i,nin]);
                }
                avgerr = avgerr/ntest;
                apserv.seterrorflag(ref result, (double)(avgerr)>(double)(2*eps));
            }
            
            //
            // Catch bug in implementation of MLPTrainEnsembleX:
            // test ensemble training on empty dataset.
            //
            // Unfixed version should crash with violation of array
            // bounds (at least in C#).
            //
            nin = 2;
            nout = 2;
            nneurons = 3;
            enssize = 3;
            nneedrest = 2;
            wstep = 0.001;
            maxits = 2;
            mlptrain.mlpcreatetrainer(nin, nout, trainer);
            mlptrain.mlpsetcond(trainer, wstep, maxits);
            mlpe.mlpecreate1(nin, nneurons, nout, enssize, netens);
            mlptrain.mlptrainensemblees(trainer, netens, nneedrest, rep);
            return result;
        }


        /*************************************************************************
        Testing for functions MLPETrainES and MLPTrainEnsembleES on classification
        problems.
        *************************************************************************/
        private static bool testmlptrainenscls()
        {
            bool result = new bool();
            mlptrain.mlptrainer trainer = new mlptrain.mlptrainer();
            mlpe.mlpensemble netens = new mlpe.mlpensemble();
            mlptrain.mlpreport rep = new mlptrain.mlpreport();
            int info = 0;
            sparse.sparsematrix xytrainsp = new sparse.sparsematrix();
            double[,] xytrain = new double[0,0];
            double[,] xytest = new double[0,0];
            int nin = 0;
            int nout = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double decay = 0;
            double wstep = 0;
            int maxits = 0;
            int nneedrest = 0;
            int enssize = 0;
            int val = 0;
            int ntrain = 0;
            int ntest = 0;
            double avgerr = 0;
            double eps = 0;
            double delta = 0;
            int issparse = 0;
            int withtrainer = 0;
            int xp = 0;
            int nxp = 0;
            int i = 0;
            int j = 0;
            int i_ = 0;

            eps = 5.0E-2;
            delta = 0.1;
            ntrain = 90;
            ntest = 90;
            nin = 3;
            nout = 3;
            apserv.rvectorsetlengthatleast(ref x, nin);
            apserv.rmatrixsetlengthatleast(ref xytrain, ntrain, nin+1);
            apserv.rmatrixsetlengthatleast(ref xytest, ntest, nin+1);
            decay = 1.0E-3;
            wstep = 1.0E-3;
            maxits = 100;
            nneedrest = 1;
            mlptrain.mlpcreatetrainercls(nin, nout, trainer);
            mlptrain.mlpsetdecay(trainer, decay);
            mlptrain.mlpsetcond(trainer, wstep, maxits);
            nxp = 5;
            for(xp=1; xp<=nxp; xp++)
            {
                enssize = (int)Math.Round(Math.Pow(10, math.randominteger(2)+1));
                withtrainer = math.randominteger(2);
                issparse = 0;
                if( withtrainer==0 )
                {
                    issparse = 0;
                }
                if( withtrainer==1 )
                {
                    issparse = math.randominteger(2);
                }
                for(i=0; i<=ntrain-1; i++)
                {
                    val = i%nin;
                    for(j=0; j<=nin-1; j++)
                    {
                        xytrain[i,j] = delta*(math.randomreal()-1);
                    }
                    xytrain[i,val] = xytrain[i,val]+1;
                    xytrain[i,nin] = val;
                }
                
                //
                // Set dense dataset in trainer
                //
                if( issparse==0 )
                {
                    mlptrain.mlpsetdataset(trainer, xytrain, ntrain);
                }
                
                //
                //  * Sparse dataset(create it with using dense dataset).
                //
                if( issparse==1 )
                {
                    sparse.sparsecreate(ntrain, nin+1, ntrain*(nin+1), xytrainsp);
                    for(i=0; i<=ntrain-1; i++)
                    {
                        for(j=0; j<=nin-1; j++)
                        {
                            sparse.sparseset(xytrainsp, i, j, xytrain[i,j]);
                        }
                        sparse.sparseset(xytrainsp, i, nin, xytrain[i,nin]);
                    }
                    sparse.sparseconverttocrs(xytrainsp);
                    
                    //
                    // Set sparse dataset in trainer
                    //
                    mlptrain.mlpsetsparsedataset(trainer, xytrainsp, ntrain);
                }
                
                //
                // Create test set
                //
                for(i=0; i<=ntest-1; i++)
                {
                    val = math.randominteger(nin);
                    for(j=0; j<=nin-1; j++)
                    {
                        xytest[i,j] = delta*(math.randomreal()-1);
                    }
                    xytest[i,val] = xytest[i,val]+1;
                    xytest[i,nin] = val;
                }
                
                //
                // Create ensemble
                //
                mlpe.mlpecreatec0(nin, nout, enssize, netens);
                
                //
                // Train ensembles:
                //  * without trainer;
                //
                if( withtrainer==0 )
                {
                    mlptrain.mlpetraines(netens, xytrain, ntrain, decay, nneedrest, ref info, rep);
                }
                
                //
                //  * with trainer.
                //
                if( withtrainer==1 )
                {
                    mlptrain.mlptrainensemblees(trainer, netens, nneedrest, rep);
                }
                
                //
                // Calculate average error:
                //  * on training dataset;
                //
                avgerr = 0;
                for(i=0; i<=ntrain-1; i++)
                {
                    if( issparse==0 )
                    {
                        for(i_=0; i_<=nin-1;i_++)
                        {
                            x[i_] = xytrain[i,i_];
                        }
                    }
                    if( issparse==1 )
                    {
                        sparse.sparsegetrow(xytrainsp, i, ref x);
                    }
                    mlpe.mlpeprocess(netens, x, ref y);
                    for(j=0; j<=nout-1; j++)
                    {
                        if( (double)(j)!=(double)(xytrain[i,nin]) )
                        {
                            avgerr = avgerr+y[j];
                        }
                        else
                        {
                            avgerr = avgerr+(1-y[j]);
                        }
                    }
                }
                avgerr = avgerr/(ntrain*nout);
                if( (double)(avgerr)>(double)(eps) )
                {
                    result = true;
                    return result;
                }
                
                //
                //  * on test dataset.
                //
                avgerr = 0;
                for(i=0; i<=ntest-1; i++)
                {
                    for(i_=0; i_<=nin-1;i_++)
                    {
                        x[i_] = xytest[i,i_];
                    }
                    mlpe.mlpeprocess(netens, x, ref y);
                    for(j=0; j<=nout-1; j++)
                    {
                        if( (double)(j)!=(double)(xytest[i,nin]) )
                        {
                            avgerr = avgerr+y[j];
                        }
                        else
                        {
                            avgerr = avgerr+(1-y[j]);
                        }
                    }
                }
                avgerr = avgerr/(ntest*nout);
                if( (double)(avgerr)>(double)(eps) )
                {
                    result = true;
                    return result;
                }
            }
            result = false;
            return result;
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testpca(bool silent)
        {
            return testpca(silent);
        }


        /*************************************************************************
        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)
            //
            alglib.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.
            //
            alglib.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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testodesolver(bool silent)
        {
            return testodesolver(silent);
        }


    }
    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.fasttransformplan plan = new ftbase.fasttransformplan();
            int maxsmalln = 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;

            maxsmalln = 128;
            errtol = 100000*Math.Pow(maxsmalln, (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<=maxsmalln; 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 for small N's
            // (we do not test large N's because reference implementation will be too slow).
            //
            referr = 0;
            refrerr = 0;
            for(n=1; n<=maxsmalln; 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 for large N's:
            // * we perform FFT
            // * we selectively calculate K (small number) of DFT components (using reference formula)
            //   and compare them with ones calculated by fast implementation
            // * K components to test are chosen at random (random sampling with possible repetitions)
            // * overall complexity of the test is O(N*logN+K*N)
            // Several N's are tested, with different kinds of factorizations
            //
            referr = 0;
            refrerr = 0;
            quicktest(1000, ref referr, ref refrerr);
            quicktest(1024, ref referr, ref refrerr);
            quicktest(1025, ref referr, ref refrerr);
            quicktest(2000, ref referr, ref refrerr);
            quicktest(2048, ref referr, ref refrerr);
            quicktest(6535, ref referr, ref refrerr);
            quicktest(65536, ref referr, ref refrerr);
            quicktest(104729, ref referr, ref refrerr);
            quicktest(139129, ref referr, ref refrerr);
            quicktest(141740, ref referr, ref refrerr);
            referrors = referrors || (double)(referr)>(double)(errtol);
            refrerrors = refrerrors || (double)(refrerr)>(double)(errtol);
            
            //
            // test internal real even FFT
            //
            reinterr = 0;
            for(k=1; k<=maxsmalln/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.ftcomplexfftplan(n/2, 1, 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.ftcomplexfftplan(n/2, 1, 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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testfft(bool silent)
        {
            return testfft(silent);
        }


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

            alglib.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;

            alglib.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];
            }
        }


        /*************************************************************************
        This function performs real/complex FFT of given length on random data,
        selects K random components and compares them with values calculated by
        DFT definition.

        It updates RefErr and RefRErr as follows:
            RefErr:=  max(RefErr, error_of_complex_FFT)
            RefRErr:= max(RefRErr,error_of_real_FFT)

        *************************************************************************/
        private static void quicktest(int n,
            ref double referr,
            ref double refrerr)
        {
            complex[] a0 = new complex[0];
            complex[] a1 = new complex[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int idx = 0;
            complex v = 0;
            double c = 0;
            double s = 0;
            double re = 0;
            double im = 0;

            k = 10;
            
            //
            // Complex FFT - forward and inverse
            //
            a0 = new complex[n];
            a1 = new complex[n];
            for(i=0; i<=n-1; i++)
            {
                a0[i].x = 2*math.randomreal()-1;
                a0[i].y = 2*math.randomreal()-1;
                a1[i] = a0[i];
            }
            fft.fftc1d(ref a0, n);
            for(i=0; i<=k-1; i++)
            {
                idx = math.randominteger(n);
                v = 0;
                for(j=0; j<=n-1; j++)
                {
                    re = a1[j].x;
                    im = a1[j].y;
                    c = Math.Cos(-(2*Math.PI*j*idx/n));
                    s = Math.Sin(-(2*Math.PI*j*idx/n));
                    v.x = v.x+c*re-s*im;
                    v.y = v.y+c*im+s*re;
                }
                referr = Math.Max(referr, math.abscomplex(v-a0[idx]));
            }
            fft.fftc1dinv(ref a0, n);
            for(i=0; i<=n-1; i++)
            {
                referr = Math.Max(referr, math.abscomplex(a0[i]-a1[i]));
            }
        }


    }
    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
                                    //
                                    alglib.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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testconv(bool silent)
        {
            return testconv(silent);
        }


        /*************************************************************************
        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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testcorr(bool silent)
        {
            return testcorr(silent);
        }


        /*************************************************************************
        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;
            }
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testfht(bool silent)
        {
            return testfht(silent);
        }


        /*************************************************************************
        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;

            alglib.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;

            alglib.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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testgq(bool silent)
        {
            return testgq(silent);
        }


        /*************************************************************************
        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];
            r = 0;
            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];
            r = 0;
            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);
            r = 0;
            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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testgkq(bool silent)
        {
            return testgkq(silent);
        }


        /*************************************************************************
        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;
                alpha = 0.0;
                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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testautogk(bool silent)
        {
            return testautogk(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testidwint(bool silent)
        {
            return testidwint(silent);
        }


        /*************************************************************************
        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 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;
            alglib.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]);
                v0 = 0;
                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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testratint(bool silent)
        {
            return testratint(silent);
        }


        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);
        }


    }
    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[] c0 = new double[0];
            double[] c1 = new double[0];
            double[] c2 = 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;
            double eps = 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 || alglib.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 || alglib.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);
                }
            }
            
            //
            // Test issue #634: instability in PolynomialBar2Pow().
            //
            // Function returns incorrect coefficients when called with
            // approximately-unit scale for data which have significantly
            // non-unit scale.
            //
            n = 7;
            eps = 1.0E-8;
            x = new double[n];
            x2 = new double[n];
            y = new double[n];
            x[0] = math.randomreal()-0.5;
            y[0] = math.randomreal()-0.5;
            for(i=1; i<=n-1; i++)
            {
                x[i] = x[i-1]+math.randomreal()+0.1;
                y[i] = math.randomreal()-0.5;
            }
            polint.polynomialbuild(x, y, n, p);
            polint.polynomialbar2pow(p, 0.0, 1.0, ref c0);
            pscale = 1.0E-10;
            for(i=0; i<=n-1; i++)
            {
                x2[i] = x[i]*pscale;
            }
            polint.polynomialbuild(x2, y, n, p);
            polint.polynomialbar2pow(p, 0.0, 1.0, ref c1);
            for(i=0; i<=n-1; i++)
            {
                apserv.seterrorflag(ref interrors, (double)(Math.Abs(c0[i]-c1[i]*Math.Pow(pscale, i)))>(double)(eps));
            }
            pscale = 1.0E10;
            for(i=0; i<=n-1; i++)
            {
                x2[i] = x[i]*pscale;
            }
            polint.polynomialbuild(x2, y, n, p);
            polint.polynomialbar2pow(p, 0.0, 1.0, ref c2);
            for(i=0; i<=n-1; i++)
            {
                apserv.seterrorflag(ref interrors, (double)(Math.Abs(c0[i]-c2[i]*Math.Pow(pscale, i)))>(double)(eps));
            }
            
            //
            // 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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testpolint(bool silent)
        {
            return testpolint(silent);
        }


        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();
            bool monotoneerr = 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];
            double[] xtest = 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 splineindex = 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);
                            }
                            alglib.ap.assert(alglib.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 Akima-specific properties is implemented below.
                    //
                    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)(l10)>(double)(1.0E-10) && (double)(l20/l10)>(double)(1.2));
                    hserrors = hserrors || ((double)(l11)>(double)(1.0E-10) && (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);
            }
            
            //
            // Linear translation errors
            //
            for(n=2; n<=maxn; n++)
            {
                
                //
                // Prepare:
                // * X, Y - grid points
                // * XTest - test points
                //
                x = new double[n];
                y = new double[n];
                a = -1-math.randomreal();
                b = 1+math.randomreal();
                for(i=0; i<=n-1; i++)
                {
                    x[i] = a+(b-a)*(i+0.2*math.randomreal()-0.1)/(n-1);
                    y[i] = 2*math.randomreal()-1;
                }
                xtest = new double[5*n+2];
                for(i=0; i<=alglib.ap.len(xtest)-1; i++)
                {
                    xtest[i] = a+(b-a)*(i-1)/(alglib.ap.len(xtest)-3);
                }
                splineindex = 0;
                while( enumerateallsplines(x, y, n, ref splineindex, c) )
                {
                    
                    //
                    // LinTransX, general A
                    //
                    sa = 4*math.randomreal()-2;
                    sb = 2*math.randomreal()-1;
                    spline1d.spline1dcopy(c, c2);
                    spline1d.spline1dlintransx(c2, sa, sb);
                    for(i=0; i<=alglib.ap.len(xtest)-1; i++)
                    {
                        lterrors = lterrors || (double)(Math.Abs(spline1d.spline1dcalc(c, xtest[i])-spline1d.spline1dcalc(c2, (xtest[i]-sb)/sa)))>(double)(threshold);
                    }
                    
                    //
                    // LinTransX, special case: A=0
                    //
                    sb = 2*math.randomreal()-1;
                    spline1d.spline1dcopy(c, c2);
                    spline1d.spline1dlintransx(c2, 0, sb);
                    for(i=0; i<=alglib.ap.len(xtest)-1; i++)
                    {
                        lterrors = lterrors || (double)(Math.Abs(spline1d.spline1dcalc(c, sb)-spline1d.spline1dcalc(c2, xtest[i])))>(double)(threshold);
                    }
                    
                    //
                    // LinTransY
                    //
                    sa = 2*math.randomreal()-1;
                    sb = 2*math.randomreal()-1;
                    spline1d.spline1dcopy(c, c2);
                    spline1d.spline1dlintransy(c2, sa, sb);
                    for(i=0; i<=alglib.ap.len(xtest)-1; i++)
                    {
                        lterrors = lterrors || (double)(Math.Abs(sa*spline1d.spline1dcalc(c, xtest[i])+sb-spline1d.spline1dcalc(c2, xtest[i])))>(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);
            }
            
            //
            // Test fo monotone cubic Hermit interpolation
            //
            monotoneerr = testmonotonespline();
            
            //
            // report
            //
            waserrors = (((((((((lserrors || cserrors) || crserrors) || hserrors) || aserrors) || dserrors) || cperrors) || uperrors) || lterrors) || ierrors) || monotoneerr;
            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();
                }
                System.Console.Write("TEST MONOTONE CUBIC HERMITE SPLINE:      ");
                if( monotoneerr )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testspline1d(bool silent)
        {
            return testspline1d(silent);
        }


        /*************************************************************************
        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;
            }
        }


        /*************************************************************************
        This function is used to enumerate all spline types  which  can  be  built
        from given dataset. It should be used as follows:

        >
        > init X, Y, N
        > SplineIndex:=0;
        > while EnumerateAllSplines(X, Y, N, SplineIndex, S) do
        > begin
        >     do something with S
        > end;
        >

        On initial call EnumerateAllSplines accepts:
        * dataset X, Y, number of points N (N>=2)
        * SplineIndex, equal to 0

        It returns:
        * True, in case there is a spline type which corresponds to SplineIndex.
          In this case S contains spline which was built using X/Y and spline type,
          as specified by input value of SplineIndex. SplineIndex is advanced to
          the next value.
        * False, in case SplineIndex contains past-the-end value, spline is not built.

        This function tries different variants of linear/cubic, periodic/nonperiodic
        splines.
        *************************************************************************/
        private static bool enumerateallsplines(double[] x,
            double[] y,
            int n,
            ref int splineindex,
            spline1d.spline1dinterpolant s)
        {
            bool result = new bool();
            int idxoffs = 0;

            alglib.ap.assert(splineindex>=0);
            result = false;
            if( splineindex==0 )
            {
                
                //
                // Linear spline
                //
                spline1d.spline1dbuildlinear(x, y, n, s);
                splineindex = splineindex+1;
                result = true;
                return result;
            }
            else
            {
                if( splineindex>=1 && splineindex<11 )
                {
                    
                    //
                    // Cubic spline, either periodic or non-periodic
                    //
                    idxoffs = splineindex-1;
                    if( idxoffs==9 )
                    {
                        
                        //
                        // Periodic spline
                        //
                        spline1d.spline1dbuildcubic(x, y, n, -1, 0.0, -1, 0.0, s);
                    }
                    else
                    {
                        
                        //
                        // Non-periodic spline
                        //
                        spline1d.spline1dbuildcubic(x, y, n, idxoffs/3, 2*math.randomreal()-1, idxoffs%3, 2*math.randomreal()-1, s);
                    }
                    splineindex = splineindex+1;
                    result = true;
                    return result;
                }
            }
            return result;
        }


        /*************************************************************************
        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);
        }


        /*************************************************************************
        Tests that built spline is monotone.
        *************************************************************************/
        private static bool testmonotonespline()
        {
            bool result = new bool();
            spline1d.spline1dinterpolant c = new spline1d.spline1dinterpolant();
            spline1d.spline1dinterpolant s2 = new spline1d.spline1dinterpolant();
            double c0 = 0;
            double c1 = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] d = new double[0];
            int m = 0;
            int[] n = new int[0];
            int alln = 0;
            int shift = 0;
            double sign0 = 0;
            double sign1 = 0;
            double r = 0;
            double st = 0;
            double eps = 0;
            double delta = 0;
            double v = 0;
            double dv = 0;
            double d2v = 0;
            int nseg = 0;
            int npoints = 0;
            int tp = 0;
            int pass = 0;
            int passcount = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int kmax = 0;
            int l = 0;

            eps = 100*math.machineepsilon;
            
            //
            // Special test - N=2.
            //
            // Following properties are tested:
            // * monotone spline must be equal to the Hermite spline with
            //   zero derivative at the ends
            // * monotone spline is constant beyond left/right boundaries
            //
            x = new double[2];
            y = new double[2];
            d = new double[2];
            x[0] = -0.1-math.randomreal();
            y[0] = 2*math.randomreal()-1;
            d[0] = 0.0;
            x[1] = 0.1+math.randomreal();
            y[1] = y[0];
            d[1] = 0.0;
            spline1d.spline1dbuildmonotone(x, y, 2, c);
            spline1d.spline1dbuildhermite(x, y, d, 2, s2);
            v = 2*math.randomreal()-1;
            if( (double)(Math.Abs(spline1d.spline1dcalc(c, v)-spline1d.spline1dcalc(s2, v)))>(double)(eps) )
            {
                result = true;
                return result;
            }
            if( (double)(spline1d.spline1dcalc(c, -5))!=(double)(y[0]) )
            {
                result = true;
                return result;
            }
            if( (double)(spline1d.spline1dcalc(c, 5))!=(double)(y[0]) )
            {
                result = true;
                return result;
            }
            
            //
            // Special test - N=3, x=[0,1,2], y=[0,1,0].
            // Monotone spline must be equal to the Hermite spline with
            // zero derivative at all points.
            //
            x = new double[3];
            y = new double[3];
            d = new double[3];
            x[0] = 0.0;
            y[0] = 0.0;
            d[0] = 0.0;
            x[1] = 1.0;
            y[1] = 1.0;
            d[1] = 0.0;
            x[2] = 2.0;
            y[2] = 0.0;
            d[2] = 0.0;
            spline1d.spline1dbuildmonotone(x, y, 3, c);
            spline1d.spline1dbuildhermite(x, y, d, 3, s2);
            for(i=0; i<=10; i++)
            {
                v = x[0]+(double)i/(double)10*(x[2]-x[0]);
                if( (double)(Math.Abs(spline1d.spline1dcalc(c, v)-spline1d.spline1dcalc(s2, v)))>(double)(eps) )
                {
                    result = true;
                    return result;
                }
            }
            
            //
            // Special test - N=5, x=[0,1,2,3,4], y=[0,1,1,2,3].
            //
            // 1) spline passes through all prescribed points
            // 2) spline derivative at all points except x=3 is exactly zero
            // 3) spline derivative at x=3 is 1.0 (within machine epsilon)
            //
            x = new double[5];
            y = new double[5];
            x[0] = 0.0;
            y[0] = 0.0;
            x[1] = 1.0;
            y[1] = 1.0;
            x[2] = 2.0;
            y[2] = 1.0;
            x[3] = 3.0;
            y[3] = 2.0;
            x[4] = 4.0;
            y[4] = 3.0;
            spline1d.spline1dbuildmonotone(x, y, 5, c);
            for(i=0; i<=4; i++)
            {
                spline1d.spline1ddiff(c, x[i], ref v, ref dv, ref d2v);
                if( (double)(Math.Abs(v-y[i]))>(double)(eps) )
                {
                    result = true;
                    return result;
                }
                if( ((double)(x[i])==(double)(3.0) && (double)(Math.Abs(dv-1.0))>(double)(eps)) || ((double)(x[i])!=(double)(3.0) && (double)(dv)!=(double)(0)) )
                {
                    result = true;
                    return result;
                }
            }
            
            //
            // Special test:
            // * N=4
            // * three fixed points - (0,0), (1,1), (2,0)
            // * one special point (x,y) with x in [0.1,0.9], y in [0.1,0.9]
            // * monotonicity of the interpolant at [0,1] is checked with very small step 1/KMax
            //
            x = new double[4];
            y = new double[4];
            x[0] = 0.0;
            y[0] = 0.0;
            x[2] = 1.0;
            y[2] = 1.0;
            x[3] = 2.0;
            y[3] = 0.0;
            for(i=1; i<=9; i++)
            {
                for(j=1; j<=9; j++)
                {
                    x[1] = (double)i/(double)10;
                    y[1] = (double)j/(double)10;
                    spline1d.spline1dbuildmonotone(x, y, 4, c);
                    kmax = 1000;
                    for(k=0; k<=kmax-1; k++)
                    {
                        if( (double)(spline1d.spline1dcalc(c, (double)k/(double)kmax))>(double)(spline1d.spline1dcalc(c, (double)(k+1)/(double)kmax)) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            
            //
            // General case
            //
            delta = 0;
            nseg = 10;
            npoints = 15;
            passcount = 30;
            for(pass=1; pass<=passcount; pass++)
            {
                tp = math.randominteger(6)+4;
                r = math.randominteger(76)+25;
                m = math.randominteger(nseg)+1;
                n = new int[m];
                alln = 0;
                for(i=0; i<=m-1; i++)
                {
                    n[i] = math.randominteger(npoints)+2;
                    alln = alln+n[i];
                }
                x = new double[alln];
                y = new double[alln];
                x[0] = r*(2*math.randomreal()-1);
                y[0] = r*(2*math.randomreal()-1);
                
                //
                // Builds monotone function
                //
                st = 0.1+0.7*math.randomreal();
                shift = 0;
                sign0 = Math.Pow(-1, 0);
                for(i=0; i<=m-1; i++)
                {
                    for(j=1; j<=n[i]-1; j++)
                    {
                        x[shift+j] = x[shift+j-1]+st+math.randomreal();
                        delta = Math.Max(delta, x[shift+j]-x[shift+j-1]);
                        y[shift+j] = y[shift+j-1]+sign0*(st+math.randomreal());
                    }
                    shift = shift+n[i];
                    if( i!=m-1 )
                    {
                        sign0 = Math.Pow(-1, i+1);
                        x[shift] = x[shift-1]+st+math.randomreal();
                        y[shift] = y[shift-1]+sign0*math.randomreal();
                    }
                }
                delta = 3*delta;
                spline1d.spline1dbuildmonotone(x, y, alln, c);
                
                //
                // Check that built function is monotone
                //
                shift = 0;
                for(i=0; i<=m-1; i++)
                {
                    for(j=1; j<=n[i]-1; j++)
                    {
                        st = (x[shift+j]-x[shift+j-1])/tp;
                        sign0 = y[shift+j]-y[shift+j-1];
                        if( (double)(sign0)!=(double)(0) )
                        {
                            sign0 = sign0/Math.Abs(sign0);
                        }
                        for(l=0; l<=tp-1; l++)
                        {
                            c0 = spline1d.spline1dcalc(c, x[shift+j-1]+l*st);
                            c1 = spline1d.spline1dcalc(c, x[shift+j-1]+(l+1)*st);
                            sign1 = c1-c0;
                            if( (double)(sign1)!=(double)(0) )
                            {
                                sign1 = sign1/Math.Abs(sign1);
                            }
                            if( (double)(sign0*sign1)<(double)(0) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                }
                c0 = spline1d.spline1dcalc(c, x[0]-delta);
                c1 = spline1d.spline1dcalc(c, x[0]);
                if( (double)(Math.Abs(c0-c1))>(double)(eps) )
                {
                    result = true;
                    return result;
                }
                c0 = spline1d.spline1dcalc(c, x[alln-1]);
                c1 = spline1d.spline1dcalc(c, x[alln-1]+delta);
                if( (double)(Math.Abs(c0-c1))>(double)(eps) )
                {
                    result = true;
                    return result;
                }
                
                //
                // Builds constant function
                //
                y[0] = r*(2*math.randomreal()-1);
                for(i=1; i<=alln-1; i++)
                {
                    y[i] = y[0];
                }
                spline1d.spline1dbuildmonotone(x, y, alln, c);
                shift = 0;
                for(i=0; i<=m-1; i++)
                {
                    for(j=1; j<=n[i]-1; j++)
                    {
                        st = (x[shift+j]-x[shift+j-1])/tp;
                        sign0 = y[shift+j]-y[shift+j-1];
                        for(l=0; l<=tp-1; l++)
                        {
                            c0 = spline1d.spline1dcalc(c, x[shift+j-1]+l*st);
                            c1 = spline1d.spline1dcalc(c, x[shift+j-1]+(l+1)*st);
                            sign1 = c1-c0;
                            if( (double)(sign0)>(double)(eps) || (double)(sign1)>(double)(eps) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


    }
    public class testnormestimatorunit
    {
        public static bool testnormestimator(bool silent)
        {
            bool result = new bool();
            double tol = 0;
            int maxmn = 0;
            int m = 0;
            int n = 0;
            int pass = 0;
            int passcount = 0;
            double[,] a = new double[0,0];
            int[] rowsizes = new int[0];
            sparse.sparsematrix s = new sparse.sparsematrix();
            double snorm = 0;
            double enorm = 0;
            double enorm2 = 0;
            int nbetter = 0;
            double sigma = 0;
            int i = 0;
            int j = 0;
            normestimator.normestimatorstate e = new normestimator.normestimatorstate();
            normestimator.normestimatorstate e2 = new normestimator.normestimatorstate();
            bool waserrors = new bool();

            tol = 0.01;
            maxmn = 5;
            waserrors = false;
            
            //
            // First test: algorithm must correctly determine matrix norm
            //
            for(m=1; m<=maxmn; m++)
            {
                for(n=1; n<=maxmn; n++)
                {
                    
                    //
                    // Create estimator with quite large NStart and NIts.
                    // It should guarantee that we converge to the correct solution.
                    //
                    normestimator.normestimatorcreate(m, n, 15, 15, e);
                    
                    //
                    // Try with zero A first
                    //
                    sparse.sparsecreate(m, n, 1, s);
                    sparse.sparseconverttocrs(s);
                    normestimator.normestimatorestimatesparse(e, s);
                    normestimator.normestimatorresults(e, ref enorm);
                    waserrors = waserrors || (double)(enorm)!=(double)(0);
                    
                    //
                    // Choose random norm, try with non-zero matrix
                    // with specified norm.
                    //
                    snorm = Math.Exp(10*math.randomreal()-5);
                    sparse.sparsecreate(m, n, 1, s);
                    if( m>=n )
                    {
                        
                        //
                        // Generate random orthogonal M*M matrix,
                        // use N leading columns as columns of A
                        //
                        matgen.rmatrixrndorthogonal(m, ref a);
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                sparse.sparseset(s, i, j, snorm*a[i,j]);
                            }
                        }
                    }
                    else
                    {
                        
                        //
                        // Generate random orthogonal N*N matrix,
                        // use M leading rows as rows of A
                        //
                        matgen.rmatrixrndorthogonal(n, ref a);
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                sparse.sparseset(s, i, j, snorm*a[i,j]);
                            }
                        }
                    }
                    sparse.sparseconverttocrs(s);
                    normestimator.normestimatorestimatesparse(e, s);
                    normestimator.normestimatorresults(e, ref enorm);
                    waserrors = (waserrors || (double)(enorm)>(double)(snorm*(1+tol))) || (double)(enorm)<(double)(snorm*(1-tol));
                }
            }
            
            //
            // NStart=10 should give statistically better results than NStart=1.
            // In order to test it we perform PassCount attempts to solve random
            // problem by means of two estimators: one with NStart=10 and another
            // one with NStart=1. Every time we compare two estimates and choose
            // better one.
            //
            // Random variable NBetter is a number of cases when NStart=10 was better.
            // Under null hypothesis (no difference) it is binomially distributed
            // with mean PassCount/2 and variance PassCount/4. However, we expect
            // to have significant deviation to the right, in the area of larger
            // values.
            //
            // NOTE: we use fixed N because this test is independent of problem size.
            //
            n = 3;
            normestimator.normestimatorcreate(n, n, 1, 1, e);
            normestimator.normestimatorcreate(n, n, 10, 1, e2);
            normestimator.normestimatorsetseed(e, 0);
            normestimator.normestimatorsetseed(e2, 0);
            nbetter = 0;
            passcount = 2000;
            sigma = 5.0;
            for(pass=1; pass<=passcount; pass++)
            {
                snorm = Math.Pow(10.0, 2*math.randomreal()-1);
                sparse.sparsecreate(n, n, 1, s);
                matgen.rmatrixrndcond(n, 2.0, ref a);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        sparse.sparseset(s, i, j, snorm*a[i,j]);
                    }
                }
                sparse.sparseconverttocrs(s);
                normestimator.normestimatorestimatesparse(e, s);
                normestimator.normestimatorresults(e, ref enorm);
                normestimator.normestimatorestimatesparse(e2, s);
                normestimator.normestimatorresults(e2, ref enorm2);
                if( (double)(Math.Abs(enorm2-snorm))<(double)(Math.Abs(enorm-snorm)) )
                {
                    nbetter = nbetter+1;
                }
            }
            waserrors = waserrors || (double)(nbetter)<(double)(0.5*passcount+sigma*Math.Sqrt(0.25*passcount));
            
            //
            // Same as previous one (for NStart), but tests dependence on NIts.
            //
            n = 3;
            normestimator.normestimatorcreate(n, n, 1, 1, e);
            normestimator.normestimatorcreate(n, n, 1, 10, e2);
            normestimator.normestimatorsetseed(e, 0);
            normestimator.normestimatorsetseed(e2, 0);
            nbetter = 0;
            passcount = 2000;
            sigma = 5.0;
            for(pass=1; pass<=passcount; pass++)
            {
                snorm = Math.Pow(10.0, 2*math.randomreal()-1);
                sparse.sparsecreate(n, n, 1, s);
                matgen.rmatrixrndcond(n, 2.0, ref a);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        sparse.sparseset(s, i, j, snorm*a[i,j]);
                    }
                }
                sparse.sparseconverttocrs(s);
                normestimator.normestimatorestimatesparse(e, s);
                normestimator.normestimatorresults(e, ref enorm);
                normestimator.normestimatorestimatesparse(e2, s);
                normestimator.normestimatorresults(e2, ref enorm2);
                if( (double)(Math.Abs(enorm2-snorm))<(double)(Math.Abs(enorm-snorm)) )
                {
                    nbetter = nbetter+1;
                }
            }
            waserrors = waserrors || (double)(nbetter)<(double)(0.5*passcount+sigma*Math.Sqrt(0.25*passcount));
            
            //
            // report
            //
            if( !silent )
            {
                System.Console.Write("TESTING NORM ESTIMATOR");
                System.Console.WriteLine();
                System.Console.Write("TEST:                                    ");
                if( !waserrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testnormestimator(bool silent)
        {
            return testnormestimator(silent);
        }


    }
    public class testminqpunit
    {
        public static bool testminqp(bool silent)
        {
            bool result = new bool();
            bool simpleerrors = new bool();
            bool func1errors = new bool();
            bool func2errors = new bool();
            bool bcqperrors = new bool();
            bool ecqperrors = new bool();
            bool icqperrors = new bool();
            bool cholerrors = new bool();
            bool quickqperrors = new bool();
            bool bleicerrors = new bool();
            bool waserrors = new bool();

            bcqperrors = false;
            
            //
            // The VERY basic tests for Cholesky and BLEIC
            //
            simpleerrors = simpletest();
            func1errors = functest1();
            func2errors = functest2();
            
            //
            // Cholesky-specific tests
            //
            cholerrors = choleskytests();
            
            //
            // QuickQP-specific tests
            //
            quickqperrors = quickqptests();
            
            //
            // BLEIC-specific tests
            //
            bleicerrors = bleictests();
            
            //
            // Test all solvers on bound-constrained problems
            //
            bcqptest(ref bcqperrors);
            
            //
            // Test Cholesky and BLEIC solvers on equality-constrained problems
            //
            ecqperrors = ecqptest();
            
            //
            // Test Cholesky and BLEIC solvers on inequality-constrained problems
            //
            icqperrors = false;
            icqptest(ref icqperrors);
            icqperrors = icqperrors || specialicqptests();
            
            //
            // report
            //
            waserrors = (((((((simpleerrors || func1errors) || func2errors) || bcqperrors) || ecqperrors) || icqperrors) || quickqperrors) || cholerrors) || bleicerrors;
            if( !silent )
            {
                System.Console.Write("TESTING MinQP");
                System.Console.WriteLine();
                System.Console.Write("SimpleTest:                               ");
                if( simpleerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Func1Test:                                ");
                if( func1errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Func2Test:                                ");
                if( func2errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Bound constrained:                        ");
                if( bcqperrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Equality constrained:                     ");
                if( ecqperrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Inequality constrained:                   ");
                if( icqperrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Cholesky solver tests:                    ");
                if( cholerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("QuickQP solver tests:                     ");
                if( quickqperrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("BLEIC solver tests:                       ");
                if( bleicerrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testminqp(bool silent)
        {
            return testminqp(silent);
        }


        /*************************************************************************
        Function to test: 'MinQPCreate', 'MinQPSetQuadraticTerm', 'MinQPSetBC', 
        'MinQPSetOrigin', 'MinQPSetStartingPoint', 'MinQPOptimize', 'MinQPResults'.

        Test problem:
            A = diag(aii), aii>0 (random)
            b = 0
            random bounds (either no bounds, one bound, two bounds a<b, two bounds a=b)
            random start point
            dimension - from 1 to 5.
            
        Returns True on success, False on failure.
        *************************************************************************/
        public static bool simpletest()
        {
            bool result = new bool();
            minqp.minqpstate state = new minqp.minqpstate();
            int nexp = 0;
            int msn = 0;
            int sn = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double[,] a = new double[0,0];
            double[] ub = new double[0];
            double[] db = new double[0];
            double[] x = new double[0];
            double[] tx = new double[0];
            double maxstb = 0;
            double[] stx = new double[0];
            double[] xori = new double[0];
            int infd = 0;
            minqp.minqpreport rep = new minqp.minqpreport();
            double maxn = 0;
            double minn = 0;
            double maxnb = 0;
            double minnb = 0;
            double eps = 0;

            eps = 0.001;
            msn = 5;
            maxstb = 10;
            nexp = 1000;
            maxn = 10;
            minn = -10;
            maxnb = 1000;
            minnb = -1000;
            for(sn=1; sn<=msn; sn++)
            {
                tx = new double[sn];
                xori = new double[sn];
                stx = new double[sn];
                db = new double[sn];
                ub = new double[sn];
                a = new double[sn, sn];
                for(i=0; i<=nexp; i++)
                {
                    
                    //
                    //create diagonal matrix
                    //
                    for(k=0; k<=sn-1; k++)
                    {
                        for(j=0; j<=k; j++)
                        {
                            if( j!=k )
                            {
                                a[k,j] = 0;
                            }
                            else
                            {
                                a[k,j] = maxn*math.randomreal()+1;
                            }
                        }
                    }
                    minqp.minqpcreate(sn, state);
                    setrandomalgobc(state);
                    minqp.minqpsetquadraticterm(state, a, false);
                    for(j=0; j<=sn-1; j++)
                    {
                        infd = math.randominteger(5);
                        if( infd==0 )
                        {
                            db[j] = Double.NegativeInfinity;
                            ub[j] = Double.PositiveInfinity;
                        }
                        else
                        {
                            if( infd==1 )
                            {
                                db[j] = Double.NegativeInfinity;
                                ub[j] = (maxnb-minnb)*math.randomreal()+minnb;
                            }
                            else
                            {
                                if( infd==2 )
                                {
                                    db[j] = (maxnb-minnb)*math.randomreal()+minnb;
                                    ub[j] = Double.PositiveInfinity;
                                }
                                else
                                {
                                    if( infd==3 )
                                    {
                                        db[j] = (maxnb-minnb)*math.randomreal()+minnb;
                                        ub[j] = db[j]+maxstb*math.randomreal()+0.01;
                                    }
                                    else
                                    {
                                        db[j] = (maxnb-minnb)*math.randomreal()+minnb;
                                        ub[j] = db[j];
                                    }
                                }
                            }
                        }
                    }
                    minqp.minqpsetbc(state, db, ub);
                    
                    //
                    //initialization for shifting
                    //initial value for 'XORi'
                    //and searching true results
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        xori[j] = (maxnb-minnb)*math.randomreal()+minnb;
                        tx[j] = apserv.boundval(xori[j], db[j], ub[j]);
                    }
                    minqp.minqpsetorigin(state, xori);
                    
                    //
                    //initialization for starting point
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        stx[j] = (maxnb-minnb)*math.randomreal()+minnb;
                    }
                    minqp.minqpsetstartingpoint(state, stx);
                    
                    //
                    //optimize and get result
                    //
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x, rep);
                    for(j=0; j<=sn-1; j++)
                    {
                        if( (double)(Math.Abs(tx[j]-x[j]))>(double)(eps) || ((double)(x[j])<(double)(db[j]) || (double)(x[j])>(double)(ub[j])) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', 'MinQPSetQuadraticTerm',
        'MinQPSetOrigin', 'MinQPSetStartingPoint', 'MinQPOptimize', 'MinQPResults'.

        Test problem:
            A = positive-definite matrix, obtained by 'SPDMatrixRndCond' function
            b <> 0
            without bounds
            random start point
            dimension - from 1 to 5.
        *************************************************************************/
        public static bool functest1()
        {
            bool result = new bool();
            minqp.minqpstate state = new minqp.minqpstate();
            int nexp = 0;
            int msn = 0;
            int sn = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double[,] a = new double[0,0];
            double[] ub = new double[0];
            double[] db = new double[0];
            double[] x = new double[0];
            double[] tx = new double[0];
            double maxstb = 0;
            double[] stx = new double[0];
            double[] xori = new double[0];
            double[] xoric = new double[0];
            minqp.minqpreport rep = new minqp.minqpreport();
            double maxn = 0;
            double minn = 0;
            double maxnb = 0;
            double minnb = 0;
            double eps = 0;
            double[] b = new double[0];
            int c2 = 0;

            eps = 0.001;
            msn = 5;
            c2 = 1000;
            maxstb = 10;
            nexp = 1000;
            maxn = 10;
            minn = -10;
            maxnb = 1000;
            minnb = -1000;
            for(sn=1; sn<=msn; sn++)
            {
                b = new double[sn];
                tx = new double[sn];
                xori = new double[sn];
                xoric = new double[sn];
                stx = new double[sn];
                for(i=0; i<=nexp; i++)
                {
                    
                    //
                    //create simmetric matrix 'A'
                    //
                    matgen.spdmatrixrndcond(sn, Math.Exp(math.randomreal()*Math.Log(c2)), ref a);
                    minqp.minqpcreate(sn, state);
                    setrandomalgobc(state);
                    minqp.minqpsetquadraticterm(state, a, false);
                    for(j=0; j<=sn-1; j++)
                    {
                        xoric[j] = 2*math.randomreal()-1;
                    }
                    
                    //
                    //create linear part
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        b[j] = 0;
                        for(k=0; k<=sn-1; k++)
                        {
                            b[j] = b[j]-xoric[k]*a[k,j];
                        }
                    }
                    minqp.minqpsetlinearterm(state, b);
                    
                    //
                    //initialization for shifting
                    //initial value for 'XORi'
                    //and searching true results
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        xori[j] = 2*math.randomreal()-1;
                        tx[j] = xori[j]+xoric[j];
                    }
                    minqp.minqpsetorigin(state, xori);
                    
                    //
                    //initialization for starting point
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        stx[j] = 2*math.randomreal()-1;
                    }
                    minqp.minqpsetstartingpoint(state, stx);
                    
                    //
                    //optimize and get result
                    //
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x, rep);
                    for(j=0; j<=sn-1; j++)
                    {
                        if( (double)(Math.Abs(tx[j]-x[j]))>(double)(eps) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', 'MinQPSetQuadraticTerm',
        'MinQPSetBC', 'MinQPSetOrigin', 'MinQPSetStartingPoint', 'MinQPOptimize', 
        'MinQPResults'.

        Test problem:
            A = positive-definite matrix, obtained by 'SPDMatrixRndCond' function
            b <> 0
            boundary constraints
            random start point
            dimension - from 1 to 5.
        *************************************************************************/
        public static bool functest2()
        {
            bool result = new bool();
            minqp.minqpstate state = new minqp.minqpstate();
            int nexp = 0;
            int msn = 0;
            int sn = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double[,] a = new double[0,0];
            double[] ub = new double[0];
            double[] db = new double[0];
            double[] x = new double[0];
            double[] tmpx = new double[0];
            double maxstb = 0;
            double[] stx = new double[0];
            double[] xori = new double[0];
            double[] xoric = new double[0];
            int infd = 0;
            minqp.minqpreport rep = new minqp.minqpreport();
            double maxn = 0;
            double minn = 0;
            double maxnb = 0;
            double minnb = 0;
            double eps = 0;
            double[] b = new double[0];
            double[] g = new double[0];
            double[] c = new double[0];
            double[] y0 = new double[0];
            double[] y1 = new double[0];
            int c2 = 0;
            double anti = 0;

            eps = 0.001;
            msn = 5;
            c2 = 1000;
            maxstb = 10;
            nexp = 1000;
            maxn = 10;
            minn = -10;
            maxnb = 1000;
            minnb = -1000;
            for(sn=1; sn<=msn; sn++)
            {
                tmpx = new double[sn];
                b = new double[sn];
                c = new double[sn];
                g = new double[sn];
                xori = new double[sn];
                xoric = new double[sn];
                stx = new double[sn];
                db = new double[sn];
                ub = new double[sn];
                y0 = new double[sn];
                y1 = new double[sn];
                for(i=0; i<=nexp; i++)
                {
                    
                    //
                    //create simmetric matrix 'A'
                    //
                    matgen.spdmatrixrndcond(sn, Math.Exp(math.randomreal()*Math.Log(c2)), ref a);
                    minqp.minqpcreate(sn, state);
                    setrandomalgobc(state);
                    minqp.minqpsetquadraticterm(state, a, false);
                    for(j=0; j<=sn-1; j++)
                    {
                        xoric[j] = (maxnb-minnb)*math.randomreal()+minnb;
                    }
                    
                    //
                    //create linear part
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        b[j] = 0;
                        for(k=0; k<=sn-1; k++)
                        {
                            b[j] = b[j]-xoric[k]*a[k,j];
                        }
                    }
                    minqp.minqpsetlinearterm(state, b);
                    for(j=0; j<=sn-1; j++)
                    {
                        infd = math.randominteger(4);
                        if( infd==0 )
                        {
                            db[j] = Double.NegativeInfinity;
                            ub[j] = Double.PositiveInfinity;
                        }
                        else
                        {
                            if( infd==1 )
                            {
                                db[j] = Double.NegativeInfinity;
                                ub[j] = (maxnb-minnb)*math.randomreal()+minnb;
                            }
                            else
                            {
                                if( infd==2 )
                                {
                                    db[j] = (maxnb-minnb)*math.randomreal()+minnb;
                                    ub[j] = Double.PositiveInfinity;
                                }
                                else
                                {
                                    db[j] = (maxnb-minnb)*math.randomreal()+minnb;
                                    ub[j] = db[j]+maxstb*math.randomreal()+0.01;
                                }
                            }
                        }
                    }
                    minqp.minqpsetbc(state, db, ub);
                    
                    //
                    //initialization for shifting
                    //initial value for 'XORi'
                    //and searching true results
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        xori[j] = (maxnb-minnb)*math.randomreal()+minnb;
                    }
                    minqp.minqpsetorigin(state, xori);
                    for(j=0; j<=sn-1; j++)
                    {
                        c[j] = 0;
                        for(k=0; k<=sn-1; k++)
                        {
                            c[j] = c[j]-xori[k]*a[k,j];
                        }
                    }
                    
                    //
                    //initialization for starting point
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        stx[j] = (maxnb-minnb)*math.randomreal()+minnb;
                    }
                    minqp.minqpsetstartingpoint(state, stx);
                    
                    //
                    //optimize and get result
                    //
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x, rep);
                    ablas.rmatrixmv(sn, sn, a, 0, 0, 0, x, 0, ref y0, 0);
                    for(j=0; j<=sn-1; j++)
                    {
                        g[j] = y0[j]+c[j]+b[j];
                    }
                    anti = projectedantigradnorm(sn, x, g, db, ub);
                    for(j=0; j<=sn-1; j++)
                    {
                        if( (double)(Math.Abs(anti))>(double)(eps) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        ConsoleTest.
        *************************************************************************/
        public static bool consoletest()
        {
            bool result = new bool();
            minqp.minqpstate state = new minqp.minqpstate();
            int nexp = 0;
            int msn = 0;
            int sn = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double[,] a = new double[0,0];
            double[] ub = new double[0];
            double[] db = new double[0];
            double[] x = new double[0];
            double maxstb = 0;
            double[] stx = new double[0];
            double[] xori = new double[0];
            double[] xoric = new double[0];
            minqp.minqpreport rep = new minqp.minqpreport();
            double maxn = 0;
            double minn = 0;
            double maxnb = 0;
            double minnb = 0;
            double eps = 0;
            double[] b = new double[0];
            double[] g = new double[0];
            double[] y0 = new double[0];
            double[] y1 = new double[0];
            int c2 = 0;
            double c = 0;
            double anti = 0;

            eps = 0.001;
            msn = 2;
            c2 = 1000;
            maxstb = 10;
            nexp = 0;
            maxn = 10;
            minn = -10;
            maxnb = 1000;
            minnb = -1000;
            for(sn=2; sn<=msn; sn++)
            {
                b = new double[sn];
                g = new double[sn];
                xori = new double[sn];
                xoric = new double[sn];
                stx = new double[sn];
                db = new double[sn];
                ub = new double[sn];
                y0 = new double[sn];
                y1 = new double[sn];
                for(i=0; i<=nexp; i++)
                {
                    
                    //
                    //create simmetric matrix 'A'
                    //
                    a = new double[sn, sn];
                    for(j=0; j<=sn-1; j++)
                    {
                        for(k=0; k<=sn-1; k++)
                        {
                            if( j==k )
                            {
                                a[j,k] = 1;
                            }
                            else
                            {
                                a[j,k] = 0;
                            }
                            System.Console.Write("{0,0:F5}",a[j,k]);
                            System.Console.Write(" ");
                        }
                        System.Console.WriteLine();
                    }
                    minqp.minqpcreate(sn, state);
                    setrandomalgobc(state);
                    minqp.minqpsetquadraticterm(state, a, false);
                    for(j=0; j<=sn-1; j++)
                    {
                        xoric[j] = 1;
                        System.Console.Write("XoriC=");
                        System.Console.Write("{0,0:F5}",xoric[j]);
                        System.Console.Write(" ");
                        System.Console.WriteLine();
                    }
                    
                    //
                    //create linear part
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        b[j] = 0;
                        for(k=0; k<=sn-1; k++)
                        {
                            b[j] = b[j]-xoric[k]*a[k,j];
                        }
                        System.Console.Write("B[");
                        System.Console.Write("{0,0:d}",j);
                        System.Console.Write("]=");
                        System.Console.Write("{0,0:F5}",b[j]);
                        System.Console.WriteLine();
                    }
                    minqp.minqpsetlinearterm(state, b);
                    for(j=0; j<=sn-1; j++)
                    {
                        db[j] = 10;
                        ub[j] = 20;
                    }
                    minqp.minqpsetbc(state, db, ub);
                    
                    //
                    //initialization for shifting
                    //initial value for 'XORi'
                    //and searching true results
                    //
                    for(j=0; j<=sn-1; j++)
                    {
                        xori[j] = 1;
                    }
                    minqp.minqpsetorigin(state, xori);
                    
                    //
                    //optimize and get result
                    //
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x, rep);
                    ablas.rmatrixmv(sn, sn, a, 0, 0, 0, x, 0, ref y0, 0);
                    ablas.rmatrixmv(sn, sn, a, 0, 0, 0, x, 0, ref y1, 0);
                    for(j=0; j<=sn-1; j++)
                    {
                        c = 0;
                        for(k=0; k<=sn-1; k++)
                        {
                            c = c-xori[k]*a[k,j];
                        }
                        g[j] = b[j]+c+y0[j]+y1[j];
                    }
                    anti = projectedantigradnorm(sn, x, b, db, ub);
                    System.Console.Write("SN=");
                    System.Console.Write("{0,0:d}",sn);
                    System.Console.WriteLine();
                    System.Console.Write("NEXP=");
                    System.Console.Write("{0,0:d}",i);
                    System.Console.WriteLine();
                    System.Console.Write("TermType=");
                    System.Console.Write("{0,0:d}",rep.terminationtype);
                    System.Console.WriteLine();
                    for(j=0; j<=sn-1; j++)
                    {
                        System.Console.Write("X[");
                        System.Console.Write("{0,0:d}",j);
                        System.Console.Write("]=");
                        System.Console.Write("{0,0:F5}",x[j]);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("DB[");
                        System.Console.Write("{0,0:d}",j);
                        System.Console.Write("]=");
                        System.Console.Write("{0,0:F5}",db[j]);
                        System.Console.Write("; UB[");
                        System.Console.Write("{0,0:d}",j);
                        System.Console.Write("]=");
                        System.Console.Write("{0,0:F5}",ub[j]);
                        System.Console.WriteLine();
                        System.Console.Write("XORi[");
                        System.Console.Write("{0,0:d}",j);
                        System.Console.Write("]=");
                        System.Console.Write("{0,0:F5}",xori[j]);
                        System.Console.Write("; XORiC[");
                        System.Console.Write("{0,0:d}",j);
                        System.Console.Write("]=");
                        System.Console.Write("{0,0:F5}",xoric[j]);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("Anti[");
                        System.Console.Write("{0,0:d}",j);
                        System.Console.Write("]=");
                        System.Console.Write("{0,0:F5}",anti);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        if( (double)(Math.Abs(anti))>(double)(eps) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function performs tests specific for Cholesky solver
            
        Returns True on success, False on failure.
        *************************************************************************/
        public static bool choleskytests()
        {
            bool result = new bool();
            minqp.minqpstate state = new minqp.minqpstate();
            minqp.minqpreport rep = new minqp.minqpreport();
            sparse.sparsematrix sa = new sparse.sparsematrix();
            double[,] a = new double[0,0];
            int n = 0;
            int i = 0;
            int j = 0;
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] x = new double[0];
            double[] xend = new double[0];
            double[] xend0 = new double[0];

            result = false;
            
            //
            // TEST: Cholesky solver should return -5 on sparse matrices.
            //
            n = 5;
            sparse.sparsecreate(n, n, 0, sa);
            for(i=0; i<=n-1; i++)
            {
                sparse.sparseset(sa, i, i, 1.0);
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgocholesky(state);
            minqp.minqpsetquadratictermsparse(state, sa, true);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend, rep);
            apserv.seterrorflag(ref result, rep.terminationtype!=-5);
            
            //
            // TEST: default solver is Cholesky one.
            //
            // It is tested by checking that default solver returns -5 on sparse matrices.
            //
            n = 5;
            sparse.sparsecreate(n, n, 0, sa);
            for(i=0; i<=n-1; i++)
            {
                sparse.sparseset(sa, i, i, 1.0);
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetquadratictermsparse(state, sa, true);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend, rep);
            apserv.seterrorflag(ref result, rep.terminationtype!=-5);
            
            //
            // Test CQP solver on non-convex problems,
            // which are bounded from below on the feasible set:
            //
            //     min -||x||^2 s.t. x[i] in [-1,+1]
            //
            // We test ability of the solver to detect such problems
            // and report failure.
            //
            n = 20;
            a = new double[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a[i,i] = -1.0;
            }
            bndl = new double[n];
            bndu = new double[n];
            x = new double[n];
            for(i=0; i<=n-1; i++)
            {
                bndl[i] = -1;
                bndu[i] = 1;
                x[i] = math.randomreal()-0.5;
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgocholesky(state);
            minqp.minqpsetquadraticterm(state, a, true);
            minqp.minqpsetbc(state, bndl, bndu);
            minqp.minqpsetstartingpoint(state, x);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            apserv.seterrorflag(ref result, rep.terminationtype!=-5);
            
            //
            // Test CQP solver on non-convex problems,
            // which are unbounded from below:
            //
            //     min -||x||^2
            //
            // We test ability of the solver to detect such problems
            // and report failure.
            //
            n = 20;
            a = new double[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a[i,j] = 0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a[i,i] = -1.0;
            }
            x = new double[n];
            for(i=0; i<=n-1; i++)
            {
                x[i] = math.randomreal()-0.5;
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgocholesky(state);
            minqp.minqpsetquadraticterm(state, a, true);
            minqp.minqpsetstartingpoint(state, x);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            apserv.seterrorflag(ref result, rep.terminationtype!=-5);
            return result;
        }


        /*************************************************************************
        This function performs tests specific for QuickQP solver
            
        Returns True on failure.
        *************************************************************************/
        public static bool quickqptests()
        {
            bool result = new bool();
            minqp.minqpstate state = new minqp.minqpstate();
            minqp.minqpreport rep = new minqp.minqpreport();
            int n = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            double g = 0;
            double gnorm = 0;
            bool flag = new bool();
            int origintype = 0;
            int scaletype = 0;
            bool isupper = new bool();
            bool issparse = new bool();
            int itscnt = 0;
            int[] nlist = new int[0];
            int nidx = 0;
            double[,] a = new double[0,0];
            double[,] za = new double[0,0];
            double[,] fulla = new double[0,0];
            double[,] halfa = new double[0,0];
            double[,] c = new double[0,0];
            sparse.sparsematrix sa = new sparse.sparsematrix();
            int[] ct = new int[0];
            double[] b = new double[0];
            double[] zb = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] xend0 = new double[0];
            double[] xend1 = new double[0];
            double[] xori = new double[0];
            double[] xz = new double[0];
            double[] s = new double[0];
            double eps = 0;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int i_ = 0;

            result = false;
            hqrnd.hqrndrandomize(rs);
            
            //
            // Convex test:
            // * N dimensions
            // * random number (0..N) of random boundary constraints
            // * positive-definite A
            // * algorithm randomly choose dense or sparse A, and for
            //   sparse matrix it randomly choose format.
            // * random B with normal entries
            // * initial point is random, feasible
            // * random origin (zero or non-zero) and scale (unit or
            //   non-unit) are generated
            //
            eps = 1.0E-5;
            for(n=1; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    origintype = hqrnd.hqrnduniformi(rs, 2);
                    scaletype = hqrnd.hqrnduniformi(rs, 2);
                    isupper = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                    issparse = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                    matgen.spdmatrixrndcond(n, 1.0E3, ref fulla);
                    halfa = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (j>=i && isupper) || (j<=i && !isupper) )
                            {
                                halfa[i,j] = fulla[i,j];
                            }
                            else
                            {
                                halfa[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                        }
                    }
                    densetosparse(halfa, n, sa);
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    xori = new double[n];
                    s = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = hqrnd.hqrndnormal(rs);
                        bndl[i] = Double.NegativeInfinity;
                        bndu[i] = Double.PositiveInfinity;
                        x0[i] = hqrnd.hqrndnormal(rs);
                        if( origintype==0 )
                        {
                            xori[i] = 0;
                        }
                        else
                        {
                            xori[i] = hqrnd.hqrndnormal(rs);
                        }
                        if( scaletype==0 )
                        {
                            s[i] = 1;
                        }
                        else
                        {
                            s[i] = Math.Exp(hqrnd.hqrndnormal(rs));
                        }
                        j = hqrnd.hqrnduniformi(rs, 5);
                        if( j==0 )
                        {
                            bndl[i] = 0;
                            x0[i] = Math.Abs(x0[i]);
                        }
                        if( j==1 )
                        {
                            bndu[i] = 0;
                            x0[i] = -Math.Abs(x0[i]);
                        }
                        if( j==2 )
                        {
                            bndl[i] = hqrnd.hqrndnormal(rs);
                            bndu[i] = bndl[i];
                            x0[i] = bndl[i];
                        }
                        if( j==3 )
                        {
                            bndl[i] = -0.1;
                            bndu[i] = 0.1;
                            x0[i] = 0.2*hqrnd.hqrnduniformr(rs)-0.1;
                        }
                    }
                    
                    //
                    // Solve problem
                    //
                    minqp.minqpcreate(n, state);
                    minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, 0, (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5));
                    minqp.minqpsetlinearterm(state, b);
                    if( issparse )
                    {
                        minqp.minqpsetquadratictermsparse(state, sa, isupper);
                    }
                    else
                    {
                        minqp.minqpsetquadraticterm(state, halfa, isupper);
                    }
                    if( origintype!=0 )
                    {
                        minqp.minqpsetorigin(state, xori);
                    }
                    if( scaletype!=0 )
                    {
                        minqp.minqpsetscale(state, s);
                    }
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref result, rep.terminationtype<=0);
                    if( rep.terminationtype<=0 )
                    {
                        return result;
                    }
                    
                    //
                    // Test - calculate constrained gradient at solution,
                    // check its norm.
                    //
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g = b[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g = g+fulla[i,j]*(x1[j]-xori[j]);
                        }
                        if( (double)(x1[i])==(double)(bndl[i]) && (double)(g)>(double)(0) )
                        {
                            g = 0;
                        }
                        if( (double)(x1[i])==(double)(bndu[i]) && (double)(g)<(double)(0) )
                        {
                            g = 0;
                        }
                        gnorm = gnorm+math.sqr(g);
                        apserv.seterrorflag(ref result, (double)(x1[i])<(double)(bndl[i]));
                        apserv.seterrorflag(ref result, (double)(x1[i])>(double)(bndu[i]));
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref result, (double)(gnorm)>(double)(eps));
                }
            }
            
            //
            // Strongly non-convex test:
            // * N dimensions, N>=2
            // * box constraints, x[i] in [-1,+1]
            // * A = A0-0.5*I, where A0 is SPD with unit norm and smallest
            //   singular value equal to 1.0E-3, I is identity matrix
            // * random B with normal entries
            // * initial point is random, feasible
            //
            // We perform two tests:
            // * unconstrained problem must be recognized as unbounded
            // * constrained problem can be successfully solved
            //
            // NOTE: it is important to have N>=2, because formula for A
            //       can be applied only to matrix with at least two
            //       singular values
            //
            eps = 1.0E-5;
            for(n=2; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    matgen.spdmatrixrndcond(n, 1.0E3, ref fulla);
                    for(i=0; i<=n-1; i++)
                    {
                        fulla[i,i] = fulla[i,i]-0.5;
                    }
                    isupper = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                    halfa = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (j>=i && isupper) || (j<=i && !isupper) )
                            {
                                halfa[i,j] = fulla[i,j];
                            }
                            else
                            {
                                halfa[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                        }
                    }
                    densetosparse(halfa, n, sa);
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = hqrnd.hqrndnormal(rs);
                        bndl[i] = -1;
                        bndu[i] = 1;
                        x0[i] = 2*hqrnd.hqrnduniformr(rs)-1;
                    }
                    
                    //
                    // Solve problem:
                    // * without constraints we expect failure
                    // * with constraints algorithm must succeed
                    //
                    minqp.minqpcreate(n, state);
                    minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, 0, (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5));
                    minqp.minqpsetlinearterm(state, b);
                    if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                    {
                        minqp.minqpsetquadraticterm(state, halfa, isupper);
                    }
                    else
                    {
                        minqp.minqpsetquadratictermsparse(state, sa, isupper);
                    }
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref result, rep.terminationtype!=-4);
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref result, rep.terminationtype<=0);
                    if( rep.terminationtype<=0 )
                    {
                        return result;
                    }
                    
                    //
                    // Test - calculate constrained gradient at solution,
                    // check its norm.
                    //
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += fulla[i,i_]*x1[i_];
                        }
                        g = v+b[i];
                        if( (double)(x1[i])==(double)(bndl[i]) && (double)(g)>(double)(0) )
                        {
                            g = 0;
                        }
                        if( (double)(x1[i])==(double)(bndu[i]) && (double)(g)<(double)(0) )
                        {
                            g = 0;
                        }
                        gnorm = gnorm+math.sqr(g);
                        apserv.seterrorflag(ref result, (double)(x1[i])<(double)(bndl[i]));
                        apserv.seterrorflag(ref result, (double)(x1[i])>(double)(bndu[i]));
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref result, (double)(gnorm)>(double)(eps));
                }
            }
            
            //
            // Basic semi-definite test:
            // * N dimensions, N>=2
            // * box constraints, x[i] in [-1,+1]
            //       [ 1 1 ... 1 1 ]
            // * A = [ ... ... ... ]
            //       [ 1 1 ... 1 1 ]
            // * random B with normal entries
            // * initial point is random, feasible
            //
            // We perform two tests:
            // * unconstrained problem must be recognized as unbounded
            // * constrained problem must be recognized as bounded and
            //   successfully solved
            //
            // Both problems require subtle programming when we work
            // with semidefinite QP.
            //
            // NOTE: unlike BLEIC-QP algorthm, QQP may detect unboundedness
            //       of the problem when started from any x0, with any b.
            //       BLEIC-based solver requires carefully chosen x0 and b
            //       to find direction of zero curvature, but this solver
            //       can find it from any point.
            //
            nlist = new int[12];
            nlist[0] = 2;
            nlist[1] = 3;
            nlist[2] = 4;
            nlist[3] = 5;
            nlist[4] = 6;
            nlist[5] = 7;
            nlist[6] = 8;
            nlist[7] = 9;
            nlist[8] = 10;
            nlist[9] = 20;
            nlist[10] = 40;
            nlist[11] = 80;
            eps = 1.0E-5;
            for(nidx=0; nidx<=alglib.ap.len(nlist)-1; nidx++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    n = nlist[nidx];
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        do
                        {
                            b[i] = hqrnd.hqrndnormal(rs);
                        }
                        while( (double)(b[i])==(double)(0) );
                        bndl[i] = -1;
                        bndu[i] = 1;
                        x0[i] = 2*hqrnd.hqrnduniformr(rs)-1;
                    }
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 1.0;
                        }
                    }
                    densetosparse(a, n, sa);
                    
                    //
                    // Solve problem:
                    // * without constraints we expect failure
                    // * with constraints algorithm must succeed
                    //
                    minqp.minqpcreate(n, state);
                    minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, 0, (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5));
                    minqp.minqpsetlinearterm(state, b);
                    if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                    {
                        minqp.minqpsetquadraticterm(state, a, true);
                    }
                    else
                    {
                        minqp.minqpsetquadratictermsparse(state, sa, true);
                    }
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref result, rep.terminationtype!=-4);
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref result, rep.terminationtype<=0);
                    if( rep.terminationtype<=0 )
                    {
                        return result;
                    }
                    
                    //
                    // Test - calculate constrained gradient at solution,
                    // check its norm.
                    //
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g = b[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g = g+a[i,j]*x1[j];
                        }
                        if( (double)(x1[i])==(double)(bndl[i]) && (double)(g)>(double)(0) )
                        {
                            g = 0;
                        }
                        if( (double)(x1[i])==(double)(bndu[i]) && (double)(g)<(double)(0) )
                        {
                            g = 0;
                        }
                        gnorm = gnorm+math.sqr(g);
                        apserv.seterrorflag(ref result, (double)(x1[i])<(double)(bndl[i]));
                        apserv.seterrorflag(ref result, (double)(x1[i])>(double)(bndu[i]));
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref result, (double)(gnorm)>(double)(eps));
                }
            }
            
            //
            // Linear (zero-quadratic) test:
            // * N dimensions, N>=1
            // * box constraints, x[i] in [-1,+1]
            // * A = 0
            // * random B with normal entries
            // * initial point is random, feasible
            //
            // We perform two tests:
            // * unconstrained problem must be recognized as unbounded
            // * constrained problem can be successfully solved
            //
            // NOTE: we may explicitly set zero A, or assume that by
            //       default it is zero. During test we will try both
            //       ways.
            //
            eps = 1.0E-5;
            for(n=1; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        do
                        {
                            b[i] = hqrnd.hqrndnormal(rs);
                        }
                        while( (double)(b[i])==(double)(0) );
                        bndl[i] = -1;
                        bndu[i] = 1;
                        x0[i] = 2*hqrnd.hqrnduniformr(rs)-1;
                    }
                    
                    //
                    // Solve problem:
                    // * without constraints we expect failure
                    // * with constraints algorithm must succeed
                    //
                    minqp.minqpcreate(n, state);
                    minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, 0, (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5));
                    minqp.minqpsetlinearterm(state, b);
                    if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                    {
                        a = new double[n, n];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a[i,j] = 0;
                            }
                        }
                        minqp.minqpsetquadraticterm(state, a, true);
                    }
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref result, rep.terminationtype!=-4);
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref result, rep.terminationtype<=0);
                    if( rep.terminationtype<=0 )
                    {
                        return result;
                    }
                    
                    //
                    // Test - calculate constrained gradient at solution,
                    // check its norm.
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref result, (double)(b[i])>(double)(0) && (double)(x1[i])>(double)(bndl[i]));
                        apserv.seterrorflag(ref result, (double)(b[i])<(double)(0) && (double)(x1[i])<(double)(bndu[i]));
                    }
                }
            }
            
            //
            // Test for Newton phase of QQP algorithm - we test that Newton
            // phase can find good solution within one step. In order to do
            // so we:
            // * solve convex QP problem (dense or sparse)
            // * with K<=N equality-only constraints ai=x=bi
            // * with number of outer iterations limited to just 1
            // * and with CG phase turned off (we modify internal structures
            //   of the QQP solver in order to make it)
            //
            eps = 1.0E-5;
            for(pass=1; pass<=10; pass++)
            {
                
                //
                // Generate problem
                //
                n = 50+hqrnd.hqrnduniformi(rs, 51);
                matgen.spdmatrixrndcond(n, 1.0E3, ref a);
                densetosparse(a, n, sa);
                b = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                x0 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    b[i] = hqrnd.hqrndnormal(rs);
                    x0[i] = hqrnd.hqrndnormal(rs);
                    if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                    {
                        bndl[i] = Double.NegativeInfinity;
                        bndu[i] = Double.PositiveInfinity;
                    }
                    else
                    {
                        bndl[i] = hqrnd.hqrndnormal(rs);
                        bndu[i] = bndl[i];
                    }
                }
                
                //
                // Solve problem
                //
                // NOTE: we modify internal structures of QQP solver in order
                //       to deactivate CG phase
                //
                minqp.minqpcreate(n, state);
                minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, 1, true);
                state.qqpsettingsuser.cgphase = false;
                minqp.minqpsetlinearterm(state, b);
                if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                {
                    minqp.minqpsetquadraticterm(state, a, (double)(hqrnd.hqrndnormal(rs))>(double)(0));
                }
                else
                {
                    minqp.minqpsetquadratictermsparse(state, sa, (double)(hqrnd.hqrndnormal(rs))>(double)(0));
                }
                minqp.minqpsetbc(state, bndl, bndu);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref x1, rep);
                apserv.seterrorflag(ref result, rep.terminationtype<=0);
                if( rep.terminationtype<=0 )
                {
                    return result;
                }
                
                //
                // Test - calculate constrained gradient at solution,
                // check its norm.
                //
                gnorm = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    g = b[i];
                    for(j=0; j<=n-1; j++)
                    {
                        g = g+a[i,j]*x1[j];
                    }
                    if( (double)(x1[i])==(double)(bndl[i]) && (double)(g)>(double)(0) )
                    {
                        g = 0;
                    }
                    if( (double)(x1[i])==(double)(bndu[i]) && (double)(g)<(double)(0) )
                    {
                        g = 0;
                    }
                    gnorm = gnorm+math.sqr(g);
                    apserv.seterrorflag(ref result, (double)(x1[i])<(double)(bndl[i]));
                    apserv.seterrorflag(ref result, (double)(x1[i])>(double)(bndu[i]));
                }
                gnorm = Math.Sqrt(gnorm);
                apserv.seterrorflag(ref result, (double)(gnorm)>(double)(eps));
            }
            
            //
            // Test for Newton phase of QQP algorithm - we test that Newton
            // updates work correctly, i.e. that CNewtonUpdate() internal
            // function correctly updates inverse Hessian matrix.
            //
            // To test it we:
            // * solve ill conditioned convex QP problem
            // * with unconstrained solution XZ whose components are within [-0.5,+0.5]
            // * with one inequality constraint X[k]>=5
            // * with initial point such that:
            //   * X0[i] = 100       for i<>k
            //   * X0[k] = 5+1.0E-5
            // * with number of outer iterations limited to just 1
            // * and with CG phase turned off (we modify internal structures
            //   of the QQP solver in order to make it)
            //
            // The idea is that single Newton step is not enough to find solution,
            // but with just one update we can move exactly to the solution.
            //
            // We perform two tests:
            // * first one with State.QQP.NewtMaxIts set to 1, in order to
            //   make sure that algorithm fails with just one iteration
            // * second one with State.QQP.NewtMaxIts set to 2, in order to
            //   make sure that algorithm converges when it can perform update
            //
            eps = 1.0E-5;
            for(pass=1; pass<=10; pass++)
            {
                
                //
                // Generate problem
                //
                n = 20+hqrnd.hqrnduniformi(rs, 20);
                matgen.spdmatrixrndcond(n, 1.0E5, ref a);
                densetosparse(a, n, sa);
                sparse.sparseconverttocrs(sa);
                b = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                x0 = new double[n];
                xz = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    xz[i] = hqrnd.hqrnduniformr(rs)-0.5;
                    x0[i] = 100;
                    bndl[i] = Double.NegativeInfinity;
                    bndu[i] = Double.PositiveInfinity;
                }
                k = hqrnd.hqrnduniformi(rs, n);
                x0[k] = 5.00001;
                bndl[k] = 5.0;
                sparse.sparsemv(sa, xz, ref b);
                for(i=0; i<=n-1; i++)
                {
                    b[i] = -b[i];
                }
                
                //
                // Create solver
                //
                minqp.minqpcreate(n, state);
                minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, 1, true);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetquadraticterm(state, a, (double)(hqrnd.hqrndnormal(rs))>(double)(0));
                minqp.minqpsetbc(state, bndl, bndu);
                minqp.minqpsetstartingpoint(state, x0);
                
                //
                // Solve problem. First time, with no Newton updates.
                // It must fail.
                //
                // NOTE: we modify internal structures of QQP solver in order
                //       to deactivate CG phase and turn off Newton updates.
                //
                state.qqpsettingsuser.cgphase = false;
                state.qqpsettingsuser.cnphase = true;
                state.qqpsettingsuser.cnmaxupdates = 0;
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref x1, rep);
                apserv.seterrorflag(ref result, rep.terminationtype<=0);
                if( result )
                {
                    return result;
                }
                flag = false;
                testbcgradandfeasibility(a, b, bndl, bndu, n, x1, eps, ref flag);
                apserv.seterrorflag(ref result, !flag);
                
                //
                // Now with Newton updates - it must succeeed.
                //
                state.qqpsettingsuser.cgphase = false;
                state.qqpsettingsuser.cnmaxupdates = n;
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref x1, rep);
                apserv.seterrorflag(ref result, rep.terminationtype<=0);
                if( result )
                {
                    return result;
                }
                flag = false;
                testbcgradandfeasibility(a, b, bndl, bndu, n, x1, eps, ref flag);
                apserv.seterrorflag(ref result, flag);
            }
            
            //
            // Check that problem with general constraints results in
            // correct error code (-5 should be returned).
            //
            c = new double[1, 3];
            ct = new int[1];
            c[0,0] = 1.0;
            c[0,1] = 1.0;
            c[0,2] = 2.0;
            ct[0] = 0;
            minqp.minqpcreate(2, state);
            minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, 0, (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5));
            minqp.minqpsetlc(state, c, ct, 1);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref x1, rep);
            apserv.seterrorflag(ref result, rep.terminationtype!=-5);
            
            //
            // Test sparse functionality. QQP solver must perform
            // same steps independently of matrix type (dense or sparse).
            //
            // We generate random unconstrained test problem and solve it
            // twice - first time we solve dense version, second time -
            // sparse version is solved.
            //
            // During this test we:
            // * use stringent stopping criteria (one outer iteration)
            // * turn off Newton phase of the algorithm to slow down
            //   convergence
            //
            eps = 1.0E-3;
            itscnt = 1;
            n = 20;
            isupper = (double)(math.randomreal())>(double)(0.5);
            matgen.spdmatrixrndcond(n, 1.0E3, ref za);
            sparse.sparsecreate(n, n, 0, sa);
            a = new double[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j>=i && isupper )
                    {
                        sparse.sparseset(sa, i, j, za[i,j]);
                        a[i,j] = za[i,j];
                    }
                    if( j<=i && !isupper )
                    {
                        sparse.sparseset(sa, i, j, za[i,j]);
                        a[i,j] = za[i,j];
                    }
                }
            }
            b = new double[n];
            s = new double[n];
            for(i=0; i<=n-1; i++)
            {
                b[i] = apserv.randomnormal();
                s[i] = Math.Pow(10.0, apserv.randomnormal()/10);
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, itscnt, false);
            minqp.minqpsetscale(state, s);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadraticterm(state, a, isupper);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, itscnt, false);
            minqp.minqpsetscale(state, s);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadratictermsparse(state, sa, isupper);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend1, rep);
            for(i=0; i<=n-1; i++)
            {
                apserv.seterrorflag(ref result, (double)(Math.Abs(xend0[i]-xend1[i]))>(double)(eps));
            }
            
            //
            // Test scale-invariance. QQP performs same steps on scaled and
            // unscaled problems (assuming that scale of the variables is known).
            //
            // We generate random scale matrix S and random well-conditioned and
            // well scaled matrix A. Then we solve two problems:
            //
            //     (1) f = 0.5*x'*A*x+b'*x
            //         (identity scale matrix is used)
            //
            // and
            //
            //     (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
            //         (scale matrix S is used)
            //
            // Solution process is started from X=0, we perform ItsCnt=1 outer
            // iterations with Newton phase turned off (to slow down convergence;
            // we want to prevent algorithm from converging to exact solution which
            // is exactly same for both problems; the idea is to test that same
            // intermediate tests are taken).
            //
            // As result, we must get S*x=y
            //
            eps = 1.0E-3;
            itscnt = 1;
            n = 100;
            s = new double[n];
            for(i=0; i<=n-1; i++)
            {
                s[i] = Math.Pow(10.0, apserv.randomnormal()/10);
            }
            matgen.spdmatrixrndcond(n, 1.0E3, ref a);
            za = new double[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    za[i,j] = a[i,j]/(s[i]*s[j]);
                }
            }
            b = new double[n];
            zb = new double[n];
            for(i=0; i<=n-1; i++)
            {
                b[i] = apserv.randomnormal();
                zb[i] = b[i]/s[i];
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, itscnt, false);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadraticterm(state, a, true);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, itscnt, false);
            minqp.minqpsetlinearterm(state, zb);
            minqp.minqpsetquadraticterm(state, za, true);
            minqp.minqpsetscale(state, s);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend1, rep);
            for(i=0; i<=n-1; i++)
            {
                apserv.seterrorflag(ref result, (double)(Math.Abs(s[i]*xend0[i]-xend1[i]))>(double)(eps));
            }
            
            //
            // Test that QQP can efficiently use sparse matrices (i.e. it is
            // not disguised version of some dense QP solver). In order to test
            // it we create very large and very sparse problem (diagonal matrix
            // with N=40.000) and perform 10 iterations of QQP solver.
            //
            // In case QP solver uses some form of dense linear algebra to solve
            // this problem, it will take TOO much time to solve it. And we will
            // notice it by EXTREME slowdown during testing.
            //
            n = 40000;
            sparse.sparsecreate(n, n, 0, sa);
            for(i=0; i<=n-1; i++)
            {
                sparse.sparseset(sa, i, i, Math.Pow(10.0, -(3*math.randomreal())));
            }
            b = new double[n];
            for(i=0; i<=n-1; i++)
            {
                b[i] = apserv.randomnormal();
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgoquickqp(state, 0.0, 0.0, 0.0, 10, (double)(hqrnd.hqrnduniformr(rs))>(double)(0.5));
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadratictermsparse(state, sa, true);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            return result;
        }


        /*************************************************************************
        This function performs tests specific for BLEIC solver
            
        Returns True on error, False on success.
        *************************************************************************/
        public static bool bleictests()
        {
            bool result = new bool();
            minqp.minqpstate state = new minqp.minqpstate();
            minqp.minqpreport rep = new minqp.minqpreport();
            int[] nlist = new int[0];
            int nidx = 0;
            double[,] a = new double[0,0];
            double[,] za = new double[0,0];
            double[,] c = new double[0,0];
            double[] b = new double[0];
            double[] zb = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] s = new double[0];
            double[] x = new double[0];
            int[] ct = new int[0];
            sparse.sparsematrix sa = new sparse.sparsematrix();
            int n = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int i = 0;
            int j = 0;
            int pass = 0;
            double[] xend0 = new double[0];
            double[] xend1 = new double[0];
            double eps = 0;
            double v = 0;
            double g = 0;
            double gnorm = 0;
            int itscnt = 0;
            bool isupper = new bool();

            result = false;
            hqrnd.hqrndrandomize(rs);
            
            //
            // Test sparse functionality. BLEIC-based solver must perform
            // same steps independently of matrix type (dense or sparse).
            //
            // We generate random unconstrained test problem and solve it
            // twice - first time we solve dense version, second time -
            // sparse version is solved.
            //
            eps = 1.0E-3;
            itscnt = 5;
            n = 20;
            isupper = (double)(math.randomreal())>(double)(0.5);
            matgen.spdmatrixrndcond(n, 1.0E3, ref za);
            sparse.sparsecreate(n, n, 0, sa);
            a = new double[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j>=i && isupper )
                    {
                        sparse.sparseset(sa, i, j, za[i,j]);
                        a[i,j] = za[i,j];
                    }
                    if( j<=i && !isupper )
                    {
                        sparse.sparseset(sa, i, j, za[i,j]);
                        a[i,j] = za[i,j];
                    }
                }
            }
            b = new double[n];
            s = new double[n];
            for(i=0; i<=n-1; i++)
            {
                b[i] = apserv.randomnormal();
                s[i] = Math.Pow(10.0, apserv.randomnormal()/10);
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgobleic(state, 0.0, 0.0, 0.0, itscnt);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadraticterm(state, a, isupper);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgobleic(state, 0.0, 0.0, 0.0, itscnt);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadratictermsparse(state, sa, isupper);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend1, rep);
            for(i=0; i<=n-1; i++)
            {
                apserv.seterrorflag(ref result, (double)(Math.Abs(xend0[i]-xend1[i]))>(double)(eps));
            }
            
            //
            // Test scale-invariance. BLEIC performs same steps on scaled and
            // unscaled problems (assuming that scale of the variables is known).
            //
            // We generate random scale matrix S and random well-conditioned and
            // well scaled matrix A. Then we solve two problems:
            //
            //     (1) f = 0.5*x'*A*x+b'*x
            //         (identity scale matrix is used)
            //
            // and
            //
            //     (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
            //         (scale matrix S is used)
            //
            // Solution process is started from X=0, we perform ItsCnt=5 steps.
            // As result, we must get S*x=y
            //
            eps = 1.0E-3;
            itscnt = 5;
            n = 20;
            s = new double[n];
            for(i=0; i<=n-1; i++)
            {
                s[i] = Math.Pow(10.0, apserv.randomnormal()/10);
            }
            matgen.spdmatrixrndcond(n, 1.0E3, ref a);
            za = new double[n, n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    za[i,j] = a[i,j]/(s[i]*s[j]);
                }
            }
            b = new double[n];
            zb = new double[n];
            for(i=0; i<=n-1; i++)
            {
                b[i] = apserv.randomnormal();
                zb[i] = b[i]/s[i];
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgobleic(state, 0.0, 0.0, 0.0, itscnt);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadraticterm(state, a, true);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgobleic(state, 0.0, 0.0, 0.0, itscnt);
            minqp.minqpsetlinearterm(state, zb);
            minqp.minqpsetquadraticterm(state, za, true);
            minqp.minqpsetscale(state, s);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend1, rep);
            for(i=0; i<=n-1; i++)
            {
                apserv.seterrorflag(ref result, (double)(Math.Abs(s[i]*xend0[i]-xend1[i]))>(double)(eps));
            }
            
            //
            // Test that BLEIC can efficiently use sparse matrices (i.e. it is
            // not disguised version of some dense QP solver). In order to test
            // it we create very large and very sparse problem (diagonal matrix
            // with N=20.000) and perform 10 iterations of BLEIC-based QP solver.
            //
            // In case QP solver uses some form of dense linear algebra to solve
            // this problem, it will take TOO much time to solve it. And we will
            // notice it by EXTREME slowdown during testing.
            //
            n = 20000;
            sparse.sparsecreate(n, n, 0, sa);
            for(i=0; i<=n-1; i++)
            {
                sparse.sparseset(sa, i, i, Math.Pow(10.0, -(3*math.randomreal())));
            }
            b = new double[n];
            for(i=0; i<=n-1; i++)
            {
                b[i] = apserv.randomnormal();
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgobleic(state, 0.0, 0.0, 0.0, 10);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadratictermsparse(state, sa, true);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            
            //
            // Special semi-definite test:
            // * N dimensions, N>=2 (important!)
            // * box constraints, x[i] in [-1,+1]
            //       [ 1 1 ... 1 1 ]
            // * A = [ ... ... ... ]
            //       [ 1 1 ... 1 1 ]
            // * random B such that SUM(b[i])=0.0 (important!)
            // * initial point x0 is chosen in such way that SUM(x[i])=0.0
            //   (important!)
            //
            // We perform two tests:
            // * unconstrained problem must be recognized as unbounded
            //   (when starting from x0!)
            // * constrained problem must be recognized as bounded
            //   and successfully solved
            //
            // Both problems require subtle programming when we work
            // with semidefinite QP.
            //
            // NOTE: it is very important to have N>=2 (otherwise problem
            //       will be bounded from below even without boundary
            //       constraints) and to have x0/b0 such that sum of 
            //       components is zero (such x0 is exact minimum of x'*A*x,
            //       which allows algorithm to find direction of zero curvature
            //       at the very first step). If x0/b are chosen in other way,
            //       algorithm may be unable to find direction of zero
            //       curvature and will cycle forever, slowly decreasing
            //       function value at each iteration.
            //       This is major difference from similar test for QQP solver -
            //       QQP can find direction of zero curvature from almost any
            //       point due to internal CG solver which favors such directions.
            //       BLEIC uses LBFGS, which is less able to find direction of
            //       zero curvature.
            //
            nlist = new int[12];
            nlist[0] = 2;
            nlist[1] = 3;
            nlist[2] = 4;
            nlist[3] = 5;
            nlist[4] = 6;
            nlist[5] = 7;
            nlist[6] = 8;
            nlist[7] = 9;
            nlist[8] = 10;
            nlist[9] = 20;
            nlist[10] = 40;
            nlist[11] = 80;
            eps = 1.0E-5;
            for(nidx=0; nidx<=alglib.ap.len(nlist)-1; nidx++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    n = nlist[nidx];
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        do
                        {
                            b[i] = hqrnd.hqrndnormal(rs);
                        }
                        while( (double)(b[i])==(double)(0) );
                        bndl[i] = -1;
                        bndu[i] = 1;
                        x0[i] = 2*hqrnd.hqrnduniformr(rs)-1;
                    }
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+x0[i];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = x0[i]-v/n;
                    }
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+b[i];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = b[i]-v/n;
                    }
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 1.0;
                        }
                    }
                    densetosparse(a, n, sa);
                    
                    //
                    // Solve problem:
                    // * without constraints we expect failure
                    // * with constraints algorithm must succeed
                    //
                    minqp.minqpcreate(n, state);
                    minqp.minqpsetalgobleic(state, 0.0, 0.0, 0.0, 0);
                    minqp.minqpsetlinearterm(state, b);
                    minqp.minqpsetstartingpoint(state, x0);
                    if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                    {
                        minqp.minqpsetquadraticterm(state, a, true);
                    }
                    else
                    {
                        minqp.minqpsetquadratictermsparse(state, sa, true);
                    }
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref result, rep.terminationtype!=-4);
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref result, rep.terminationtype<=0);
                    if( rep.terminationtype<=0 )
                    {
                        return result;
                    }
                    
                    //
                    // Test - calculate constrained gradient at solution,
                    // check its norm.
                    //
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g = b[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g = g+a[i,j]*x1[j];
                        }
                        if( (double)(x1[i])==(double)(bndl[i]) && (double)(g)>(double)(0) )
                        {
                            g = 0;
                        }
                        if( (double)(x1[i])==(double)(bndu[i]) && (double)(g)<(double)(0) )
                        {
                            g = 0;
                        }
                        gnorm = gnorm+math.sqr(g);
                        apserv.seterrorflag(ref result, (double)(x1[i])<(double)(bndl[i]));
                        apserv.seterrorflag(ref result, (double)(x1[i])>(double)(bndu[i]));
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref result, (double)(gnorm)>(double)(eps));
                }
            }
            
            //
            // Test that BLEIC-based QP solver can solve non-convex problems
            // which are bounded from below on the feasible set:
            //
            //     min -||x||^2 s.t. x[i] in [-1,+1]
            //
            // We also test ability of the solver to detect unbounded problems
            // (we remove one of the constraints and repeat solution process).
            //
            n = 20;
            eps = 1.0E-14;
            sparse.sparsecreate(n, n, 0, sa);
            for(i=0; i<=n-1; i++)
            {
                sparse.sparseset(sa, i, i, -1);
            }
            bndl = new double[n];
            bndu = new double[n];
            x = new double[n];
            for(i=0; i<=n-1; i++)
            {
                bndl[i] = -1;
                bndu[i] = 1;
                x[i] = math.randomreal()-0.5;
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgobleic(state, eps, 0.0, 0.0, 0);
            minqp.minqpsetquadratictermsparse(state, sa, true);
            minqp.minqpsetbc(state, bndl, bndu);
            minqp.minqpsetstartingpoint(state, x);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            apserv.seterrorflag(ref result, rep.terminationtype<=0);
            if( rep.terminationtype>0 )
            {
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref result, (double)(xend0[i])!=(double)(-1) && (double)(xend0[i])!=(double)(1));
                }
            }
            i = math.randominteger(n);
            bndl[i] = Double.NegativeInfinity;
            bndu[i] = Double.PositiveInfinity;
            minqp.minqpsetbc(state, bndl, bndu);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            apserv.seterrorflag(ref result, rep.terminationtype!=-4);
            
            //
            // Test that BLEIC-based QP solver can solve non-convex problems
            // which are bounded from below on the feasible set:
            //
            //     min -||x||^2 s.t. x[i] in [-1,+1],
            //     with inequality constraints handled as general linear ones
            //
            // We also test ability of the solver to detect unbounded problems
            // (we remove last pair of constraints and try to solve modified
            // problem).
            //
            n = 20;
            eps = 1.0E-14;
            sparse.sparsecreate(n, n, 0, sa);
            for(i=0; i<=n-1; i++)
            {
                sparse.sparseset(sa, i, i, -1);
            }
            c = new double[2*n, n+1];
            ct = new int[2*n];
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n; j++)
                {
                    c[2*i+0,j] = 0;
                    c[2*i+1,j] = 0;
                }
                c[2*i+0,i] = 1.0;
                c[2*i+0,n] = 1.0;
                ct[2*i+0] = -1;
                c[2*i+1,i] = 1.0;
                c[2*i+1,n] = -1.0;
                ct[2*i+1] = 1;
            }
            x = new double[n];
            for(i=0; i<=n-1; i++)
            {
                x[i] = math.randomreal()-0.5;
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgobleic(state, eps, 0.0, 0.0, 0);
            minqp.minqpsetquadratictermsparse(state, sa, true);
            minqp.minqpsetlc(state, c, ct, 2*n);
            minqp.minqpsetstartingpoint(state, x);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            apserv.seterrorflag(ref result, rep.terminationtype<=0);
            if( rep.terminationtype>0 )
            {
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref result, (double)(Math.Abs(xend0[i]+1))>(double)(100*math.machineepsilon) && (double)(Math.Abs(xend0[i]-1))>(double)(100*math.machineepsilon));
                }
            }
            minqp.minqpsetlc(state, c, ct, 2*(n-1));
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            apserv.seterrorflag(ref result, rep.terminationtype!=-4);
            
            //
            // Test that BLEIC-based QP solver can solve QP problems with
            // zero quadratic term:
            //
            //     min b'*x  s.t. x[i] in [-1,+1]
            //
            // It means that QP solver can be used as linear programming solver
            // (altough performance of such solver is worse than that of specialized
            // LP solver).
            //
            // NOTE: we perform this test twice - first time without explicitly setting
            //       quadratic term (we test that default quadratic term is zero), and
            //       second time - with explicitly set quadratic term.
            //
            n = 20;
            sparse.sparsecreate(n, n, 0, sa);
            bndl = new double[n];
            bndu = new double[n];
            b = new double[n];
            for(i=0; i<=n-1; i++)
            {
                bndl[i] = -1;
                bndu[i] = 1;
                b[i] = apserv.randomnormal();
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgobleic(state, eps, 0.0, 0.0, 0);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetbc(state, bndl, bndu);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            apserv.seterrorflag(ref result, rep.terminationtype<=0);
            if( rep.terminationtype>0 )
            {
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref result, (double)(b[i])>(double)(0) && (double)(xend0[i])!=(double)(bndl[i]));
                    apserv.seterrorflag(ref result, (double)(b[i])<(double)(0) && (double)(xend0[i])!=(double)(bndu[i]));
                }
            }
            minqp.minqpcreate(n, state);
            minqp.minqpsetalgobleic(state, eps, 0.0, 0.0, 0);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetbc(state, bndl, bndu);
            minqp.minqpsetquadratictermsparse(state, sa, true);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend0, rep);
            apserv.seterrorflag(ref result, rep.terminationtype<=0);
            if( rep.terminationtype>0 )
            {
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref result, (double)(b[i])>(double)(0) && (double)(xend0[i])!=(double)(bndl[i]));
                    apserv.seterrorflag(ref result, (double)(b[i])<(double)(0) && (double)(xend0[i])!=(double)(bndu[i]));
                }
            }
            
            //
            // Test specific problem sent by V.Semenenko, which resulted in
            // the initinite loop in FindFeasiblePoint (before fix). We do
            // not test results returned by solver - simply being able to
            // stop is enough for this test.
            //
            // NOTE: it is important that modifications to problem are applied
            //       sequentially. Test fails after 100-5000 such modifications.
            //       One modification is not enough to cause failure.
            //
            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.222990;
            a[1,1] = 1.934900;
            a[2,2] = 0.603924;
            b = new double[n];
            b[0] = -4.97245;
            b[1] = -9.09039;
            b[2] = -4.63856;
            c = new double[8, n+1];
            for(i=0; i<=alglib.ap.rows(c)-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c[i,j] = 0.0;
                }
            }
            c[0,0] = 1;
            c[0,n] = 4.94298;
            c[1,0] = 1;
            c[1,n] = 4.79981;
            c[2,1] = 1;
            c[2,n] = -0.4848;
            c[3,1] = 1;
            c[3,n] = -0.73804;
            c[4,2] = 1;
            c[4,n] = 0.575729;
            c[5,2] = 1;
            c[5,n] = 0.458645;
            c[6,0] = 1;
            c[6,2] = -1;
            c[6,n] = -0.0546574;
            c[7,0] = 1;
            c[7,2] = -1;
            c[7,n] = -0.5900440;
            ct = new int[8];
            ct[0] = -1;
            ct[1] = 1;
            ct[2] = -1;
            ct[3] = 1;
            ct[4] = -1;
            ct[5] = 1;
            ct[6] = -1;
            ct[7] = 1;
            s = new double[n];
            s[0] = 0.143171;
            s[1] = 0.253240;
            s[2] = 0.117084;
            x0 = new double[n];
            x0[0] = 3.51126;
            x0[1] = 4.05731;
            x0[2] = 6.63307;
            for(pass=1; pass<=10000; pass++)
            {
                
                //
                // Apply random distortion
                //
                for(j=0; j<=n-1; j++)
                {
                    b[j] = b[j]+(2*hqrnd.hqrnduniformi(rs, 2)-1)*0.1;
                }
                for(j=0; j<=6-1; j++)
                {
                    c[j,n] = c[j,n]+(2*hqrnd.hqrnduniformi(rs, 2)-1)*0.1;
                }
                
                //
                // Solve
                //
                minqp.minqpcreate(3, state);
                minqp.minqpsetquadraticterm(state, a, true);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetlc(state, c, ct, 8);
                minqp.minqpsetalgobleic(state, 0.0, 0.0, 0.0, 0);
                minqp.minqpsetstartingpoint(state, x0);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref x1, rep);
            }
            return result;
        }


        /*************************************************************************
        This function tests bound constrained quadratic programming algorithm.

        On failure sets error flag.
        *************************************************************************/
        private static void bcqptest(ref bool wereerrors)
        {
            minqp.minqpstate state = new minqp.minqpstate();
            minqp.minqpreport rep = new minqp.minqpreport();
            int n = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            double g = 0;
            double gnorm = 0;
            int origintype = 0;
            int scaletype = 0;
            bool isupper = new bool();
            bool issparse = new bool();
            double[,] a = new double[0,0];
            double[,] fulla = new double[0,0];
            double[,] halfa = new double[0,0];
            double[,] c = new double[0,0];
            sparse.sparsematrix sa = new sparse.sparsematrix();
            int[] ct = new int[0];
            double[] b = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] xori = new double[0];
            double[] xz = new double[0];
            double[] s = new double[0];
            double eps = 0;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            
            //
            // Convex test:
            // * N dimensions
            // * random number (0..N) of random boundary constraints
            // * positive-definite A
            // * algorithm randomly choose dense or sparse A, and for
            //   sparse matrix it randomly choose format.
            // * random B with normal entries
            // * initial point is random, feasible
            // * random origin (zero or non-zero) and scale (unit or
            //   non-unit) are generated
            //
            eps = 1.0E-4;
            for(n=1; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    origintype = hqrnd.hqrnduniformi(rs, 2);
                    scaletype = hqrnd.hqrnduniformi(rs, 2);
                    isupper = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                    issparse = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                    matgen.spdmatrixrndcond(n, 1.0E3, ref fulla);
                    halfa = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (j>=i && isupper) || (j<=i && !isupper) )
                            {
                                halfa[i,j] = fulla[i,j];
                            }
                            else
                            {
                                halfa[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                        }
                    }
                    densetosparse(halfa, n, sa);
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    xori = new double[n];
                    s = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = hqrnd.hqrndnormal(rs);
                        bndl[i] = Double.NegativeInfinity;
                        bndu[i] = Double.PositiveInfinity;
                        x0[i] = hqrnd.hqrndnormal(rs);
                        if( origintype==0 )
                        {
                            xori[i] = 0;
                        }
                        else
                        {
                            xori[i] = hqrnd.hqrndnormal(rs);
                        }
                        if( scaletype==0 )
                        {
                            s[i] = 1;
                        }
                        else
                        {
                            s[i] = Math.Exp(hqrnd.hqrndnormal(rs));
                        }
                        j = hqrnd.hqrnduniformi(rs, 5);
                        if( j==0 )
                        {
                            bndl[i] = 0;
                            x0[i] = Math.Abs(x0[i]);
                        }
                        if( j==1 )
                        {
                            bndu[i] = 0;
                            x0[i] = -Math.Abs(x0[i]);
                        }
                        if( j==2 )
                        {
                            bndl[i] = hqrnd.hqrndnormal(rs);
                            bndu[i] = bndl[i];
                            x0[i] = bndl[i];
                        }
                        if( j==3 )
                        {
                            bndl[i] = -0.1;
                            bndu[i] = 0.1;
                            x0[i] = 0.2*hqrnd.hqrnduniformr(rs)-0.1;
                        }
                    }
                    
                    //
                    // Solve problem
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgoallmodern(state);
                    minqp.minqpsetlinearterm(state, b);
                    minqp.minqpsetstartingpoint(state, x0);
                    if( issparse )
                    {
                        minqp.minqpsetquadratictermsparse(state, sa, isupper);
                    }
                    else
                    {
                        minqp.minqpsetquadraticterm(state, halfa, isupper);
                    }
                    if( origintype!=0 )
                    {
                        minqp.minqpsetorigin(state, xori);
                    }
                    if( scaletype!=0 )
                    {
                        minqp.minqpsetscale(state, s);
                    }
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( rep.terminationtype<=0 )
                    {
                        return;
                    }
                    
                    //
                    // Test - calculate constrained gradient at solution,
                    // check its norm.
                    //
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g = b[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g = g+fulla[i,j]*(x1[j]-xori[j]);
                        }
                        if( (double)(x1[i])==(double)(bndl[i]) && (double)(g)>(double)(0) )
                        {
                            g = 0;
                        }
                        if( (double)(x1[i])==(double)(bndu[i]) && (double)(g)<(double)(0) )
                        {
                            g = 0;
                        }
                        gnorm = gnorm+math.sqr(g);
                        apserv.seterrorflag(ref wereerrors, (double)(x1[i])<(double)(bndl[i]));
                        apserv.seterrorflag(ref wereerrors, (double)(x1[i])>(double)(bndu[i]));
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref wereerrors, (double)(gnorm)>(double)(eps));
                }
            }
            
            //
            // Semidefinite test:
            // * N dimensions
            // * nonnegativity constraints
            // * A = [ 1 1 ... 1 1 ; 1 1 ... 1 1 ; .... ; 1 1 ... 1 1 ]
            // * algorithm randomly choose dense or sparse A, and for
            //   sparse matrix it randomly choose format.
            // * random B with normal entries
            // * initial point is random, feasible
            //
            eps = 1.0E-4;
            for(n=1; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    isupper = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                    issparse = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                    fulla = new double[n, n];
                    halfa = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            fulla[i,j] = 1.0;
                            if( (j>=i && isupper) || (j<=i && !isupper) )
                            {
                                halfa[i,j] = fulla[i,j];
                            }
                            else
                            {
                                halfa[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                        }
                    }
                    densetosparse(halfa, n, sa);
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = hqrnd.hqrndnormal(rs);
                        bndl[i] = 0.0;
                        bndu[i] = Double.PositiveInfinity;
                        x0[i] = hqrnd.hqrnduniformi(rs, 2);
                    }
                    
                    //
                    // Solve problem
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgosemidefinite(state);
                    minqp.minqpsetstartingpoint(state, x0);
                    minqp.minqpsetlinearterm(state, b);
                    if( issparse )
                    {
                        minqp.minqpsetquadratictermsparse(state, sa, isupper);
                    }
                    else
                    {
                        minqp.minqpsetquadraticterm(state, halfa, isupper);
                    }
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( rep.terminationtype<=0 )
                    {
                        return;
                    }
                    
                    //
                    // Test - calculate constrained gradient at solution,
                    // check its norm.
                    //
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g = b[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g = g+fulla[i,j]*x1[j];
                        }
                        if( (double)(x1[i])==(double)(bndl[i]) && (double)(g)>(double)(0) )
                        {
                            g = 0;
                        }
                        if( (double)(x1[i])==(double)(bndu[i]) && (double)(g)<(double)(0) )
                        {
                            g = 0;
                        }
                        gnorm = gnorm+math.sqr(g);
                        apserv.seterrorflag(ref wereerrors, (double)(x1[i])<(double)(bndl[i]));
                        apserv.seterrorflag(ref wereerrors, (double)(x1[i])>(double)(bndu[i]));
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref wereerrors, (double)(gnorm)>(double)(eps));
                }
            }
            
            //
            // Non-convex test:
            // * N dimensions, N>=2
            // * box constraints, x[i] in [-1,+1]
            // * A = A0-0.5*I, where A0 is SPD with unit norm and smallest
            //   singular value equal to 1.0E-3, I is identity matrix
            // * random B with normal entries
            // * initial point is random, feasible
            //
            // We perform two tests:
            // * unconstrained problem must be recognized as unbounded
            // * constrained problem can be successfully solved
            //
            // NOTE: it is important to have N>=2, because formula for A
            //       can be applied only to matrix with at least two
            //       singular values
            //
            eps = 1.0E-4;
            for(n=2; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    matgen.spdmatrixrndcond(n, 1.0E3, ref fulla);
                    for(i=0; i<=n-1; i++)
                    {
                        fulla[i,i] = fulla[i,i]-0.5;
                    }
                    isupper = (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5);
                    halfa = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( (j>=i && isupper) || (j<=i && !isupper) )
                            {
                                halfa[i,j] = fulla[i,j];
                            }
                            else
                            {
                                halfa[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                        }
                    }
                    densetosparse(halfa, n, sa);
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = hqrnd.hqrndnormal(rs);
                        bndl[i] = -1;
                        bndu[i] = 1;
                        x0[i] = 2*hqrnd.hqrnduniformr(rs)-1;
                    }
                    
                    //
                    // Solve problem:
                    // * without constraints we expect failure
                    // * with constraints algorithm must succeed
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgononconvex(state);
                    minqp.minqpsetstartingpoint(state, x0);
                    minqp.minqpsetlinearterm(state, b);
                    if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                    {
                        minqp.minqpsetquadraticterm(state, halfa, isupper);
                    }
                    else
                    {
                        minqp.minqpsetquadratictermsparse(state, sa, isupper);
                    }
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype!=-4);
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( rep.terminationtype<=0 )
                    {
                        return;
                    }
                    
                    //
                    // Test - calculate constrained gradient at solution,
                    // check its norm.
                    //
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += fulla[i,i_]*x1[i_];
                        }
                        g = v+b[i];
                        if( (double)(x1[i])==(double)(bndl[i]) && (double)(g)>(double)(0) )
                        {
                            g = 0;
                        }
                        if( (double)(x1[i])==(double)(bndu[i]) && (double)(g)<(double)(0) )
                        {
                            g = 0;
                        }
                        gnorm = gnorm+math.sqr(g);
                        apserv.seterrorflag(ref wereerrors, (double)(x1[i])<(double)(bndl[i]));
                        apserv.seterrorflag(ref wereerrors, (double)(x1[i])>(double)(bndu[i]));
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref wereerrors, (double)(gnorm)>(double)(eps));
                }
            }
            
            //
            // Linear (zero-quadratic) test:
            // * N dimensions, N>=1
            // * box constraints, x[i] in [-1,+1]
            // * A = 0
            // * random B with normal entries
            // * initial point is random, feasible
            //
            // We perform two tests:
            // * unconstrained problem must be recognized as unbounded
            // * constrained problem can be successfully solved
            //
            // NOTE: we may explicitly set zero A, or assume that by
            //       default it is zero. During test we will try both
            //       ways.
            //
            eps = 1.0E-4;
            for(n=1; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        do
                        {
                            b[i] = hqrnd.hqrndnormal(rs);
                        }
                        while( (double)(b[i])==(double)(0) );
                        bndl[i] = -1;
                        bndu[i] = 1;
                        x0[i] = 2*hqrnd.hqrnduniformr(rs)-1;
                    }
                    
                    //
                    // Solve problem:
                    // * without constraints we expect failure
                    // * with constraints algorithm must succeed
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgononconvex(state);
                    minqp.minqpsetlinearterm(state, b);
                    minqp.minqpsetstartingpoint(state, x0);
                    if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                    {
                        a = new double[n, n];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a[i,j] = 0;
                            }
                        }
                        minqp.minqpsetquadraticterm(state, a, true);
                    }
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype!=-4);
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( rep.terminationtype<=0 )
                    {
                        return;
                    }
                    
                    //
                    // Test - calculate constrained gradient at solution,
                    // check its norm.
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref wereerrors, (double)(b[i])>(double)(0) && (double)(x1[i])>(double)(bndl[i]));
                        apserv.seterrorflag(ref wereerrors, (double)(b[i])<(double)(0) && (double)(x1[i])<(double)(bndu[i]));
                    }
                }
            }
        }


        /*************************************************************************
        This function tests equality constrained quadratic programming algorithm.

        Returns True on errors.
        *************************************************************************/
        private static bool ecqptest()
        {
            bool result = new bool();
            int n = 0;
            int k = 0;
            double[,] a = new double[0,0];
            double[,] q = new double[0,0];
            double[,] c = new double[0,0];
            double[,] a2 = new double[0,0];
            double[] b = new double[0];
            double[] b2 = new double[0];
            double[] xstart = new double[0];
            double[] xstart2 = new double[0];
            double[] xend = new double[0];
            double[] xend2 = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] xd = new double[0];
            double[] xs = new double[0];
            double[] tmp = new double[0];
            double[] g = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] xorigin = new double[0];
            int[] ct = new int[0];
            double eps = 0;
            double theta = 0;
            double f0 = 0;
            double f1 = 0;
            minqp.minqpstate state = new minqp.minqpstate();
            minqp.minqpstate state2 = new minqp.minqpstate();
            minqp.minqpreport rep = new minqp.minqpreport();
            int i = 0;
            int j = 0;
            int pass = 0;
            int rk = 0;
            double v = 0;
            int aulits = 0;
            bool waserrors = new bool();
            int i_ = 0;

            waserrors = false;
            
            //
            // First test:
            // * N*N identity A
            // * K<N equality constraints Q*x = Q*x0, where Q is random
            //   orthogonal K*N matrix, x0 is some random vector
            // * x1 is some random vector such that Q*x1=0. It is always possible
            //   to find such x1, because K<N
            // * optimization problem has form 0.5*x'*A*x-(x1*A)*x
            // * exact solution must be equal to x0
            //
            eps = 1.0E-4;
            for(n=2; n<=6; n++)
            {
                for(k=1; k<=n-1; k++)
                {
                    
                    //
                    // Generate problem: A, b, CMatrix, x0, XStart
                    //
                    matgen.rmatrixrndorthogonal(n, ref q);
                    a = new double[n, n];
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 0;
                        }
                        a[i,i] = 1;
                    }
                    b = new double[n];
                    x0 = new double[n];
                    x1 = new double[n];
                    xstart = new double[n];
                    c = new double[k, n+1];
                    ct = new int[k];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 2*math.randomreal()-1;
                        x1[i] = x0[i];
                        xstart[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            c[i,i_] = q[i,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += q[i,i_]*x0[i_];
                        }
                        c[i,n] = v;
                        ct[i] = 0;
                        v = 2*math.randomreal()-1;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            x1[i_] = x1[i_] + v*q[i,i_];
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*x1[i_];
                        }
                        b[i] = -v;
                    }
                    
                    //
                    // Create optimizer, solve
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgoconvexlc(state);
                    minqp.minqpsetlinearterm(state, b);
                    minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                    minqp.minqpsetstartingpoint(state, xstart);
                    minqp.minqpsetlc(state, c, ct, k);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref xend, rep);
                    
                    //
                    // Compare with analytic solution
                    //
                    if( rep.terminationtype<=0 )
                    {
                        waserrors = true;
                        continue;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors || (double)(Math.Abs(xend[i]-x0[i]))>(double)(eps);
                    }
                }
            }
            
            //
            // Second test:
            // * N*N SPD A
            // * K<N equality constraints Q*x = Q*x0, where Q is random
            //   orthogonal K*N matrix, x0 is some random vector
            // * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
            //   where x1 is some random vector
            // * we check feasibility properties of the solution
            // * we do not know analytic form of the exact solution,
            //   but we know that projection of gradient into equality constrained
            //   subspace must be zero at the solution
            //
            eps = 1.0E-4;
            for(n=2; n<=6; n++)
            {
                for(k=1; k<=n-1; k++)
                {
                    
                    //
                    // Generate problem: A, b, CMatrix, x0, XStart
                    //
                    matgen.rmatrixrndorthogonal(n, ref q);
                    matgen.spdmatrixrndcond(n, Math.Pow(10.0, 3*math.randomreal()), ref a);
                    b = new double[n];
                    x0 = new double[n];
                    x1 = new double[n];
                    xstart = new double[n];
                    c = new double[k, n+1];
                    ct = new int[k];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 2*math.randomreal()-1;
                        x1[i] = 2*math.randomreal()-1;
                        xstart[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            c[i,i_] = q[i,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += q[i,i_]*x0[i_];
                        }
                        c[i,n] = v;
                        ct[i] = 0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*x1[i_];
                        }
                        b[i] = -v;
                    }
                    
                    //
                    // Create optimizer, solve
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgoconvexlc(state);
                    minqp.minqpsetlinearterm(state, b);
                    minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                    minqp.minqpsetstartingpoint(state, xstart);
                    minqp.minqpsetlc(state, c, ct, k);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref xend, rep);
                    
                    //
                    // Calculate gradient, check projection
                    //
                    if( rep.terminationtype<=0 )
                    {
                        waserrors = true;
                        continue;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += xend[i_]*c[i,i_];
                        }
                        waserrors = waserrors || (double)(Math.Abs(v-c[i,n]))>(double)(eps);
                    }
                    g = new double[n];
                    for(i_=0; i_<=n-1;i_++)
                    {
                        g[i_] = b[i_];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*xend[i_];
                        }
                        g[i] = g[i]+v;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += g[i_]*c[i,i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            g[i_] = g[i_] - v*c[i,i_];
                        }
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += g[i_]*g[i_];
                    }
                    waserrors = waserrors || (double)(Math.Sqrt(v))>(double)(eps);
                }
            }
            
            //
            // Boundary and linear equality constrained QP problem:
            // * N*N SPD A with moderate condtion number (up to 100)
            // * boundary constraints 0<=x[i]<=1
            // * K<N equality constraints C*x = C*x0, where Q is random
            //   K*N matrix, x0 is some random vector from the
            //   feasible hypercube (0<=x0[i]<=1)
            // * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
            //   where x1 is some random vector with -1<=x1[i]<=+1.
            //   (sometimes solution is in the inner area, sometimes at the boundary)
            // * every component of the initial point XStart is either 0 or 1
            //   (point is located at the vertices of the feasible hypercube)
            // 
            // Solution of such problem is calculated using two methods:
            // a) boundary and linearly constrained QP
            // b) augmented Lagrangian boundary constrained QP: we add explicit quadratic
            //    penalty to the problem; we also add Lagrangian terms and perform many
            //    subsequent iterations to find good estimates of the Lagrange multipliers.
            //
            // Sometimes augmented Largangian converges to slightly different point
            // (boundary constraints lead to extremely slow, non-smooth convergence of
            // the Lagrange multipliers). In order to correctly handle such situations
            // we compare function values instead of final points - and use relaxed criteria
            // to test for convergence.
            //
            // NOTE: sometimes we need as much as 300 Augmented Lagrangian iterations for
            //       method to converge.
            //
            eps = 5.0E-2;
            theta = 1.0E+4;
            aulits = 300;
            for(n=2; n<=6; n++)
            {
                for(k=1; k<=n-1; k++)
                {
                    
                    //
                    // Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
                    //
                    matgen.spdmatrixrndcond(n, Math.Pow(10.0, 2*math.randomreal()), ref a);
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    x1 = new double[n];
                    xstart = new double[n];
                    c = new double[k, n+1];
                    ct = new int[k];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = math.randomreal();
                        x1[i] = 2*math.randomreal()-1;
                        bndl[i] = 0.0;
                        bndu[i] = 1.0;
                        xstart[i] = math.randominteger(2);
                    }
                    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_]*x0[i_];
                        }
                        c[i,n] = v;
                        ct[i] = 0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*x1[i_];
                        }
                        b[i] = -v;
                    }
                    
                    //
                    // Create exact optimizer, solve
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgoconvexlc(state);
                    minqp.minqpsetlinearterm(state, b);
                    minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                    minqp.minqpsetstartingpoint(state, xstart);
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpsetlc(state, c, ct, k);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref xend, rep);
                    if( rep.terminationtype<=0 )
                    {
                        waserrors = true;
                        continue;
                    }
                    
                    //
                    // Solve problem using barrier functions (quadrative objective, boundary constraints,
                    // explicit penalty term added to the main quadratic matrix. Lagrangian terms improve
                    // solution quality):
                    // * A2 := A+C'*C
                    // * b2 := b-r'*C
                    // * b2 is iteratively updated using augmented Lagrangian update
                    // 
                    // NOTE: we may need many outer iterations to converge to the optimal values
                    //       of Lagrange multipliers. Convergence is slowed down by the presense
                    //       of boundary constraints, whose activation/deactivation slows down
                    //       process.
                    //
                    a2 = new double[n, n];
                    ablas.rmatrixcopy(n, n, a, 0, 0, ref a2, 0, 0);
                    ablas.rmatrixsyrk(n, k, theta, c, 0, 0, 2, 1.0, a2, 0, 0, true);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=i+1; j<=n-1; j++)
                        {
                            a2[j,i] = a2[i,j];
                        }
                    }
                    b2 = new double[n];
                    for(i_=0; i_<=n-1;i_++)
                    {
                        b2[i_] = b[i_];
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v = c[i,n]*theta;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            b2[i_] = b2[i_] - v*c[i,i_];
                        }
                    }
                    minqp.minqpcreate(n, state2);
                    setrandomalgoconvexlc(state);
                    minqp.minqpsetquadraticterm(state2, a2, (double)(math.randomreal())>(double)(0.5));
                    minqp.minqpsetstartingpoint(state2, xstart);
                    minqp.minqpsetbc(state2, bndl, bndu);
                    for(i=1; i<=aulits; i++)
                    {
                        
                        //
                        // Solve, update B2 according to augmented Lagrangian algorithm
                        //
                        minqp.minqpsetlinearterm(state2, b2);
                        minqp.minqpoptimize(state2);
                        minqp.minqpresults(state2, ref xend2, rep);
                        if( rep.terminationtype<=0 )
                        {
                            waserrors = true;
                            continue;
                        }
                        for(j=0; j<=k-1; j++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += c[j,i_]*xend2[i_];
                            }
                            v = theta*(v-c[j,n]);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                b2[i_] = b2[i_] + v*c[j,i_];
                            }
                        }
                    }
                    
                    //
                    // Calculate function value and XEnd and XEnd2
                    //
                    f0 = 0.0;
                    f1 = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            f0 = f0+0.5*xend[i]*a[i,j]*xend[j];
                            f1 = f1+0.5*xend2[i]*a[i,j]*xend2[j];
                        }
                        f0 = f0+xend[i]*b[i];
                        f1 = f1+xend2[i]*b[i];
                    }
                    
                    //
                    // Check feasibility properties and compare
                    //
                    waserrors = waserrors || (double)(Math.Abs(f0-f1))>(double)(eps);
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += xend[i_]*c[i,i_];
                        }
                        waserrors = waserrors || (double)(Math.Abs(v-c[i,n]))>(double)(1.0E6*math.machineepsilon);
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors || (double)(xend[i])<(double)(0);
                        waserrors = waserrors || (double)(xend[i])>(double)(1);
                    }
                    if( waserrors )
                    {
                        waserrors = waserrors;
                    }
                }
            }
            
            //
            // Boundary and linear equality constrained QP problem,
            // test for correct handling of non-zero XOrigin:
            // * N*N SPD A with moderate condtion number (up to 100)
            // * boundary constraints 0<=x[i]<=1
            // * K<N equality constraints Q*x = Q*x0, where Q is random
            //   orthogonal K*N matrix, x0 is some random vector from the
            //   inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
            // * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
            //   where b is some random vector with -1<=b[i]<=+1.
            //   (sometimes solution is in the inner area, sometimes at the boundary)
            // * every component of the initial point XStart is random from [-1,1]
            // 
            // Solution of such problem is calculated using two methods:
            // a) QP with SetOrigin() call
            // b) QP with XOrigin explicitly added to the quadratic function,
            //
            // Both methods should give same results; any significant difference is
            // evidence of some error in the QP implementation.
            //
            eps = 1.0E-4;
            for(n=2; n<=6; n++)
            {
                for(k=1; k<=n-1; k++)
                {
                    
                    //
                    // Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
                    // Additionally, we compute modified b: b2 = b-xorigin'*A
                    //
                    matgen.rmatrixrndorthogonal(n, ref q);
                    matgen.spdmatrixrndcond(n, Math.Pow(10.0, 2*math.randomreal()), ref a);
                    b = new double[n];
                    b2 = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    x1 = new double[n];
                    xorigin = new double[n];
                    xstart = new double[n];
                    c = new double[k, n+1];
                    ct = new int[k];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 0.1+0.8*math.randomreal();
                        b[i] = 2*math.randomreal()-1;
                        bndl[i] = 0.0;
                        bndu[i] = 1.0;
                        xstart[i] = math.randominteger(2);
                        xorigin[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            c[i,i_] = q[i,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += q[i,i_]*x0[i_];
                        }
                        c[i,n] = v;
                        ct[i] = 0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*xorigin[i_];
                        }
                        b2[i] = b[i]-v;
                    }
                    
                    //
                    // Solve with SetOrigin() call
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgoconvexlc(state);
                    minqp.minqpsetlinearterm(state, b);
                    minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                    minqp.minqpsetstartingpoint(state, xstart);
                    minqp.minqpsetorigin(state, xorigin);
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpsetlc(state, c, ct, k);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref xend, rep);
                    if( rep.terminationtype<=0 )
                    {
                        waserrors = true;
                        continue;
                    }
                    
                    //
                    // Solve problem using explicit origin
                    //
                    minqp.minqpcreate(n, state2);
                    setrandomalgoconvexlc(state);
                    minqp.minqpsetlinearterm(state2, b2);
                    minqp.minqpsetquadraticterm(state2, a, (double)(math.randomreal())>(double)(0.5));
                    minqp.minqpsetstartingpoint(state2, xstart);
                    minqp.minqpsetbc(state2, bndl, bndu);
                    minqp.minqpsetlc(state2, c, ct, k);
                    minqp.minqpoptimize(state2);
                    minqp.minqpresults(state2, ref xend2, rep);
                    if( rep.terminationtype<=0 )
                    {
                        waserrors = true;
                        continue;
                    }
                    
                    //
                    // Check feasibility properties and compare solutions
                    //
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += xend[i_]*c[i,i_];
                        }
                        waserrors = waserrors || (double)(Math.Abs(v-c[i,n]))>(double)(eps);
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        waserrors = waserrors || (double)(Math.Abs(xend[i]-xend2[i]))>(double)(eps);
                        waserrors = waserrors || (double)(xend[i])<(double)(0);
                        waserrors = waserrors || (double)(xend[i])>(double)(1);
                    }
                    if( waserrors )
                    {
                        waserrors = waserrors;
                    }
                }
            }
            
            //
            // Boundary and linear equality constrained QP problem with excessive
            // equality constraints:
            // * N*N SPD A with moderate condtion number (up to 100)
            // * boundary constraints 0<=x[i]<=1
            // * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix,
            //   x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
            // * optimization problem has form 0.5*x'*A*x-b*x,
            //   where b is some random vector
            // * because constraints are excessive, the main problem is to find
            //   feasible point; the only existing feasible point is solution,
            //   so we have to check only feasibility
            //
            eps = 1.0E-4;
            for(n=1; n<=6; n++)
            {
                
                //
                // Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
                //
                k = 2*n;
                matgen.spdmatrixrndcond(n, Math.Pow(10.0, 3*math.randomreal()), ref a);
                b = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                x0 = new double[n];
                x1 = new double[n];
                xstart = new double[n];
                c = new double[k, n+1];
                ct = new int[k];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 0.1+0.8*math.randomreal();
                    x1[i] = 2*math.randomreal()-1;
                    bndl[i] = 0.0;
                    bndu[i] = 1.0;
                    xstart[i] = math.randominteger(2);
                }
                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_]*x0[i_];
                    }
                    c[i,n] = v;
                    ct[i] = 0;
                }
                for(i=0; i<=n-1; i++)
                {
                    b[i] = 2*math.randomreal()-1;
                }
                
                //
                // Create optimizer, solve
                //
                minqp.minqpcreate(n, state);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                minqp.minqpsetstartingpoint(state, xstart);
                minqp.minqpsetbc(state, bndl, bndu);
                minqp.minqpsetlc(state, c, ct, k);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend, rep);
                
                //
                // Check feasibility properties of the solution
                //
                if( rep.terminationtype<=0 )
                {
                    waserrors = true;
                    continue;
                }
                for(i=0; i<=k-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += xend[i_]*c[i,i_];
                    }
                    waserrors = waserrors || (double)(Math.Abs(v-c[i,n]))>(double)(eps);
                }
            }
            
            //
            // Boundary and linear equality constrained QP problem,
            // test checks that different starting points yield same final point:
            // * random N from [1..6], random K from [1..2*N]
            // * N*N SPD A with moderate condtion number (up to 100)
            // * boundary constraints 0<=x[i]<=1
            // * K<2*N random linear equality constraints C*x = C*x0,
            //   where x0 is some random vector from the inner area of the
            //   feasible hypercube (0.1<=x0[i]<=0.9)
            // * optimization problem has form 0.5*x'*A*x+b*x,
            //   where b is some random vector with -5<=b[i]<=+5
            // * every component of the initial point XStart is random from [-2,+2]
            // * we perform two starts from random different XStart and compare values
            //   of the target function (although final points may be slightly different,
            //   function values should match each other)
            // 
            // Both points should give same results; any significant difference is
            // evidence of some error in the QP implementation.
            //
            eps = 1.0E-4;
            for(pass=1; pass<=50; pass++)
            {
                
                //
                // Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, x1, XStart.
                //
                n = math.randominteger(5)+2;
                k = math.randominteger(2*n)+1;
                matgen.spdmatrixrndcond(n, Math.Pow(10.0, 2*math.randomreal()), ref a);
                b = new double[n];
                b2 = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                x0 = new double[n];
                xstart = new double[n];
                xstart2 = new double[n];
                c = new double[k, n+1];
                ct = new int[k];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 0.1+0.8*math.randomreal();
                    b[i] = 2*math.randomreal()-1;
                    bndl[i] = 0.0;
                    bndu[i] = 1.0;
                    xstart[i] = 4*math.randomreal()-2;
                    xstart2[i] = 4*math.randomreal()-2;
                }
                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_]*x0[i_];
                    }
                    c[i,n] = v;
                    ct[i] = 0;
                }
                
                //
                // Solve with XStart
                //
                minqp.minqpcreate(n, state);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                minqp.minqpsetstartingpoint(state, xstart);
                minqp.minqpsetbc(state, bndl, bndu);
                minqp.minqpsetlc(state, c, ct, k);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend, rep);
                if( rep.terminationtype<=0 )
                {
                    waserrors = true;
                    continue;
                }
                
                //
                // Solve with XStart2
                //
                minqp.minqpsetstartingpoint(state, xstart2);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend2, rep);
                if( rep.terminationtype<=0 )
                {
                    waserrors = true;
                    continue;
                }
                
                //
                // Calculate function value and XEnd and XEnd2, compare solutions
                //
                f0 = 0.0;
                f1 = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        f0 = f0+0.5*xend[i]*a[i,j]*xend[j];
                        f1 = f1+0.5*xend2[i]*a[i,j]*xend2[j];
                    }
                    f0 = f0+xend[i]*b[i];
                    f1 = f1+xend2[i]*b[i];
                }
                apserv.seterrorflag(ref waserrors, (double)(Math.Abs(f0-f1))>(double)(eps));
            }
            
            //
            // Test ability to correctly handle situation where algorithm
            // either:
            // (1) starts from point with gradient whose projection to
            //     active set is almost zero (but not exactly zero)
            // (2) performs step to such point
            //
            // In order to do this we solve problem
            // * min 0.5*x'*x - (x0+c)'*x
            // * subject to c'*x = c'*x0, with c and x0 random unit vectors
            // * with initial point xs = x0+r*xd, where r is scalar,
            //   xd is vector which is orthogonal to c.
            // * we try different r=power(2,-rk) for rk=0...70. The idea
            //   is that as we approach closer and closer to x0, which is
            //   a solution of the constrained problem, constrained gradient
            //   of the function rapidly vanishes.
            //
            eps = 1.0E-6;
            for(rk=0; rk<=70; rk++)
            {
                n = 10;
                
                //
                // Generate x0, c, xd, xs, generate unit A
                //
                apserv.randomunit(n, ref x0);
                apserv.randomunit(n, ref xd);
                apserv.randomunit(n, ref tmp);
                c = new double[1, n+1];
                ct = new int[1];
                xs = new double[n];
                b = new double[n];
                c[0,n] = 0;
                ct[0] = 0;
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    c[0,i] = tmp[i];
                    c[0,n] = c[0,n]+tmp[i]*x0[i];
                    b[i] = -(x0[i]+tmp[i]);
                    v = v+tmp[i]*xd[i];
                }
                for(i=0; i<=n-1; i++)
                {
                    xd[i] = xd[i]-v*tmp[i];
                    xs[i] = x0[i]+xd[i]*Math.Pow(2, -rk);
                }
                a = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a[i,j] = 0;
                    }
                    a[i,i] = 1;
                }
                
                //
                // Create and solve optimization problem
                //
                minqp.minqpcreate(n, state);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetquadraticterm(state, a, true);
                minqp.minqpsetstartingpoint(state, xs);
                minqp.minqpsetlc(state, c, ct, 1);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend, rep);
                if( apserv.seterrorflag(ref waserrors, rep.terminationtype<=0) )
                {
                    continue;
                }
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref waserrors, (double)(Math.Abs(xend[i]-x0[i]))>(double)(eps));
                }
                v = -c[0,n];
                for(i=0; i<=n-1; i++)
                {
                    v = v+xend[i]*c[0,i];
                }
                apserv.seterrorflag(ref waserrors, (double)(Math.Abs(v))>(double)(1.0E5*math.machineepsilon));
            }
            result = waserrors;
            return result;
        }


        /*************************************************************************
        This function tests inequality constrained quadratic programming algorithm.

        On failure sets Err to True; on success leaves it unchanged.
        *************************************************************************/
        private static void icqptest(ref bool err)
        {
            int n = 0;
            int k = 0;
            double[,] a = new double[0,0];
            double[,] q = new double[0,0];
            double[,] c = new double[0,0];
            double[,] a2 = new double[0,0];
            double[,] t2 = new double[0,0];
            double[,] t3 = new double[0,0];
            double[,] ce = new double[0,0];
            double[] xs0 = new double[0];
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] tmp = new double[0];
            double[] x = new double[0];
            double[] xstart = new double[0];
            double[] xstart2 = new double[0];
            double[] xend = new double[0];
            double[] xend2 = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] b = new double[0];
            double[] b2 = new double[0];
            double[] g = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] xorigin = new double[0];
            double[] tmp0 = new double[0];
            double[] tmp1 = new double[0];
            double[] da = new double[0];
            int[] ct = new int[0];
            bool[] nonnegative = new bool[0];
            double eps = 0;
            minqp.minqpstate state = new minqp.minqpstate();
            minqp.minqpstate state2 = new minqp.minqpstate();
            minqp.minqpreport rep = new minqp.minqpreport();
            minqp.minqpreport rep2 = new minqp.minqpreport();
            int i = 0;
            int j = 0;
            int pass = 0;
            double v = 0;
            double vv = 0;
            double f0 = 0;
            double f1 = 0;
            double tolconstr = 0;
            int bscale = 0;
            int akind = 0;
            int shiftkind = 0;
            int ccnt = 0;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            snnls.snnlssolver nnls = new snnls.snnlssolver();
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            
            //
            // Inequality constrained problem:
            // * N*N diagonal A
            // * one inequality constraint q'*x>=0, where q is random unit vector
            // * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
            //   where x1 is some random vector
            // * either:
            //   a) x1 is feasible => we must stop at x1
            //   b) x1 is infeasible => we must stop at the boundary q'*x=0 and
            //      projection of gradient onto q*x=0 must be zero
            //
            // NOTE: we make several passes because some specific kind of errors is rarely
            //       caught by this test, so we need several repetitions.
            //
            eps = 1.0E-4;
            for(n=2; n<=6; n++)
            {
                for(pass=0; pass<=4; pass++)
                {
                    
                    //
                    // Generate problem: A, b, CMatrix, x0, XStart
                    //
                    matgen.spdmatrixrndcond(n, Math.Pow(10.0, 3*math.randomreal()), ref a);
                    b = new double[n];
                    x1 = new double[n];
                    xstart = new double[n];
                    c = new double[1, n+1];
                    ct = new int[1];
                    for(i=0; i<=n-1; i++)
                    {
                        x1[i] = 2*math.randomreal()-1;
                        xstart[i] = 2*math.randomreal()-1;
                    }
                    do
                    {
                        v = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            c[0,i] = 2*math.randomreal()-1;
                            v = v+math.sqr(c[0,i]);
                        }
                        v = Math.Sqrt(v);
                    }
                    while( (double)(v)==(double)(0) );
                    for(i=0; i<=n-1; i++)
                    {
                        c[0,i] = c[0,i]/v;
                    }
                    c[0,n] = 0;
                    ct[0] = 1;
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*x1[i_];
                        }
                        b[i] = -v;
                    }
                    
                    //
                    // Create optimizer, solve
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgoconvexlc(state);
                    minqp.minqpsetlinearterm(state, b);
                    minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                    minqp.minqpsetstartingpoint(state, xstart);
                    minqp.minqpsetlc(state, c, ct, 1);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref xend, rep);
                    
                    //
                    // Test
                    //
                    if( rep.terminationtype<=0 )
                    {
                        apserv.seterrorflag(ref err, true);
                        continue;
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += x1[i_]*c[0,i_];
                    }
                    if( (double)(v)>=(double)(0) )
                    {
                        
                        //
                        // X1 is feasible
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref err, (double)(Math.Abs(xend[i]-x1[i]))>(double)(eps));
                        }
                    }
                    else
                    {
                        
                        //
                        // X1 is infeasible:
                        // * XEnd must be approximately feasible
                        // * gradient projection onto c'*x=0 must be zero
                        //
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += xend[i_]*c[0,i_];
                        }
                        apserv.seterrorflag(ref err, (double)(v)<(double)(-eps));
                        g = new double[n];
                        for(i_=0; i_<=n-1;i_++)
                        {
                            g[i_] = b[i_];
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*xend[i_];
                            }
                            g[i] = g[i]+v;
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += g[i_]*c[0,i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            g[i_] = g[i_] - v*c[0,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += g[i_]*g[i_];
                        }
                        apserv.seterrorflag(ref err, (double)(Math.Sqrt(v))>(double)(eps));
                    }
                }
            }
            
            //
            // Boundary and linear equality/inequality constrained QP problem,
            // test for correct handling of non-zero XOrigin:
            // * N*N SPD A with moderate condtion number (up to 100)
            // * boundary constraints 0<=x[i]<=1
            // * K<N linear equality/inequality constraints Q*x = Q*x0, where
            //   Q is random orthogonal K*N matrix, x0 is some random vector from the
            //   inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
            // * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
            //   where b is some random vector with -1<=b[i]<=+1.
            //   (sometimes solution is in the inner area, sometimes at the boundary)
            // * every component of the initial point XStart is random from [-1,1]
            // 
            // Solution of such problem is calculated using two methods:
            // a) QP with SetOrigin() call
            // b) QP with XOrigin explicitly added to the quadratic function,
            //
            // Both methods should give same results; any significant difference is
            // evidence of some error in the QP implementation.
            //
            eps = 1.0E-4;
            for(n=2; n<=6; n++)
            {
                for(k=1; k<=n-1; k++)
                {
                    
                    //
                    // Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
                    // Additionally, we compute modified b: b2 = b-xorigin'*A
                    //
                    matgen.rmatrixrndorthogonal(n, ref q);
                    matgen.spdmatrixrndcond(n, Math.Pow(10.0, 2*math.randomreal()), ref a);
                    b = new double[n];
                    b2 = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    x1 = new double[n];
                    xorigin = new double[n];
                    xstart = new double[n];
                    c = new double[k, n+1];
                    ct = new int[k];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 0.1+0.8*math.randomreal();
                        b[i] = 2*math.randomreal()-1;
                        bndl[i] = 0.0;
                        bndu[i] = 1.0;
                        xstart[i] = math.randominteger(2);
                        xorigin[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            c[i,i_] = q[i,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += q[i,i_]*x0[i_];
                        }
                        c[i,n] = v;
                        ct[i] = math.randominteger(3)-1;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += a[i,i_]*xorigin[i_];
                        }
                        b2[i] = b[i]-v;
                    }
                    
                    //
                    // Solve with SetOrigin() call
                    //
                    minqp.minqpcreate(n, state);
                    setrandomalgoconvexlc(state);
                    minqp.minqpsetlinearterm(state, b);
                    minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                    minqp.minqpsetstartingpoint(state, xstart);
                    minqp.minqpsetorigin(state, xorigin);
                    minqp.minqpsetbc(state, bndl, bndu);
                    minqp.minqpsetlc(state, c, ct, k);
                    minqp.minqpoptimize(state);
                    minqp.minqpresults(state, ref xend, rep);
                    if( rep.terminationtype<=0 )
                    {
                        apserv.seterrorflag(ref err, true);
                        continue;
                    }
                    
                    //
                    // Solve problem using explicit origin
                    //
                    minqp.minqpcreate(n, state2);
                    setrandomalgoconvexlc(state);
                    minqp.minqpsetlinearterm(state2, b2);
                    minqp.minqpsetquadraticterm(state2, a, (double)(math.randomreal())>(double)(0.5));
                    minqp.minqpsetstartingpoint(state2, xstart);
                    minqp.minqpsetbc(state2, bndl, bndu);
                    minqp.minqpsetlc(state2, c, ct, k);
                    minqp.minqpoptimize(state2);
                    minqp.minqpresults(state2, ref xend2, rep);
                    if( rep.terminationtype<=0 )
                    {
                        apserv.seterrorflag(ref err, true);
                        continue;
                    }
                    
                    //
                    // Calculate function value and XEnd and XEnd2
                    //
                    f0 = 0.0;
                    f1 = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            f0 = f0+0.5*(xend[i]-xorigin[i])*a[i,j]*(xend[j]-xorigin[j]);
                            f1 = f1+0.5*(xend2[i]-xorigin[i])*a[i,j]*(xend2[j]-xorigin[j]);
                        }
                        f0 = f0+(xend[i]-xorigin[i])*b[i];
                        f1 = f1+(xend2[i]-xorigin[i])*b[i];
                    }
                    
                    //
                    // Check feasibility properties and compare solutions
                    //
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += xend[i_]*c[i,i_];
                        }
                        if( ct[i]==0 )
                        {
                            apserv.seterrorflag(ref err, (double)(Math.Abs(v-c[i,n]))>(double)(eps));
                        }
                        if( ct[i]>0 )
                        {
                            apserv.seterrorflag(ref err, (double)(v)<(double)(c[i,n]-eps));
                        }
                        if( ct[i]<0 )
                        {
                            apserv.seterrorflag(ref err, (double)(v)>(double)(c[i,n]+eps));
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref err, (double)(Math.Abs(xend[i]-xend2[i]))>(double)(eps));
                        apserv.seterrorflag(ref err, (double)(xend[i])<(double)(0));
                        apserv.seterrorflag(ref err, (double)(xend[i])>(double)(1));
                    }
                }
            }
            
            //
            // Boundary constraints vs linear ones:
            // * N*N SPD A
            // * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
            //   where x1 is some random vector from [-1,+1]
            // * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
            //   with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
            // * initial point xstart is from [-1,+2]
            // * we solve two related QP problems:
            //   a) one with constraints posed as boundary ones
            //   b) another one with same constraints posed as general linear ones
            // both problems must have same solution.
            // Here we test that boundary constrained and linear inequality constrained
            // solvers give same results.
            //
            eps = 1.0E-4;
            for(n=1; n<=6; n++)
            {
                
                //
                // Generate problem: A, b, x0, XStart, C, CT
                //
                matgen.spdmatrixrndcond(n, Math.Pow(10.0, 3*math.randomreal()), ref a);
                b = new double[n];
                x1 = new double[n];
                xstart = new double[n];
                c = new double[2*n, n+1];
                ct = new int[2*n];
                bndl = new double[n];
                bndu = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x1[i] = 2*math.randomreal()-1;
                    xstart[i] = 3*math.randomreal()-1;
                    bndl[i] = -(0.1+0.9*math.randomreal());
                    bndu[i] = 0.1+0.9*math.randomreal();
                    for(j=0; j<=n-1; j++)
                    {
                        c[2*i+0,j] = 0;
                        c[2*i+1,j] = 0;
                    }
                    c[2*i+0,i] = 1;
                    c[2*i+0,n] = bndl[i];
                    ct[2*i+0] = 1;
                    c[2*i+1,i] = 1;
                    c[2*i+1,n] = bndu[i];
                    ct[2*i+1] = -1;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += a[i,i_]*x1[i_];
                    }
                    b[i] = -v;
                }
                
                //
                // Solve linear inequality constrained problem
                //
                minqp.minqpcreate(n, state);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                minqp.minqpsetstartingpoint(state, xstart);
                minqp.minqpsetlc(state, c, ct, 2*n);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend, rep);
                
                //
                // Solve boundary constrained problem
                //
                minqp.minqpcreate(n, state2);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlinearterm(state2, b);
                minqp.minqpsetquadraticterm(state2, a, (double)(math.randomreal())>(double)(0.5));
                minqp.minqpsetstartingpoint(state2, xstart);
                minqp.minqpsetbc(state2, bndl, bndu);
                minqp.minqpoptimize(state2);
                minqp.minqpresults(state2, ref xend2, rep2);
                
                //
                // Calculate gradient, check projection
                //
                if( rep.terminationtype<=0 || rep2.terminationtype<=0 )
                {
                    apserv.seterrorflag(ref err, true);
                    continue;
                }
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref err, (double)(xend[i])<(double)(bndl[i]-eps));
                    apserv.seterrorflag(ref err, (double)(xend[i])>(double)(bndu[i]+eps));
                    apserv.seterrorflag(ref err, (double)(Math.Abs(xend[i]-xend2[i]))>(double)(eps));
                }
            }
            
            //
            // Boundary 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(n=1; n<=5; n++)
            {
                
                //
                // Generate X, BL, BU.
                //
                a = new double[n, n];
                b = new double[n];
                xstart = 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++)
                {
                    xstart[i] = math.randomreal();
                    x0[i] = 3*math.randomreal()-1;
                    b[i] = -x0[i];
                    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;
                }
                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;
                        }
                    }
                }
                
                //
                // Create and optimize
                //
                minqp.minqpcreate(n, state);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlc(state, c, ct, 2*n);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                minqp.minqpsetstartingpoint(state, xstart);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend, rep);
                if( rep.terminationtype<=0 )
                {
                    apserv.seterrorflag(ref err, true);
                    continue;
                }
                
                //
                // * compare solution with analytic one
                // * check feasibility
                //
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref err, (double)(Math.Abs(xend[i]-apserv.boundval(x0[i], 0.0, 1.0)))>(double)(0.05));
                    apserv.seterrorflag(ref err, (double)(xend[i])<(double)(0.0-1.0E-6));
                    apserv.seterrorflag(ref err, (double)(xend[i])>(double)(1.0+1.0E-6));
                }
            }
            
            //
            // Boundary and linear equality/inequality constrained QP problem with
            // excessive constraints:
            // * N*N SPD A with moderate condtion number (up to 100)
            // * boundary constraints 0<=x[i]<=1
            // * K=2*N equality/inequality constraints Q*x = Q*x0, where Q is random matrix,
            //   x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
            // * optimization problem has form 0.5*x'*A*x-b*x,
            //   where b is some random vector
            // * because constraints are excessive, the main problem is to find
            //   feasible point; usually, the only existing feasible point is solution,
            //   so we have to check only feasibility
            //
            eps = 1.0E-4;
            for(n=1; n<=6; n++)
            {
                
                //
                // Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
                //
                k = 2*n;
                matgen.spdmatrixrndcond(n, Math.Pow(10.0, 3*math.randomreal()), ref a);
                b = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                x0 = new double[n];
                x1 = new double[n];
                xstart = new double[n];
                c = new double[k, n+1];
                ct = new int[k];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 0.1+0.8*math.randomreal();
                    x1[i] = 2*math.randomreal()-1;
                    bndl[i] = 0.0;
                    bndu[i] = 1.0;
                    xstart[i] = math.randominteger(2);
                }
                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_]*x0[i_];
                    }
                    ct[i] = math.randominteger(3)-1;
                    if( ct[i]==0 )
                    {
                        c[i,n] = v;
                    }
                    if( ct[i]>0 )
                    {
                        c[i,n] = v-1.0E-3;
                    }
                    if( ct[i]<0 )
                    {
                        c[i,n] = v+1.0E-3;
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    b[i] = 2*math.randomreal()-1;
                }
                
                //
                // Create optimizer, solve
                //
                minqp.minqpcreate(n, state);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                minqp.minqpsetstartingpoint(state, xstart);
                minqp.minqpsetbc(state, bndl, bndu);
                minqp.minqpsetlc(state, c, ct, k);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend, rep);
                
                //
                // Check feasibility properties of the solution
                //
                if( rep.terminationtype<=0 )
                {
                    apserv.seterrorflag(ref err, true);
                    continue;
                }
                for(i=0; i<=k-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += xend[i_]*c[i,i_];
                    }
                    if( ct[i]==0 )
                    {
                        apserv.seterrorflag(ref err, (double)(Math.Abs(v-c[i,n]))>(double)(eps));
                    }
                    if( ct[i]>0 )
                    {
                        apserv.seterrorflag(ref err, (double)(v)<(double)(c[i,n]-eps));
                    }
                    if( ct[i]<0 )
                    {
                        apserv.seterrorflag(ref err, (double)(v)>(double)(c[i,n]+eps));
                    }
                }
            }
            
            //
            // General inequality constrained problem:
            // * N*N SPD diagonal A with moderate condtion number
            // * no boundary constraints
            // * K=N inequality constraints C*x >= C*x0, where C is N*N well conditioned
            //   matrix, x0 is some random vector [-1,+1]
            // * optimization problem has form 0.5*x'*A*x-b'*x,
            //   where b is random vector from [-1,+1]
            // * using duality, we can obtain solution of QP problem as follows:
            //   a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
            //      is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
            //   b) after we got dual solution ys, we calculate primal solution
            //      xs = inv(A)*(C'*ys-b)
            //
            eps = 1.0E-3;
            for(n=1; n<=6; n++)
            {
                
                //
                // Generate problem
                //
                da = new double[n];
                a = new double[n, n];
                matgen.rmatrixrndcond(n, Math.Pow(10.0, 2*math.randomreal()), ref t2);
                b = new double[n];
                x0 = new double[n];
                xstart = new double[n];
                c = new double[n, n+1];
                ct = new int[n];
                for(i=0; i<=n-1; i++)
                {
                    da[i] = Math.Exp(8*math.randomreal()-4);
                    for(j=0; j<=n-1; j++)
                    {
                        a[i,j] = 0;
                    }
                    a[i,i] = da[i];
                }
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 2*math.randomreal()-1;
                    b[i] = 2*math.randomreal()-1;
                    xstart[i] = 2*math.randomreal()-1;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(i_=0; i_<=n-1;i_++)
                    {
                        c[i,i_] = t2[i,i_];
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += c[i,i_]*x0[i_];
                    }
                    c[i,n] = v;
                    ct[i] = 1;
                }
                
                //
                // Solve primal problem, check feasibility
                //
                minqp.minqpcreate(n, state);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                minqp.minqpsetstartingpoint(state, xstart);
                minqp.minqpsetlc(state, c, ct, n);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend, rep);
                if( rep.terminationtype<=0 )
                {
                    apserv.seterrorflag(ref err, true);
                    continue;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += xend[i_]*c[i,i_];
                    }
                    apserv.seterrorflag(ref err, (double)(v)<(double)(c[i,n]-eps));
                }
                
                //
                // Generate dual problem:
                // * A2 stores new quadratic term
                // * B2 stores new linear term
                // * BndL/BndU store boundary constraints
                //
                t3 = new double[n, n];
                a2 = new double[n, n];
                ablas.rmatrixtranspose(n, n, c, 0, 0, t3, 0, 0);
                for(i=0; i<=n-1; i++)
                {
                    v = 1/Math.Sqrt(da[i]);
                    for(i_=0; i_<=n-1;i_++)
                    {
                        t3[i,i_] = v*t3[i,i_];
                    }
                }
                ablas.rmatrixsyrk(n, n, 1.0, t3, 0, 0, 2, 0.0, a2, 0, 0, true);
                tmp0 = new double[n];
                for(i_=0; i_<=n-1;i_++)
                {
                    tmp0[i_] = b[i_];
                }
                for(i=0; i<=n-1; i++)
                {
                    tmp0[i] = tmp0[i]/da[i];
                }
                b2 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += c[i,i_]*tmp0[i_];
                    }
                    b2[i] = -(v+c[i,n]);
                }
                bndl = new double[n];
                bndu = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    bndl[i] = 0.0;
                    bndu[i] = Double.PositiveInfinity;
                }
                minqp.minqpcreate(n, state2);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlinearterm(state2, b2);
                minqp.minqpsetquadraticterm(state2, a2, true);
                minqp.minqpsetbc(state2, bndl, bndu);
                minqp.minqpoptimize(state2);
                minqp.minqpresults(state2, ref xend2, rep2);
                if( rep2.terminationtype<=0 )
                {
                    apserv.seterrorflag(ref err, true);
                    continue;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += c[i_,i]*xend2[i_];
                    }
                    tmp0[i] = v-b[i];
                }
                for(i=0; i<=n-1; i++)
                {
                    tmp0[i] = tmp0[i]/da[i];
                }
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref err, (double)(Math.Abs(tmp0[i]-xend[i]))>(double)(eps*Math.Max(Math.Abs(tmp0[i]), 1.0)));
                }
            }
            
            //
            // Boundary and linear equality/inequality constrained QP problem,
            // test checks that different starting points yield same final point:
            // * random N from [1..6], random K from [1..2*N]
            // * N*N SPD A with moderate condtion number (up to 100)
            // * boundary constraints 0<=x[i]<=1
            // * K<2*N linear inequality constraints Q*x <= Q*x0, where
            //   Q is random K*N matrix, x0 is some random vector from the
            //   inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
            // * optimization problem has form 0.5*x'*A*x+b*x,
            //   where b is some random vector with -5<=b[i]<=+5
            // * every component of the initial point XStart is random from [-2,+2]
            // * we perform two starts from random different XStart and compare values
            //   of the target function (although final points may be slightly different,
            //   function values should match each other)
            //
            eps = 1.0E-4;
            for(pass=1; pass<=50; pass++)
            {
                
                //
                // Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, x1, XStart.
                //
                n = math.randominteger(5)+2;
                k = math.randominteger(2*n)+1;
                matgen.spdmatrixrndcond(n, Math.Pow(10.0, 2*math.randomreal()), ref a);
                b = new double[n];
                b2 = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                x0 = new double[n];
                xstart = new double[n];
                xstart2 = new double[n];
                c = new double[k, n+1];
                ct = new int[k];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 0.1+0.8*math.randomreal();
                    b[i] = 2*math.randomreal()-1;
                    bndl[i] = 0.0;
                    bndu[i] = 1.0;
                    xstart[i] = 4*math.randomreal()-2;
                    xstart2[i] = 4*math.randomreal()-2;
                }
                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_]*x0[i_];
                    }
                    c[i,n] = v;
                    ct[i] = math.randominteger(3)-1;
                }
                
                //
                // Solve with XStart
                //
                minqp.minqpcreate(n, state);
                setrandomalgoconvexlc(state);
                minqp.minqpsetlinearterm(state, b);
                minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                minqp.minqpsetstartingpoint(state, xstart);
                minqp.minqpsetbc(state, bndl, bndu);
                minqp.minqpsetlc(state, c, ct, k);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend, rep);
                if( rep.terminationtype<=0 )
                {
                    apserv.seterrorflag(ref err, true);
                    continue;
                }
                
                //
                // Solve with XStart2
                //
                minqp.minqpsetstartingpoint(state, xstart2);
                minqp.minqpoptimize(state);
                minqp.minqpresults(state, ref xend2, rep);
                if( rep.terminationtype<=0 )
                {
                    apserv.seterrorflag(ref err, true);
                    continue;
                }
                
                //
                // Calculate function value and XEnd and XEnd2, compare solutions
                //
                f0 = 0.0;
                f1 = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        f0 = f0+0.5*xend[i]*a[i,j]*xend[j];
                        f1 = f1+0.5*xend2[i]*a[i,j]*xend2[j];
                    }
                    f0 = f0+xend[i]*b[i];
                    f1 = f1+xend2[i]*b[i];
                }
                apserv.seterrorflag(ref err, (double)(Math.Abs(f0-f1))>(double)(eps));
            }
            
            //
            // Convex/nonconvex optimization problem with excessive
            // (degenerate constraints):
            //
            // * N=2..5
            // * f = 0.5*x'*A*x+b'*x
            // * b has normally distributed entries with scale 10^BScale
            // * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank
            // * box constraints: x[i] in [-1,+1]
            // * 2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
            //   where v_k is one of 2^N vertices of feasible hypercube, v_shift is
            //   a shift parameter:
            //   * with zero v_shift such constraints are degenerate (each vertex has
            //     N box constraints and one "redundant" linear constraint)
            //   * with positive v_shift linear constraint is always inactive
            //   * with small (about machine epsilon) but negative v_shift,
            //     constraint is close to degenerate - but not exactly
            //
            // We check that constrained gradient is close to zero at solution.
            // Box constraint is considered active if distance to boundary is less
            // than TolConstr.
            //
            // NOTE: TolConstr must be large enough so it won't conflict with
            //       perturbation introduced by v_shift
            //
            tolconstr = 1.0E-8;
            for(n=2; n<=8; n++)
            {
                for(akind=0; akind<=3; akind++)
                {
                    for(shiftkind=-5; shiftkind<=1; shiftkind++)
                    {
                        for(bscale=0; bscale>=-2; bscale--)
                        {
                            
                            //
                            // Generate A, B and initial point
                            //
                            a = new double[n, n];
                            b = new double[n];
                            x = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                b[i] = Math.Pow(10, bscale)*hqrnd.hqrndnormal(rs);
                                x[i] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0.0;
                                }
                            }
                            if( akind==1 )
                            {
                                
                                //
                                // Dense well conditioned SPD
                                //
                                matgen.spdmatrixrndcond(n, 50.0, ref a);
                            }
                            if( akind==2 )
                            {
                                
                                //
                                // Dense well conditioned indefinite
                                //
                                matgen.smatrixrndcond(n, 50.0, ref a);
                            }
                            if( akind==3 )
                            {
                                
                                //
                                // Low rank
                                //
                                tmp = new double[n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 0.0;
                                    }
                                }
                                for(k=1; k<=Math.Min(3, n-1); k++)
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        tmp[i] = hqrnd.hqrndnormal(rs);
                                    }
                                    v = hqrnd.hqrndnormal(rs);
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=0; j<=n-1; j++)
                                        {
                                            a[i,j] = a[i,j]+v*tmp[i]*tmp[j];
                                        }
                                    }
                                }
                            }
                            
                            //
                            // Generate constraints
                            //
                            bl = new double[n];
                            bu = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                bl[i] = -1.0;
                                bu[i] = 1.0;
                            }
                            ccnt = (int)Math.Round(Math.Pow(2, n));
                            c = new double[ccnt, n+1];
                            ct = new int[ccnt];
                            for(i=0; i<=ccnt-1; i++)
                            {
                                ct[i] = -1;
                                k = i;
                                c[i,n] = Math.Sign(shiftkind)*Math.Pow(10, Math.Abs(shiftkind))*math.machineepsilon;
                                for(j=0; j<=n-1; j++)
                                {
                                    c[i,j] = 2*(k%2)-1;
                                    c[i,n] = c[i,n]+c[i,j]*c[i,j];
                                    k = k/2;
                                }
                            }
                            
                            //
                            // Create and optimize
                            //
                            minqp.minqpcreate(n, state);
                            minqp.minqpsetstartingpoint(state, x);
                            setrandomalgononconvexlc(state);
                            minqp.minqpsetbc(state, bl, bu);
                            minqp.minqpsetlc(state, c, ct, ccnt);
                            minqp.minqpsetlinearterm(state, b);
                            minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                            minqp.minqpoptimize(state);
                            minqp.minqpresults(state, ref xs0, rep);
                            apserv.seterrorflag(ref err, rep.terminationtype<=0);
                            if( err )
                            {
                                return;
                            }
                            
                            //
                            // Evaluate gradient at solution and test
                            //
                            vv = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*xs0[i_];
                                }
                                v = v+b[i];
                                if( (double)(xs0[i])<=(double)(bl[i]+tolconstr) && (double)(v)>(double)(0) )
                                {
                                    v = 0.0;
                                }
                                if( (double)(xs0[i])>=(double)(bu[i]-tolconstr) && (double)(v)<(double)(0) )
                                {
                                    v = 0.0;
                                }
                                vv = vv+math.sqr(v);
                            }
                            vv = Math.Sqrt(vv);
                            apserv.seterrorflag(ref err, (double)(vv)>(double)(1.0E-5));
                        }
                    }
                }
            }
            
            //
            // Convex/nonconvex optimization problem with combination of
            // box and linear constraints:
            //
            // * N=2..8
            // * f = 0.5*x'*A*x+b'*x
            // * b has normally distributed entries with scale 10^BScale
            // * several kinds of A are tried: zero, well conditioned SPD,
            //   well conditioned indefinite, low rank
            // * box constraints: x[i] in [-1,+1]
            // * initial point x0 = [0 0 ... 0 0]
            // * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
            //   random mix of equality/inequality constraints is tried.
            //   x0 is guaranteed to be feasible.
            //
            // We check that constrained gradient is close to zero at solution.
            // Inequality constraint is considered active if distance to boundary
            // is less than TolConstr. We use nonnegative least squares solver
            // in order to compute constrained gradient.
            //
            tolconstr = 1.0E-8;
            for(n=2; n<=8; n++)
            {
                for(akind=0; akind<=3; akind++)
                {
                    for(bscale=0; bscale>=-2; bscale--)
                    {
                        
                        //
                        // Generate A, B and initial point
                        //
                        a = new double[n, n];
                        b = new double[n];
                        x = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            b[i] = Math.Pow(10, bscale)*hqrnd.hqrndnormal(rs);
                            x[i] = 0.0;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a[i,j] = 0.0;
                            }
                        }
                        if( akind==1 )
                        {
                            
                            //
                            // Dense well conditioned SPD
                            //
                            matgen.spdmatrixrndcond(n, 50.0, ref a);
                        }
                        if( akind==2 )
                        {
                            
                            //
                            // Dense well conditioned indefinite
                            //
                            matgen.smatrixrndcond(n, 50.0, ref a);
                        }
                        if( akind==3 )
                        {
                            
                            //
                            // Low rank
                            //
                            tmp = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0.0;
                                }
                            }
                            for(k=1; k<=Math.Min(3, n-1); k++)
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    tmp[i] = hqrnd.hqrndnormal(rs);
                                }
                                v = hqrnd.hqrndnormal(rs);
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = a[i,j]+v*tmp[i]*tmp[j];
                                    }
                                }
                            }
                        }
                        
                        //
                        // Generate constraints
                        //
                        bl = new double[n];
                        bu = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            bl[i] = -1.0;
                            bu[i] = 1.0;
                        }
                        ccnt = Math.Min(3, n-1);
                        c = new double[ccnt, n+1];
                        ct = new int[ccnt];
                        for(i=0; i<=ccnt-1; i++)
                        {
                            ct[i] = hqrnd.hqrnduniformi(rs, 3)-1;
                            c[i,n] = 0.0;
                            for(j=0; j<=n-1; j++)
                            {
                                c[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                        }
                        
                        //
                        // Create and optimize
                        //
                        minqp.minqpcreate(n, state);
                        minqp.minqpsetstartingpoint(state, x);
                        setrandomalgononconvexlc(state);
                        minqp.minqpsetbc(state, bl, bu);
                        minqp.minqpsetlc(state, c, ct, ccnt);
                        minqp.minqpsetlinearterm(state, b);
                        minqp.minqpsetquadraticterm(state, a, (double)(math.randomreal())>(double)(0.5));
                        minqp.minqpoptimize(state);
                        minqp.minqpresults(state, ref xs0, rep);
                        apserv.seterrorflag(ref err, rep.terminationtype<=0);
                        if( err )
                        {
                            return;
                        }
                        
                        //
                        // 1. evaluate unconstrained gradient at solution
                        //
                        // 2. calculate constrained gradient (NNLS solver is used
                        //    to evaluate gradient subject to active constraints).
                        //    In order to do this we form CE matrix, matrix of active
                        //    constraints (columns store constraint vectors). Then
                        //    we try to approximate gradient vector by columns of CE,
                        //    subject to non-negativity restriction placed on variables
                        //    corresponding to inequality constraints.
                        //
                        //    Residual from such regression is a constrained gradient vector.
                        //
                        g = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*xs0[i_];
                            }
                            g[i] = v+b[i];
                        }
                        ce = new double[n, n+ccnt];
                        nonnegative = new bool[n+ccnt];
                        k = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref err, (double)(xs0[i])<(double)(bl[i]));
                            apserv.seterrorflag(ref err, (double)(xs0[i])>(double)(bu[i]));
                            if( (double)(xs0[i])<=(double)(bl[i]+tolconstr) )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = 0.0;
                                }
                                ce[i,k] = 1.0;
                                nonnegative[k] = true;
                                apserv.inc(ref k);
                                continue;
                            }
                            if( (double)(xs0[i])>=(double)(bu[i]-tolconstr) )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = 0.0;
                                }
                                ce[i,k] = -1.0;
                                nonnegative[k] = true;
                                apserv.inc(ref k);
                                continue;
                            }
                        }
                        for(i=0; i<=ccnt-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += c[i,i_]*xs0[i_];
                            }
                            v = v-c[i,n];
                            apserv.seterrorflag(ref err, ct[i]==0 && (double)(Math.Abs(v))>(double)(tolconstr));
                            apserv.seterrorflag(ref err, ct[i]>0 && (double)(v)<(double)(-tolconstr));
                            apserv.seterrorflag(ref err, ct[i]<0 && (double)(v)>(double)(tolconstr));
                            if( ct[i]==0 )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = c[i,j];
                                }
                                nonnegative[k] = false;
                                apserv.inc(ref k);
                                continue;
                            }
                            if( (ct[i]>0 && (double)(v)<=(double)(tolconstr)) || (ct[i]<0 && (double)(v)>=(double)(-tolconstr)) )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = Math.Sign(ct[i])*c[i,j];
                                }
                                nonnegative[k] = true;
                                apserv.inc(ref k);
                                continue;
                            }
                        }
                        snnls.snnlsinit(0, 0, 0, nnls);
                        snnls.snnlssetproblem(nnls, ce, g, 0, k, n);
                        for(i=0; i<=k-1; i++)
                        {
                            if( !nonnegative[i] )
                            {
                                snnls.snnlsdropnnc(nnls, i);
                            }
                        }
                        snnls.snnlssolve(nnls, ref tmp);
                        for(i=0; i<=k-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                g[j] = g[j]-tmp[i]*ce[j,i];
                            }
                        }
                        vv = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vv += g[i_]*g[i_];
                        }
                        vv = Math.Sqrt(vv);
                        apserv.seterrorflag(ref err, (double)(vv)>(double)(1.0E-5));
                    }
                }
            }
        }


        /*************************************************************************
        This function tests special inequality constrained QP problems.

        Returns True on errors.
        *************************************************************************/
        private static bool specialicqptests()
        {
            bool result = new bool();
            double[,] a = new double[0,0];
            double[,] c = new double[0,0];
            double[] xstart = new double[0];
            double[] xend = new double[0];
            double[] xexact = new double[0];
            double[] b = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            int[] ct = new int[0];
            minqp.minqpstate state = new minqp.minqpstate();
            minqp.minqpreport rep = new minqp.minqpreport();
            bool waserrors = new bool();
            int i = 0;
            int j = 0;

            waserrors = false;
            
            //
            // Test 1: reported by Vanderlande Industries.
            //         Tests algorithm ability to handle degenerate constraints.
            //
            a = new double[3, 3];
            for(i=0; i<=2; i++)
            {
                for(j=0; j<=2; j++)
                {
                    a[i,j] = 0;
                }
            }
            for(i=0; i<=2; i++)
            {
                a[i,i] = 1;
            }
            b = new double[3];
            b[0] = -50;
            b[1] = -50;
            b[2] = -75;
            bndl = new double[3];
            bndl[0] = 0;
            bndl[1] = 0;
            bndl[2] = 0;
            bndu = new double[3];
            bndu[0] = 100;
            bndu[1] = 100;
            bndu[2] = 150;
            xstart = new double[3];
            xstart[0] = 0;
            xstart[1] = 100;
            xstart[2] = 0;
            xexact = new double[3];
            xexact[0] = 0;
            xexact[1] = 100;
            xexact[2] = 50;
            c = new double[3, 4];
            c[0,0] = 1;
            c[0,1] = -1;
            c[0,2] = 0;
            c[0,3] = -100;
            c[1,0] = 1;
            c[1,1] = 0;
            c[1,2] = -1;
            c[1,3] = 0;
            c[2,0] = -1;
            c[2,1] = 0;
            c[2,2] = 1;
            c[2,3] = 50;
            ct = new int[3];
            ct[0] = -1;
            ct[1] = -1;
            ct[2] = -1;
            minqp.minqpcreate(3, state);
            setrandomalgoconvexlc(state);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadraticterm(state, a, true);
            minqp.minqpsetstartingpoint(state, xstart);
            minqp.minqpsetbc(state, bndl, bndu);
            minqp.minqpsetlc(state, c, ct, 3);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend, rep);
            if( rep.terminationtype>0 )
            {
                for(i=0; i<=2; i++)
                {
                    waserrors = waserrors || (double)(Math.Abs(xend[i]-xexact[i]))>(double)(1.0E6*math.machineepsilon);
                }
            }
            else
            {
                waserrors = true;
            }
            
            //
            // Test 2: reported by Vanderlande Industries.
            //         Tests algorithm ability to handle degenerate constraints.
            //
            a = new double[3, 3];
            for(i=0; i<=2; i++)
            {
                for(j=0; j<=2; j++)
                {
                    a[i,j] = 0;
                }
            }
            for(i=0; i<=2; i++)
            {
                a[i,i] = 1;
            }
            b = new double[3];
            b[0] = -50;
            b[1] = -50;
            b[2] = -75;
            bndl = new double[3];
            bndl[0] = 0;
            bndl[1] = 0;
            bndl[2] = 0;
            bndu = new double[3];
            bndu[0] = 100;
            bndu[1] = 100;
            bndu[2] = 150;
            xstart = new double[3];
            xstart[0] = 0;
            xstart[1] = 100;
            xstart[2] = 150;
            xexact = new double[3];
            xexact[0] = 0;
            xexact[1] = 100;
            xexact[2] = 100;
            c = new double[3, 4];
            c[0,0] = 1;
            c[0,1] = -1;
            c[0,2] = 0;
            c[0,3] = -100;
            c[1,0] = 0;
            c[1,1] = 1;
            c[1,2] = -1;
            c[1,3] = 0;
            c[2,0] = 0;
            c[2,1] = -1;
            c[2,2] = 1;
            c[2,3] = 50;
            ct = new int[3];
            ct[0] = -1;
            ct[1] = -1;
            ct[2] = -1;
            minqp.minqpcreate(3, state);
            setrandomalgoconvexlc(state);
            minqp.minqpsetlinearterm(state, b);
            minqp.minqpsetquadraticterm(state, a, true);
            minqp.minqpsetstartingpoint(state, xstart);
            minqp.minqpsetbc(state, bndl, bndu);
            minqp.minqpsetlc(state, c, ct, 3);
            minqp.minqpoptimize(state);
            minqp.minqpresults(state, ref xend, rep);
            if( rep.terminationtype>0 )
            {
                for(i=0; i<=2; i++)
                {
                    waserrors = waserrors || (double)(Math.Abs(xend[i]-xexact[i]))>(double)(1.0E6*math.machineepsilon);
                }
            }
            else
            {
                waserrors = true;
            }
            result = waserrors;
            return result;
        }


        /*************************************************************************
        Function normal
        *************************************************************************/
        private static double projectedantigradnorm(int n,
            double[] x,
            double[] g,
            double[] bndl,
            double[] bndu)
        {
            double result = 0;
            int i = 0;
            double r = 0;

            r = 0;
            for(i=0; i<=n-1; i++)
            {
                alglib.ap.assert((double)(x[i])>=(double)(bndl[i]) && (double)(x[i])<=(double)(bndu[i]), "ProjectedAntiGradNormal: boundary constraints violation");
                if( (((double)(x[i])>(double)(bndl[i]) && (double)(x[i])<(double)(bndu[i])) || ((double)(x[i])==(double)(bndl[i]) && (double)(-g[i])>(double)(0))) || ((double)(x[i])==(double)(bndu[i]) && (double)(-g[i])<(double)(0)) )
                {
                    r = r+g[i]*g[i];
                }
            }
            result = Math.Sqrt(r);
            return result;
        }


        /*************************************************************************
        This function tests that norm of bound-constrained gradient at point X is
        less than Eps:
        * unconstrained gradient is A*x+b
        * if I-th component is at the boundary, and antigradient points outside of
          the feasible area, I-th component of constrained gradient is zero

        This function accepts QP terms A and B, bound constraints, current point,
        and performs test. Additionally, it checks that point is feasible w.r.t.
        boundary constraints.

        In case of failure, error flag is set. Otherwise, it is not modified.

        IMPORTANT: this function does NOT use SetErrorFlag() to modify flag.
                   If you want to use SetErrorFlag() for easier tracking of errors,
                   you should store flag returned by this function into separate
                   variable TmpFlag and call SetErrorFlag(ErrorFlag, TmpFlag) yourself.
        *************************************************************************/
        private static void testbcgradandfeasibility(double[,] a,
            double[] b,
            double[] bndl,
            double[] bndu,
            int n,
            double[] x,
            double eps,
            ref bool errorflag)
        {
            int i = 0;
            int j = 0;
            double g = 0;
            double gnorm = 0;

            gnorm = 0.0;
            for(i=0; i<=n-1; i++)
            {
                g = b[i];
                for(j=0; j<=n-1; j++)
                {
                    g = g+a[i,j]*x[j];
                }
                if( (double)(x[i])==(double)(bndl[i]) && (double)(g)>(double)(0) )
                {
                    g = 0;
                }
                if( (double)(x[i])==(double)(bndu[i]) && (double)(g)<(double)(0) )
                {
                    g = 0;
                }
                gnorm = gnorm+math.sqr(g);
                if( (double)(x[i])<(double)(bndl[i]) )
                {
                    errorflag = true;
                }
                if( (double)(x[i])>(double)(bndu[i]) )
                {
                    errorflag = true;
                }
            }
            gnorm = Math.Sqrt(gnorm);
            if( (double)(gnorm)>(double)(eps) )
            {
                errorflag = true;
            }
        }


        /*************************************************************************
        set random type of the QP solver.
        All "modern" solvers can be chosen.
        *************************************************************************/
        private static void setrandomalgoallmodern(minqp.minqpstate s)
        {
            int i = 0;

            i = 1+math.randominteger(2);
            if( i==1 )
            {
                minqp.minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0);
            }
            if( i==2 )
            {
                minqp.minqpsetalgoquickqp(s, 1.0E-12, 0.0, 0.0, 0, (double)(math.randomreal())>(double)(0.5));
            }
        }


        /*************************************************************************
        set random type of theQP solver
        *************************************************************************/
        private static void setrandomalgononconvex(minqp.minqpstate s)
        {
            int i = 0;

            i = 1+math.randominteger(2);
            if( i==1 )
            {
                minqp.minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0);
            }
            if( i==2 )
            {
                minqp.minqpsetalgoquickqp(s, 1.0E-12, 0.0, 0.0, 0, (double)(math.randomreal())>(double)(0.5));
            }
        }


        /*************************************************************************
        set random type of theQP solver
        *************************************************************************/
        private static void setrandomalgosemidefinite(minqp.minqpstate s)
        {
            int i = 0;

            i = 1+math.randominteger(2);
            if( i==1 )
            {
                minqp.minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0);
            }
            if( i==2 )
            {
                minqp.minqpsetalgoquickqp(s, 1.0E-12, 0.0, 0.0, 0, (double)(math.randomreal())>(double)(0.5));
            }
        }


        /*************************************************************************
        set random type of the QP solver, must support boundary constraints
        *************************************************************************/
        private static void setrandomalgobc(minqp.minqpstate s)
        {
            int i = 0;

            i = math.randominteger(2);
            if( i==0 )
            {
                minqp.minqpsetalgocholesky(s);
            }
            if( i==1 )
            {
                minqp.minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0);
            }
        }


        /*************************************************************************
        set random type of the QP solver,
        must support convex problems with boundary/linear constraints
        *************************************************************************/
        private static void setrandomalgoconvexlc(minqp.minqpstate s)
        {
            int i = 0;

            i = math.randominteger(2);
            if( i==0 )
            {
                minqp.minqpsetalgocholesky(s);
            }
            if( i==1 )
            {
                minqp.minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0);
            }
        }


        /*************************************************************************
        set random type of the QP solver,
        must support nonconvex problems with boundary/linear constraints
        *************************************************************************/
        private static void setrandomalgononconvexlc(minqp.minqpstate s)
        {
            int i = 0;

            i = math.randominteger(1);
            if( i==0 )
            {
                minqp.minqpsetalgobleic(s, 1.0E-12, 0.0, 0.0, 0);
            }
        }


        /*************************************************************************
        Convert dense matrix to sparse matrix using random format
        *************************************************************************/
        private static void densetosparse(double[,] a,
            int n,
            sparse.sparsematrix s)
        {
            int i = 0;
            int j = 0;
            sparse.sparsematrix s0 = new sparse.sparsematrix();

            sparse.sparsecreate(n, n, n*n, s0);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    sparse.sparseset(s0, i, j, a[i,j]);
                }
            }
            sparse.sparsecopytobuf(s0, math.randominteger(3), s);
        }


    }
    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();
            bool graderrors = 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 stopcallidx = 0;
            int callidx = 0;
            bool terminationrequested = new bool();
            int pass = 0;
            int i_ = 0;

            waserrors = false;
            referror = false;
            lin1error = false;
            lin2error = false;
            eqerror = false;
            converror = false;
            scerror = false;
            othererrors = false;
            restartserror = false;
            
            //
            // Try to reproduce previously fixed bugs
            //
            tryreproducefixedbugs(ref othererrors);
            
            //
            // 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);
                    }
                    alglib.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];
                }
            }
            
            //
            // Check algorithm ability to handle request for termination:
            // * to terminate with correct return code = 8
            // * to return point which was "current" at the moment of termination
            //
            for(pass=1; pass<=50; pass++)
            {
                n = 3;
                m = 3;
                s = 100;
                x = new double[n];
                xlast = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 6+math.randomreal();
                }
                stopcallidx = math.randominteger(20);
                maxits = 25;
                minlm.minlmcreatevj(n, m, x, state);
                minlm.minlmsetcond(state, 0, 0, 0, maxits);
                minlm.minlmsetxrep(state, true);
                callidx = 0;
                terminationrequested = false;
                for(i_=0; i_<=n-1;i_++)
                {
                    xlast[i_] = x[i_];
                }
                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( callidx==stopcallidx )
                        {
                            minlm.minlmrequesttermination(state);
                            terminationrequested = true;
                        }
                        apserv.inc(ref callidx);
                        continue;
                    }
                    if( state.xupdated )
                    {
                        if( !terminationrequested )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xlast[i_] = state.x[i_];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minlm.minlmresults(state, ref x, rep);
                apserv.seterrorflag(ref othererrors, rep.terminationtype!=8);
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref othererrors, (double)(x[i])!=(double)(xlast[i]));
                }
            }
            
            //
            //  Test for MinLMGradientCheck
            //
            graderrors = gradientchecktest();
            
            //
            // end
            //
            waserrors = (((((((referror || lin1error) || lin2error) || eqerror) || converror) || scerror) || othererrors) || restartserror) || graderrors;
            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();
                }
                System.Console.Write("TEST FOR VERIFICATION OF DERIVATIVES:     ");
                if( graderrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testminlm(bool silent)
        {
            return testminlm(silent);
        }


        /*************************************************************************
        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_];
                    }
                }
            }
        }


        /*************************************************************************
        This function tries to reproduce previously fixed bugs; in case of bug
        being present sets Err to True;
        *************************************************************************/
        private static void tryreproducefixedbugs(ref bool err)
        {
            minlm.minlmstate s = new minlm.minlmstate();
            minlm.minlmreport rep = new minlm.minlmreport();
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] x = new double[0];

            
            //
            // Reproduce bug reported by ISS:
            // when solving bound constrained problem with numerical differentiation
            // and starting from infeasible point, we won't stop at the feasible point
            //
            x = new double[2];
            bl = new double[2];
            bu = new double[2];
            x[0] = 2.0;
            bl[0] = -1.0;
            bu[0] = 1.0;
            x[1] = 2.0;
            bl[1] = -1.0;
            bu[1] = 1.0;
            minlm.minlmcreatev(2, 2, x, 0.001, s);
            minlm.minlmsetbc(s, bl, bu);
            while( minlm.minlmiteration(s) )
            {
                if( s.needfi )
                {
                    s.fi[0] = math.sqr(s.x[0]);
                    s.fi[1] = math.sqr(s.x[1]);
                }
            }
            minlm.minlmresults(s, ref x, rep);
            err = (((double)(x[0])<(double)(bl[0]) || (double)(x[0])>(double)(bu[0])) || (double)(x[1])<(double)(bl[1])) || (double)(x[1])>(double)(bu[1]);
        }


        /*************************************************************************
        This function tests, that gradient verified correctly.
        *************************************************************************/
        private static bool gradientchecktest()
        {
            bool result = new bool();
            minlm.minlmstate state = new minlm.minlmstate();
            minlm.minlmreport rep = new minlm.minlmreport();
            int n = 0;
            int m = 0;
            double[] a = new double[0];
            double[] x0 = new double[0];
            double[] x = new double[0];
            double[] bl = new double[0];
            double[] bu = new double[0];
            int infcomp = 0;
            double teststep = 0;
            double noise = 0;
            double rndconst = 0;
            int nbrfunc = 0;
            int nbrcomp = 0;
            double spp = 0;
            int func = 0;
            int pass = 0;
            int passcount = 0;
            int i = 0;

            passcount = 35;
            spp = 1.0;
            teststep = 0.01;
            for(pass=1; pass<=passcount; pass++)
            {
                n = math.randominteger(10)+1;
                m = math.randominteger(10)+1;
                x = new double[n];
                x0 = new double[n];
                a = new double[n];
                bl = new double[n];
                bu = new double[n];
                
                //
                // Prepare test's parameters
                //
                func = math.randominteger(3)+1;
                nbrfunc = math.randominteger(m);
                nbrcomp = math.randominteger(n);
                noise = 2*math.randominteger(2)-1;
                rndconst = 2*math.randomreal()-1;
                
                //
                // Prepare function's parameters
                //
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 5*apserv.randomnormal();
                    a[i] = 5*math.randomreal()+1;
                    x0[i] = 5*(2*math.randomreal()-1);
                }
                
                //
                // Prepare boundary parameters
                //
                for(i=0; i<=n-1; i++)
                {
                    bl[i] = -(3*math.randomreal())-0.1;
                    bu[i] = 3*math.randomreal()+0.1;
                }
                infcomp = math.randominteger(n+1);
                if( infcomp<n )
                {
                    bl[infcomp] = Double.NegativeInfinity;
                }
                infcomp = math.randominteger(n+1);
                if( infcomp<n )
                {
                    bu[infcomp] = Double.PositiveInfinity;
                }
                minlm.minlmcreatevj(n, m, x, state);
                minlm.minlmsetcond(state, 0, 0, 0, 0);
                minlm.minlmsetgradientcheck(state, teststep);
                minlm.minlmsetbc(state, bl, bu);
                
                //
                // Check that the criterion passes a derivative if it is correct
                //
                while( minlm.minlmiteration(state) )
                {
                    if( state.needfij )
                    {
                        
                        //
                        // Check hat .X within the boundaries
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            if( (math.isfinite(bl[i]) && (double)(state.x[i])<(double)(bl[i])) || (math.isfinite(bu[i]) && (double)(state.x[i])>(double)(bu[i])) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        funcderiv(a, x0, state.x, m, n, rndconst, func, ref state.fi, ref state.j);
                    }
                }
                minlm.minlmresults(state, ref x, rep);
                
                //
                // Check that error code does not equal to -7 and parameter .VarIdx
                // equal to -1.
                //
                if( (rep.terminationtype==-7 || rep.funcidx!=-1) || rep.varidx!=-1 )
                {
                    result = true;
                    return result;
                }
                for(i=0; i<=n-1; i++)
                {
                    x[i] = 5*apserv.randomnormal();
                }
                minlm.minlmrestartfrom(state, x);
                
                //
                // Check that the criterion does not miss a derivative if
                // it is incorrect
                //
                while( minlm.minlmiteration(state) )
                {
                    if( state.needfij )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            if( (math.isfinite(bl[i]) && (double)(state.x[i])<(double)(bl[i])) || (math.isfinite(bu[i]) && (double)(state.x[i])>(double)(bu[i])) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        funcderiv(a, x0, state.x, m, n, rndconst, func, ref state.fi, ref state.j);
                        state.j[nbrfunc,nbrcomp] = state.j[nbrfunc,nbrcomp]+noise;
                    }
                }
                minlm.minlmresults(state, ref x, rep);
                
                //
                // Check that error code equal to -7 and parameter .VarIdx
                // equal to number of incorrect component.
                //
                if( (rep.terminationtype!=-7 || rep.funcidx!=nbrfunc) || rep.varidx!=nbrcomp )
                {
                    result = true;
                    return result;
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function return function value and it derivatives. The number of
        functions is M, dimension for each of functions is N.
        F(XI)=SUM(fi(XI)); (XI={x,y,z}; i=0..M-1);
            Function's list:
                * funcType=1:
                    fi(X)=(Aj*Xj-X0j)^2;
                * funcType=2:
                    fi(X)=Aj*sin(Xj-X0j);
                * funcType=3:
                    fi(X)=Aj*Xj-X0j;
                    fM-1(X)=A(M-1)*((X(M-1)-X0(M-1))-(X0-X00)).
        *************************************************************************/
        private static void funcderiv(double[] a,
            double[] x0,
            double[] x,
            int m,
            int n,
            double anyconst,
            int functype,
            ref double[] f,
            ref double[,] j)
        {
            int i0 = 0;
            int j0 = 0;

            alglib.ap.assert(functype>=1 && functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).");
            alglib.ap.assert(n>0, "FuncDeriv: N<=0");
            alglib.ap.assert(m>0, "FuncDeriv: M<=0");
            alglib.ap.assert(alglib.ap.len(x)>=n, "FuncDeriv: Length(X)<N");
            alglib.ap.assert(apserv.isfinitevector(x, n), "FuncDeriv: X contains NaN or Infinite.");
            alglib.ap.assert(alglib.ap.len(x0)>=n, "FuncDeriv: Length(X0)<N");
            alglib.ap.assert(apserv.isfinitevector(x0, n), "FuncDeriv: X0 contains NaN or Infinite.");
            alglib.ap.assert(alglib.ap.len(a)>=n, "FuncDeriv: Length(X)<N");
            alglib.ap.assert(apserv.isfinitevector(a, n), "FuncDeriv: A contains NaN or Infinite.");
            if( functype==1 )
            {
                for(i0=0; i0<=m-1; i0++)
                {
                    if( i0<n )
                    {
                        f[i0] = a[i0]*x[i0]-x0[i0];
                    }
                    else
                    {
                        f[i0] = anyconst;
                    }
                }
                for(i0=0; i0<=m-1; i0++)
                {
                    for(j0=0; j0<=n-1; j0++)
                    {
                        if( i0==j0 )
                        {
                            j[i0,j0] = a[j0];
                        }
                        else
                        {
                            j[i0,j0] = 0;
                        }
                    }
                }
                return;
            }
            if( functype==2 )
            {
                for(i0=0; i0<=m-1; i0++)
                {
                    if( i0<n )
                    {
                        f[i0] = a[i0]*Math.Sin(x[i0]-x0[i0]);
                    }
                    else
                    {
                        f[i0] = anyconst;
                    }
                }
                for(i0=0; i0<=m-1; i0++)
                {
                    for(j0=0; j0<=n-1; j0++)
                    {
                        if( i0==j0 )
                        {
                            j[i0,j0] = a[j0]*Math.Cos(x[j0]-x0[j0]);
                        }
                        else
                        {
                            j[i0,j0] = 0;
                        }
                    }
                }
                return;
            }
            if( functype==3 )
            {
                for(i0=0; i0<=m-1; i0++)
                {
                    if( i0<n )
                    {
                        f[i0] = a[i0]*x[i0]-x0[i0];
                    }
                    else
                    {
                        f[i0] = anyconst;
                    }
                }
                for(i0=0; i0<=m-1; i0++)
                {
                    for(j0=0; j0<=n-1; j0++)
                    {
                        if( i0==j0 )
                        {
                            j[i0,j0] = a[j0];
                        }
                        else
                        {
                            j[i0,j0] = 0;
                        }
                    }
                }
                if( m>n && n>1 )
                {
                    f[n-1] = a[n-1]*(x[n-1]-x0[n-1]-(x[0]-x0[0]));
                    j[n-1,0] = -a[n-1];
                    j[n-1,n-1] = a[n-1];
                    for(i0=1; i0<=n-2; i0++)
                    {
                        j[n-1,i0] = 0;
                    }
                }
                return;
            }
        }


    }
    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();
            bool graderrors = new bool();
            bool logisticerrors = new bool();
            bool rdperrors = new bool();

            waserrors = false;
            polfiterrors = false;
            ratfiterrors = false;
            splfiterrors = false;
            llserrors = false;
            nlserrors = false;
            graderrors = false;
            logisticerrors = false;
            rdperrors = false;
            testrdp(ref rdperrors);
            testlogisticfitting(ref logisticerrors);
            testpolynomialfitting(ref polfiterrors);
            testrationalfitting(ref ratfiterrors);
            testsplinefitting(ref splfiterrors);
            testgeneralfitting(ref llserrors, ref nlserrors);
            testgradientcheck(ref graderrors);
            
            //
            // report
            //
            waserrors = ((((((llserrors || nlserrors) || polfiterrors) || ratfiterrors) || splfiterrors) || graderrors) || logisticerrors) || rdperrors;
            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();
                }
                System.Console.Write("TEST FOR VERIFICATION OF THE GRADIENT:   ");
                if( graderrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LOGISTIC FITTING (4PL/5PL):              ");
                if( logisticerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("RDP ALGORITHM:                           ");
                if( rdperrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testlsfit(bool silent)
        {
            return testlsfit(silent);
        }


        /*************************************************************************
        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] = i+(math.randomreal()-0.5);
                        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 || !apserv.approxequalrel(v2, v1, threshold);
                        fiterrors = fiterrors || !apserv.approxequalrel(rep.rmserror, rep2.rmserror, threshold);
                        fiterrors = fiterrors || !apserv.approxequalrel(rep.avgerror, rep2.avgerror, threshold);
                        fiterrors = fiterrors || !apserv.approxequalrel(rep.avgrelerror, rep2.avgrelerror, threshold);
                        fiterrors = fiterrors || !apserv.approxequalrel(rep.maxerror, rep2.maxerror, threshold);
                    }
                }
            }
            for(m=1; m<=maxn; m++)
            {
                for(pass=1; pass<=passcount; pass++)
                {
                    alglib.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] = i+(math.randomreal()-0.5);
                        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 || !apserv.approxequalrel(v2, v1, threshold);
                        fiterrors = fiterrors || !apserv.approxequalrel(rep.rmserror, rep2.rmserror, threshold);
                        fiterrors = fiterrors || !apserv.approxequalrel(rep.avgerror, rep2.avgerror, threshold);
                        fiterrors = fiterrors || !apserv.approxequalrel(rep.avgrelerror, rep2.avgrelerror, threshold);
                        fiterrors = fiterrors || !apserv.approxequalrel(rep.maxerror, rep2.maxerror, threshold);
                    }
                }
            }
            for(pass=1; pass<=passcount; pass++)
            {
                alglib.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];
                        }
                        info = -1;
                        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
                    //
                    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] = i+(math.randomreal()-0.5);
                        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 = math.randomreal()*(n-1);
                                fiterrors = fiterrors || !apserv.approxequalrel(spline1d.spline1dcalc(c, v), spline1d.spline1dcalc(c2, v), threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.taskrcond, rep2.taskrcond, threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.rmserror, rep2.rmserror, threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.avgerror, rep2.avgerror, threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.avgrelerror, rep2.avgrelerror, threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.maxerror, rep2.maxerror, threshold);
                            }
                        }
                    }
                    
                    //
                    // 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 = math.randomreal()*(n-1);
                                fiterrors = fiterrors || !apserv.approxequalrel(spline1d.spline1dcalc(c, v), spline1d.spline1dcalc(c2, v), threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.taskrcond, rep2.taskrcond, threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.rmserror, rep2.rmserror, threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.avgerror, rep2.avgerror, threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.avgrelerror, rep2.avgrelerror, threshold);
                                fiterrors = fiterrors || !apserv.approxequalrel(rep.maxerror, rep2.maxerror, threshold);
                            }
                        }
                    }
                }
            }
            
            //
            // 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++)
            {
                alglib.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 skind = 0;
            int pkind = 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 cscale = 0;
            double wscale = 0;
            double noiselevel = 0;
            double tol = 0;
            double diffstep = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] y2 = new double[0];
            double[] w = new double[0];
            double[] w2 = new double[0];
            double[] s = new double[0];
            double[] c = new double[0];
            double[] cstart = new double[0];
            double[] cend = 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;
            double avgdeviationpar = 0;
            double avgdeviationcurve = 0;
            double avgdeviationnoise = 0;
            double adccnt = 0;
            double adpcnt = 0;
            double adncnt = 0;
            lsfit.lsfitstate state = new lsfit.lsfitstate();
            int i_ = 0;

            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);
                }
            }
            
            //
            // Check covariance matrix, errors-in-parameters.
            //
            // We test three different solvers:
            // * nonlinear solver
            // * unconstrained linear solver
            // * constrained linear solver with empty set of constrains
            // on two random problems:
            // * problem with known prior, noise, unit weights
            // * problem with known prior, noise, non-unit weights
            //
            // We test that:
            // * rep.ErrPar=sqrt(diag(Rep.CovPar))
            // * Rep.ErrPar is not too optimistic  - average value of ratio
            //   between  |c_fit-c_prior| and ErrPar[] is less than TOL
            // * Rep.ErrPar is not too pessimistic - average value of ratio
            //   is larger than 1/TOL
            // * similarly, Rep.ErrCurve gives good estimate of |A*c_fit - A*c_prior|
            //   - not optimistic, not pessimistic.
            // * similarly, per-point noise estimates are good enough (we use
            //   slightly different tolerances, though)
            // In order to have these estimates we perform many different tests
            // and calculate average deviation divided by ErrPar/ErrCurve. Then
            // we perform test.
            //
            // Due to stochastic nature of the test it is not good idea to
            // consider each case individually - it is better to average over
            // many runs.
            // 
            //
            tol = 10.0;
            for(n=1; n<=10; n++)
            {
                for(skind=0; skind<=2; skind++)
                {
                    for(pkind=0; pkind<=1; pkind++)
                    {
                        
                        //
                        // Generate problem:
                        // * PKind=0 - unit weights
                        // * PKind=1 - non-unit weights, exact estimate of noise at I-th point
                        //
                        // We generate:
                        // * C      -   prior values of parameters
                        // * CStart -   random initial point
                        // * A      -   function matrix
                        // * Y      -   noisy version of A*C
                        // * W      -   weights vector
                        // * S      -   vector of per-point estimates of noise
                        //
                        cscale = Math.Pow(10.0, 2*apserv.randomnormal());
                        xscale = Math.Pow(10.0, 2*apserv.randomnormal());
                        noiselevel = 0.01*cscale*xscale;
                        c = new double[n];
                        cstart = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            c[i] = cscale*apserv.randomnormal();
                            cstart[i] = cscale*apserv.randomnormal();
                        }
                        a = new double[1000, n];
                        y = new double[alglib.ap.rows(a)];
                        w = new double[alglib.ap.rows(a)];
                        s = new double[alglib.ap.rows(a)];
                        for(i=0; i<=alglib.ap.rows(a)-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a[i,j] = xscale*apserv.randomnormal();
                            }
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*c[i_];
                            }
                            if( pkind==0 )
                            {
                                w[i] = 1;
                                s[i] = noiselevel;
                                y[i] = v+s[i]*apserv.randomnormal();
                            }
                            if( pkind==1 )
                            {
                                w[i] = 1/noiselevel;
                                s[i] = noiselevel;
                                y[i] = v+s[i]*apserv.randomnormal();
                            }
                        }
                        
                        //
                        // Test different solvers:
                        // * SKind=0 - nonlinear solver
                        // * SKind=1 - linear unconstrained
                        // * SKind=2 - linear constrained with empty set of constraints
                        //
                        info = -1;
                        if( skind==0 )
                        {
                            if( (double)(math.randomreal())>(double)(0.5) )
                            {
                                lsfit.lsfitcreatefg(a, y, cstart, alglib.ap.rows(a), n, n, true, state);
                            }
                            else
                            {
                                lsfit.lsfitcreatef(a, y, cstart, alglib.ap.rows(a), n, n, 0.001*cscale, state);
                            }
                            lsfit.lsfitsetcond(state, 0.0, 0.0, 10);
                            while( lsfit.lsfititeration(state) )
                            {
                                if( state.needf )
                                {
                                    state.f = 0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        state.f = state.f+state.c[i]*state.x[i];
                                    }
                                }
                                if( state.needfg )
                                {
                                    state.f = 0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        state.f = state.f+state.c[i]*state.x[i];
                                        state.g[i] = state.x[i];
                                    }
                                }
                            }
                            lsfit.lsfitresults(state, ref info, ref cend, rep);
                        }
                        if( skind==1 )
                        {
                            if( pkind==0 )
                            {
                                lsfit.lsfitlinear(y, a, alglib.ap.rows(a), n, ref info, ref cend, rep);
                            }
                            else
                            {
                                lsfit.lsfitlinearw(y, w, a, alglib.ap.rows(a), n, ref info, ref cend, rep);
                            }
                        }
                        if( skind==2 )
                        {
                            if( pkind==0 )
                            {
                                lsfit.lsfitlinearc(y, a, a2, alglib.ap.rows(a), n, 0, ref info, ref cend, rep);
                            }
                            else
                            {
                                lsfit.lsfitlinearwc(y, w, a, a2, alglib.ap.rows(a), n, 0, ref info, ref cend, rep);
                            }
                        }
                        
                        //
                        // Tests:
                        // * check relation between CovPar and ErrPar
                        // * accumulate average deviation in parameters
                        // * accumulate average deviation in curve fit
                        // * accumulate average deviation in noise estimate
                        //
                        avgdeviationpar = 0;
                        adpcnt = 0;
                        avgdeviationcurve = 0;
                        adccnt = 0;
                        avgdeviationnoise = 0;
                        adncnt = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref llserrors, (double)(Math.Abs(rep.covpar[i,i]-math.sqr(rep.errpar[i])))>(double)(100*math.machineepsilon*Math.Max(math.sqr(rep.errpar[i]), rep.covpar[i,i])));
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            avgdeviationpar = (avgdeviationpar*adpcnt+Math.Abs(c[i]-cend[i])/rep.errpar[i])/(adpcnt+1);
                            adpcnt = adpcnt+1;
                        }
                        for(i=0; i<=alglib.ap.rows(a)-1; i++)
                        {
                            v1 = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v1 += c[i_]*a[i,i_];
                            }
                            v2 = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v2 += cend[i_]*a[i,i_];
                            }
                            avgdeviationcurve = (avgdeviationcurve*adccnt+Math.Abs(v1-v2)/rep.errcurve[i])/(adccnt+1);
                            adccnt = adccnt+1;
                            avgdeviationnoise = (avgdeviationnoise*adncnt+rep.noise[i]/s[i])/(adncnt+1);
                            adncnt = adncnt+1;
                        }
                        
                        //
                        // Check that estimates are not too optimistic.
                        // This test is performed always.
                        //
                        apserv.seterrorflag(ref llserrors, (double)(avgdeviationpar)>(double)(tol));
                        apserv.seterrorflag(ref llserrors, (double)(avgdeviationcurve)>(double)(tol));
                        apserv.seterrorflag(ref llserrors, (double)(avgdeviationnoise)>(double)(1.50));
                        apserv.seterrorflag(ref llserrors, (double)(avgdeviationnoise)<(double)(0.66));
                        
                        //
                        // Test for estimates being too pessimistic is performed only
                        // when we have more than 4 parameters.
                        //
                        apserv.seterrorflag(ref llserrors, n>=5 && (double)(avgdeviationcurve)<(double)(0.01));
                        apserv.seterrorflag(ref llserrors, n>=5 && (double)(avgdeviationpar)<(double)(0.01));
                    }
                }
            }
            
            //
            // Check special property of the LSFit solver: it does not include points with
            // zero weight in the estimate of the noise level. Such property seems to be
            // quite natural, but in fact it requires some additional code in order to
            // ignore such points.
            //
            // In order to test it we solve two problems: one 300xN, with 150 non-zero
            // weights and 150 zero weights - and another one with only 150 points with
            // non-zero weights. Both problems should give us same covariance matrix.
            //
            tol = 10;
            for(n=1; n<=10; n++)
            {
                
                //
                // Generate N-dimensional linear problem with 300 points:
                // * y = c'*x + noise
                // * prior values of coefficients C has scale CScale
                // * coordinates X has scale XScale
                // * noise in I-th point has magnitude 0.1*CScale*XScale*WScale/W[i]
                //
                cscale = Math.Pow(10.0, 2*apserv.randomnormal());
                xscale = Math.Pow(10.0, 2*apserv.randomnormal());
                wscale = Math.Pow(10.0, 2*apserv.randomnormal());
                noiselevel = 0.1*cscale*xscale;
                c = new double[n];
                cstart = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    c[i] = cscale*apserv.randomnormal();
                    cstart[i] = cscale*apserv.randomnormal();
                }
                a = new double[300, n];
                y = new double[alglib.ap.rows(a)];
                w = new double[alglib.ap.rows(a)];
                for(i=0; i<=alglib.ap.rows(a)-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a[i,j] = xscale*apserv.randomnormal();
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += a[i,i_]*c[i_];
                    }
                    if( i<alglib.ap.rows(a)/2 )
                    {
                        w[i] = Math.Pow(10, apserv.randomnormal())*wscale;
                        y[i] = v+noiselevel/w[i]*apserv.randomnormal();
                    }
                    else
                    {
                        w[i] = 0;
                        y[i] = v+noiselevel*apserv.randomnormal();
                    }
                }
                
                //
                // Solve problem #1 (with zero weights).
                // We randomly choose between analytic gradient and numerical differentiation.
                //
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    lsfit.lsfitcreatewfg(a, y, w, cstart, alglib.ap.rows(a), n, n, true, state);
                }
                else
                {
                    lsfit.lsfitcreatewf(a, y, w, cstart, alglib.ap.rows(a), n, n, 0.001*cscale, state);
                }
                lsfit.lsfitsetcond(state, 0.0, 0.0, 10);
                while( lsfit.lsfititeration(state) )
                {
                    if( state.needf )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+state.c[i]*state.x[i];
                        }
                    }
                    if( state.needfg )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+state.c[i]*state.x[i];
                            state.g[i] = state.x[i];
                        }
                    }
                }
                lsfit.lsfitresults(state, ref info, ref c2, rep);
                
                //
                // Solve problem #2 (only points with non-zero weights).
                // We randomly choose between analytic gradient and numerical differentiation.
                //
                if( (double)(math.randomreal())>(double)(0.5) )
                {
                    lsfit.lsfitcreatewfg(a, y, w, cstart, alglib.ap.rows(a)/2, n, n, true, state);
                }
                else
                {
                    lsfit.lsfitcreatewf(a, y, w, cstart, alglib.ap.rows(a)/2, n, n, 0.001*cscale, state);
                }
                lsfit.lsfitsetcond(state, 0.0, 0.0, 10);
                while( lsfit.lsfititeration(state) )
                {
                    if( state.needf )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+state.c[i]*state.x[i];
                        }
                    }
                    if( state.needfg )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+state.c[i]*state.x[i];
                            state.g[i] = state.x[i];
                        }
                    }
                }
                lsfit.lsfitresults(state, ref info, ref c2, rep2);
                
                //
                // Compare covariance matrices, it should be enough to test algorithm
                //
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        nlserrors = nlserrors || (double)(Math.Abs(rep.covpar[i,j]-rep2.covpar[i,j]))>(double)(1.0E-6*Math.Max(rep.covpar[i,i], rep.covpar[j,j]));
                    }
                }
            }
            
            //
            // Check correctness of Rep.R2
            // Solve several problems with different values of R2.
            //
            // NOTE: we check only LSFitLinear() because other functions should use same algorithm
            //       for calculation of Rep.R2
            //
            n = 4;
            a = new double[4, 2];
            y = new double[4];
            a[0,0] = 1;
            a[0,1] = -2;
            y[0] = -2;
            a[1,0] = 1;
            a[1,1] = -1;
            y[1] = -1;
            a[2,0] = 1;
            a[2,1] = 1;
            y[2] = 1;
            a[3,0] = 1;
            a[3,1] = 2;
            y[3] = 2;
            lsfit.lsfitlinear(y, a, 4, 1, ref info, ref c, rep);
            llserrors = (llserrors || info<=0) || (double)(Math.Abs(rep.r2-0))>(double)(threshold);
            lsfit.lsfitlinear(y, a, 4, 2, ref info, ref c, rep);
            llserrors = (llserrors || info<=0) || (double)(Math.Abs(rep.r2-1))>(double)(threshold);
            a[0,0] = 1;
            a[0,1] = -1;
            y[0] = -1;
            a[1,0] = 1;
            a[1,1] = -1;
            y[1] = 0;
            a[2,0] = 1;
            a[2,1] = 1;
            y[2] = 1;
            a[3,0] = 1;
            a[3,1] = 1;
            y[3] = 0;
            lsfit.lsfitlinear(y, a, 4, 2, ref info, ref c, rep);
            llserrors = (llserrors || info<=0) || (double)(Math.Abs(rep.r2-0.5))>(double)(threshold);
            n = 3;
            a = new double[3, 1];
            y = new double[3];
            a[0,0] = 0;
            y[0] = 0;
            a[1,0] = 0;
            y[1] = 0;
            a[2,0] = 0;
            y[2] = 0;
            lsfit.lsfitlinear(y, a, 3, 1, ref info, ref c, rep);
            llserrors = ((llserrors || info<=0) || !math.isfinite(rep.r2)) || (double)(rep.r2)!=(double)(1);
        }


        /*************************************************************************
        This function tests RDP functionality. On error sets FitErrors flag variable;
        on success - flag is not changed.
        *************************************************************************/
        private static void testrdp(ref bool errorflag)
        {
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            double[] x = new double[0];
            double[] y = new double[0];
            double[] e = new double[0];
            double[] x2 = new double[0];
            double[] y2 = new double[0];
            double[] x3 = new double[0];
            double[] y3 = new double[0];
            double[,] xy = new double[0,0];
            double[,] xy2 = new double[0,0];
            double[,] xy3 = new double[0,0];
            int[] idx2 = new int[0];
            int[] idx3 = new int[0];
            int nsections = 0;
            int nsections3 = 0;
            double eps = 0;
            double v = 0;
            int i = 0;
            int k = 0;
            int n = 0;
            spline1d.spline1dinterpolant s = new spline1d.spline1dinterpolant();

            hqrnd.hqrndrandomize(rs);
            
            //
            // Non-parametric, single section basic test (fixed)
            //
            x = new double[2];
            y = new double[2];
            x[0] = 0;
            x[1] = 1.5;
            y[0] = 2;
            y[1] = 3;
            lsfit.lstfitpiecewiselinearrdpfixed(x, y, 2, 1, ref x2, ref y2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=1);
            if( nsections==1 )
            {
                apserv.seterrorflag(ref errorflag, (double)(x2[0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(x2[1])!=(double)(1.5));
                apserv.seterrorflag(ref errorflag, (double)(y2[0])!=(double)(2));
                apserv.seterrorflag(ref errorflag, (double)(y2[1])!=(double)(3));
            }
            
            //
            // Non-parametric, single section tied test (fixed)
            //
            x = new double[5];
            y = new double[5];
            x[0] = 0;
            x[1] = 1.5;
            x[2] = 0;
            x[3] = 0;
            x[4] = 1.5;
            y[0] = 2;
            y[1] = 1;
            y[2] = 3;
            y[3] = 1;
            y[4] = 5;
            lsfit.lstfitpiecewiselinearrdpfixed(x, y, 5, 1, ref x2, ref y2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=1);
            if( nsections==1 )
            {
                apserv.seterrorflag(ref errorflag, (double)(x2[0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(x2[1])!=(double)(1.5));
                apserv.seterrorflag(ref errorflag, (double)(y2[0])!=(double)(2));
                apserv.seterrorflag(ref errorflag, (double)(y2[1])!=(double)(3));
            }
            
            //
            // Non-parametric, two-section test (fixed)
            //
            x = new double[5];
            y = new double[5];
            x[0] = 0;
            x[1] = 0.5;
            x[2] = 1;
            x[3] = 1.75;
            x[4] = 2;
            y[0] = 1;
            y[1] = 2.1;
            y[2] = 3;
            y[3] = 5.21;
            y[4] = 6;
            lsfit.lstfitpiecewiselinearrdpfixed(x, y, 5, 2, ref x2, ref y2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=2);
            if( nsections==2 )
            {
                apserv.seterrorflag(ref errorflag, (double)(x2[0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(x2[1])!=(double)(1));
                apserv.seterrorflag(ref errorflag, (double)(x2[2])!=(double)(2));
                apserv.seterrorflag(ref errorflag, (double)(y2[0])!=(double)(1));
                apserv.seterrorflag(ref errorflag, (double)(y2[1])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(y2[2])!=(double)(6));
            }
            
            //
            // Non-parametric, variable precision test (non-fixed), results are compared against fixed-section test
            //
            eps = 10.0;
            n = 100;
            x = new double[n];
            y = new double[n];
            while( (double)(eps)>=(double)(0.0001) )
            {
                
                //
                // Generate set of randomly rearranged points
                //
                for(i=0; i<=n-1; i++)
                {
                    x[i] = Math.PI*i/(n-1);
                    y[i] = Math.Sin(x[i])+0.01*(hqrnd.hqrnduniformr(rs)-0.5);
                }
                for(i=0; i<=n-2; i++)
                {
                    k = i+hqrnd.hqrnduniformi(rs, n-i);
                    v = x[i];
                    x[i] = x[k];
                    x[k] = v;
                    v = y[i];
                    y[i] = y[k];
                    y[k] = v;
                }
                
                //
                // Perform run of eps-based RDP algorithm
                //
                lsfit.lstfitpiecewiselinearrdp(x, y, n, eps, ref x2, ref y2, ref nsections);
                apserv.seterrorflag(ref errorflag, nsections==0);
                if( nsections==0 )
                {
                    return;
                }
                
                //
                // Check properties
                //
                for(i=0; i<=nsections-1; i++)
                {
                    apserv.seterrorflag(ref errorflag, (double)(x2[i])>(double)(x2[i+1]));
                }
                spline1d.spline1dbuildlinear(x2, y2, nsections+1, s);
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref errorflag, (double)(Math.Abs(spline1d.spline1dcalc(s, x[i])-y[i]))>(double)(eps));
                }
                
                //
                // compare results with values returned by section-based algorithm
                //
                lsfit.lstfitpiecewiselinearrdpfixed(x, y, n, nsections, ref x3, ref y3, ref nsections3);
                apserv.seterrorflag(ref errorflag, nsections3!=nsections);
                if( errorflag )
                {
                    return;
                }
                for(i=0; i<=nsections-1; i++)
                {
                    apserv.seterrorflag(ref errorflag, (double)(Math.Abs(x2[i]-x3[i]))>(double)(1000*math.machineepsilon));
                    apserv.seterrorflag(ref errorflag, (double)(Math.Abs(y2[i]-y3[i]))>(double)(1000*math.machineepsilon));
                }
                
                //
                // Next epsilon
                //
                eps = eps*0.5;
            }
            
            //
            // Test that non-parametric RDP correctly handles requests for more than N-1 section
            //
            n = 100;
            x = new double[n];
            y = new double[n];
            for(i=0; i<=n-1; i++)
            {
                x[i] = Math.PI*hqrnd.hqrnduniformr(rs);
                y[i] = Math.Sin(x[i])+0.01*(hqrnd.hqrnduniformr(rs)-0.5);
            }
            lsfit.lstfitpiecewiselinearrdpfixed(x, y, n, n, ref x2, ref y2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=n-1);
        }


        /*************************************************************************
        This function tests 4PL/5PL fitting. On error sets FitErrors flag variable;
        on success - flag is not changed.
        *************************************************************************/
        private static void testlogisticfitting(ref bool fiterrors)
        {
            double[] x = new double[0];
            double[] y = new double[0];
            int n = 0;
            int nz = 0;
            int ntotal = 0;
            int i = 0;
            int k = 0;
            double v = 0;
            double vv = 0;
            int k0 = 0;
            int k1 = 0;
            double v0 = 0;
            double v1 = 0;
            int pass = 0;
            int idxa = 0;
            int idxb = 0;
            int idxc = 0;
            int idxd = 0;
            int idxg = 0;
            int idxx = 0;
            double a = 0;
            double b = 0;
            double c = 0;
            double d = 0;
            double g = 0;
            double ae = 0;
            double be = 0;
            double ce = 0;
            double de = 0;
            double ge = 0;
            double scalex = 0;
            double scaley = 0;
            double noise = 0;
            double tol = 0;
            lsfit.lsfitreport rep = new lsfit.lsfitreport();
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            double er2 = 0;
            double erms = 0;
            double eavg = 0;
            double eavgrel = 0;
            double emax = 0;
            double rss = 0;
            double tss = 0;
            double meany = 0;

            hqrnd.hqrndrandomize(rs);
            
            //
            // 4PL/5PL calculation
            //
            tol = 1.0E-6;
            for(idxa=-5; idxa<=5; idxa++)
            {
                for(idxb=-3; idxb<=3; idxb++)
                {
                    for(idxc=-5; idxc<=5; idxc++)
                    {
                        for(idxd=-5; idxd<=5; idxd++)
                        {
                            for(idxg=-5; idxg<=5; idxg++)
                            {
                                for(idxx=-5; idxx<=5; idxx++)
                                {
                                    
                                    //
                                    // Convert Idx* to corresponding parameter value
                                    //
                                    a = idxa;
                                    b = idxb;
                                    c = Math.Pow(2, idxc);
                                    d = idxd;
                                    g = Math.Pow(2, idxg);
                                    if( idxx!=0 )
                                    {
                                        v = Math.Pow(2, idxx);
                                    }
                                    else
                                    {
                                        v = 0;
                                    }
                                    
                                    //
                                    // Test 4PL calculation
                                    //
                                    vv = 0.0;
                                    if( (double)(v)!=(double)(0) )
                                    {
                                        vv = d+(a-d)/(1+Math.Pow(v/c, b));
                                    }
                                    else
                                    {
                                        if( (double)(b)>(double)(0) )
                                        {
                                            vv = a;
                                        }
                                        if( (double)(b)<(double)(0) )
                                        {
                                            vv = d;
                                        }
                                        if( (double)(b)==(double)(0) )
                                        {
                                            vv = 0.5*(a+d);
                                        }
                                    }
                                    v0 = lsfit.logisticcalc4(v, a, b, c, d);
                                    apserv.seterrorflag(ref fiterrors, (double)(Math.Abs(vv-v0))>(double)(tol));
                                    
                                    //
                                    // Test 5PL calculation
                                    //
                                    if( (double)(v)!=(double)(0) )
                                    {
                                        vv = d+(a-d)/Math.Pow(1+Math.Pow(v/c, b), g);
                                    }
                                    else
                                    {
                                        if( (double)(b)>(double)(0) )
                                        {
                                            vv = a;
                                        }
                                        if( (double)(b)<(double)(0) )
                                        {
                                            vv = d;
                                        }
                                        if( (double)(b)==(double)(0) )
                                        {
                                            vv = d+(a-d)/Math.Pow(2, g);
                                        }
                                    }
                                    v0 = lsfit.logisticcalc5(v, a, b, c, d, g);
                                    apserv.seterrorflag(ref fiterrors, (double)(Math.Abs(vv-v0))>(double)(tol));
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // 4PL fitting
            //
            // Generate random AE/BE/CE/DE, generate random set of points and for
            // each point generate two function values: F(x)+eps and F(x)-eps.
            // Such problem has solution which is exactly AE/BE/CE/DE which were
            // used to generate points.
            //
            // This test checks both unconstrained and constrained fitting (latter
            // one is performed with A constrained to AE, B constrained to BE).
            //
            tol = 0.15;
            for(pass=1; pass<=100; pass++)
            {
                
                //
                // Generate 2*N points with non-zero X and 2*NZ points with
                // zero X. In most cases we choose N<>0 and NZ<>0, but in
                // some cases either N or NZ (but not both) is zero.
                //
                // X-values have scale equal to ScaleX
                //
                scalex = Math.Pow(10, 30*hqrnd.hqrnduniformr(rs)-15);
                n = 40+hqrnd.hqrnduniformi(rs, 40);
                nz = 4+hqrnd.hqrnduniformi(rs, 4);
                if( (double)(hqrnd.hqrnduniformr(rs))<(double)(0.1) )
                {
                    if( (double)(hqrnd.hqrnduniformr(rs))<(double)(0.5) )
                    {
                        n = 0;
                    }
                    else
                    {
                        nz = 0;
                    }
                }
                ntotal = 2*(n+nz);
                x = new double[ntotal];
                for(i=0; i<=n-1; i++)
                {
                    v = scalex*Math.Exp(Math.Log(5)*(2*hqrnd.hqrnduniformr(rs)-1));
                    x[2*i+0] = v;
                    x[2*i+1] = v;
                }
                for(i=0; i<=nz-1; i++)
                {
                    x[2*n+2*i+0] = 0;
                    x[2*n+2*i+1] = 0;
                }
                
                //
                // Fenerate A/B/C/D:
                // * A/D are random with scale equal to ScaleY
                // * B is in [0.25,4.0] (always positive)
                // * for C we choose one of X[], if N>0;
                //   if N=0, we set C=1.
                //
                scaley = Math.Pow(10, 30*hqrnd.hqrnduniformr(rs)-15);
                ae = scaley*(hqrnd.hqrnduniformr(rs)-0.5);
                be = Math.Exp(Math.Log(4)*(2*hqrnd.hqrnduniformr(rs)-1));
                ce = scalex*Math.Exp(Math.Log(2)*(2*hqrnd.hqrnduniformr(rs)-1));
                de = ae+scaley*(2*hqrnd.hqrnduniformi(rs, 2)-1)*(hqrnd.hqrnduniformr(rs)+0.5);
                
                //
                // Choose noise level and generate Y[].
                //
                noise = 0.05*scaley;
                y = new double[ntotal];
                for(i=0; i<=ntotal/2-1; i++)
                {
                    if( (double)(x[2*i+0])!=(double)(0) )
                    {
                        v = de+(ae-de)/(1.0+Math.Pow(x[2*i+0]/ce, be));
                    }
                    else
                    {
                        v = ae;
                    }
                    y[2*i+0] = v+noise;
                    y[2*i+1] = v-noise;
                }
                
                //
                // Unconstrained fit and test
                //
                // NOTE: we test that B>=0 is returned. If BE<0, we use
                //       symmetry property of 4PL model.
                //
                lsfit.logisticfit4(x, y, ntotal, ref a, ref b, ref c, ref d, rep);
                apserv.seterrorflag(ref fiterrors, !math.isfinite(a));
                apserv.seterrorflag(ref fiterrors, !math.isfinite(b));
                apserv.seterrorflag(ref fiterrors, !math.isfinite(c));
                apserv.seterrorflag(ref fiterrors, !math.isfinite(d));
                apserv.seterrorflag(ref fiterrors, (double)(b)<(double)(0));
                v = 0.0;
                for(i=0; i<=ntotal-1; i++)
                {
                    if( (double)(x[i])!=(double)(0) )
                    {
                        vv = d+(a-d)/(1.0+Math.Pow(x[i]/c, b));
                    }
                    else
                    {
                        vv = a;
                    }
                    v = v+math.sqr(y[i]-vv);
                }
                v = Math.Sqrt(v/ntotal);
                apserv.seterrorflag(ref fiterrors, (double)(v)>(double)((1+tol)*noise));
                
                //
                // Constrained fit and test
                //
                // NOTE: we test that B>=0 is returned. If BE<0, we use
                //       symmetry property of 4PL model.
                //
                for(k0=0; k0<=1; k0++)
                {
                    for(k1=0; k1<=1; k1++)
                    {
                        
                        //
                        // Choose constraints.
                        //
                        if( k0==0 )
                        {
                            v0 = Double.NaN;
                        }
                        else
                        {
                            v0 = ae;
                        }
                        if( k1==0 )
                        {
                            v1 = Double.NaN;
                        }
                        else
                        {
                            v1 = de;
                        }
                        
                        //
                        // Fit
                        //
                        lsfit.logisticfit4ec(x, y, ntotal, v0, v1, ref a, ref b, ref c, ref d, rep);
                        
                        //
                        // Check
                        //
                        apserv.seterrorflag(ref fiterrors, !math.isfinite(a));
                        apserv.seterrorflag(ref fiterrors, !math.isfinite(b));
                        apserv.seterrorflag(ref fiterrors, !math.isfinite(c));
                        apserv.seterrorflag(ref fiterrors, !math.isfinite(d));
                        apserv.seterrorflag(ref fiterrors, (double)(b)<(double)(0));
                        apserv.seterrorflag(ref fiterrors, k0!=0 && (double)(a)!=(double)(v0));
                        apserv.seterrorflag(ref fiterrors, k1!=0 && (double)(d)!=(double)(v1));
                        v = 0.0;
                        for(i=0; i<=ntotal-1; i++)
                        {
                            if( (double)(x[i])!=(double)(0) )
                            {
                                vv = d+(a-d)/(1.0+Math.Pow(x[i]/c, b));
                            }
                            else
                            {
                                if( (double)(b)>=(double)(0) )
                                {
                                    vv = a;
                                }
                                else
                                {
                                    vv = d;
                                }
                            }
                            v = v+math.sqr(y[i]-vv);
                        }
                        v = Math.Sqrt(v/ntotal);
                        apserv.seterrorflag(ref fiterrors, (double)(v)>(double)((1+tol)*noise));
                    }
                }
            }
            
            //
            // 5PL fitting
            //
            // Generate random AE/BE/CE/DE/GE, generate random set of points and for
            // each point generate two function values: F(x)+eps and F(x)-eps.
            // Such problem has solution which is exactly AE/BE/CE/DE which were
            // used to generate points.
            //
            // NOTE: because problem has higher condition number, we use lower
            //       tolerance for power parameters B and G.
            //
            // This test checks both unconstrained and constrained fitting.
            //
            tol = 0.01;
            for(pass=1; pass<=100; pass++)
            {
                
                //
                // Generate N points, N-1 of them with non-zero X and
                // last one with zero X.
                // X-values have scale equal to ScaleX
                //
                scalex = Math.Pow(10, 30*hqrnd.hqrnduniformr(rs)-15);
                n = 22;
                x = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = scalex*Math.Pow(2, i-10);
                }
                x[n-1] = 0;
                
                //
                // Generate A/B/C/D/G:
                // * A/D are random with scale equal to ScaleY
                // * B is in +-[0.25,4.0]
                // * G is in   [0.25,4.0]
                // * C is in   [0.25,4.0]*ScaleX
                //   if N=0, we set C=1.
                // Generate Y[].
                //
                scaley = Math.Pow(10, 30*hqrnd.hqrnduniformr(rs)-15);
                ae = scaley*(hqrnd.hqrnduniformr(rs)-0.5);
                be = (2*hqrnd.hqrnduniformi(rs, 2)-1)*Math.Exp(Math.Log(4)*(2*hqrnd.hqrnduniformr(rs)-1));
                ce = scalex*Math.Exp(Math.Log(5)*(2*hqrnd.hqrnduniformr(rs)-1));
                de = ae+scaley*(2*hqrnd.hqrnduniformi(rs, 2)-1)*(hqrnd.hqrnduniformr(rs)+0.5);
                ge = Math.Exp(Math.Log(1.5)*(2*hqrnd.hqrnduniformr(rs)-1));
                y = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    if( (double)(x[i])!=(double)(0) )
                    {
                        v = de+(ae-de)/Math.Pow(1.0+Math.Pow(x[i]/ce, be), ge);
                    }
                    else
                    {
                        if( (double)(be)>=(double)(0) )
                        {
                            v = ae;
                        }
                        else
                        {
                            v = de;
                        }
                    }
                    y[i] = v;
                }
                
                //
                // Unconstrained fit and test
                //
                // NOTE: we test that B>=0 is returned. If BE<0, we use
                //       symmetry property of 4PL model.
                //
                lsfit.logisticfit5(x, y, n, ref a, ref b, ref c, ref d, ref g, rep);
                v = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    if( (double)(x[i])!=(double)(0) )
                    {
                        vv = d+(a-d)/Math.Pow(1.0+Math.Pow(x[i]/c, b), g);
                    }
                    else
                    {
                        if( (double)(b)>=(double)(0) )
                        {
                            vv = a;
                        }
                        else
                        {
                            vv = d;
                        }
                    }
                    v = v+math.sqr(y[i]-vv);
                }
                v = Math.Sqrt(v/n);
                apserv.seterrorflag(ref fiterrors, (double)(v)>(double)(scaley*1.0E-6));
                
                //
                // Constrained fit and test
                //
                for(k0=0; k0<=1; k0++)
                {
                    for(k1=0; k1<=1; k1++)
                    {
                        
                        //
                        // Choose constraints.
                        //
                        if( k0==0 )
                        {
                            v0 = Double.NaN;
                        }
                        else
                        {
                            if( (double)(be)>=(double)(0) )
                            {
                                v0 = ae;
                            }
                            else
                            {
                                v0 = de;
                            }
                        }
                        if( k1==0 )
                        {
                            v1 = Double.NaN;
                        }
                        else
                        {
                            if( (double)(be)>=(double)(0) )
                            {
                                v1 = de;
                            }
                            else
                            {
                                v1 = ae;
                            }
                        }
                        
                        //
                        // Fit
                        //
                        lsfit.logisticfit5ec(x, y, n, v0, v1, ref a, ref b, ref c, ref d, ref g, rep);
                        apserv.seterrorflag(ref fiterrors, !math.isfinite(a));
                        apserv.seterrorflag(ref fiterrors, !math.isfinite(b));
                        apserv.seterrorflag(ref fiterrors, !math.isfinite(c));
                        apserv.seterrorflag(ref fiterrors, !math.isfinite(d));
                        if( (double)(b)>(double)(0) )
                        {
                            apserv.seterrorflag(ref fiterrors, k0!=0 && (double)(a)!=(double)(v0));
                            apserv.seterrorflag(ref fiterrors, k1!=0 && (double)(d)!=(double)(v1));
                        }
                        else
                        {
                            apserv.seterrorflag(ref fiterrors, k0!=0 && (double)(d)!=(double)(v0));
                            apserv.seterrorflag(ref fiterrors, k1!=0 && (double)(a)!=(double)(v1));
                        }
                        v = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            if( (double)(x[i])!=(double)(0) )
                            {
                                vv = d+(a-d)/Math.Pow(1.0+Math.Pow(x[i]/c, b), g);
                            }
                            else
                            {
                                if( (double)(b)>=(double)(0) )
                                {
                                    vv = a;
                                }
                                else
                                {
                                    vv = d;
                                }
                            }
                            v = v+math.sqr(y[i]-vv);
                        }
                        v = Math.Sqrt(v/n);
                        apserv.seterrorflag(ref fiterrors, (double)(v)>(double)(scaley*1.0E-6));
                    }
                }
            }
            
            //
            // Test correctness of errors
            //
            tol = 1.0E-6;
            for(pass=1; pass<=50; pass++)
            {
                n = 10;
                meany = 0.0;
                x = new double[n];
                y = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = i;
                    y[i] = hqrnd.hqrnduniformr(rs)-0.5;
                    meany = meany+y[i];
                }
                meany = meany/n;
                x[1] = 0;
                
                //
                // Choose model fitting function to test
                //
                k = hqrnd.hqrnduniformi(rs, 4);
                a = 0;
                d = 0;
                c = 1;
                b = 1;
                g = 1;
                if( k==0 )
                {
                    lsfit.logisticfit4(x, y, n, ref a, ref b, ref c, ref d, rep);
                    g = 1.0;
                }
                if( k==1 )
                {
                    lsfit.logisticfit4ec(x, y, n, hqrnd.hqrnduniformr(rs)-0.5, hqrnd.hqrnduniformr(rs)-0.5, ref a, ref b, ref c, ref d, rep);
                    g = 1.0;
                }
                if( k==2 )
                {
                    lsfit.logisticfit5(x, y, n, ref a, ref b, ref c, ref d, ref g, rep);
                }
                if( k==3 )
                {
                    lsfit.logisticfit5ec(x, y, n, hqrnd.hqrnduniformr(rs)-0.5, hqrnd.hqrnduniformr(rs)-0.5, ref a, ref b, ref c, ref d, ref g, rep);
                }
                k = 0;
                erms = 0;
                eavg = 0;
                eavgrel = 0;
                emax = 0;
                rss = 0.0;
                tss = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    if( (double)(x[i])!=(double)(0) )
                    {
                        v = d+(a-d)/Math.Pow(1.0+Math.Pow(x[i]/c, b), g);
                    }
                    else
                    {
                        if( (double)(b)>=(double)(0) )
                        {
                            v = a;
                        }
                        else
                        {
                            v = d;
                        }
                    }
                    v = v-y[i];
                    rss = rss+v*v;
                    tss = tss+math.sqr(y[i]-meany);
                    erms = erms+math.sqr(v);
                    eavg = eavg+Math.Abs(v);
                    if( (double)(y[i])!=(double)(0) )
                    {
                        eavgrel = eavgrel+Math.Abs(v/y[i]);
                        k = k+1;
                    }
                    emax = Math.Max(emax, Math.Abs(v));
                }
                er2 = 1.0-rss/tss;
                erms = Math.Sqrt(erms/n);
                eavg = eavg/n;
                if( k>0 )
                {
                    eavgrel = eavgrel/k;
                }
                apserv.seterrorflag(ref fiterrors, (double)(Math.Abs(erms-rep.rmserror))>(double)(tol));
                apserv.seterrorflag(ref fiterrors, (double)(Math.Abs(eavg-rep.avgerror))>(double)(tol));
                apserv.seterrorflag(ref fiterrors, (double)(Math.Abs(emax-rep.maxerror))>(double)(tol));
                apserv.seterrorflag(ref fiterrors, (double)(Math.Abs(eavgrel-rep.avgrelerror))>(double)(tol));
                apserv.seterrorflag(ref fiterrors, (double)(Math.Abs(er2-rep.r2))>(double)(tol));
            }
        }


        /*************************************************************************
        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;
                }
            }
        }


        /*************************************************************************
        This function tests, that gradient verified correctly.
        *************************************************************************/
        private static void testgradientcheck(ref bool testg)
        {
            lsfit.lsfitstate state = new lsfit.lsfitstate();
            lsfit.lsfitreport rep = new lsfit.lsfitreport();
            int n = 0;
            int m = 0;
            int k = 0;
            double[] c = new double[0];
            double[] cres = new double[0];
            double[,] x = new double[0,0];
            double[] y = new double[0];
            double[] x0 = new double[0];
            int info = 0;
            double[] bl = new double[0];
            double[] bu = new double[0];
            int infcomp = 0;
            double teststep = 0;
            double noise = 0;
            int nbrcomp = 0;
            double spp = 0;
            int func = 0;
            int pass = 0;
            int passcount = 0;
            int i = 0;
            int j = 0;

            testg = new bool();

            passcount = 35;
            spp = 1.0;
            teststep = 0.01;
            for(pass=1; pass<=passcount; pass++)
            {
                m = math.randominteger(5)+1;
                x0 = new double[m];
                k = math.randominteger(5)+1;
                c = new double[k];
                bl = new double[k];
                bu = new double[k];
                
                //
                // Prepare test's parameters
                //
                func = math.randominteger(3)+1;
                n = math.randominteger(8)+3;
                x = new double[n, m];
                y = new double[n];
                nbrcomp = math.randominteger(k);
                noise = 2*math.randominteger(2)-1;
                
                //
                // Prepare function's parameters
                //
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        x[i,j] = spp*(2*math.randomreal()-1);
                    }
                    y[i] = spp*(2*math.randomreal()-1);
                }
                for(i=0; i<=k-1; i++)
                {
                    c[i] = spp*(2*math.randomreal()-1);
                }
                for(i=0; i<=m-1; i++)
                {
                    x0[i] = 10*(2*math.randomreal()-1);
                }
                
                //
                // Prepare boundary parameters
                //
                for(i=0; i<=k-1; i++)
                {
                    bl[i] = math.randomreal()-spp;
                    bu[i] = math.randomreal()+spp-1;
                }
                infcomp = math.randominteger(k+1);
                if( infcomp<k )
                {
                    bl[infcomp] = Double.NegativeInfinity;
                }
                infcomp = math.randominteger(k+1);
                if( infcomp<k )
                {
                    bu[infcomp] = Double.PositiveInfinity;
                }
                lsfit.lsfitcreatefg(x, y, c, n, m, k, true, state);
                lsfit.lsfitsetgradientcheck(state, teststep);
                lsfit.lsfitsetcond(state, 0.0, 0.0, 100);
                lsfit.lsfitsetbc(state, bl, bu);
                
                //
                // Check that the criterion passes a derivative if it is correct
                //
                while( lsfit.lsfititeration(state) )
                {
                    if( state.needfg )
                    {
                        funcderiv(state.c, state.x, x0, k, m, func, ref state.f, ref state.g);
                    }
                }
                lsfit.lsfitresults(state, ref info, ref cres, rep);
                
                //
                // Check that error code does not equal to -7 and parameter .VarIdx
                // equal to -1.
                //
                if( info==-7 || rep.varidx!=-1 )
                {
                    testg = true;
                    return;
                }
                
                //
                // Create again and...
                //
                lsfit.lsfitcreatefg(x, y, c, n, m, k, true, state);
                lsfit.lsfitsetgradientcheck(state, teststep);
                lsfit.lsfitsetcond(state, 0.0, 0.0, 100);
                lsfit.lsfitsetbc(state, bl, bu);
                
                //
                // Check that the criterion does not miss a derivative if
                // it is incorrect
                //
                while( lsfit.lsfititeration(state) )
                {
                    if( state.needfg )
                    {
                        funcderiv(state.c, state.x, x0, k, m, func, ref state.f, ref state.g);
                        state.g[nbrcomp] = state.g[nbrcomp]+noise;
                    }
                }
                lsfit.lsfitresults(state, ref info, ref cres, rep);
                
                //
                // Check that error code equal to -7 and parameter .VarIdx
                // equal to number of incorrect component.
                //
                if( info!=-7 || rep.varidx!=nbrcomp )
                {
                    testg = true;
                    return;
                }
            }
            testg = false;
        }


        /*************************************************************************
        This function return function's value(F=F(X,C)) and it derivatives(DF=dF/dC).
        Function dimension is M. Length(C) is K.
            Function's list:
                * funcType=1:
                    K>M:
                    F(X)=C0^2*(X0-CX0)^2+C1^2*(X1-CX1)^2+...+CM^2*(XM-CXM)^2
                        +C(M+1)^2+...+CK^2;
                    K<M:
                    F(X)=C0^2*(X0-CX0)^2+C1^2*(X1-CX1)^2+...+CK^2*(XK-CXK)^2
                        +(X(K+1)-CX(K+1))^2+...+(XM-CXM)^2;
                * funcType=2:
                    K>M:
                    F(X)=C0*sin(X0-CX0)^2+C1*sin(X1-CX1)^2+...+CM*sin(XM-CXM)^2
                        +C(M+1)^3+...+CK^3;
                    K<M
                    F(X)=C0*sin(X0-CX0)^2+C1*sin(X1-CX1)^2+...+CK*sin(XK-CXK)^2
                        +sin(X(K+1)-CX(K+1))^2+...+sin(XM-CXM)^2;
                * funcType=3:
                    F(X)=C0^2+C1^2+...+CK^2+(X0-CX0)^2+(X1-CX1)^2+...+(XM-CXM)^2.
        *************************************************************************/
        private static void funcderiv(double[] c,
            double[] x,
            double[] x0,
            int k,
            int m,
            int functype,
            ref double f,
            ref double[] g)
        {
            int i = 0;

            alglib.ap.assert(functype>=1 && functype<=3, "FuncDeriv: incorrect funcType(funcType<1 or funcType>3).");
            alglib.ap.assert(k>0, "FuncDeriv: K<=0");
            alglib.ap.assert(m>0, "FuncDeriv: M<=0");
            alglib.ap.assert(alglib.ap.len(x)>=m, "FuncDeriv: Length(X)<M");
            alglib.ap.assert(apserv.isfinitevector(x, m), "FuncDeriv: X contains NaN or Infinite.");
            alglib.ap.assert(alglib.ap.len(x0)>=m, "FuncDeriv: Length(X0)<M");
            alglib.ap.assert(apserv.isfinitevector(x0, m), "FuncDeriv: X0 contains NaN or Infinite.");
            alglib.ap.assert(alglib.ap.len(c)>=k, "FuncDeriv: Length(X)<K");
            alglib.ap.assert(apserv.isfinitevector(c, k), "FuncDeriv: C contains NaN or Infinite.");
            if( functype==1 )
            {
                f = 0;
                for(i=0; i<=Math.Min(m, k)-1; i++)
                {
                    f = f+math.sqr(c[i]*(x[i]-x0[i]));
                    g[i] = 2*c[i]*math.sqr(x[i]-x0[i]);
                }
                if( k>m )
                {
                    for(i=m; i<=k-1; i++)
                    {
                        f = f+math.sqr(c[i]);
                        g[i] = 2*c[i];
                    }
                }
                if( k<m )
                {
                    for(i=k; i<=m-1; i++)
                    {
                        f = f+math.sqr(x[i]-x0[i]);
                    }
                }
                return;
            }
            if( functype==2 )
            {
                f = 0;
                for(i=0; i<=Math.Min(m, k)-1; i++)
                {
                    f = f+c[i]*math.sqr(Math.Sin(x[i]-x0[i]));
                    g[i] = math.sqr(Math.Sin(x[i]-x0[i]));
                }
                if( k>m )
                {
                    for(i=m; i<=k-1; i++)
                    {
                        f = f+c[i]*c[i]*c[i];
                        g[i] = 3*math.sqr(c[i]);
                    }
                }
                if( k<m )
                {
                    for(i=k; i<=m-1; i++)
                    {
                        f = f+math.sqr(Math.Sin(x[i]-x0[i]));
                    }
                }
                return;
            }
            if( functype==3 )
            {
                f = 0;
                for(i=0; i<=m-1; i++)
                {
                    f = f+math.sqr(x[i]-x0[i]);
                }
                for(i=0; i<=k-1; i++)
                {
                    f = f+c[i]*c[i];
                }
                for(i=0; i<=k-1; i++)
                {
                    g[i] = 2*c[i];
                }
                return;
            }
        }


    }
    public class testparametricunit
    {
        public static bool testparametric(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool p2errors = new bool();
            bool p3errors = new bool();
            bool rdperrors = 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];
            parametric.pspline2interpolant p2 = new parametric.pspline2interpolant();
            parametric.pspline3interpolant p3 = new parametric.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;
            rdperrors = false;
            testrdp(ref rdperrors);
            
            //
            // 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 )
                            {
                                parametric.pspline2buildperiodic(xy, n, skind, pkind, p2);
                                parametric.pspline3buildperiodic(xyz, n, skind, pkind, p3);
                            }
                            else
                            {
                                parametric.pspline2build(xy, n, skind, pkind, p2);
                                parametric.pspline3build(xyz, n, skind, pkind, p3);
                            }
                            
                            //
                            // PSpline2ParameterValues() properties
                            //
                            parametric.pspline2parametervalues(p2, ref tmpn, ref t2);
                            if( tmpn!=n )
                            {
                                p2errors = true;
                                continue;
                            }
                            parametric.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
                                //
                                parametric.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
                                //
                                parametric.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
                                    //
                                    parametric.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);
                                    parametric.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);
                                    parametric.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
                                    //
                                    parametric.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);
                                    parametric.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);
                                    parametric.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();
                                parametric.pspline2calc(p2, v0, ref vx, ref vy);
                                parametric.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);
                                parametric.pspline3calc(p3, v0, ref vx, ref vy, ref vz);
                                parametric.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
                                //
                                alglib.ap.assert(skind==1 || skind==2, "TEST: unexpected spline type!");
                                v0 = 100*math.machineepsilon;
                                v1 = 1-v0;
                                parametric.pspline2calc(p2, v0, ref vx, ref vy);
                                parametric.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);
                                parametric.pspline2diff(p2, v0, ref vx, ref vdx, ref vy, ref vdy);
                                parametric.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);
                                parametric.pspline2diff2(p2, v0, ref vx, ref vdx, ref vd2x, ref vy, ref vdy, ref vd2y);
                                parametric.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
                                //
                                alglib.ap.assert(skind==1 || skind==2, "TEST: unexpected spline type!");
                                v0 = 100*math.machineepsilon;
                                v1 = 1-v0;
                                parametric.pspline3calc(p3, v0, ref vx, ref vy, ref vz);
                                parametric.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);
                                parametric.pspline3diff(p3, v0, ref vx, ref vdx, ref vy, ref vdy, ref vz, ref vdz);
                                parametric.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);
                                parametric.pspline3diff2(p3, v0, ref vx, ref vdx, ref vd2x, ref vy, ref vdy, ref vd2y, ref vz, ref vdz, ref vd2z);
                                parametric.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);
                parametric.pspline2build(xy, n, 2, 0, p2);
                parametric.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
                //
                parametric.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);
                parametric.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);
                parametric.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
                //
                parametric.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);
                parametric.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);
                parametric.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
                //
                parametric.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);
                parametric.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;
                }
                parametric.pspline2build(xy, n, 1, 0, p2);
                parametric.pspline3build(xyz, n, 1, 0, p3);
                a = math.randomreal();
                b = math.randomreal();
                p2errors = p2errors || (double)(Math.Abs(parametric.pspline2arclength(p2, a, b)-(b-a)*Math.Sqrt(2)*(n-1)))>(double)(nonstrictthreshold);
                p3errors = p3errors || (double)(Math.Abs(parametric.pspline3arclength(p3, a, b)-(b-a)*Math.Sqrt(3)*(n-1)))>(double)(nonstrictthreshold);
            }
            
            //
            // report
            //
            waserrors = (p2errors || p3errors) || rdperrors;
            if( !silent )
            {
                System.Console.Write("TESTING PARAMETRIC INTERPOLATION");
                System.Console.WriteLine();
                
                //
                // Normal tests
                //
                System.Console.Write("2D SPLINES:                              ");
                if( p2errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("3D SPLINES:                              ");
                if( p3errors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("RDP:                                     ");
                if( rdperrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testparametric(bool silent)
        {
            return testparametric(silent);
        }


        /*************************************************************************
        This function tests 4PL/5PL fitting. On error sets FitErrors flag variable;
        on success - flag is not changed.
        *************************************************************************/
        private static void testrdp(ref bool errorflag)
        {
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            double[] x = new double[0];
            double[] y = new double[0];
            double[] e = new double[0];
            double[] x2 = new double[0];
            double[] y2 = new double[0];
            double[] x3 = new double[0];
            double[] y3 = new double[0];
            double[,] xy = new double[0,0];
            double[,] xy2 = new double[0,0];
            double[,] xy3 = new double[0,0];
            int[] idx2 = new int[0];
            int[] idx3 = new int[0];
            int nsections = 0;
            int nsections3 = 0;
            double eps = 0;
            double v = 0;
            int i = 0;
            int j = 0;
            int n = 0;
            int d = 0;
            spline1d.spline1dinterpolant s = new spline1d.spline1dinterpolant();

            hqrnd.hqrndrandomize(rs);
            
            //
            // Parametric test 1: non-closed curve
            //
            xy = new double[4, 2];
            xy[0,0] = 0;
            xy[0,1] = 0;
            xy[1,0] = 1;
            xy[1,1] = 2;
            xy[2,0] = 3;
            xy[2,1] = 1;
            xy[3,0] = 3;
            xy[3,1] = 3;
            parametric.parametricrdpfixed(xy, 4, 2, 0, Math.Sqrt(2)+0.001, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=1);
            if( nsections==1 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(3));
                apserv.seterrorflag(ref errorflag, idx2[1]!=3);
            }
            parametric.parametricrdpfixed(xy, 4, 2, 0, Math.Sqrt(2)-0.001, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=3);
            if( nsections==3 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(1));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(2));
                apserv.seterrorflag(ref errorflag, idx2[1]!=1);
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,1])!=(double)(1));
                apserv.seterrorflag(ref errorflag, idx2[2]!=2);
                apserv.seterrorflag(ref errorflag, (double)(xy2[3,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[3,1])!=(double)(3));
                apserv.seterrorflag(ref errorflag, idx2[3]!=3);
            }
            parametric.parametricrdpfixed(xy, 4, 2, 1, 0.0, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=1);
            if( nsections==1 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(3));
                apserv.seterrorflag(ref errorflag, idx2[1]!=3);
            }
            parametric.parametricrdpfixed(xy, 4, 2, 2, 0.0, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=2);
            if( nsections==2 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(1));
                apserv.seterrorflag(ref errorflag, idx2[1]!=2);
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,1])!=(double)(3));
                apserv.seterrorflag(ref errorflag, idx2[2]!=3);
            }
            parametric.parametricrdpfixed(xy, 4, 2, 3, 0.0, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=3);
            if( nsections==3 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(1));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(2));
                apserv.seterrorflag(ref errorflag, idx2[1]!=1);
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,1])!=(double)(1));
                apserv.seterrorflag(ref errorflag, idx2[2]!=2);
                apserv.seterrorflag(ref errorflag, (double)(xy2[3,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[3,1])!=(double)(3));
                apserv.seterrorflag(ref errorflag, idx2[3]!=3);
            }
            parametric.parametricrdpfixed(xy, 4, 2, 4, 0.0, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=3);
            if( nsections==3 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(1));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(2));
                apserv.seterrorflag(ref errorflag, idx2[1]!=1);
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,1])!=(double)(1));
                apserv.seterrorflag(ref errorflag, idx2[2]!=2);
                apserv.seterrorflag(ref errorflag, (double)(xy2[3,0])!=(double)(3));
                apserv.seterrorflag(ref errorflag, (double)(xy2[3,1])!=(double)(3));
                apserv.seterrorflag(ref errorflag, idx2[3]!=3);
            }
            
            //
            // Parametric test 2: closed curve
            //
            xy = new double[5, 2];
            xy[0,0] = 0;
            xy[0,1] = 0;
            xy[1,0] = 1;
            xy[1,1] = 0;
            xy[2,0] = 1;
            xy[2,1] = 1;
            xy[3,0] = 0;
            xy[3,1] = 1;
            xy[4,0] = 0;
            xy[4,1] = 0;
            parametric.parametricrdpfixed(xy, 5, 2, 0, Math.Sqrt(2)+0.001, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=1);
            if( nsections==1 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[1]!=4);
            }
            parametric.parametricrdpfixed(xy, 5, 2, 0, Math.Sqrt(2)-0.001, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=2);
            if( nsections==2 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(1));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(1));
                apserv.seterrorflag(ref errorflag, idx2[1]!=2);
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[2]!=4);
            }
            parametric.parametricrdpfixed(xy, 5, 2, 0, Math.Sqrt(2)/2+0.001, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=2);
            if( nsections==2 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(1));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(1));
                apserv.seterrorflag(ref errorflag, idx2[1]!=2);
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[2]!=4);
            }
            parametric.parametricrdpfixed(xy, 5, 2, 0, Math.Sqrt(2)/2-0.001, ref xy2, ref idx2, ref nsections);
            apserv.seterrorflag(ref errorflag, nsections!=4);
            if( nsections==4 )
            {
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[0,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,0])!=(double)(1));
                apserv.seterrorflag(ref errorflag, (double)(xy2[1,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[1]!=1);
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,0])!=(double)(1));
                apserv.seterrorflag(ref errorflag, (double)(xy2[2,1])!=(double)(1));
                apserv.seterrorflag(ref errorflag, idx2[2]!=2);
                apserv.seterrorflag(ref errorflag, (double)(xy2[3,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[3,1])!=(double)(1));
                apserv.seterrorflag(ref errorflag, idx2[3]!=3);
                apserv.seterrorflag(ref errorflag, (double)(xy2[4,0])!=(double)(0));
                apserv.seterrorflag(ref errorflag, (double)(xy2[4,1])!=(double)(0));
                apserv.seterrorflag(ref errorflag, idx2[4]!=4);
            }
            
            //
            // Parametric, variable precision test (non-fixed), results are compared against fixed-section test
            //
            eps = 10.0;
            n = 100;
            while( (double)(eps)>=(double)(0.0001) )
            {
                
                //
                // Try different dimension counts
                //
                for(d=1; d<=5; d++)
                {
                    
                    //
                    // Generate dataset
                    //
                    xy = new double[n, d];
                    for(i=0; i<=n-1; i++)
                    {
                        v = Math.PI*i/(n-1);
                        for(j=0; j<=d-1; j++)
                        {
                            xy[i,j] = Math.Pow(Math.Sin(v), j+1)+0.01*(hqrnd.hqrnduniformr(rs)-0.5);
                        }
                    }
                    
                    //
                    // Perform run of eps-based RDP algorithm
                    //
                    parametric.parametricrdpfixed(xy, n, d, 0, eps, ref xy2, ref idx2, ref nsections);
                    apserv.seterrorflag(ref errorflag, nsections==0);
                    if( nsections==0 )
                    {
                        return;
                    }
                    
                    //
                    // Check properties
                    //
                    apserv.seterrorflag(ref errorflag, idx2[0]!=0);
                    for(i=0; i<=nsections-1; i++)
                    {
                        apserv.seterrorflag(ref errorflag, idx2[i]>=idx2[i+1]);
                    }
                    apserv.seterrorflag(ref errorflag, idx2[nsections]!=n-1);
                    for(i=0; i<=nsections; i++)
                    {
                        for(j=0; j<=d-1; j++)
                        {
                            apserv.seterrorflag(ref errorflag, (double)(xy2[i,j])!=(double)(xy[idx2[i],j]));
                        }
                    }
                    x = new double[nsections+1];
                    y = new double[nsections+1];
                    e = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        e[i] = 0;
                    }
                    for(j=0; j<=d-1; j++)
                    {
                        for(i=0; i<=nsections; i++)
                        {
                            x[i] = idx2[i];
                            y[i] = xy2[i,j];
                        }
                        spline1d.spline1dbuildlinear(x, y, nsections+1, s);
                        for(i=0; i<=n-1; i++)
                        {
                            e[i] = e[i]+math.sqr(spline1d.spline1dcalc(s, i)-xy[i,j]);
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref errorflag, (double)(Math.Sqrt(e[i]))>(double)(eps));
                    }
                    
                    //
                    // compare results with values returned by section-based algorithm
                    //
                    parametric.parametricrdpfixed(xy, n, d, nsections, 0.0, ref xy3, ref idx3, ref nsections3);
                    apserv.seterrorflag(ref errorflag, nsections3!=nsections);
                    if( errorflag )
                    {
                        return;
                    }
                    for(i=0; i<=nsections; i++)
                    {
                        apserv.seterrorflag(ref errorflag, idx2[i]!=idx3[i]);
                        for(j=0; j<=d-1; j++)
                        {
                            apserv.seterrorflag(ref errorflag, (double)(Math.Abs(xy2[i,j]-xy3[i,j]))>(double)(1000*math.machineepsilon));
                        }
                    }
                }
                
                //
                // Next epsilon
                //
                eps = eps*0.5;
            }
        }


        /*************************************************************************
        Unset spline, i.e. initialize it with random garbage
        *************************************************************************/
        private static void unsetp2(parametric.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;
            parametric.pspline2build(xy, 2, 1, 0, p);
        }


        /*************************************************************************
        Unset spline, i.e. initialize it with random garbage
        *************************************************************************/
        private static void unsetp3(parametric.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;
            parametric.pspline3build(xy, 2, 1, 0, p);
        }


    }
    public class testlinlsqrunit
    {
        public const double e0 = 1.0E-6;
        public const double tolort = 1.0E-4;
        public const double e1 = 1.0E+6;
        public const double emergencye0 = 1.0E-12;


        public static bool testlinlsqr(bool silent)
        {
            bool result = new bool();
            bool svdtesterrors = new bool();
            bool mwcranksvderr = new bool();
            bool mwicranksvderr = new bool();
            bool bidiagonalerr = new bool();
            bool zeromatrixerr = new bool();
            bool reportcorrectnesserr = new bool();
            bool stoppingcriteriaerr = new bool();
            bool analytictesterrors = new bool();
            bool prectesterrors = new bool();
            bool waserrors = new bool();

            svdtesterrors = svdtest(true);
            mwcranksvderr = mwcranksvdtest(true);
            mwicranksvderr = mwicranksvdtest(true);
            bidiagonalerr = bidiagonaltest(true);
            zeromatrixerr = zeromatrixtest(true);
            reportcorrectnesserr = reportcorrectnesstest(true);
            stoppingcriteriaerr = stoppingcriteriatest(true);
            analytictesterrors = analytictest(true);
            prectesterrors = preconditionertest();
            
            //
            // report
            //
            waserrors = (((((((svdtesterrors || mwcranksvderr) || mwicranksvderr) || bidiagonalerr) || zeromatrixerr) || reportcorrectnesserr) || stoppingcriteriaerr) || analytictesterrors) || prectesterrors;
            if( !silent )
            {
                System.Console.Write("TESTING LinLSQR");
                System.Console.WriteLine();
                System.Console.Write("SVDTest:                                      ");
                if( svdtesterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("MWCRankSVDTest:                               ");
                if( mwcranksvderr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("MWICRankSVDTest:                              ");
                if( mwicranksvderr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("BidiagonalTest:                               ");
                if( bidiagonalerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("ZeroMatrixTest:                               ");
                if( zeromatrixerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("ReportCorrectnessTest:                        ");
                if( reportcorrectnesserr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("StoppingCriteriaTest:                         ");
                if( stoppingcriteriaerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Analytic properties:                          ");
                if( analytictesterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Preconditioner test:                          ");
                if( prectesterrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                
                //
                //was errors?
                //
                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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testlinlsqr(bool silent)
        {
            return testlinlsqr(silent);
        }


        /*************************************************************************
        This  function  generates  random  MxN  problem,  solves  it with LSQR and
        compares with results obtained from SVD solver. Matrix A is  generated  as
        MxN  matrix  with  uniformly  distributed  random  entries, i.e. it has no
        special properties (like conditioning or separation of singular values).

        We apply random amount regularization to our problem (from zero to  large)
        in  order  to  test  regularizer.  Default  stopping  criteria  are  used.
        Preconditioning is turned off because it skews results for  rank-deficient
        problems.

        INPUT: 
            Silent   -   if true then function output report

          -- ALGLIB --
             Copyright 30.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool svdtest(bool silent)
        {
            bool result = new bool();
            linlsqr.linlsqrstate s = new linlsqr.linlsqrstate();
            linlsqr.linlsqrreport rep = new linlsqr.linlsqrreport();
            sparse.sparsematrix spa = new sparse.sparsematrix();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double[] x0 = new double[0];
            int szn = 0;
            int szm = 0;
            int n = 0;
            int m = 0;
            double lambdai = 0;
            int i = 0;
            int j = 0;

            szm = 5;
            szn = 5;
            for(m=1; m<=szm; m++)
            {
                for(n=1; n<=szn; n++)
                {
                    
                    //
                    // Prepare MxN matrix A, right part B, lambda
                    //
                    lambdai = math.randomreal();
                    a = new double[m, n];
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 2*math.randomreal()-1;
                        }
                    }
                    sparse.sparsecreate(m, n, 1, spa);
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            sparse.sparseset(spa, i, j, a[i,j]);
                        }
                    }
                    sparse.sparseconverttocrs(spa);
                    b = new double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        b[i] = 2*math.randomreal()-1;
                    }
                    
                    //
                    // Solve by calling LinLSQRIteration
                    //
                    linlsqr.linlsqrcreate(m, n, s);
                    linlsqr.linlsqrsetb(s, b);
                    linlsqr.linlsqrsetlambdai(s, lambdai);
                    linlsqr.linlsqrsetprecunit(s);
                    while( linlsqr.linlsqriteration(s) )
                    {
                        if( s.needmv )
                        {
                            for(i=0; i<=m-1; i++)
                            {
                                s.mv[i] = 0;
                                for(j=0; j<=n-1; j++)
                                {
                                    s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                }
                            }
                        }
                        if( s.needmtv )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                s.mtv[i] = 0;
                                for(j=0; j<=m-1; j++)
                                {
                                    s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                                }
                            }
                        }
                    }
                    linlsqr.linlsqrresults(s, ref x0, rep);
                    if( !isitgoodsolution(a, b, m, n, lambdai, x0, e0, tolort) )
                    {
                        result = true;
                        return result;
                    }
                    
                    //
                    //test LinLSQRRestart and LinLSQRSolveSparse
                    //
                    linlsqr.linlsqrrestart(s);
                    linlsqr.linlsqrsolvesparse(s, spa, b);
                    linlsqr.linlsqrresults(s, ref x0, rep);
                    if( !isitgoodsolution(a, b, m, n, lambdai, x0, e0, tolort) )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            if( !silent )
            {
                System.Console.Write("SVDTest::Ok");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        The test checks that algorithm can solve MxN (with N<=M)  well-conditioned
        problems - and can do so within exactly  N  iterations.  We  use  moderate
        condition numbers, from 1.0 to 10.0, because larger condition  number  may
        require several additional iterations to converge.

        We try different scalings of the A and B.

        INPUT: 
            Silent   -   if true then function does not outputs results to console

          -- ALGLIB --
             Copyright 30.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool mwcranksvdtest(bool silent)
        {
            bool result = new bool();
            linlsqr.linlsqrstate s = new linlsqr.linlsqrstate();
            linlsqr.linlsqrreport rep = new linlsqr.linlsqrreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double bnorm = 0;
            double[] x0 = new double[0];
            int szm = 0;
            int n = 0;
            int m = 0;
            int ns0 = 0;
            int ns1 = 0;
            int nlambdai = 0;
            double s0 = 0;
            double s1 = 0;
            double lambdai = 0;
            double c = 0;
            int i = 0;
            int j = 0;

            szm = 5;
            for(m=1; m<=szm; m++)
            {
                for(n=1; n<=m; n++)
                {
                    for(nlambdai=0; nlambdai<=3; nlambdai++)
                    {
                        for(ns0=-1; ns0<=1; ns0++)
                        {
                            for(ns1=-1; ns1<=1; ns1++)
                            {
                                
                                //
                                // Generate problem:
                                // * scale factors s0, s1
                                // * MxN well conditioned A (with condition number C in [1,10] and norm s0)
                                // * regularization coefficient LambdaI
                                // * right part b, with |b|=s1
                                //
                                s0 = Math.Pow(10, 10*ns0);
                                s1 = Math.Pow(10, 10*ns1);
                                lambdai = 0;
                                if( nlambdai==0 )
                                {
                                    lambdai = 0;
                                }
                                if( nlambdai==1 )
                                {
                                    lambdai = s0/1000;
                                }
                                if( nlambdai==2 )
                                {
                                    lambdai = s0;
                                }
                                if( nlambdai==3 )
                                {
                                    lambdai = s0*1000;
                                }
                                c = (10-1)*math.randomreal()+1;
                                matgen.rmatrixrndcond(m, c, ref a);
                                for(i=0; i<=m-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = s0*a[i,j];
                                    }
                                }
                                b = new double[m];
                                do
                                {
                                    bnorm = 0;
                                    for(i=0; i<=m-1; i++)
                                    {
                                        b[i] = 2*math.randomreal()-1;
                                        bnorm = bnorm+b[i]*b[i];
                                    }
                                    bnorm = Math.Sqrt(bnorm);
                                }
                                while( (double)(bnorm)<=(double)(e0) );
                                for(i=0; i<=m-1; i++)
                                {
                                    b[i] = b[i]*s1/bnorm;
                                }
                                
                                //
                                // Solve by LSQR method
                                //
                                linlsqr.linlsqrcreate(m, n, s);
                                linlsqr.linlsqrsetb(s, b);
                                linlsqr.linlsqrsetcond(s, 0, 0, n);
                                linlsqr.linlsqrsetlambdai(s, lambdai);
                                while( linlsqr.linlsqriteration(s) )
                                {
                                    if( s.needmv )
                                    {
                                        for(i=0; i<=m-1; i++)
                                        {
                                            s.mv[i] = 0;
                                            for(j=0; j<=n-1; j++)
                                            {
                                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                            }
                                        }
                                    }
                                    if( s.needmtv )
                                    {
                                        for(i=0; i<=n-1; i++)
                                        {
                                            s.mtv[i] = 0;
                                            for(j=0; j<=m-1; j++)
                                            {
                                                s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                                            }
                                        }
                                    }
                                }
                                linlsqr.linlsqrresults(s, ref x0, rep);
                                if( !isitgoodsolution(a, b, m, n, lambdai, x0, e0, tolort) )
                                {
                                    result = true;
                                    return result;
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        The test checks that algorithm can find a solution with minimum norm for a
        singular rectangular problem. System matrix has special property - singular 
        values are either zero or well separated from zero.

        INPUT: 
            Silent   -   if true then function output report

          -- ALGLIB --
             Copyright 30.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool mwicranksvdtest(bool silent)
        {
            bool result = new bool();
            linlsqr.linlsqrstate s = new linlsqr.linlsqrstate();
            linlsqr.linlsqrreport rep = new linlsqr.linlsqrreport();
            sparse.sparsematrix spa = new sparse.sparsematrix();
            double[] b = new double[0];
            double bnorm = 0;
            double[] x0 = new double[0];
            int szm = 0;
            int n = 0;
            int m = 0;
            int nz = 0;
            int ns0 = 0;
            int ns1 = 0;
            int nlambdai = 0;
            double s0 = 0;
            double s1 = 0;
            double lambdai = 0;
            int i = 0;
            int j = 0;
            double[,] a = new double[0,0];

            result = false;
            szm = 5;
            for(m=1; m<=szm; m++)
            {
                for(n=1; n<=m; n++)
                {
                    for(nlambdai=0; nlambdai<=2; nlambdai++)
                    {
                        for(ns0=-1; ns0<=1; ns0++)
                        {
                            for(ns1=-1; ns1<=1; ns1++)
                            {
                                for(nz=0; nz<=n-1; nz++)
                                {
                                    
                                    //
                                    // Generate problem:
                                    // * scale coefficients s0, s1
                                    // * regularization coefficient LambdaI
                                    // * MxN matrix A, norm(A)=s0, with NZ zero singular values and N-NZ nonzero ones
                                    // * right part b with norm(b)=s1
                                    //
                                    s0 = Math.Pow(10, 10*ns0);
                                    s1 = Math.Pow(10, 10*ns1);
                                    lambdai = 0;
                                    if( nlambdai==0 )
                                    {
                                        lambdai = 0;
                                    }
                                    if( nlambdai==1 )
                                    {
                                        lambdai = s0;
                                    }
                                    if( nlambdai==2 )
                                    {
                                        lambdai = s0*1000;
                                    }
                                    a = new double[m, n];
                                    for(i=0; i<=m-1; i++)
                                    {
                                        for(j=0; j<=n-1; j++)
                                        {
                                            a[i,j] = 0;
                                        }
                                    }
                                    for(i=0; i<=n-nz-1; i++)
                                    {
                                        a[i,i] = s0*(0.1+0.9*math.randomreal());
                                    }
                                    matgen.rmatrixrndorthogonalfromtheleft(ref a, m, n);
                                    matgen.rmatrixrndorthogonalfromtheright(ref a, m, n);
                                    b = new double[m];
                                    do
                                    {
                                        bnorm = 0;
                                        for(i=0; i<=m-1; i++)
                                        {
                                            b[i] = 2*math.randomreal()-1;
                                            bnorm = bnorm+b[i]*b[i];
                                        }
                                        bnorm = Math.Sqrt(bnorm);
                                    }
                                    while( (double)(bnorm)<=(double)(e0) );
                                    for(i=0; i<=m-1; i++)
                                    {
                                        b[i] = b[i]*s1/bnorm;
                                    }
                                    
                                    //
                                    // Solve by LSQR method
                                    //
                                    linlsqr.linlsqrcreate(m, n, s);
                                    linlsqr.linlsqrsetb(s, b);
                                    linlsqr.linlsqrsetcond(s, emergencye0, emergencye0, n);
                                    linlsqr.linlsqrsetlambdai(s, lambdai);
                                    while( linlsqr.linlsqriteration(s) )
                                    {
                                        if( s.needmv )
                                        {
                                            for(i=0; i<=m-1; i++)
                                            {
                                                s.mv[i] = 0;
                                                for(j=0; j<=n-1; j++)
                                                {
                                                    s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                                }
                                            }
                                        }
                                        if( s.needmtv )
                                        {
                                            for(i=0; i<=n-1; i++)
                                            {
                                                s.mtv[i] = 0;
                                                for(j=0; j<=m-1; j++)
                                                {
                                                    s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                                                }
                                            }
                                        }
                                    }
                                    linlsqr.linlsqrresults(s, ref x0, rep);
                                    
                                    //
                                    // Check
                                    //
                                    if( !isitgoodsolution(a, b, m, n, lambdai, x0, e0, tolort) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        The test does check, that algorithm can find a solution with minimum norm,
        if a problem has bidiagonal matrix on diagonals of a lot of zeros. This
        problem has to lead to case when State.Alpha and State.Beta are zero, and we
        we can be sure that the algorithm correctly handles it.

        We do not use iteration count as stopping condition, because problem can
        be degenerate and we may need more than N iterations to converge.

        INPUT: 
            Silent   -   if true then function output report

          -- ALGLIB --
             Copyright 30.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool bidiagonaltest(bool silent)
        {
            bool result = new bool();
            linlsqr.linlsqrstate s = new linlsqr.linlsqrstate();
            linlsqr.linlsqrreport rep = new linlsqr.linlsqrreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double bnorm = 0;
            double[] x0 = new double[0];
            int sz = 0;
            int n = 0;
            int m = 0;
            int minmn = 0;
            int ns0 = 0;
            int ns1 = 0;
            double s0 = 0;
            double s1 = 0;
            int i = 0;
            int j = 0;
            int p = 0;
            int diag = 0;
            double pz = 0;

            sz = 5;
            for(m=1; m<=sz; m++)
            {
                for(n=1; n<=sz; n++)
                {
                    minmn = Math.Min(m, n);
                    for(p=0; p<=2; p++)
                    {
                        for(ns0=-1; ns0<=1; ns0++)
                        {
                            for(ns1=-1; ns1<=1; ns1++)
                            {
                                for(diag=0; diag<=1; diag++)
                                {
                                    
                                    //
                                    // Generate problem:
                                    // * scaling coefficients s0, s1
                                    // * bidiagonal A, with probability of having zero element at diagonal equal to PZ
                                    //
                                    s0 = Math.Pow(10, 10*ns0);
                                    s1 = Math.Pow(10, 10*ns1);
                                    pz = 0.0;
                                    if( p==0 )
                                    {
                                        pz = 0.25;
                                    }
                                    if( p==1 )
                                    {
                                        pz = 0.5;
                                    }
                                    if( p==2 )
                                    {
                                        pz = 0.75;
                                    }
                                    a = new double[m, n];
                                    for(i=0; i<=m-1; i++)
                                    {
                                        for(j=0; j<=n-1; j++)
                                        {
                                            a[i,j] = 0;
                                        }
                                    }
                                    for(i=0; i<=minmn-1; i++)
                                    {
                                        if( (double)(math.randomreal())>=(double)(pz) )
                                        {
                                            a[i,i] = 2*math.randomreal()-1;
                                        }
                                    }
                                    for(i=1; i<=minmn-1; i++)
                                    {
                                        if( (double)(math.randomreal())>=(double)(pz) )
                                        {
                                            if( diag==0 )
                                            {
                                                a[i-1,i] = 2*math.randomreal()-1;
                                            }
                                            if( diag==1 )
                                            {
                                                a[i,i-1] = 2*math.randomreal()-1;
                                            }
                                        }
                                    }
                                    for(i=0; i<=m-1; i++)
                                    {
                                        for(j=0; j<=n-1; j++)
                                        {
                                            a[i,j] = s0*a[i,j];
                                        }
                                    }
                                    b = new double[m];
                                    do
                                    {
                                        bnorm = 0;
                                        for(i=0; i<=m-1; i++)
                                        {
                                            b[i] = 2*math.randomreal()-1;
                                            bnorm = bnorm+b[i]*b[i];
                                        }
                                        bnorm = Math.Sqrt(bnorm);
                                    }
                                    while( (double)(bnorm)<=(double)(e0) );
                                    for(i=0; i<=m-1; i++)
                                    {
                                        b[i] = b[i]*s1/bnorm;
                                    }
                                    
                                    //
                                    // LSQR solution
                                    //
                                    linlsqr.linlsqrcreate(m, n, s);
                                    linlsqr.linlsqrsetb(s, b);
                                    linlsqr.linlsqrsetcond(s, e0, e0, 0);
                                    while( linlsqr.linlsqriteration(s) )
                                    {
                                        if( s.needmv )
                                        {
                                            for(i=0; i<=m-1; i++)
                                            {
                                                s.mv[i] = 0;
                                                for(j=0; j<=n-1; j++)
                                                {
                                                    s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                                }
                                            }
                                        }
                                        if( s.needmtv )
                                        {
                                            for(i=0; i<=n-1; i++)
                                            {
                                                s.mtv[i] = 0;
                                                for(j=0; j<=m-1; j++)
                                                {
                                                    s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                                                }
                                            }
                                        }
                                    }
                                    linlsqr.linlsqrresults(s, ref x0, rep);
                                    
                                    //
                                    // Check
                                    //
                                    if( !isitgoodsolution(a, b, m, n, 0.0, x0, e0, tolort) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        The test does check, that algorithm correctly solves a problem in cases:
            1. A=0, B<>0;
            2. A<>0, B=0;
            3. A=0, B=0.
        If some part is not zero then it filled with ones.

        INPUT: 
            Silent   -   if true then function output report

          -- ALGLIB --
             Copyright 30.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool zeromatrixtest(bool silent)
        {
            bool result = new bool();
            linlsqr.linlsqrstate s = new linlsqr.linlsqrstate();
            linlsqr.linlsqrreport rep = new linlsqr.linlsqrreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double[] x0 = new double[0];
            int sz = 0;
            int n = 0;
            int m = 0;
            int i = 0;
            int j = 0;
            int nzeropart = 0;

            sz = 5;
            result = false;
            for(m=1; m<=sz; m++)
            {
                for(n=1; n<=sz; n++)
                {
                    for(nzeropart=0; nzeropart<=2; nzeropart++)
                    {
                        
                        //
                        // Initialize A, b
                        //
                        a = new double[m, n];
                        if( nzeropart==0 || nzeropart==2 )
                        {
                            for(i=0; i<=m-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        else
                        {
                            for(i=0; i<=m-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 1;
                                }
                            }
                        }
                        b = new double[m];
                        if( nzeropart==1 || nzeropart==2 )
                        {
                            for(i=0; i<=m-1; i++)
                            {
                                b[i] = 0;
                            }
                        }
                        else
                        {
                            for(i=0; i<=m-1; i++)
                            {
                                b[i] = 1;
                            }
                        }
                        
                        //
                        // LSQR
                        //
                        linlsqr.linlsqrcreate(m, n, s);
                        linlsqr.linlsqrsetb(s, b);
                        linlsqr.linlsqrsetcond(s, 0, 0, n);
                        while( linlsqr.linlsqriteration(s) )
                        {
                            if( s.needmv )
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    s.mv[i] = 0;
                                    for(j=0; j<=n-1; j++)
                                    {
                                        s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                    }
                                }
                            }
                            if( s.needmtv )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    s.mtv[i] = 0;
                                    for(j=0; j<=m-1; j++)
                                    {
                                        s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                                    }
                                }
                            }
                        }
                        linlsqr.linlsqrresults(s, ref x0, rep);
                        
                        //
                        // Check
                        //
                        if( !isitgoodsolution(a, b, m, n, 0.0, x0, e0, tolort) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        The test does check, that algorithm correctly displays a progress report.

        INPUT: 
            Silent   -   if true then function output report

          -- ALGLIB --
             Copyright 30.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool reportcorrectnesstest(bool silent)
        {
            bool result = new bool();
            linlsqr.linlsqrstate s = new linlsqr.linlsqrstate();
            linlsqr.linlsqrreport rep = new linlsqr.linlsqrreport();
            double[,] a = new double[0,0];
            double[,] u = new double[0,0];
            double[,] v = new double[0,0];
            double[] w = new double[0];
            double[] b = new double[0];
            double[] x0 = new double[0];
            double[] firstx = new double[0];
            double[] lastx = new double[0];
            double rnorm = 0;
            double tnorm = 0;
            int sz = 0;
            int n = 0;
            int m = 0;
            int lambdai = 0;
            double mn = 0;
            double mx = 0;
            double c = 0;
            int i = 0;
            int j = 0;
            int its = 0;
            double tmp = 0;
            double eps = 0;

            eps = 0.001;
            sz = 5;
            mn = -100;
            mx = 100;
            c = 100;
            for(m=1; m<=sz; m++)
            {
                for(n=1; n<=m; n++)
                {
                    for(lambdai=0; lambdai<=1; lambdai++)
                    {
                        its = -1;
                        
                        //
                        //initialize matrix A
                        //
                        matgen.spdmatrixrndcond(m+n, c, ref a);
                        for(i=m; i<=m+n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( i-m==j )
                                {
                                    a[i,j] = lambdai;
                                }
                                else
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        
                        //
                        //initialize b
                        //
                        b = new double[m+n];
                        rnorm = 0;
                        for(i=0; i<=m+n-1; i++)
                        {
                            if( i<m )
                            {
                                b[i] = (mx-mn)*math.randomreal()+mn;
                                rnorm = rnorm+b[i]*b[i];
                            }
                            else
                            {
                                b[i] = 0;
                            }
                        }
                        
                        //
                        //initialize FirstX and LastX
                        //
                        firstx = new double[n];
                        lastx = new double[n];
                        
                        //
                        //calculating by LSQR method
                        //
                        linlsqr.linlsqrcreate(m, n, s);
                        linlsqr.linlsqrsetb(s, b);
                        linlsqr.linlsqrsetcond(s, 0, 0, n);
                        linlsqr.linlsqrsetlambdai(s, lambdai);
                        linlsqr.linlsqrsetxrep(s, true);
                        while( linlsqr.linlsqriteration(s) )
                        {
                            if( s.needmv )
                            {
                                for(i=0; i<=m-1; i++)
                                {
                                    s.mv[i] = 0;
                                    for(j=0; j<=n-1; j++)
                                    {
                                        s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                    }
                                }
                            }
                            if( s.needmtv )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    s.mtv[i] = 0;
                                    for(j=0; j<=m-1; j++)
                                    {
                                        s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                                    }
                                }
                            }
                            if( s.xupdated )
                            {
                                tnorm = 0;
                                for(i=0; i<=m+n-1; i++)
                                {
                                    tmp = 0;
                                    for(j=0; j<=n-1; j++)
                                    {
                                        tmp = tmp+a[i,j]*s.x[j];
                                    }
                                    tnorm = tnorm+(b[i]-tmp)*(b[i]-tmp);
                                }
                                
                                //
                                //check, that RNorm is't more than S.R2
                                //and difference between S.R2 and TNorm
                                //is't more than 'eps'(here S.R2=||rk||,
                                //calculated by the algorithm for LSQR, and
                                //TNorm=||A*S.x-b||, calculated by test function).
                                //
                                if( (double)(s.r2)>(double)(rnorm) || (double)(Math.Abs(s.r2-tnorm))>(double)(eps) )
                                {
                                    if( !silent )
                                    {
                                        System.Console.Write("ReportCorrectnessTest::Fail");
                                        System.Console.WriteLine();
                                        System.Console.Write("TNorm=");
                                        System.Console.Write("{0,0:E2}",tnorm);
                                        System.Console.Write(";RNorm=");
                                        System.Console.Write("{0,0:E2}",rnorm);
                                        System.Console.Write(";S.R2=");
                                        System.Console.Write("{0,0:E2}",s.r2);
                                        System.Console.Write(";");
                                        System.Console.WriteLine();
                                    }
                                    result = true;
                                    return result;
                                }
                                rnorm = s.r2;
                                its = its+1;
                                
                                //
                                //get X value from first iteration 
                                //and from last iteration.
                                //
                                if( its==0 )
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        firstx[i] = s.x[i];
                                    }
                                }
                                if( its==n )
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        lastx[i] = s.x[i];
                                    }
                                }
                            }
                        }
                        linlsqr.linlsqrresults(s, ref x0, rep);
                        
                        //
                        //check, that FirstX is equal to zero and LastX
                        //is equal to x0.
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            if( (double)(firstx[i])!=(double)(0) || (double)(lastx[i])!=(double)(x0[i]) )
                            {
                                if( !silent )
                                {
                                    System.Console.Write("ReportCorrectnessTest::Fail");
                                    System.Console.WriteLine();
                                    System.Console.Write("IterationsCount=");
                                    System.Console.Write("{0,0:d}",rep.iterationscount);
                                    System.Console.WriteLine();
                                    System.Console.Write("NMV=");
                                    System.Console.Write("{0,0:d}",rep.nmv);
                                    System.Console.WriteLine();
                                    System.Console.Write("TerminationType=");
                                    System.Console.Write("{0,0:d}",rep.terminationtype);
                                    System.Console.WriteLine();
                                    System.Console.Write("X and LastX...");
                                    System.Console.WriteLine();
                                    for(j=0; j<=n-1; j++)
                                    {
                                        System.Console.Write("x[");
                                        System.Console.Write("{0,0:d}",j);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:E10}",x0[j]);
                                        System.Console.Write("; LastX[");
                                        System.Console.Write("{0,0:d}",j);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:E10}",lastx[j]);
                                        System.Console.WriteLine();
                                    }
                                }
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            if( !silent )
            {
                System.Console.Write("ReportCorrectnessTest::Ok");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        The test does check, that correctly executed stop criteria by algorithm.

        INPUT: 
            Silent   -   if true then function output report

          -- ALGLIB --
             Copyright 30.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool stoppingcriteriatest(bool silent)
        {
            bool result = new bool();
            linlsqr.linlsqrstate s = new linlsqr.linlsqrstate();
            linlsqr.linlsqrreport rep = new linlsqr.linlsqrreport();
            double[,] a = new double[0,0];
            double[,] u = new double[0,0];
            double[,] v = new double[0,0];
            double[] w = new double[0];
            double[] b = new double[0];
            double bnorm = 0;
            double[] x0 = new double[0];
            int sz = 0;
            int n = 0;
            int k0 = 0;
            int k1 = 0;
            double[] ark = new double[0];
            double anorm = 0;
            double arknorm = 0;
            double rknorm = 0;
            double[] rk = new double[0];
            double mn = 0;
            double mx = 0;
            double c = 0;
            int maxits = 0;
            int i = 0;
            int j = 0;
            double tmp = 0;
            double eps = 0;
            double epsmod = 0;
            int i_ = 0;

            sz = 5;
            mn = -100;
            mx = 100;
            c = 100;
            for(n=1; n<=sz; n++)
            {
                
                //
                // Initialize A, unit b
                //
                matgen.spdmatrixrndcond(n, c, ref a);
                b = new double[n];
                bnorm = 0;
                for(i=0; i<=n-1; i++)
                {
                    b[i] = (mx-mn)*math.randomreal()+mn;
                    bnorm = bnorm+b[i]*b[i];
                }
                bnorm = Math.Sqrt(bnorm);
                
                //
                // Test MaxIts
                //
                // NOTE: we do not check TerminationType because algorithm may terminate for
                // several reasons. The only thing which is guaranteed is that stopping condition
                // MaxIts holds.
                //
                maxits = 1+math.randominteger(n);
                linlsqr.linlsqrcreate(n, n, s);
                linlsqr.linlsqrsetb(s, b);
                linlsqr.linlsqrsetcond(s, 0, 0, maxits);
                while( linlsqr.linlsqriteration(s) )
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                        }
                    }
                    if( s.needmtv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mtv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                            }
                        }
                    }
                }
                linlsqr.linlsqrresults(s, ref x0, rep);
                if( rep.iterationscount>maxits || rep.terminationtype<=0 )
                {
                    if( !silent )
                    {
                        System.Console.Write("StoppingCriteriaTest::Fail");
                        System.Console.WriteLine();
                        System.Console.Write("N=");
                        System.Console.Write("{0,0:d}",n);
                        System.Console.WriteLine();
                        System.Console.Write("IterationsCount=");
                        System.Console.Write("{0,0:d}",rep.iterationscount);
                        System.Console.WriteLine();
                        System.Console.Write("NMV=");
                        System.Console.Write("{0,0:d}",rep.nmv);
                        System.Console.WriteLine();
                        System.Console.Write("TerminationType=");
                        System.Console.Write("{0,0:d}",rep.terminationtype);
                        System.Console.WriteLine();
                    }
                    result = true;
                    return result;
                }
                
                //
                // Test EpsB.
                // Set EpsB=eps, check that |r|<epsMod*|b|, where epsMod=1.1*eps.
                // This modified epsilon is used to avoid influence of the numerical errors.
                //
                // NOTE: we do not check TerminationType because algorithm may terminate for
                // several reasons. The only thing which is guaranteed is that stopping condition
                // EpsB holds.
                //
                eps = Math.Pow(10, -(2+math.randominteger(3)));
                epsmod = 1.1*eps;
                rk = new double[n];
                linlsqr.linlsqrcreate(n, n, s);
                linlsqr.linlsqrsetb(s, b);
                linlsqr.linlsqrsetcond(s, 0, eps, 0);
                while( linlsqr.linlsqriteration(s) )
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                        }
                    }
                    if( s.needmtv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mtv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                            }
                        }
                    }
                }
                linlsqr.linlsqrresults(s, ref x0, rep);
                rknorm = 0;
                for(i=0; i<=n-1; i++)
                {
                    tmp = 0;
                    for(j=0; j<=n-1; j++)
                    {
                        tmp = tmp+a[i,j]*x0[j];
                    }
                    rknorm = rknorm+(tmp-b[i])*(tmp-b[i]);
                }
                rknorm = Math.Sqrt(rknorm);
                if( (double)(rknorm)>(double)(epsmod*bnorm) || rep.terminationtype<=0 )
                {
                    if( !silent )
                    {
                        System.Console.Write("StoppingCriteriaTest::Fail");
                        System.Console.WriteLine();
                        System.Console.Write("rkNorm=");
                        System.Console.Write("{0,0:E2}",rknorm);
                        System.Console.WriteLine();
                        System.Console.Write("IterationsCount=");
                        System.Console.Write("{0,0:d}",rep.iterationscount);
                        System.Console.WriteLine();
                        System.Console.Write("NMV=");
                        System.Console.Write("{0,0:d}",rep.nmv);
                        System.Console.WriteLine();
                        System.Console.Write("TerminationType=");
                        System.Console.Write("{0,0:d}",rep.terminationtype);
                        System.Console.WriteLine();
                    }
                    result = true;
                    return result;
                }
            }
            
            //
            // Test EpsA.
            //
            // Generate well conditioned underdetermined system with nonzero residual
            // at the solution. Such system can be generated by generating random
            // orthogonal matrix (N>=2) and using first N-1 columns as rectangular
            // system matrix, and sum of all columns with random non-zero coefficients 
            // as right part.
            //
            for(n=2; n<=sz; n++)
            {
                for(k0=-1; k0<=1; k0++)
                {
                    for(k1=-1; k1<=1; k1++)
                    {
                        
                        //
                        // Initialize A with non-unit norm 10^(10*K0), b with non-unit norm 10^(10*K1)
                        //
                        anorm = Math.Pow(10, 10*k0);
                        matgen.rmatrixrndorthogonal(n, ref a);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a[i,j] = anorm*a[i,j];
                            }
                        }
                        b = new double[n];
                        for(j=0; j<=n-1; j++)
                        {
                            b[j] = 0;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            tmp = 1+math.randomreal();
                            for(i_=0; i_<=n-1;i_++)
                            {
                                b[i_] = b[i_] + tmp*a[i_,i];
                            }
                        }
                        tmp = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            tmp = tmp+math.sqr(b[i]);
                        }
                        tmp = Math.Pow(10, 10*k1)/Math.Sqrt(tmp);
                        for(i_=0; i_<=n-1;i_++)
                        {
                            b[i_] = tmp*b[i_];
                        }
                        
                        //
                        // Test EpsA
                        //
                        // NOTE: it is guaranteed that algorithm will terminate with correct
                        // TerminationType because other stopping criteria (EpsB) won't be satisfied
                        // on such system.
                        //
                        eps = Math.Pow(10, -(2+math.randominteger(3)));
                        epsmod = 1.1*eps;
                        linlsqr.linlsqrcreate(n, n-1, s);
                        linlsqr.linlsqrsetb(s, b);
                        linlsqr.linlsqrsetcond(s, eps, 0, 0);
                        while( linlsqr.linlsqriteration(s) )
                        {
                            if( s.needmv )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    s.mv[i] = 0;
                                    for(j=0; j<=n-2; j++)
                                    {
                                        s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                    }
                                }
                            }
                            if( s.needmtv )
                            {
                                for(i=0; i<=n-2; i++)
                                {
                                    s.mtv[i] = 0;
                                    for(j=0; j<=n-1; j++)
                                    {
                                        s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                                    }
                                }
                            }
                        }
                        linlsqr.linlsqrresults(s, ref x0, rep);
                        
                        //
                        // Check condition
                        //
                        rk = new double[n];
                        ark = new double[n-1];
                        rknorm = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            rk[i] = b[i];
                            for(j=0; j<=n-2; j++)
                            {
                                rk[i] = rk[i]-a[i,j]*x0[j];
                            }
                            rknorm = rknorm+math.sqr(rk[i]);
                        }
                        rknorm = Math.Sqrt(rknorm);
                        arknorm = 0;
                        for(i=0; i<=n-2; i++)
                        {
                            ark[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                ark[i] = ark[i]+a[j,i]*rk[j];
                            }
                            arknorm = arknorm+math.sqr(ark[i]);
                        }
                        arknorm = Math.Sqrt(arknorm);
                        if( (double)(arknorm/(anorm*rknorm))>(double)(epsmod) || rep.terminationtype!=4 )
                        {
                            if( !silent )
                            {
                                System.Console.Write("StoppingCriteriaTest::Fail");
                                System.Console.WriteLine();
                                System.Console.Write("N=");
                                System.Console.Write("{0,0:d}",n);
                                System.Console.WriteLine();
                                System.Console.Write("IterationsCount=");
                                System.Console.Write("{0,0:d}",rep.iterationscount);
                                System.Console.WriteLine();
                                System.Console.Write("NMV=");
                                System.Console.Write("{0,0:d}",rep.nmv);
                                System.Console.WriteLine();
                                System.Console.Write("TerminationType=");
                                System.Console.Write("{0,0:d}",rep.terminationtype);
                                System.Console.WriteLine();
                            }
                            result = true;
                            return result;
                        }
                    }
                }
            }
            if( !silent )
            {
                System.Console.Write("StoppingCriteriaTest::Ok");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This test compares LSQR  for  original  system  A*x=b  against  CG  for  a
        modified system (A'*A)x = A*b. Both algorithms should give same  sequences
        of trial points (under exact arithmetics, or  for  very  good  conditioned
        systems).

        INPUT: 
            Silent   -   if true then function does not output report

          -- ALGLIB --
             Copyright 30.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool analytictest(bool silent)
        {
            bool result = new bool();
            linlsqr.linlsqrstate s = new linlsqr.linlsqrstate();
            linlsqr.linlsqrreport rep = new linlsqr.linlsqrreport();
            double[,] a = new double[0,0];
            double[,] xk = new double[0,0];
            double[,] ap = new double[0,0];
            double[,] r = new double[0,0];
            double[] b = new double[0];
            double[] tmp = new double[0];
            int n = 0;
            int m = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int smallk = 0;
            int pointsstored = 0;
            double v = 0;
            double tol = 0;
            int i_ = 0;

            
            //
            // Set:
            // * SmallK - number of steps to check, must be small number in order
            //   to reduce influence of the rounding errors
            // * Tol - error tolerance for orthogonality/conjugacy criteria
            //
            result = false;
            smallk = 4;
            tol = 1.0E-7;
            for(m=smallk; m<=smallk+5; m++)
            {
                for(n=smallk; n<=m; n++)
                {
                    
                    //
                    // Prepare problem:
                    // * MxN matrix A, Mx1 vector B
                    // * A is filled with random values from [-1,+1]
                    // * diagonal elements are filled with large positive values
                    //   (should make system better conditioned)
                    //
                    a = new double[m, n];
                    b = new double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a[i,j] = 2*math.randomreal()-1;
                        }
                        b[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        a[i,i] = 10*(1+math.randomreal());
                    }
                    
                    //
                    // Solve with LSQR, save trial points into XK[] array
                    //
                    xk = new double[smallk+1, n];
                    linlsqr.linlsqrcreate(m, n, s);
                    linlsqr.linlsqrsetb(s, b);
                    linlsqr.linlsqrsetcond(s, 0, 0, smallk);
                    linlsqr.linlsqrsetxrep(s, true);
                    pointsstored = 0;
                    while( linlsqr.linlsqriteration(s) )
                    {
                        if( s.needmv )
                        {
                            for(i=0; i<=m-1; i++)
                            {
                                s.mv[i] = 0;
                                for(j=0; j<=n-1; j++)
                                {
                                    s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                }
                            }
                        }
                        if( s.needmtv )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                s.mtv[i] = 0;
                                for(j=0; j<=m-1; j++)
                                {
                                    s.mtv[i] = s.mtv[i]+a[j,i]*s.x[j];
                                }
                            }
                        }
                        if( s.xupdated )
                        {
                            alglib.ap.assert(pointsstored<alglib.ap.rows(xk), "LinLSQR test: internal error");
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xk[pointsstored,i_] = s.x[i_];
                            }
                            pointsstored = pointsstored+1;
                        }
                    }
                    if( pointsstored<3 )
                    {
                        
                        //
                        // At least two iterations should be performed
                        // (our task is not that easy to solve)
                        //
                        result = true;
                        return result;
                    }
                    
                    //
                    // We have recorded sequence of points generated by LSQR,
                    // and now we want to make a comparion against linear CG.
                    //
                    // Of course, we could just perform CG solution of (A'*A)*x = A'*b,
                    // but it will need a CG solver, and we do not want to reference one
                    // just to perform testing.
                    //
                    // However, we can do better - we can check that sequence of steps
                    // satisfies orthogonality/conjugacy conditions, which are stated
                    // as follows:
                    // * (r[i]^T)*r[j]=0 for i<>j
                    // * (p[i]^T)*A'*A*p[j]=0 for i<>j
                    // where r[i]=(A'*A)*x[i]-A'*b is I-th residual , p[i] is I-th step.
                    //
                    // In order to test these criteria we generate two matrices:
                    // * (PointsStored-1)*M matrix AP (matrix of A*p products)
                    // * (PointsStored-1)*N matrix R  (matrix of residuals)
                    // Then, we check that each matrix has orthogonal rows.
                    //
                    ap = new double[pointsstored-1, m];
                    r = new double[pointsstored-1, n];
                    tmp = new double[m];
                    for(k=0; k<=pointsstored-2; k++)
                    {
                        
                        //
                        // Calculate K-th row of AP
                        //
                        for(i=0; i<=m-1; i++)
                        {
                            ap[k,i] = 0.0;
                            for(j=0; j<=n-1; j++)
                            {
                                ap[k,i] = ap[k,i]+a[i,j]*(xk[k+1,j]-xk[k,j]);
                            }
                        }
                        
                        //
                        // Calculate K-th row of R
                        //
                        for(i=0; i<=m-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*xk[k,i_];
                            }
                            tmp[i] = v-b[i];
                        }
                        for(j=0; j<=n-1; j++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=m-1;i_++)
                            {
                                v += a[i_,j]*tmp[i_];
                            }
                            r[k,j] = v;
                        }
                    }
                    for(i=0; i<=pointsstored-2; i++)
                    {
                        for(j=0; j<=pointsstored-2; j++)
                        {
                            if( i!=j )
                            {
                                v = 0.0;
                                for(i_=0; i_<=m-1;i_++)
                                {
                                    v += ap[i,i_]*ap[j,i_];
                                }
                                result = result || (double)(Math.Abs(v))>(double)(tol);
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += r[i,i_]*r[j,i_];
                                }
                                result = result || (double)(Math.Abs(v))>(double)(tol);
                            }
                        }
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        This function compares solution calculated by LSQR with one calculated  by
        SVD solver. Following comparisons are performed:
        1. either:
           1.a) residual norm |Rk| for LSQR solution is at most epsErr*|B|
           1.b) |A^T*Rk|/(|A|*|Rk|)<=EpsOrt
        2. norm(LSQR_solution) is at most 1.2*norm(SVD_solution)

        Test (1) verifies that LSQR found good solution, test  (2)  verifies  that
        LSQR finds solution with close-to-minimum norm. We use factor as large  as
        1.2 to test deviation from SVD solution because LSQR is not very  good  at
        solving degenerate problems.

        INPUT PARAMETERS:
            A       -   array[M,N], system matrix
            B       -   right part
            M, N    -   problem size
            LambdaV -   regularization value for the problem, >=0
            X       -   array[N], solution found by LSQR
            EpsErr  -   tolerance for |A*x-b|
            EpsOrt  -   tolerance for |A^T*Rk|/(|A|*|Rk|)

        RESULT
            True, for solution which passess all the tests
        *************************************************************************/
        private static bool isitgoodsolution(double[,] a,
            double[] b,
            int m,
            int n,
            double lambdav,
            double[] x,
            double epserr,
            double epsort)
        {
            bool result = new bool();
            double[,] svda = new double[0,0];
            double[,] u = new double[0,0];
            double[,] vt = new double[0,0];
            double[] w = new double[0];
            double[] svdx = new double[0];
            double[] tmparr = new double[0];
            double[] r = new double[0];
            int i = 0;
            int j = 0;
            int minmn = 0;
            bool svdresult = new bool();
            double v = 0;
            double rnorm = 0;
            double bnorm = 0;
            double anorm = 0;
            double atrnorm = 0;
            double xnorm = 0;
            double svdxnorm = 0;
            bool clause1holds = new bool();
            bool clause2holds = new bool();
            int i_ = 0;

            
            //
            // Solve regularized problem with SVD solver
            //
            svda = new double[m+n, n];
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    svda[i,j] = a[i,j];
                }
            }
            for(i=m; i<=m+n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( i-m==j )
                    {
                        svda[i,j] = lambdav;
                    }
                    else
                    {
                        svda[i,j] = 0;
                    }
                }
            }
            svdresult = svd.rmatrixsvd(svda, m+n, n, 1, 1, 0, ref w, ref u, ref vt);
            alglib.ap.assert(svdresult, "LINLSQR: internal error in unit test (SVD failed)");
            minmn = Math.Min(m, n);
            svdx = new double[n];
            tmparr = new double[minmn];
            for(i=0; i<=minmn-1; i++)
            {
                tmparr[i] = 0;
                for(j=0; j<=m-1; j++)
                {
                    tmparr[i] = tmparr[i]+u[j,i]*b[j];
                }
                if( (double)(w[i])<=(double)(Math.Sqrt(math.machineepsilon)*w[0]) )
                {
                    tmparr[i] = 0;
                }
                else
                {
                    tmparr[i] = tmparr[i]/w[i];
                }
            }
            for(i=0; i<=n-1; i++)
            {
                svdx[i] = 0;
                for(j=0; j<=minmn-1; j++)
                {
                    svdx[i] = svdx[i]+vt[j,i]*tmparr[j];
                }
            }
            
            //
            // Calculate residual, perform checks 1.a and 1.b:
            // * first, we check 1.a
            // * in case 1.a fails we check 1.b
            //
            r = new double[m+n];
            for(i=0; i<=m+n-1; i++)
            {
                v = 0.0;
                for(i_=0; i_<=n-1;i_++)
                {
                    v += svda[i,i_]*x[i_];
                }
                r[i] = v;
                if( i<m )
                {
                    r[i] = r[i]-b[i];
                }
            }
            v = 0.0;
            for(i_=0; i_<=m+n-1;i_++)
            {
                v += r[i_]*r[i_];
            }
            rnorm = Math.Sqrt(v);
            v = 0.0;
            for(i_=0; i_<=m-1;i_++)
            {
                v += b[i_]*b[i_];
            }
            bnorm = Math.Sqrt(v);
            if( (double)(rnorm)<=(double)(epserr*bnorm) )
            {
                
                //
                // 1.a is true, no further checks is needed
                //
                clause1holds = true;
            }
            else
            {
                
                //
                // 1.a is false, we have to check 1.b
                //
                // In order to do so, we calculate ||A|| and ||A^T*Rk||. We do
                // not store product of A and Rk to some array, all we need is
                // just one component of product at time, stored in V.
                // 
                //
                anorm = 0;
                atrnorm = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = 0;
                    for(j=0; j<=m+n-1; j++)
                    {
                        v = v+svda[j,i]*r[j];
                        anorm = anorm+math.sqr(svda[j,i]);
                    }
                    atrnorm = atrnorm+math.sqr(v);
                }
                anorm = Math.Sqrt(anorm);
                atrnorm = Math.Sqrt(atrnorm);
                clause1holds = (double)(anorm*rnorm)==(double)(0) || (double)(atrnorm/(anorm*rnorm))<=(double)(epsort);
            }
            
            //
            // Check (2).
            // Here we assume that Result=True when we enter this block.
            //
            v = 0.0;
            for(i_=0; i_<=n-1;i_++)
            {
                v += x[i_]*x[i_];
            }
            xnorm = Math.Sqrt(v);
            v = 0.0;
            for(i_=0; i_<=n-1;i_++)
            {
                v += svdx[i_]*svdx[i_];
            }
            svdxnorm = Math.Sqrt(v);
            clause2holds = (double)(xnorm)<=(double)(1.2*svdxnorm);
            
            //
            // End
            //
            result = clause1holds && clause2holds;
            return result;
        }


        /*************************************************************************
        Function for testing preconditioned LSQR method.

          -- ALGLIB --
             Copyright 14.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool preconditionertest()
        {
            bool result = new bool();
            linlsqr.linlsqrstate s = new linlsqr.linlsqrstate();
            linlsqr.linlsqrreport rep = new linlsqr.linlsqrreport();
            double[,] a = new double[0,0];
            double[,] ta = new double[0,0];
            sparse.sparsematrix sa = new sparse.sparsematrix();
            double[] b = new double[0];
            double[] d = new double[0];
            double[] xe = new double[0];
            double[] x0 = new double[0];
            bool bflag = new bool();
            int i = 0;
            int j = 0;
            int n = 0;

            
            //
            // Test 1.
            //
            // We test automatic diagonal preconditioning used by SolveSparse.
            // In order to do so we:
            // 1. generate 20*20 matrix A0 with condition number equal to 1.0E1
            // 2. generate random "exact" solution xe and right part b=A0*xe
            // 3. generate random ill-conditioned diagonal scaling matrix D with
            //    condition number equal to 1.0E50:
            // 4. transform A*x=b into badly scaled problem:
            //    A0*x0=b0
            //    (A0*D)*(inv(D)*x0)=b0
            //    finally we got new problem A*x=b with A=A0*D, b=b0, x=inv(D)*x0
            //
            // Then we solve A*x=b:
            // 1. with default preconditioner
            // 2. with explicitly activayed diagonal preconditioning
            // 3. with unit preconditioner.
            // 1st and 2nd solutions must be close to xe, 3rd solution must be very
            // far from the true one.
            //
            n = 20;
            matgen.rmatrixrndcond(n, 1.0E1, ref ta);
            xe = new double[n];
            for(i=0; i<=n-1; i++)
            {
                xe[i] = apserv.randomnormal();
            }
            b = new double[n];
            for(i=0; i<=n-1; i++)
            {
                b[i] = 0;
                for(j=0; j<=n-1; j++)
                {
                    b[i] = b[i]+ta[i,j]*xe[j];
                }
            }
            d = new double[n];
            for(i=0; i<=n-1; i++)
            {
                d[i] = Math.Pow(10, 100*math.randomreal()-50);
            }
            a = new double[n, n];
            sparse.sparsecreate(n, n, n*n, sa);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a[i,j] = ta[i,j]*d[j];
                    sparse.sparseset(sa, i, j, a[i,j]);
                }
                xe[i] = xe[i]/d[i];
            }
            sparse.sparseconverttocrs(sa);
            linlsqr.linlsqrcreate(n, n, s);
            linlsqr.linlsqrsetcond(s, 0, 0, 2*n);
            linlsqr.linlsqrsolvesparse(s, sa, b);
            linlsqr.linlsqrresults(s, ref x0, rep);
            if( rep.terminationtype<=0 )
            {
                result = true;
                return result;
            }
            for(i=0; i<=n-1; i++)
            {
                if( (double)(Math.Abs(xe[i]-x0[i]))>(double)(5.0E-2/d[i]) )
                {
                    result = true;
                    return result;
                }
            }
            linlsqr.linlsqrsetprecunit(s);
            linlsqr.linlsqrsolvesparse(s, sa, b);
            linlsqr.linlsqrresults(s, ref x0, rep);
            if( rep.terminationtype>0 )
            {
                bflag = false;
                for(i=0; i<=n-1; i++)
                {
                    bflag = bflag || (double)(Math.Abs(xe[i]-x0[i]))>(double)(5.0E-2/d[i]);
                }
                if( !bflag )
                {
                    result = true;
                    return result;
                }
            }
            linlsqr.linlsqrsetprecdiag(s);
            linlsqr.linlsqrsolvesparse(s, sa, b);
            linlsqr.linlsqrresults(s, ref x0, rep);
            if( rep.terminationtype<=0 )
            {
                result = true;
                return result;
            }
            for(i=0; i<=n-1; i++)
            {
                if( (double)(Math.Abs(xe[i]-x0[i]))>(double)(5.0E-2/d[i]) )
                {
                    result = true;
                    return result;
                }
            }
            
            //
            //test has been passed
            //
            result = false;
            return result;
        }


    }
    public class testrbfunit
    {
        public const int mxnx = 3;
        public const double eps = 1.0E-6;
        public const double tol = 1.0E-10;
        public const int mxits = 0;
        public const double heps = 1.0E-12;


        public static bool testrbf(bool silent)
        {
            bool result = new bool();
            bool specialerrors = new bool();
            bool basicrbferrors = new bool();
            bool irregularrbferrors = new bool();
            bool linearitymodelrbferr = new bool();
            bool sqrdegmatrixrbferr = new bool();
            bool sererrors = new bool();
            bool multilayerrbf1derrors = new bool();
            bool multilayerrbferrors = new bool();
            bool waserrors = new bool();

            specialerrors = specialtest();
            basicrbferrors = basicrbftest();
            irregularrbferrors = irregularrbftest();
            linearitymodelrbferr = linearitymodelrbftest();
            sqrdegmatrixrbferr = sqrdegmatrixrbftest(true);
            multilayerrbf1derrors = false;
            multilayerrbferrors = basicmultilayerrbftest();
            sererrors = serializationtest();
            
            //
            // report
            //
            waserrors = ((((((specialerrors || basicrbferrors) || irregularrbferrors) || linearitymodelrbferr) || sqrdegmatrixrbferr) || sererrors) || multilayerrbf1derrors) || multilayerrbferrors;
            if( !silent )
            {
                System.Console.Write("TESTING RBF");
                System.Console.WriteLine();
                System.Console.Write("Special cases:                                     ");
                if( specialerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("BasicRBFTest:                                      ");
                if( basicrbferrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("IrregularRBFTest:                                  ");
                if( irregularrbferrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LinearityModelRBFTest:                             ");
                if( linearitymodelrbferr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("SqrDegMatrixRBFTest:                               ");
                if( sqrdegmatrixrbferr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("Serialization test:                                ");
                if( sererrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("MultiLayerRBFErrors in 1D test:                    ");
                if( multilayerrbf1derrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("MultiLayerRBFErrors in 2-3D test:                  ");
                if( multilayerrbferrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                
                //
                // was errors?
                //
                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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testrbf(bool silent)
        {
            return testrbf(silent);
        }


        /*************************************************************************
        The test  has  to  check, that  algorithm can solve problems of matrix are
        degenerate.
            * used model with linear term;
            * points locate in a subspace of dimension less than an original space.

          -- ALGLIB --
             Copyright 13.12.2011 by Bochkanov Sergey
        *************************************************************************/
        public static bool sqrdegmatrixrbftest(bool silent)
        {
            bool result = new bool();
            rbf.rbfmodel s = new rbf.rbfmodel();
            rbf.rbfreport rep = new rbf.rbfreport();
            int nx = 0;
            int ny = 0;
            int k0 = 0;
            int k1 = 0;
            int np = 0;
            double sx = 0;
            double sy = 0;
            double zx = 0;
            double px = 0;
            double zy = 0;
            double py = 0;
            double q = 0;
            double z = 0;
            double[] point = new double[0];
            double[,] a = new double[0,0];
            double[] d0 = new double[0];
            double[] d1 = new double[0];
            int gen = 0;
            double[] pvd0 = new double[0];
            double[] pvd1 = new double[0];
            double pvdnorm = 0;
            double vnorm = 0;
            double dd0 = 0;
            double dd1 = 0;
            double[,] gp = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            int unx = 0;
            int uny = 0;
            double[,] xwr = new double[0,0];
            double[,] v = new double[0,0];
            int i = 0;
            int j = 0;
            int k = 0;
            int i_ = 0;

            zx = 10;
            px = 15;
            zy = 10;
            py = 15;
            ny = 1;
            for(nx=2; nx<=3; nx++)
            {
                
                //
                // prepare test problem
                //
                sx = Math.Pow(zx, px*(math.randominteger(3)-1));
                sy = Math.Pow(zy, py*(math.randominteger(3)-1));
                x = new double[nx];
                y = new double[ny];
                point = new double[nx];
                rbf.rbfcreate(nx, ny, s);
                rbf.rbfsetcond(s, heps, heps, mxits);
                q = 0.25+math.randomreal();
                z = 4.5+math.randomreal();
                rbf.rbfsetalgoqnn(s, q, z);
                
                //
                // start points for grid
                //
                for(i=0; i<=nx-1; i++)
                {
                    point[i] = sx*(2*math.randomreal()-1);
                }
                if( nx==2 )
                {
                    for(k0=2; k0<=4; k0++)
                    {
                        matgen.rmatrixrndorthogonal(nx, ref a);
                        d0 = new double[nx];
                        for(i_=0; i_<=nx-1;i_++)
                        {
                            d0[i_] = a[i_,0];
                        }
                        np = k0;
                        gp = new double[np, nx+ny];
                        
                        //
                        // create grid
                        //
                        for(i=0; i<=k0-1; i++)
                        {
                            gp[i,0] = point[0]+sx*i*d0[0];
                            gp[i,1] = point[1]+sx*i*d0[1];
                            for(k=0; k<=ny-1; k++)
                            {
                                gp[i,nx+k] = sy*(2*math.randomreal()-1);
                            }
                        }
                        rbf.rbfsetpoints(s, gp, np);
                        rbf.rbfbuildmodel(s, rep);
                        for(i=0; i<=np-1; i++)
                        {
                            x[0] = gp[i,0];
                            x[1] = gp[i,1];
                            rbf.rbfcalc(s, x, ref y);
                            for(j=0; j<=ny-1; j++)
                            {
                                if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                {
                                    result = true;
                                    return result;
                                }
                            }
                        }
                    }
                }
                if( nx==3 )
                {
                    for(k0=2; k0<=4; k0++)
                    {
                        for(k1=2; k1<=4; k1++)
                        {
                            for(gen=1; gen<=2; gen++)
                            {
                                matgen.rmatrixrndorthogonal(nx, ref a);
                                d0 = new double[nx];
                                for(i_=0; i_<=nx-1;i_++)
                                {
                                    d0[i_] = a[i_,0];
                                }
                                
                                //
                                // create grid
                                //
                                np = -1;
                                if( gen==1 )
                                {
                                    np = k0;
                                    gp = new double[np, nx+ny];
                                    for(i=0; i<=k0-1; i++)
                                    {
                                        gp[i,0] = point[0]+sx*i*d0[0];
                                        gp[i,1] = point[1]+sx*i*d0[1];
                                        gp[i,2] = point[2]+sx*i*d0[2];
                                        for(k=0; k<=ny-1; k++)
                                        {
                                            gp[i,nx+k] = sy*(2*math.randomreal()-1);
                                        }
                                    }
                                }
                                if( gen==2 )
                                {
                                    d1 = new double[nx];
                                    for(i_=0; i_<=nx-1;i_++)
                                    {
                                        d1[i_] = a[i_,1];
                                    }
                                    np = k0*k1;
                                    gp = new double[np, nx+ny];
                                    for(i=0; i<=k0-1; i++)
                                    {
                                        for(j=0; j<=k1-1; j++)
                                        {
                                            gp[i*k1+j,0] = sx*i*d0[0]+sx*j*d1[0];
                                            gp[i*k1+j,1] = sx*i*d0[1]+sx*j*d1[1];
                                            gp[i*k1+j,2] = sx*i*d0[2]+sx*j*d1[2];
                                            for(k=0; k<=ny-1; k++)
                                            {
                                                gp[i*k1+j,nx+k] = sy*(2*math.randomreal()-1);
                                            }
                                        }
                                    }
                                }
                                alglib.ap.assert(np>=0, "rbf test: integrity error");
                                rbf.rbfsetpoints(s, gp, np);
                                rbf.rbfbuildmodel(s, rep);
                                for(i=0; i<=np-1; i++)
                                {
                                    x[0] = gp[i,0];
                                    x[1] = gp[i,1];
                                    x[2] = gp[i,2];
                                    rbf.rbfcalc(s, x, ref y);
                                    for(j=0; j<=ny-1; j++)
                                    {
                                        if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                        {
                                            result = true;
                                            return result;
                                        }
                                    }
                                }
                                if( gen==2 )
                                {
                                    rbf.rbfunpack(s, ref unx, ref uny, ref xwr, ref np, ref v);
                                    dd0 = (d0[0]*v[0,0]+d0[1]*v[0,1]+d0[2]*v[0,2])/(d0[0]*d0[0]+d0[1]*d0[1]+d0[2]*d0[2]);
                                    dd1 = (d1[0]*v[0,0]+d1[1]*v[0,1]+d1[2]*v[0,2])/(d1[0]*d1[0]+d1[1]*d1[1]+d1[2]*d1[2]);
                                    pvd0 = new double[nx];
                                    pvd1 = new double[nx];
                                    for(i=0; i<=nx-1; i++)
                                    {
                                        pvd0[i] = dd0*d0[i];
                                        pvd1[i] = dd1*d1[i];
                                    }
                                    pvdnorm = Math.Sqrt(math.sqr(v[0,0]-pvd0[0]-pvd1[0])+math.sqr(v[0,1]-pvd0[1]-pvd1[1])+math.sqr(v[0,2]-pvd0[2]-pvd1[2]));
                                    vnorm = Math.Sqrt(math.sqr(v[0,0])+math.sqr(v[0,1])+math.sqr(v[0,2]));
                                    if( (double)(pvdnorm)>(double)(vnorm*tol) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing basic functionality of RBF module on regular grids with
        multi-layer algorithm in 1D.

          -- ALGLIB --
             Copyright 2.03.2012 by Bochkanov Sergey
        *************************************************************************/
        public static bool basicmultilayerrbf1dtest()
        {
            bool result = new bool();
            rbf.rbfmodel s = new rbf.rbfmodel();
            rbf.rbfreport rep = new rbf.rbfreport();
            int nx = 0;
            int ny = 0;
            int linterm = 0;
            int n = 0;
            double q = 0;
            double r = 0;
            int errtype = 0;
            double[] delta = new double[0];
            int nlayers = 0;
            double a = 0;
            double b = 0;
            double f1 = 0;
            double f2 = 0;
            double[] a1 = new double[0];
            double[] b1 = new double[0];
            double[,] gp = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            double[,] mody0 = new double[0,0];
            double[,] mody1 = new double[0,0];
            double[,] gy = new double[0,0];
            double[] gpgx0 = new double[0];
            double[] gpgx1 = new double[0];
            int pass = 0;
            int passcount = 0;
            int i = 0;
            int j = 0;

            a = 1.0;
            b = (double)1/(double)9;
            f1 = 1.0;
            f2 = 10.0;
            passcount = 5;
            n = 100;
            gpgx0 = new double[n];
            gpgx1 = new double[n];
            for(i=0; i<=n-1; i++)
            {
                gpgx0[i] = (double)i/(double)n;
                gpgx1[i] = 0;
            }
            r = 1;
            for(pass=0; pass<=passcount-1; pass++)
            {
                nx = math.randominteger(2)+2;
                ny = math.randominteger(3)+1;
                linterm = math.randominteger(3)+1;
                x = new double[nx];
                y = new double[ny];
                a1 = new double[ny];
                b1 = new double[ny];
                delta = new double[ny];
                mody0 = new double[n, ny];
                mody1 = new double[n, ny];
                for(i=0; i<=ny-1; i++)
                {
                    a1[i] = a+0.01*a*(2*math.randomreal()-1);
                    b1[i] = b+0.01*b*(2*math.randomreal()-1);
                    delta[i] = 0.35*b1[i];
                }
                gp = new double[n, nx+ny];
                
                //
                // create grid
                //
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=nx-1; j++)
                    {
                        gp[i,j] = 0;
                    }
                    gp[i,0] = (double)i/(double)n;
                    for(j=0; j<=ny-1; j++)
                    {
                        gp[i,nx+j] = a1[j]*Math.Cos(f1*2*Math.PI*gp[i,0])+b1[j]*Math.Cos(f2*2*Math.PI*gp[i,0]);
                        mody0[i,j] = gp[i,nx+j];
                    }
                }
                q = 1;
                nlayers = 1;
                errtype = 1;
                
                //
                // test multilayer algorithm with different parameters
                //
                while( (double)(q)>=(double)(1/(2*f2)) )
                {
                    rbf.rbfcreate(nx, ny, s);
                    rbf.rbfsetalgomultilayer(s, r, nlayers, 0.0);
                    if( linterm==1 )
                    {
                        rbf.rbfsetlinterm(s);
                    }
                    if( linterm==2 )
                    {
                        rbf.rbfsetconstterm(s);
                    }
                    if( linterm==3 )
                    {
                        rbf.rbfsetzeroterm(s);
                    }
                    rbf.rbfsetpoints(s, gp, n);
                    rbf.rbfbuildmodel(s, rep);
                    if( ny==1 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=nx-1; j++)
                            {
                                x[j] = gp[i,j];
                            }
                            if( nx==2 )
                            {
                                mody1[i,0] = rbf.rbfcalc2(s, x[0], x[1]);
                            }
                            else
                            {
                                if( nx==3 )
                                {
                                    mody1[i,0] = rbf.rbfcalc3(s, x[0], x[1], x[2]);
                                }
                                else
                                {
                                    alglib.ap.assert(false, "BasicMultiLayerRBFTest1D: Invalid variable NX(NX neither 2 nor 3)");
                                }
                            }
                        }
                        if( searcherr(mody0, mody1, n, ny, errtype, b1, delta) )
                        {
                            result = true;
                            return result;
                        }
                        if( nx==2 )
                        {
                            rbf.rbfgridcalc2(s, gpgx0, n, gpgx1, n, ref gy);
                            for(i=0; i<=n-1; i++)
                            {
                                mody1[i,0] = gy[i,0];
                            }
                        }
                        if( searcherr(mody0, mody1, n, ny, errtype, b1, delta) )
                        {
                            result = true;
                            return result;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=nx-1; j++)
                        {
                            x[j] = gp[i,j];
                        }
                        rbf.rbfcalc(s, x, ref y);
                        for(j=0; j<=ny-1; j++)
                        {
                            mody1[i,j] = y[j];
                        }
                    }
                    if( searcherr(mody0, mody1, n, ny, errtype, b1, delta) )
                    {
                        result = true;
                        return result;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=nx-1; j++)
                        {
                            x[j] = gp[i,j];
                        }
                        rbf.rbfcalcbuf(s, x, ref y);
                        for(j=0; j<=ny-1; j++)
                        {
                            mody1[i,j] = y[j];
                        }
                    }
                    if( searcherr(mody0, mody1, n, ny, errtype, b1, delta) )
                    {
                        result = true;
                        return result;
                    }
                    q = q/2;
                    nlayers = nlayers+1;
                    if( errtype==1 && (double)(q)<=(double)(1/f2) )
                    {
                        errtype = 2;
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function tests special cases:
        * uninitialized RBF model will correctly return zero values
        * RBF correctly handles 1 or 2 distinct points
        * when  we have many uniformly spaced points and one outlier, filter which
          is applied to radii, makes all radii equal.
        * RBF with NLayers=0 gives linear model

          -- ALGLIB --
             Copyright 13.12.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool specialtest()
        {
            bool result = new bool();
            rbf.rbfmodel s = new rbf.rbfmodel();
            rbf.rbfreport rep = new rbf.rbfreport();
            int n = 0;
            int nx = 0;
            int ny = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int t = 0;
            double[,] xy = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            int termtype = 0;
            double errtol = 0;
            int tmpnx = 0;
            int tmpny = 0;
            int tmpnc = 0;
            double[,] xwr = new double[0,0];
            double[,] v = new double[0,0];
            double sx = 0;
            double z = 0;
            double va = 0;
            double vb = 0;
            double vc = 0;
            double vd = 0;

            errtol = 1.0E-9;
            result = false;
            
            //
            // Create model in the default state (no parameters/points specified).
            // With probability 0.5 we do one of the following:
            // * test that default state of the model is a zero model (all Calc()
            //   functions return zero)
            // * call RBFBuildModel() (without specifying anything) and test  that
            //   all Calc() functions return zero.
            //
            for(nx=2; nx<=3; nx++)
            {
                for(ny=1; ny<=3; ny++)
                {
                    rbf.rbfcreate(nx, ny, s);
                    if( (double)(math.randomreal())>(double)(0.5) )
                    {
                        rbf.rbfbuildmodel(s, rep);
                        if( rep.terminationtype<=0 )
                        {
                            result = true;
                            return result;
                        }
                    }
                    x = new double[nx];
                    for(i=0; i<=nx-1; i++)
                    {
                        x[i] = 2*math.randomreal()-1;
                    }
                    rbf.rbfcalc(s, x, ref y);
                    if( alglib.ap.len(y)!=ny )
                    {
                        result = true;
                        return result;
                    }
                    for(i=0; i<=ny-1; i++)
                    {
                        if( (double)(y[i])!=(double)(0) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            
            //
            // Create model with 1 point and different types of linear term.
            // Test algorithm on such dataset.
            //
            for(nx=2; nx<=3; nx++)
            {
                for(ny=1; ny<=3; ny++)
                {
                    rbf.rbfcreate(nx, ny, s);
                    for(termtype=0; termtype<=2; termtype++)
                    {
                        if( termtype==0 )
                        {
                            rbf.rbfsetlinterm(s);
                        }
                        if( termtype==1 )
                        {
                            rbf.rbfsetconstterm(s);
                        }
                        if( termtype==2 )
                        {
                            rbf.rbfsetzeroterm(s);
                        }
                        xy = new double[1, nx+ny];
                        for(i=0; i<=nx+ny-1; i++)
                        {
                            xy[0,i] = 2*math.randomreal()-1;
                        }
                        rbf.rbfsetpoints(s, xy, 1);
                        rbf.rbfbuildmodel(s, rep);
                        if( rep.terminationtype<=0 )
                        {
                            result = true;
                            return result;
                        }
                        
                        //
                        // First, test that model exactly reproduces our dataset 
                        //
                        x = new double[nx];
                        for(i=0; i<=nx-1; i++)
                        {
                            x[i] = xy[0,i];
                        }
                        rbf.rbfcalc(s, x, ref y);
                        if( alglib.ap.len(y)!=ny )
                        {
                            result = true;
                            return result;
                        }
                        for(i=0; i<=ny-1; i++)
                        {
                            if( (double)(Math.Abs(y[i]-xy[0,nx+i]))>(double)(errtol) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        
                        //
                        // Second, test that model is constant unless it has zero polynomial term
                        // (in the latter case we have small "bump" around lone interpolation center)
                        //
                        if( termtype==0 || termtype==1 )
                        {
                            for(i=0; i<=nx-1; i++)
                            {
                                x[i] = 2*math.randomreal()-1;
                            }
                            rbf.rbfcalc(s, x, ref y);
                            if( alglib.ap.len(y)!=ny )
                            {
                                result = true;
                                return result;
                            }
                            for(i=0; i<=ny-1; i++)
                            {
                                if( (double)(Math.Abs(y[i]-xy[0,nx+i]))>(double)(errtol) )
                                {
                                    result = true;
                                    return result;
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // Create model with 2 points and different types of linear term.
            // Test algorithm on such dataset.
            //
            for(nx=2; nx<=3; nx++)
            {
                for(ny=1; ny<=3; ny++)
                {
                    rbf.rbfcreate(nx, ny, s);
                    for(termtype=0; termtype<=2; termtype++)
                    {
                        if( termtype==0 )
                        {
                            rbf.rbfsetlinterm(s);
                        }
                        if( termtype==1 )
                        {
                            rbf.rbfsetconstterm(s);
                        }
                        if( termtype==2 )
                        {
                            rbf.rbfsetzeroterm(s);
                        }
                        xy = new double[2, nx+ny];
                        for(i=0; i<=nx+ny-1; i++)
                        {
                            xy[0,i] = 2*math.randomreal()-1;
                        }
                        for(i=0; i<=nx+ny-1; i++)
                        {
                            xy[1,i] = xy[0,i]+1.0;
                        }
                        rbf.rbfsetpoints(s, xy, 2);
                        rbf.rbfbuildmodel(s, rep);
                        if( rep.terminationtype<=0 )
                        {
                            result = true;
                            return result;
                        }
                        for(j=0; j<=1; j++)
                        {
                            x = new double[nx];
                            for(i=0; i<=nx-1; i++)
                            {
                                x[i] = xy[j,i];
                            }
                            rbf.rbfcalc(s, x, ref y);
                            if( alglib.ap.len(y)!=ny )
                            {
                                result = true;
                                return result;
                            }
                            for(i=0; i<=ny-1; i++)
                            {
                                if( (double)(Math.Abs(y[i]-xy[j,nx+i]))>(double)(errtol) )
                                {
                                    result = true;
                                    return result;
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // Generate a set of points (xi,yi) = (SX*i,0), and one
            // outlier (x_far,y_far)=(-1000*SX,0).
            //
            // Radii filtering should place a bound on the radius of outlier.
            //
            for(nx=2; nx<=3; nx++)
            {
                for(ny=1; ny<=3; ny++)
                {
                    sx = Math.Exp(-5+10*math.randomreal());
                    rbf.rbfcreate(nx, ny, s);
                    xy = new double[20, nx+ny];
                    for(i=0; i<=alglib.ap.rows(xy)-1; i++)
                    {
                        xy[i,0] = sx*i;
                        for(j=1; j<=nx-1; j++)
                        {
                            xy[i,j] = 0;
                        }
                        for(j=0; j<=ny-1; j++)
                        {
                            xy[i,nx+j] = math.randomreal();
                        }
                    }
                    xy[alglib.ap.rows(xy)-1,0] = -(1000*sx);
                    rbf.rbfsetpoints(s, xy, alglib.ap.rows(xy));
                    
                    //
                    // Try random Z from [1,5]
                    //
                    z = 1+math.randomreal()*4;
                    rbf.rbfsetalgoqnn(s, 1.0, z);
                    rbf.rbfbuildmodel(s, rep);
                    if( rep.terminationtype<=0 )
                    {
                        result = true;
                        return result;
                    }
                    rbf.rbfunpack(s, ref tmpnx, ref tmpny, ref xwr, ref tmpnc, ref v);
                    if( (((tmpnx!=nx || tmpny!=ny) || tmpnc!=alglib.ap.rows(xy)) || alglib.ap.cols(xwr)!=nx+ny+1) || alglib.ap.rows(xwr)!=tmpnc )
                    {
                        result = true;
                        return result;
                    }
                    for(i=0; i<=tmpnc-2; i++)
                    {
                        if( (double)(Math.Abs(xwr[i,nx+ny]-sx))>(double)(errtol) )
                        {
                            result = true;
                            return result;
                        }
                    }
                    if( (double)(Math.Abs(xwr[tmpnc-1,nx+ny]-z*sx))>(double)(errtol) )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            
            //
            // RBF with NLayers=0 gives us linear model.
            //
            // In order to perform this test, we use test function which
            // is perfectly linear and see whether RBF model is able to
            // reproduce such function.
            //
            n = 5;
            for(ny=1; ny<=3; ny++)
            {
                va = 2*math.randomreal()-1;
                vb = 2*math.randomreal()-1;
                vc = 2*math.randomreal()-1;
                vd = 2*math.randomreal()-1;
                
                //
                // Test NX=2.
                // Generate linear function using random coefficients VA/VB/VC.
                // Function is K-dimensional vector-valued, each component has slightly
                // different coefficients.
                //
                xy = new double[n*n, 2+ny];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        xy[n*i+j,0] = i;
                        xy[n*i+j,1] = j;
                        for(k=0; k<=ny-1; k++)
                        {
                            xy[n*i+j,2+k] = (va+0.1*k)*i+(vb+0.2*k)*j+(vc+0.3*k);
                        }
                    }
                }
                rbf.rbfcreate(2, ny, s);
                rbf.rbfsetpoints(s, xy, n*n);
                rbf.rbfsetalgomultilayer(s, 1.0, 0, 0.01);
                rbf.rbfbuildmodel(s, rep);
                if( rep.terminationtype<=0 )
                {
                    result = true;
                    return result;
                }
                x = new double[2];
                x[0] = (n-1)*math.randomreal();
                x[1] = (n-1)*math.randomreal();
                if( ny==1 && (double)(Math.Abs(rbf.rbfcalc2(s, x[0], x[1])-(va*x[0]+vb*x[1]+vc)))>(double)(errtol) )
                {
                    result = true;
                    return result;
                }
                rbf.rbfcalc(s, x, ref y);
                for(k=0; k<=ny-1; k++)
                {
                    if( (double)(Math.Abs(y[k]-((va+0.1*k)*x[0]+(vb+0.2*k)*x[1]+(vc+0.3*k))))>(double)(errtol) )
                    {
                        result = true;
                        return result;
                    }
                }
                
                //
                // Test NX=3.
                // Generate linear function using random coefficients VA/VB/VC/VC.
                // Function is K-dimensional vector-valued, each component has slightly
                // different coefficients.
                //
                xy = new double[n*n*n, 3+ny];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        for(t=0; t<=n-1; t++)
                        {
                            xy[n*n*i+n*j+t,0] = i;
                            xy[n*n*i+n*j+t,1] = j;
                            xy[n*n*i+n*j+t,2] = t;
                            for(k=0; k<=ny-1; k++)
                            {
                                xy[n*n*i+n*j+t,3+k] = (va+0.1*k)*i+(vb+0.2*k)*j+(vc+0.3*k)*t+(vd+0.4*k);
                            }
                        }
                    }
                }
                rbf.rbfcreate(3, ny, s);
                rbf.rbfsetpoints(s, xy, n*n*n);
                rbf.rbfsetalgomultilayer(s, 1.0, 0, 0.01);
                rbf.rbfbuildmodel(s, rep);
                if( rep.terminationtype<=0 )
                {
                    result = true;
                    return result;
                }
                x = new double[3];
                x[0] = (n-1)*math.randomreal();
                x[1] = (n-1)*math.randomreal();
                x[2] = (n-1)*math.randomreal();
                if( ny==1 && (double)(Math.Abs(rbf.rbfcalc3(s, x[0], x[1], x[2])-(va*x[0]+vb*x[1]+vc*x[2]+vd)))>(double)(errtol) )
                {
                    result = true;
                    return result;
                }
                rbf.rbfcalc(s, x, ref y);
                for(k=0; k<=ny-1; k++)
                {
                    if( (double)(Math.Abs(y[k]-((va+0.1*k)*x[0]+(vb+0.2*k)*x[1]+(vc+0.3*k)*x[2]+(vd+0.4*k))))>(double)(errtol) )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            return result;
        }


        /*************************************************************************
        Function for testing basic functionality of RBF module on regular grids.

          -- ALGLIB --
             Copyright 13.12.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool basicrbftest()
        {
            bool result = new bool();
            rbf.rbfmodel s = new rbf.rbfmodel();
            rbf.rbfreport rep = new rbf.rbfreport();
            int nx = 0;
            int ny = 0;
            int k0 = 0;
            int k1 = 0;
            int k2 = 0;
            int linterm = 0;
            int np = 0;
            double sx = 0;
            double sy = 0;
            double zx = 0;
            double px = 0;
            double zy = 0;
            double py = 0;
            double q = 0;
            double z = 0;
            double[] point = new double[0];
            double[,] gp = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            double[,] gy = new double[0,0];
            int unx = 0;
            int uny = 0;
            double[,] xwr = new double[0,0];
            double[,] v = new double[0,0];
            double[] gpgx0 = new double[0];
            double[] gpgx1 = new double[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int l = 0;

            zx = 10;
            px = 15;
            zy = 10;
            py = 15;
            
            //
            // Problem types:
            // * 2 and 3-dimensional problems
            // * problems with zero, constant, linear terms
            // * different scalings of X and Y values (1.0, 1E-15, 1E+15)
            // * regular grids different grid sizes (from 2 to 4 points for each dimension)
            //
            // We check that:
            // * RBF model correctly reproduces function value (testes with different Calc() functions)
            // * unpacked model containt correct radii
            // * linear term has correct form
            //
            for(nx=2; nx<=3; nx++)
            {
                for(ny=1; ny<=3; ny++)
                {
                    for(linterm=1; linterm<=3; linterm++)
                    {
                        
                        //
                        // prepare test problem
                        //
                        sx = Math.Pow(zx, px*(math.randominteger(3)-1));
                        sy = Math.Pow(zy, py*(math.randominteger(3)-1));
                        x = new double[nx];
                        y = new double[ny];
                        point = new double[nx];
                        rbf.rbfcreate(nx, ny, s);
                        rbf.rbfsetcond(s, heps, heps, mxits);
                        q = 0.25+math.randomreal();
                        z = 4.5+math.randomreal();
                        rbf.rbfsetalgoqnn(s, q, z);
                        if( linterm==1 )
                        {
                            rbf.rbfsetlinterm(s);
                        }
                        if( linterm==2 )
                        {
                            rbf.rbfsetconstterm(s);
                        }
                        if( linterm==3 )
                        {
                            rbf.rbfsetzeroterm(s);
                        }
                        
                        //
                        // start points for grid
                        //
                        for(i=0; i<=nx-1; i++)
                        {
                            point[i] = sx*(2*math.randomreal()-1);
                        }
                        
                        //
                        // 2-dimensional test problem
                        //
                        if( nx==2 )
                        {
                            for(k0=2; k0<=4; k0++)
                            {
                                for(k1=2; k1<=4; k1++)
                                {
                                    np = k0*k1;
                                    gp = new double[np, nx+ny];
                                    
                                    //
                                    // create grid
                                    //
                                    for(i=0; i<=k0-1; i++)
                                    {
                                        for(j=0; j<=k1-1; j++)
                                        {
                                            gp[i*k1+j,0] = point[0]+sx*i;
                                            gp[i*k1+j,1] = point[1]+sx*j;
                                            for(k=0; k<=ny-1; k++)
                                            {
                                                gp[i*k1+j,nx+k] = sy*(2*math.randomreal()-1);
                                            }
                                        }
                                    }
                                    rbf.rbfsetpoints(s, gp, np);
                                    rbf.rbfbuildmodel(s, rep);
                                    if( ny==1 )
                                    {
                                        gpgx0 = new double[k0];
                                        gpgx1 = new double[k1];
                                        for(i=0; i<=k0-1; i++)
                                        {
                                            gpgx0[i] = point[0]+sx*i;
                                        }
                                        for(i=0; i<=k1-1; i++)
                                        {
                                            gpgx1[i] = point[1]+sx*i;
                                        }
                                        rbf.rbfgridcalc2(s, gpgx0, k0, gpgx1, k1, ref gy);
                                        for(i=0; i<=k0-1; i++)
                                        {
                                            for(j=0; j<=k1-1; j++)
                                            {
                                                if( (double)(Math.Abs(gy[i,j]-gp[i*k1+j,nx]))>(double)(sy*eps) )
                                                {
                                                    result = true;
                                                    return result;
                                                }
                                            }
                                        }
                                    }
                                    for(i=0; i<=np-1; i++)
                                    {
                                        x[0] = gp[i,0];
                                        x[1] = gp[i,1];
                                        if( ny==1 )
                                        {
                                            y[0] = rbf.rbfcalc2(s, x[0], x[1]);
                                            if( (double)(Math.Abs(gp[i,nx]-y[0]))>(double)(sy*eps) )
                                            {
                                                result = true;
                                                return result;
                                            }
                                        }
                                        rbf.rbfcalc(s, x, ref y);
                                        for(j=0; j<=ny-1; j++)
                                        {
                                            if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                            {
                                                result = true;
                                                return result;
                                            }
                                        }
                                        rbf.rbfcalcbuf(s, x, ref y);
                                        for(j=0; j<=ny-1; j++)
                                        {
                                            if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                            {
                                                result = true;
                                                return result;
                                            }
                                        }
                                    }
                                    
                                    //
                                    // test for RBFUnpack
                                    //
                                    rbf.rbfunpack(s, ref unx, ref uny, ref xwr, ref np, ref v);
                                    if( ((((nx!=unx || ny!=uny) || alglib.ap.rows(xwr)!=np) || alglib.ap.cols(xwr)!=nx+ny+1) || alglib.ap.rows(v)!=ny) || alglib.ap.cols(v)!=nx+1 )
                                    {
                                        result = true;
                                        return result;
                                    }
                                    for(i=0; i<=np-1; i++)
                                    {
                                        if( (double)(Math.Abs(xwr[i,unx+uny]-q*sx))>(double)(sx*eps) )
                                        {
                                            result = true;
                                            return result;
                                        }
                                    }
                                    if( linterm==2 )
                                    {
                                        for(i=0; i<=unx-1; i++)
                                        {
                                            for(j=0; j<=uny-1; j++)
                                            {
                                                if( (double)(v[j,i])!=(double)(0) )
                                                {
                                                    result = true;
                                                    return result;
                                                }
                                            }
                                        }
                                    }
                                    if( linterm==3 )
                                    {
                                        for(i=0; i<=unx; i++)
                                        {
                                            for(j=0; j<=uny-1; j++)
                                            {
                                                if( (double)(v[j,i])!=(double)(0) )
                                                {
                                                    result = true;
                                                    return result;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        
                        //
                        // 3-dimensional test problems
                        //
                        if( nx==3 )
                        {
                            for(k0=2; k0<=4; k0++)
                            {
                                for(k1=2; k1<=4; k1++)
                                {
                                    for(k2=2; k2<=4; k2++)
                                    {
                                        np = k0*k1*k2;
                                        gp = new double[np, nx+ny];
                                        
                                        //
                                        // create grid
                                        //
                                        for(i=0; i<=k0-1; i++)
                                        {
                                            for(j=0; j<=k1-1; j++)
                                            {
                                                for(k=0; k<=k2-1; k++)
                                                {
                                                    gp[(i*k1+j)*k2+k,0] = point[0]+sx*i;
                                                    gp[(i*k1+j)*k2+k,1] = point[1]+sx*j;
                                                    gp[(i*k1+j)*k2+k,2] = point[2]+sx*k;
                                                    for(l=0; l<=ny-1; l++)
                                                    {
                                                        gp[(i*k1+j)*k2+k,nx+l] = sy*(2*math.randomreal()-1);
                                                    }
                                                }
                                            }
                                        }
                                        rbf.rbfsetpoints(s, gp, np);
                                        rbf.rbfbuildmodel(s, rep);
                                        for(i=0; i<=np-1; i++)
                                        {
                                            x[0] = gp[i,0];
                                            x[1] = gp[i,1];
                                            x[2] = gp[i,2];
                                            if( ny==1 )
                                            {
                                                y[0] = rbf.rbfcalc3(s, x[0], x[1], x[2]);
                                                if( (double)(Math.Abs(gp[i,nx]-y[0]))>(double)(sy*eps) )
                                                {
                                                    result = true;
                                                    return result;
                                                }
                                            }
                                            rbf.rbfcalc(s, x, ref y);
                                            for(j=0; j<=ny-1; j++)
                                            {
                                                if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                                {
                                                    result = true;
                                                    return result;
                                                }
                                            }
                                            rbf.rbfcalcbuf(s, x, ref y);
                                            for(j=0; j<=ny-1; j++)
                                            {
                                                if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                                {
                                                    result = true;
                                                    return result;
                                                }
                                            }
                                        }
                                        
                                        //
                                        // test for RBFUnpack
                                        //
                                        rbf.rbfunpack(s, ref unx, ref uny, ref xwr, ref np, ref v);
                                        if( ((((nx!=unx || ny!=uny) || alglib.ap.rows(xwr)!=np) || alglib.ap.cols(xwr)!=nx+ny+1) || alglib.ap.rows(v)!=ny) || alglib.ap.cols(v)!=nx+1 )
                                        {
                                            result = true;
                                            return result;
                                        }
                                        for(i=0; i<=np-1; i++)
                                        {
                                            if( (double)(Math.Abs(xwr[i,unx+uny]-q*sx))>(double)(sx*eps) )
                                            {
                                                result = true;
                                                return result;
                                            }
                                        }
                                        if( linterm==2 )
                                        {
                                            for(i=0; i<=unx-1; i++)
                                            {
                                                for(j=0; j<=uny-1; j++)
                                                {
                                                    if( (double)(v[j,i])!=(double)(0) )
                                                    {
                                                        result = true;
                                                        return result;
                                                    }
                                                }
                                            }
                                        }
                                        if( linterm==3 )
                                        {
                                            for(i=0; i<=unx; i++)
                                            {
                                                for(j=0; j<=uny-1; j++)
                                                {
                                                    if( (double)(v[j,i])!=(double)(0) )
                                                    {
                                                        result = true;
                                                        return result;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing RBF module on irregular grids.

          -- ALGLIB --
             Copyright 13.12.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool irregularrbftest()
        {
            bool result = new bool();
            rbf.rbfmodel s = new rbf.rbfmodel();
            rbf.rbfreport rep = new rbf.rbfreport();
            int nx = 0;
            int ny = 0;
            int k0 = 0;
            int k1 = 0;
            int k2 = 0;
            int linterm = 0;
            int np = 0;
            double sx = 0;
            double sy = 0;
            double zx = 0;
            double px = 0;
            double zy = 0;
            double py = 0;
            double q = 0;
            double z = 0;
            double[] point = new double[0];
            double[,] gp = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            double[,] gy = new double[0,0];
            double noiselevel = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int l = 0;

            zx = 10;
            px = 15;
            zy = 10;
            py = 15;
            noiselevel = 0.1;
            
            //
            // Problem types:
            // * 2 and 3-dimensional problems
            // * problems with zero, constant, linear terms
            // * different scalings of X and Y values (1.0, 1E-15, 1E+15)
            // * noisy grids, which are just regular grids with different grid sizes
            //   (from 2 to 4 points for each dimension) and moderate amount of random
            //   noise added to all node positions.
            //
            // We check that:
            // * RBF model correctly reproduces function value (testes with different Calc() functions)
            //
            for(nx=2; nx<=3; nx++)
            {
                for(ny=1; ny<=3; ny++)
                {
                    for(linterm=1; linterm<=3; linterm++)
                    {
                        
                        //
                        // prepare test problem
                        //
                        sx = Math.Pow(zx, px*(math.randominteger(3)-1));
                        sy = Math.Pow(zy, py*(math.randominteger(3)-1));
                        x = new double[nx];
                        y = new double[ny];
                        point = new double[nx];
                        rbf.rbfcreate(nx, ny, s);
                        rbf.rbfsetcond(s, heps, heps, mxits);
                        q = 0.25+math.randomreal();
                        z = 4.5+math.randomreal();
                        rbf.rbfsetalgoqnn(s, q, z);
                        if( linterm==1 )
                        {
                            rbf.rbfsetlinterm(s);
                        }
                        if( linterm==2 )
                        {
                            rbf.rbfsetconstterm(s);
                        }
                        if( linterm==3 )
                        {
                            rbf.rbfsetzeroterm(s);
                        }
                        
                        //
                        // start points for grid
                        //
                        for(i=0; i<=nx-1; i++)
                        {
                            point[i] = sx*(2*math.randomreal()-1);
                        }
                        
                        //
                        // 2-dimensional test problems
                        //
                        if( nx==2 )
                        {
                            for(k0=2; k0<=4; k0++)
                            {
                                for(k1=2; k1<=4; k1++)
                                {
                                    np = k0*k1;
                                    gp = new double[np, nx+ny];
                                    
                                    //
                                    // create grid
                                    //
                                    for(i=0; i<=k0-1; i++)
                                    {
                                        for(j=0; j<=k1-1; j++)
                                        {
                                            gp[i*k1+j,0] = point[0]+sx*i+noiselevel*sx*(2*math.randomreal()-1);
                                            gp[i*k1+j,1] = point[1]+sx*j+noiselevel*sx*(2*math.randomreal()-1);
                                            for(k=0; k<=ny-1; k++)
                                            {
                                                gp[i*k1+j,nx+k] = sy*(2*math.randomreal()-1);
                                            }
                                        }
                                    }
                                    rbf.rbfsetpoints(s, gp, np);
                                    rbf.rbfbuildmodel(s, rep);
                                    for(i=0; i<=np-1; i++)
                                    {
                                        x[0] = gp[i,0];
                                        x[1] = gp[i,1];
                                        if( ny==1 )
                                        {
                                            y[0] = rbf.rbfcalc2(s, x[0], x[1]);
                                            if( (double)(Math.Abs(gp[i,nx]-y[0]))>(double)(sy*eps) )
                                            {
                                                result = true;
                                                return result;
                                            }
                                        }
                                        rbf.rbfcalc(s, x, ref y);
                                        for(j=0; j<=ny-1; j++)
                                        {
                                            if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                            {
                                                result = true;
                                                return result;
                                            }
                                        }
                                        rbf.rbfcalcbuf(s, x, ref y);
                                        for(j=0; j<=ny-1; j++)
                                        {
                                            if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                            {
                                                result = true;
                                                return result;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        
                        //
                        // 2-dimensional test problems
                        //
                        if( nx==3 )
                        {
                            for(k0=2; k0<=4; k0++)
                            {
                                for(k1=2; k1<=4; k1++)
                                {
                                    for(k2=2; k2<=4; k2++)
                                    {
                                        np = k0*k1*k2;
                                        gp = new double[np, nx+ny];
                                        
                                        //
                                        // create grid
                                        //
                                        for(i=0; i<=k0-1; i++)
                                        {
                                            for(j=0; j<=k1-1; j++)
                                            {
                                                for(k=0; k<=k2-1; k++)
                                                {
                                                    gp[(i*k1+j)*k2+k,0] = point[0]+sx*i+noiselevel*sx*(2*math.randomreal()-1);
                                                    gp[(i*k1+j)*k2+k,1] = point[1]+sx*j+noiselevel*sx*(2*math.randomreal()-1);
                                                    gp[(i*k1+j)*k2+k,2] = point[2]+sx*k+noiselevel*sx*(2*math.randomreal()-1);
                                                    for(l=0; l<=ny-1; l++)
                                                    {
                                                        gp[(i*k1+j)*k2+k,nx+l] = sy*(2*math.randomreal()-1);
                                                    }
                                                }
                                            }
                                        }
                                        rbf.rbfsetpoints(s, gp, np);
                                        rbf.rbfbuildmodel(s, rep);
                                        for(i=0; i<=np-1; i++)
                                        {
                                            x[0] = gp[i,0];
                                            x[1] = gp[i,1];
                                            x[2] = gp[i,2];
                                            if( ny==1 )
                                            {
                                                y[0] = rbf.rbfcalc3(s, x[0], x[1], x[2]);
                                                if( (double)(Math.Abs(gp[i,nx]-y[0]))>(double)(sy*eps) )
                                                {
                                                    result = true;
                                                    return result;
                                                }
                                            }
                                            rbf.rbfcalc(s, x, ref y);
                                            for(j=0; j<=ny-1; j++)
                                            {
                                                if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                                {
                                                    result = true;
                                                    return result;
                                                }
                                            }
                                            rbf.rbfcalcbuf(s, x, ref y);
                                            for(j=0; j<=ny-1; j++)
                                            {
                                                if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(sy*eps) )
                                                {
                                                    result = true;
                                                    return result;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        The test does  check, that  algorithm  can build linear model for the data
        sets, when Y depends on X linearly.

          -- ALGLIB --
             Copyright 13.12.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool linearitymodelrbftest()
        {
            bool result = new bool();
            rbf.rbfmodel s = new rbf.rbfmodel();
            rbf.rbfreport rep = new rbf.rbfreport();
            int nx = 0;
            int ny = 0;
            int k0 = 0;
            int k1 = 0;
            int k2 = 0;
            int linterm = 0;
            int np = 0;
            double sx = 0;
            double sy = 0;
            double zx = 0;
            double px = 0;
            double zy = 0;
            double py = 0;
            double q = 0;
            double z = 0;
            double[] point = new double[0];
            double[] a = new double[0];
            double[,] gp = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            int unx = 0;
            int uny = 0;
            double[,] xwr = new double[0,0];
            double[,] v = new double[0,0];
            int i = 0;
            int j = 0;
            int k = 0;
            int l = 0;

            zx = 10;
            px = 15;
            zy = 10;
            py = 15;
            ny = 1;
            for(nx=2; nx<=3; nx++)
            {
                for(linterm=1; linterm<=3; linterm++)
                {
                    
                    //
                    // prepare test problem
                    //
                    sx = Math.Pow(zx, px*(math.randominteger(3)-1));
                    sy = Math.Pow(zy, py*(math.randominteger(3)-1));
                    x = new double[nx];
                    y = new double[ny];
                    point = new double[nx];
                    rbf.rbfcreate(nx, ny, s);
                    q = 0.25+math.randomreal();
                    z = 4.5+math.randomreal();
                    rbf.rbfsetalgoqnn(s, q, z);
                    a = new double[nx+1];
                    if( linterm==1 )
                    {
                        rbf.rbfsetlinterm(s);
                        for(i=0; i<=nx-1; i++)
                        {
                            a[i] = sy*(2*math.randomreal()-1)/sx;
                        }
                        a[nx] = sy*(2*math.randomreal()-1);
                    }
                    if( linterm==2 )
                    {
                        rbf.rbfsetconstterm(s);
                        for(i=0; i<=nx-1; i++)
                        {
                            a[i] = 0;
                        }
                        a[nx] = sy*(2*math.randomreal()-1);
                    }
                    if( linterm==3 )
                    {
                        rbf.rbfsetzeroterm(s);
                        for(i=0; i<=nx; i++)
                        {
                            a[i] = 0;
                        }
                    }
                    
                    //
                    // start points for grid
                    //
                    for(i=0; i<=nx-1; i++)
                    {
                        point[i] = sx*(2*math.randomreal()-1);
                    }
                    if( nx==2 )
                    {
                        for(k0=2; k0<=4; k0++)
                        {
                            for(k1=2; k1<=4; k1++)
                            {
                                np = k0*k1;
                                gp = new double[np, nx+ny];
                                
                                //
                                // create grid
                                //
                                for(i=0; i<=k0-1; i++)
                                {
                                    for(j=0; j<=k1-1; j++)
                                    {
                                        gp[i*k1+j,0] = point[0]+sx*i;
                                        gp[i*k1+j,1] = point[1]+sx*j;
                                        gp[i*k1+j,nx] = a[nx];
                                        for(k=0; k<=nx-1; k++)
                                        {
                                            gp[i*k1+j,nx] = gp[i*k1+j,nx]+gp[i*k1+j,k]*a[k];
                                        }
                                    }
                                }
                                rbf.rbfsetpoints(s, gp, np);
                                rbf.rbfbuildmodel(s, rep);
                                
                                //
                                // test for RBFUnpack
                                //
                                rbf.rbfunpack(s, ref unx, ref uny, ref xwr, ref np, ref v);
                                if( ((((nx!=unx || ny!=uny) || alglib.ap.rows(xwr)!=np) || alglib.ap.cols(xwr)!=nx+ny+1) || alglib.ap.rows(v)!=ny) || alglib.ap.cols(v)!=nx+1 )
                                {
                                    result = true;
                                    return result;
                                }
                                for(i=0; i<=nx-1; i++)
                                {
                                    if( (double)(Math.Abs(v[0,i]-a[i]))>(double)(sy/sx*tol) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                                if( (double)(Math.Abs(v[0,nx]-a[nx]))>(double)(sy*tol) )
                                {
                                    result = true;
                                    return result;
                                }
                                for(i=0; i<=np-1; i++)
                                {
                                    if( (double)(Math.Abs(xwr[i,unx]))>(double)(sy*tol) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                    }
                    if( nx==3 )
                    {
                        for(k0=2; k0<=4; k0++)
                        {
                            for(k1=2; k1<=4; k1++)
                            {
                                for(k2=2; k2<=4; k2++)
                                {
                                    np = k0*k1*k2;
                                    gp = new double[np, nx+ny];
                                    
                                    //
                                    // create grid
                                    //
                                    for(i=0; i<=k0-1; i++)
                                    {
                                        for(j=0; j<=k1-1; j++)
                                        {
                                            for(k=0; k<=k2-1; k++)
                                            {
                                                gp[(i*k1+j)*k2+k,0] = point[0]+sx*i;
                                                gp[(i*k1+j)*k2+k,1] = point[1]+sx*j;
                                                gp[(i*k1+j)*k2+k,2] = point[2]+sx*k;
                                                gp[(i*k1+j)*k2+k,nx] = a[nx];
                                                for(l=0; l<=nx-1; l++)
                                                {
                                                    gp[(i*k1+j)*k2+k,nx] = gp[(i*k1+j)*k2+k,nx]+gp[(i*k1+j)*k2+k,l]*a[l];
                                                }
                                            }
                                        }
                                    }
                                    rbf.rbfsetpoints(s, gp, np);
                                    rbf.rbfbuildmodel(s, rep);
                                    
                                    //
                                    // test for RBFUnpack
                                    //
                                    rbf.rbfunpack(s, ref unx, ref uny, ref xwr, ref np, ref v);
                                    if( ((((nx!=unx || ny!=uny) || alglib.ap.rows(xwr)!=np) || alglib.ap.cols(xwr)!=nx+ny+1) || alglib.ap.rows(v)!=ny) || alglib.ap.cols(v)!=nx+1 )
                                    {
                                        result = true;
                                        return result;
                                    }
                                    for(i=0; i<=nx-1; i++)
                                    {
                                        if( (double)(Math.Abs(v[0,i]-a[i]))>(double)(sy/sx*tol) )
                                        {
                                            result = true;
                                            return result;
                                        }
                                    }
                                    if( (double)(Math.Abs(v[0,nx]-a[nx]))>(double)(sy*tol) )
                                    {
                                        result = true;
                                        return result;
                                    }
                                    for(i=0; i<=np-1; i++)
                                    {
                                        if( (double)(Math.Abs(xwr[i,unx]))>(double)(sy*tol) )
                                        {
                                            result = true;
                                            return result;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function tests serialization

          -- ALGLIB --
             Copyright 02.02.2012 by Bochkanov Sergey
        *************************************************************************/
        private static bool serializationtest()
        {
            bool result = new bool();
            rbf.rbfmodel s = new rbf.rbfmodel();
            rbf.rbfmodel s2 = new rbf.rbfmodel();
            rbf.rbfreport rep = new rbf.rbfreport();
            int nx = 0;
            int ny = 0;
            int k0 = 0;
            int k1 = 0;
            int k2 = 0;
            int i0 = 0;
            int i1 = 0;
            int i2 = 0;
            int j = 0;
            double[,] xy = new double[0,0];
            double[] testpoint = new double[0];
            double[] y0 = new double[0];
            double[] y1 = new double[0];

            result = false;
            
            //
            // This function generates random 2 or 3 dimensional problem,
            // builds RBF model (QNN is used), serializes/unserializes it, then compares
            // models by calculating model value at some random point.
            //
            // Additionally we test that new model (one which was restored
            // after serialization) has lost all model construction settings,
            // i.e. if we call RBFBuildModel() on a NEW model, we will get
            // empty (zero) model.
            //
            for(nx=2; nx<=3; nx++)
            {
                for(ny=1; ny<=2; ny++)
                {
                    
                    //
                    // prepare test problem
                    //
                    rbf.rbfcreate(nx, ny, s);
                    rbf.rbfsetalgoqnn(s, 1.0, 5.0);
                    rbf.rbfsetlinterm(s);
                    if( nx==2 )
                    {
                        
                        //
                        // 2-dimensional problem
                        //
                        k0 = 2+math.randominteger(4);
                        k1 = 2+math.randominteger(4);
                        xy = new double[k0*k1, nx+ny];
                        for(i0=0; i0<=k0-1; i0++)
                        {
                            for(i1=0; i1<=k1-1; i1++)
                            {
                                xy[i0*k1+i1,0] = i0+0.1*(2*math.randomreal()-1);
                                xy[i0*k1+i1,1] = i1+0.1*(2*math.randomreal()-1);
                                for(j=0; j<=ny-1; j++)
                                {
                                    xy[i0*k1+i1,nx+j] = 2*math.randomreal()-1;
                                }
                            }
                        }
                        testpoint = new double[nx];
                        testpoint[0] = math.randomreal()*(k0-1);
                        testpoint[1] = math.randomreal()*(k1-1);
                    }
                    else
                    {
                        
                        //
                        // 3-dimensional problem
                        //
                        k0 = 2+math.randominteger(4);
                        k1 = 2+math.randominteger(4);
                        k2 = 2+math.randominteger(4);
                        xy = new double[k0*k1*k2, nx+ny];
                        for(i0=0; i0<=k0-1; i0++)
                        {
                            for(i1=0; i1<=k1-1; i1++)
                            {
                                for(i2=0; i2<=k2-1; i2++)
                                {
                                    xy[i0*k1*k2+i1*k2+i2,0] = i0+0.1*(2*math.randomreal()-1);
                                    xy[i0*k1*k2+i1*k2+i2,1] = i1+0.1*(2*math.randomreal()-1);
                                    xy[i0*k1*k2+i1*k2+i2,2] = i2+0.1*(2*math.randomreal()-1);
                                    for(j=0; j<=ny-1; j++)
                                    {
                                        xy[i0*k1*k2+i1*k2+i2,nx+j] = 2*math.randomreal()-1;
                                    }
                                }
                            }
                        }
                        testpoint = new double[nx];
                        testpoint[0] = math.randomreal()*(k0-1);
                        testpoint[1] = math.randomreal()*(k1-1);
                        testpoint[2] = math.randomreal()*(k2-1);
                    }
                    rbf.rbfsetpoints(s, xy, alglib.ap.rows(xy));
                    
                    //
                    // Build model, serialize, compare
                    //
                    rbf.rbfbuildmodel(s, rep);
                    {
                        //
                        // 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();
                        rbf.rbfalloc(_local_serializer, s);
                        _local_serializer.sstart_str();
                        rbf.rbfserialize(_local_serializer, s);
                        _local_serializer.stop();
                        _local_str = _local_serializer.get_string();
                        
                        _local_serializer = new serializer();
                        _local_serializer.ustart_str(_local_str);
                        rbf.rbfunserialize(_local_serializer, s2);
                        _local_serializer.stop();
                    }
                    rbf.rbfcalc(s, testpoint, ref y0);
                    rbf.rbfcalc(s2, testpoint, ref y1);
                    if( alglib.ap.len(y0)!=ny || alglib.ap.len(y1)!=ny )
                    {
                        result = true;
                        return result;
                    }
                    for(j=0; j<=ny-1; j++)
                    {
                        if( (double)(y0[j])!=(double)(y1[j]) )
                        {
                            result = true;
                            return result;
                        }
                    }
                    
                    //
                    // Check that calling RBFBuildModel() on S2 (new model)
                    // will result in construction of zero model, i.e. test
                    // that serialization restores model, but not dataset
                    // which was used to build model.
                    //
                    rbf.rbfbuildmodel(s2, rep);
                    rbf.rbfcalc(s2, testpoint, ref y1);
                    if( alglib.ap.len(y1)!=ny )
                    {
                        result = true;
                        return result;
                    }
                    for(j=0; j<=ny-1; j++)
                    {
                        if( (double)(y1[j])!=(double)(0) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            
            //
            // This function generates random 2 or 3 dimensional problem,
            // builds model using RBF-NN algo, serializes/unserializes it,
            // then compares models by calculating model value at some
            // random point.
            //
            // Additionally we test that new model (one which was restored
            // after serialization) has lost all model construction settings,
            // i.e. if we call RBFBuildModel() on a NEW model, we will get
            // empty (zero) model.
            //
            for(nx=2; nx<=3; nx++)
            {
                for(ny=1; ny<=2; ny++)
                {
                    
                    //
                    // prepare test problem
                    //
                    rbf.rbfcreate(nx, ny, s);
                    rbf.rbfsetalgomultilayer(s, 5.0, 5, 1.0E-3);
                    rbf.rbfsetlinterm(s);
                    if( nx==2 )
                    {
                        
                        //
                        // 2-dimensional problem
                        //
                        k0 = 2+math.randominteger(4);
                        k1 = 2+math.randominteger(4);
                        xy = new double[k0*k1, nx+ny];
                        for(i0=0; i0<=k0-1; i0++)
                        {
                            for(i1=0; i1<=k1-1; i1++)
                            {
                                xy[i0*k1+i1,0] = i0+0.1*(2*math.randomreal()-1);
                                xy[i0*k1+i1,1] = i1+0.1*(2*math.randomreal()-1);
                                for(j=0; j<=ny-1; j++)
                                {
                                    xy[i0*k1+i1,nx+j] = 2*math.randomreal()-1;
                                }
                            }
                        }
                        testpoint = new double[nx];
                        testpoint[0] = math.randomreal()*(k0-1);
                        testpoint[1] = math.randomreal()*(k1-1);
                    }
                    else
                    {
                        
                        //
                        // 3-dimensional problem
                        //
                        k0 = 2+math.randominteger(4);
                        k1 = 2+math.randominteger(4);
                        k2 = 2+math.randominteger(4);
                        xy = new double[k0*k1*k2, nx+ny];
                        for(i0=0; i0<=k0-1; i0++)
                        {
                            for(i1=0; i1<=k1-1; i1++)
                            {
                                for(i2=0; i2<=k2-1; i2++)
                                {
                                    xy[i0*k1*k2+i1*k2+i2,0] = i0+0.1*(2*math.randomreal()-1);
                                    xy[i0*k1*k2+i1*k2+i2,1] = i1+0.1*(2*math.randomreal()-1);
                                    xy[i0*k1*k2+i1*k2+i2,2] = i2+0.1*(2*math.randomreal()-1);
                                    for(j=0; j<=ny-1; j++)
                                    {
                                        xy[i0*k1*k2+i1*k2+i2,nx+j] = 2*math.randomreal()-1;
                                    }
                                }
                            }
                        }
                        testpoint = new double[nx];
                        testpoint[0] = math.randomreal()*(k0-1);
                        testpoint[1] = math.randomreal()*(k1-1);
                        testpoint[2] = math.randomreal()*(k2-1);
                    }
                    rbf.rbfsetpoints(s, xy, alglib.ap.rows(xy));
                    
                    //
                    // Build model, serialize, compare
                    //
                    rbf.rbfbuildmodel(s, rep);
                    {
                        //
                        // 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();
                        rbf.rbfalloc(_local_serializer, s);
                        _local_serializer.sstart_str();
                        rbf.rbfserialize(_local_serializer, s);
                        _local_serializer.stop();
                        _local_str = _local_serializer.get_string();
                        
                        _local_serializer = new serializer();
                        _local_serializer.ustart_str(_local_str);
                        rbf.rbfunserialize(_local_serializer, s2);
                        _local_serializer.stop();
                    }
                    rbf.rbfcalc(s, testpoint, ref y0);
                    rbf.rbfcalc(s2, testpoint, ref y1);
                    if( alglib.ap.len(y0)!=ny || alglib.ap.len(y1)!=ny )
                    {
                        result = true;
                        return result;
                    }
                    for(j=0; j<=ny-1; j++)
                    {
                        if( (double)(y0[j])!=(double)(y1[j]) )
                        {
                            result = true;
                            return result;
                        }
                    }
                    
                    //
                    // Check that calling RBFBuildModel() on S2 (new model)
                    // will result in construction of zero model, i.e. test
                    // that serialization restores model, but not dataset
                    // which was used to build model.
                    //
                    rbf.rbfbuildmodel(s2, rep);
                    rbf.rbfcalc(s2, testpoint, ref y1);
                    if( alglib.ap.len(y1)!=ny )
                    {
                        result = true;
                        return result;
                    }
                    for(j=0; j<=ny-1; j++)
                    {
                        if( (double)(y1[j])!=(double)(0) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
            }
            return result;
        }


        private static bool searcherr(double[,] y0,
            double[,] y1,
            int n,
            int ny,
            int errtype,
            double[] b1,
            double[] delta)
        {
            bool result = new bool();
            double oralerr = 0;
            double iralerr = 0;
            double[] irerr = new double[0];
            double[] orerr = new double[0];
            int lb = 0;
            int rb = 0;
            int i = 0;
            int j = 0;

            y0 = (double[,])y0.Clone();
            y1 = (double[,])y1.Clone();
            b1 = (double[])b1.Clone();
            delta = (double[])delta.Clone();

            alglib.ap.assert(n>0, "SearchErr: invalid parameter N(N<=0).");
            alglib.ap.assert(ny>0, "SearchErr: invalid parameter NY(NY<=0).");
            oralerr = 1.0E-1;
            iralerr = 1.0E-2;
            lb = 25;
            rb = 75;
            orerr = new double[ny];
            irerr = new double[ny];
            for(j=0; j<=ny-1; j++)
            {
                orerr[j] = 0;
                irerr[j] = 0;
            }
            if( errtype==1 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=ny-1; j++)
                    {
                        if( (double)(orerr[j])<(double)(Math.Abs(y0[i,j]-y1[i,j])) )
                        {
                            orerr[j] = Math.Abs(y0[i,j]-y1[i,j]);
                        }
                    }
                }
                for(i=0; i<=ny-1; i++)
                {
                    if( (double)(orerr[i])>(double)(b1[i]+delta[i]) || (double)(orerr[i])<(double)(b1[i]-delta[i]) )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            else
            {
                if( errtype==2 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=ny-1; j++)
                        {
                            if( i>lb && i<rb )
                            {
                                if( (double)(irerr[j])<(double)(Math.Abs(y0[i,j]-y1[i,j])) )
                                {
                                    irerr[j] = Math.Abs(y0[i,j]-y1[i,j]);
                                }
                            }
                            else
                            {
                                if( (double)(orerr[j])<(double)(Math.Abs(y0[i,j]-y1[i,j])) )
                                {
                                    orerr[j] = Math.Abs(y0[i,j]-y1[i,j]);
                                }
                            }
                        }
                    }
                    for(i=0; i<=ny-1; i++)
                    {
                        if( (double)(orerr[i])>(double)(oralerr) || (double)(irerr[i])>(double)(iralerr) )
                        {
                            result = true;
                            return result;
                        }
                    }
                }
                else
                {
                    alglib.ap.assert(false, "SearchErr: invalid argument ErrType(ErrType neither 1 nor 2)");
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing basic functionality of RBF module on regular grids with
        multi-layer algorithm in 2-3D.

          -- ALGLIB --
             Copyright 2.03.2012 by Bochkanov Sergey
        *************************************************************************/
        private static bool basicmultilayerrbftest()
        {
            bool result = new bool();
            rbf.rbfmodel s = new rbf.rbfmodel();
            rbf.rbfreport rep = new rbf.rbfreport();
            int nx = 0;
            int ny = 0;
            int k0 = 0;
            int k1 = 0;
            int k2 = 0;
            int linterm = 0;
            int np = 0;
            double q = 0;
            int layers = 0;
            double[] epss = new double[0];
            int range = 0;
            double s1 = 0;
            double s2 = 0;
            double gstep = 0;
            double[] point = new double[0];
            double[,] gp = new double[0,0];
            double[] x = new double[0];
            double[] y = new double[0];
            double[,] gy = new double[0,0];
            double[] gpgx0 = new double[0];
            double[] gpgx1 = new double[0];
            int pass = 0;
            int passcount = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int l = 0;

            range = 10;
            k0 = 6;
            k1 = 6;
            k2 = 6;
            passcount = 10;
            epss = new double[2];
            epss[0] = 0.05;
            epss[1] = 1.0E-6;
            for(pass=0; pass<=passcount-1; pass++)
            {
                
                //
                // prepare test problem
                //
                s1 = Math.Pow(range, math.randominteger(3)-1);
                s2 = Math.Pow(range, math.randominteger(3)-1);
                nx = math.randominteger(2)+2;
                ny = math.randominteger(2)+1;
                linterm = math.randominteger(3)+1;
                layers = math.randominteger(2);
                gstep = s1/6;
                x = new double[nx];
                y = new double[ny];
                point = new double[nx];
                rbf.rbfcreate(nx, ny, s);
                q = s1;
                rbf.rbfsetalgomultilayer(s, q, layers+5, 0.0);
                if( linterm==1 )
                {
                    rbf.rbfsetlinterm(s);
                }
                if( linterm==2 )
                {
                    rbf.rbfsetconstterm(s);
                }
                if( linterm==3 )
                {
                    rbf.rbfsetzeroterm(s);
                }
                
                //
                // start points for grid
                //
                for(i=0; i<=nx-1; i++)
                {
                    point[i] = s1*(2*math.randomreal()-1);
                }
                
                //
                // 2-dimensional test problem
                //
                if( nx==2 )
                {
                    np = k0*k1;
                    gp = new double[np, nx+ny];
                    
                    //
                    // create grid
                    //
                    for(i=0; i<=k0-1; i++)
                    {
                        for(j=0; j<=k1-1; j++)
                        {
                            gp[i*k1+j,0] = point[0]+gstep*i;
                            gp[i*k1+j,1] = point[1]+gstep*j;
                            for(k=0; k<=ny-1; k++)
                            {
                                gp[i*k1+j,nx+k] = s2*(2*math.randomreal()-1);
                            }
                        }
                    }
                    rbf.rbfsetpoints(s, gp, np);
                    rbf.rbfbuildmodel(s, rep);
                    if( ny==1 )
                    {
                        gpgx0 = new double[k0];
                        gpgx1 = new double[k1];
                        for(i=0; i<=k0-1; i++)
                        {
                            gpgx0[i] = point[0]+gstep*i;
                        }
                        for(i=0; i<=k1-1; i++)
                        {
                            gpgx1[i] = point[1]+gstep*i;
                        }
                        rbf.rbfgridcalc2(s, gpgx0, k0, gpgx1, k1, ref gy);
                        for(i=0; i<=k0-1; i++)
                        {
                            for(j=0; j<=k1-1; j++)
                            {
                                if( (double)(Math.Abs(gy[i,j]-gp[i*k1+j,nx]))>(double)(s2*epss[layers]) )
                                {
                                    result = true;
                                    return result;
                                }
                            }
                        }
                    }
                    for(i=0; i<=np-1; i++)
                    {
                        x[0] = gp[i,0];
                        x[1] = gp[i,1];
                        if( ny==1 )
                        {
                            y[0] = rbf.rbfcalc2(s, x[0], x[1]);
                            if( (double)(Math.Abs(gp[i,nx]-y[0]))>(double)(s2*epss[layers]) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        rbf.rbfcalc(s, x, ref y);
                        for(j=0; j<=ny-1; j++)
                        {
                            if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(s2*epss[layers]) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        rbf.rbfcalcbuf(s, x, ref y);
                        for(j=0; j<=ny-1; j++)
                        {
                            if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(s2*epss[layers]) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                }
                
                //
                // 3-dimensional test problems
                //
                if( nx==3 )
                {
                    np = k0*k1*k2;
                    gp = new double[np, nx+ny];
                    
                    //
                    // create grid
                    //
                    for(i=0; i<=k0-1; i++)
                    {
                        for(j=0; j<=k1-1; j++)
                        {
                            for(k=0; k<=k2-1; k++)
                            {
                                gp[(i*k1+j)*k2+k,0] = point[0]+gstep*i;
                                gp[(i*k1+j)*k2+k,1] = point[1]+gstep*j;
                                gp[(i*k1+j)*k2+k,2] = point[2]+gstep*k;
                                for(l=0; l<=ny-1; l++)
                                {
                                    gp[(i*k1+j)*k2+k,nx+l] = s2*(2*math.randomreal()-1);
                                }
                            }
                        }
                    }
                    rbf.rbfsetpoints(s, gp, np);
                    rbf.rbfbuildmodel(s, rep);
                    for(i=0; i<=np-1; i++)
                    {
                        x[0] = gp[i,0];
                        x[1] = gp[i,1];
                        x[2] = gp[i,2];
                        if( ny==1 )
                        {
                            y[0] = rbf.rbfcalc3(s, x[0], x[1], x[2]);
                            if( (double)(Math.Abs(gp[i,nx]-y[0]))>(double)(s2*epss[layers]) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        rbf.rbfcalc(s, x, ref y);
                        for(j=0; j<=ny-1; j++)
                        {
                            if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(s2*epss[layers]) )
                            {
                                result = true;
                                return result;
                            }
                        }
                        rbf.rbfcalcbuf(s, x, ref y);
                        for(j=0; j<=ny-1; j++)
                        {
                            if( (double)(Math.Abs(gp[i,nx+j]-y[j]))>(double)(s2*epss[layers]) )
                            {
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


    }
    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();
            bool vferrors = 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[] fv = 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 d = 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;
            vferrors = false;
            
            //
            // Test: bilinear, bicubic
            //
            for(n=2; n<=7; n++)
            {
                for(m=2; m<=7; m++)
                {
                    d = math.randominteger(2)+2;
                    x = new double[n];
                    y = new double[m];
                    lx = new double[2*n-1];
                    ly = new double[2*m-1];
                    f = new double[m, n];
                    fv = new double[m*n*d];
                    ft = new double[n, m];
                    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]);
                            }
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                for(k=0; k<=d-1; k++)
                                {
                                    fv[d*(n*j+i)+k] = Math.Exp(0.6*x[i])-Math.Exp(-(0.3*y[j])+0.08*x[i])+2*Math.Cos(Math.PI*(x[i]+1.2*y[j]+k))+0.1*Math.Cos(20*x[i]+15*y[j]+k);
                                }
                            }
                        }
                        
                        //
                        // 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, 1, ax, bx, ay, by);
                        
                        //
                        // Lin.Trans. test for vector-function
                        //
                        spline2d.spline2dbuildbilinearv(x, n, y, m, fv, d, c);
                        lterrors = lterrors || !testlintrans(c, d, 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, 1, ax, bx, ay, by);
                        
                        //
                        // Lin.Trans. test for vector-function
                        //
                        spline2d.spline2dbuildbicubicv(x, n, y, m, fv, d, c);
                        lterrors = lterrors || !testlintrans(c, d, ax, bx, ay, by);
                        
                        //
                        // Copy 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);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            //
            // Test for vector-function
            //
            vferrors = testspline2dvf(true);
            
            //
            // Report
            //
            waserrors = ((((((((blerrors || bcerrors) || dserrors) || cperrors) || uperrors) || lterrors) || syerrors) || rlerrors) || rcerrors) || vferrors;
            if( !silent )
            {
                System.Console.Write("TESTING 2D SPLINE");
                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();
                }
                System.Console.Write("VECTOR FUNCTION TEST:                    ");
                if( vferrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testspline2d(bool silent)
        {
            return testspline2d(silent);
        }


        /*************************************************************************
        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 for scalar
        *************************************************************************/
        private static bool testlintrans(spline2d.spline2dinterpolant c,
            int d,
            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 = new double[0];
            double[] v2 = new double[0];
            int pass = 0;
            int passcount = 0;
            int xjob = 0;
            int yjob = 0;
            spline2d.spline2dinterpolant c2 = new spline2d.spline2dinterpolant();
            int di = 0;

            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;
                        }
                        spline2d.spline2dcalcv(c, tx, ty, ref v1);
                        spline2d.spline2dcalcv(c2, vx, vy, ref v2);
                        for(di=0; di<=d-1; di++)
                        {
                            err = Math.Max(err, Math.Abs(v1[di]-v2[di]));
                        }
                        
                        //
                        // Test F
                        //
                        spline2d.spline2dcopy(c, c2);
                        spline2d.spline2dlintransf(c2, a1, b1);
                        tx = ax+math.randomreal()*(bx-ax);
                        ty = ay+math.randomreal()*(by-ay);
                        spline2d.spline2dcalcv(c, tx, ty, ref v1);
                        spline2d.spline2dcalcv(c2, tx, ty, ref v2);
                        for(di=0; di<=d-1; di++)
                        {
                            err = Math.Max(err, Math.Abs(a1*v1[di]+b1-v2[di]));
                        }
                    }
                }
            }
            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);
        }


        /*************************************************************************
        The function check, that follow functions works correctly:
            Spline2DBilinearV, Spline2DBicubicV, Spline2DCalcV
            and Spline2DUnpackV.
        *************************************************************************/
        private static bool testspline2dvf(bool silent)
        {
            bool result = new bool();
            spline2d.spline2dinterpolant vc = new spline2d.spline2dinterpolant();
            spline2d.spline2dinterpolant sc = new spline2d.spline2dinterpolant();
            double range = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] f = new double[0];
            double rndx = 0;
            double rndy = 0;
            int nrnd = 0;
            double[] resf = new double[0];
            double[,] ef = new double[0,0];
            double resef = 0;
            int m = 0;
            int n = 0;
            int d = 0;
            int tstn = 0;
            int tstm = 0;
            int tstd = 0;
            double[,] tsttbl0 = new double[0,0];
            double[,] tsttbl1 = new double[0,0];
            double eps = 0;
            double st = 0;
            int p0 = 0;
            int p1 = 0;
            int variant = 0;
            int pass = 0;
            int passcount = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int i0 = 0;
            int j0 = 0;
            double xmin = 0;
            double xmax = 0;
            double ymin = 0;
            double ymax = 0;

            eps = 10000.0*math.machineepsilon;
            st = 0.1;
            passcount = 5;
            for(pass=1; pass<=passcount; pass++)
            {
                for(variant=1; variant<=2; variant++)
                {
                    range = math.randominteger(71)+30.0;
                    nrnd = math.randominteger(26)+25;
                    range = math.randominteger(71)+30;
                    m = math.randominteger(4)+2;
                    n = math.randominteger(4)+2;
                    d = math.randominteger(3)+1;
                    apserv.rvectorsetlengthatleast(ref x, n);
                    apserv.rvectorsetlengthatleast(ref y, m);
                    apserv.rvectorsetlengthatleast(ref f, n*m*d);
                    apserv.rmatrixsetlengthatleast(ref ef, m, n);
                    
                    //
                    // Build a grid for spline
                    //
                    x[0] = range*(2*math.randomreal()-1);
                    y[0] = range*(2*math.randomreal()-1);
                    for(i=1; i<=n-1; i++)
                    {
                        x[i] = x[i-1]+st+math.randomreal();
                    }
                    for(i=1; i<=m-1; i++)
                    {
                        y[i] = y[i-1]+st+math.randomreal();
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            for(k=0; k<=d-1; k++)
                            {
                                f[d*(n*j+i)+k] = range*(2*math.randomreal()-1);
                            }
                        }
                    }
                    xmin = x[0];
                    xmax = x[n-1];
                    ymin = y[0];
                    ymax = y[m-1];
                    
                    //
                    // Build a spline
                    //
                    if( variant==1 )
                    {
                        spline2d.spline2dbuildbilinearv(x, n, y, m, f, d, vc);
                    }
                    if( variant==2 )
                    {
                        spline2d.spline2dbuildbicubicv(x, n, y, m, f, d, vc);
                    }
                    
                    //
                    // Part of test, which shows that Spline2DBuildBilinearV function
                    // works correctly.
                    // And there is test for Spline2DUnpackV.
                    //
                    spline2d.spline2dunpackv(vc, ref tstm, ref tstn, ref tstd, ref tsttbl1);
                    if( (tstm!=m || tstn!=n) || tstd!=d )
                    {
                        if( !silent )
                        {
                            System.Console.Write("TestSpline2DVF fail Spline2DUnpack:");
                            System.Console.WriteLine();
                            System.Console.Write("    TstM=");
                            System.Console.Write("{0,0:d}",tstm);
                            System.Console.Write("; M=");
                            System.Console.Write("{0,0:d}",m);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                            System.Console.Write("    TstN=");
                            System.Console.Write("{0,0:d}",tstn);
                            System.Console.Write("; N=");
                            System.Console.Write("{0,0:d}",n);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                            System.Console.Write("    TstD=");
                            System.Console.Write("{0,0:d}",tstd);
                            System.Console.Write("; D=");
                            System.Console.Write("{0,0:d}",d);
                            System.Console.Write(".");
                            System.Console.WriteLine();
                        }
                        result = true;
                        return result;
                    }
                    for(k=0; k<=d-1; k++)
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                ef[i,j] = f[d*(i*n+j)+k];
                            }
                        }
                        if( variant==1 )
                        {
                            spline2d.spline2dbuildbilinear(x, y, ef, m, n, sc);
                        }
                        if( variant==2 )
                        {
                            spline2d.spline2dbuildbicubic(x, y, ef, m, n, sc);
                        }
                        spline2d.spline2dunpack(sc, ref tstm, ref tstn, ref tsttbl0);
                        if( tstm!=m || tstn!=n )
                        {
                            if( !silent )
                            {
                                System.Console.Write("TestSpline2DVF fail Spline2DUnpack:");
                                System.Console.WriteLine();
                                System.Console.Write("    TstM=");
                                System.Console.Write("{0,0:d}",tstm);
                                System.Console.Write("; M=");
                                System.Console.Write("{0,0:d}",m);
                                System.Console.Write(";");
                                System.Console.WriteLine();
                                System.Console.Write("    TstN=");
                                System.Console.Write("{0,0:d}",tstn);
                                System.Console.Write("; N=");
                                System.Console.Write("{0,0:d}",n);
                                System.Console.Write(".");
                                System.Console.WriteLine();
                            }
                            result = true;
                            return result;
                        }
                        for(i=0; i<=m-2; i++)
                        {
                            for(j=0; j<=n-2; j++)
                            {
                                p0 = i*(n-1)+j;
                                p1 = d*p0;
                                for(i0=0; i0<=19; i0++)
                                {
                                    if( (double)(tsttbl1[p1+k,i0])!=(double)(tsttbl0[p0,i0]) )
                                    {
                                        if( !silent )
                                        {
                                            System.Console.Write("TestSpline2DVF: Tbl error");
                                            System.Console.WriteLine();
                                        }
                                        result = true;
                                        return result;
                                    }
                                }
                            }
                        }
                    }
                    
                    //
                    // Part of test, which shows that functions Spline2DCalcVBuf and Spline2DCalcV   
                    // works correctly
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            
                            //
                            // Test for grid points
                            //
                            spline2d.spline2dcalcv(vc, x[i], y[j], ref resf);
                            for(k=0; k<=d-1; k++)
                            {
                                for(i0=0; i0<=m-1; i0++)
                                {
                                    for(j0=0; j0<=n-1; j0++)
                                    {
                                        ef[i0,j0] = f[d*(i0*n+j0)+k];
                                    }
                                }
                                if( variant==1 )
                                {
                                    spline2d.spline2dbuildbilinear(x, y, ef, m, n, sc);
                                }
                                if( variant==2 )
                                {
                                    spline2d.spline2dbuildbicubic(x, y, ef, m, n, sc);
                                }
                                resef = spline2d.spline2dcalc(sc, x[i], y[j]);
                                if( (double)(Math.Abs(resf[k]-resef))>(double)(eps) )
                                {
                                    if( !silent )
                                    {
                                        System.Console.Write("TestSpline2DVF fail Spline2DCalcV:");
                                        System.Console.WriteLine();
                                        System.Console.Write("    ");
                                        System.Console.Write("{0,0:F5}",Math.Abs(resf[k]-resef));
                                        System.Console.Write("=|resF[");
                                        System.Console.Write("{0,0:d}",k);
                                        System.Console.Write("]-resEF|=|");
                                        System.Console.Write("{0,0:F5}",resf[k]);
                                        System.Console.Write("-");
                                        System.Console.Write("{0,0:F5}",resef);
                                        System.Console.Write("|>Eps=");
                                        System.Console.Write("{0,0:E2}",eps);
                                        System.Console.Write(";");
                                        System.Console.WriteLine();
                                        System.Console.Write("    resF[");
                                        System.Console.Write("{0,0:d}",k);
                                        System.Console.Write("]=");
                                        System.Console.Write("{0,0:F5}",resf[k]);
                                        System.Console.Write(";");
                                        System.Console.WriteLine();
                                        System.Console.Write("    resEF=");
                                        System.Console.Write("{0,0:F5}",resef);
                                        System.Console.Write(".");
                                        System.Console.WriteLine();
                                    }
                                    result = true;
                                    return result;
                                }
                            }
                        }
                    }
                    
                    //
                    // Test for random points
                    //
                    for(i=1; i<=nrnd; i++)
                    {
                        rndx = xmin+(xmax-xmin)*math.randomreal();
                        rndy = ymin+(ymax-ymin)*math.randomreal();
                        
                        //
                        // Calculate value for vector-function in random point
                        //
                        spline2d.spline2dcalcv(vc, rndx, rndy, ref resf);
                        for(k=0; k<=d-1; k++)
                        {
                            
                            //
                            // Build spline for scalar-function, each of which correspond
                            // to one of vector-function's components.
                            //
                            for(i0=0; i0<=m-1; i0++)
                            {
                                for(j0=0; j0<=n-1; j0++)
                                {
                                    ef[i0,j0] = f[d*(i0*n+j0)+k];
                                }
                            }
                            if( variant==1 )
                            {
                                spline2d.spline2dbuildbilinear(x, y, ef, m, n, sc);
                            }
                            if( variant==2 )
                            {
                                spline2d.spline2dbuildbicubic(x, y, ef, m, n, sc);
                            }
                            resef = spline2d.spline2dcalc(sc, rndx, rndy);
                            if( (double)(Math.Abs(resf[k]-resef))>(double)(eps) )
                            {
                                if( !silent )
                                {
                                    System.Console.Write("TestSpline2DVF fail Spline2DCalcV:");
                                    System.Console.WriteLine();
                                    System.Console.Write("    ");
                                    System.Console.Write("{0,0:F5}",Math.Abs(resf[k]-resef));
                                    System.Console.Write("=|resF[");
                                    System.Console.Write("{0,0:d}",k);
                                    System.Console.Write("]-resEF|=|");
                                    System.Console.Write("{0,0:F5}",resf[k]);
                                    System.Console.Write("-");
                                    System.Console.Write("{0,0:F5}",resef);
                                    System.Console.Write("|>Eps=");
                                    System.Console.Write("{0,0:E2}",eps);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("    resF[");
                                    System.Console.Write("{0,0:d}",k);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F5}",resf[k]);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("    resEF=");
                                    System.Console.Write("{0,0:F5}",resef);
                                    System.Console.Write(".");
                                    System.Console.WriteLine();
                                }
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            if( !silent )
            {
                System.Console.Write("TestSpline2DVF: OK");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


    }
    public class testspline3dunit
    {
        public static bool testspline3d(bool silence)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool basicerr = new bool();
            bool unpackerr = new bool();
            bool lintransferr = new bool();
            bool trilinreserr = new bool();

            basicerr = basictest();
            unpackerr = testunpack();
            lintransferr = testlintrans();
            trilinreserr = testtrilinearresample();
            waserrors = ((basicerr || unpackerr) || lintransferr) || trilinreserr;
            if( !silence )
            {
                System.Console.Write("TESTING 3D SPLINE");
                System.Console.WriteLine();
                System.Console.Write("BASIC TEST:                              ");
                if( basicerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("UNPACK TEST:                             ");
                if( unpackerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("LIN_TRANSF TEST:                         ");
                if( lintransferr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("TRILINEAR RESAMPLING TEST:               ");
                if( trilinreserr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                
                //
                // Summary
                //
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testspline3d(bool silence)
        {
            return testspline3d(silence);
        }


        /*************************************************************************
        The function does test basic functionality.
        *************************************************************************/
        private static bool basictest()
        {
            bool result = new bool();
            spline3d.spline3dinterpolant c = new spline3d.spline3dinterpolant();
            spline3d.spline3dinterpolant cc = new spline3d.spline3dinterpolant();
            double[] vvf = new double[0];
            double vsf = 0;
            int d = 0;
            int m = 0;
            int n = 0;
            int l = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] z = new double[0];
            double[] sf = new double[0];
            double[] vf = new double[0];
            double eps = 0;
            int pass = 0;
            int passcount = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int offs = 0;
            int di = 0;
            double ax = 0;
            double ay = 0;
            double az = 0;
            double axy = 0;
            double ayz = 0;
            double vx = 0;
            double vy = 0;
            double vz = 0;

            eps = 1000*math.machineepsilon;
            
            //
            // Test spline ability to reproduce D-dimensional vector function
            //     f[idx](x,y,z) = idx+AX*x + AY*y + AZ*z + AXY*x*y + AYZ*y*z
            // with random AX/AY/...
            //
            // We generate random test function, build spline, then evaluate
            // it in the random test point.
            //
            for(d=1; d<=3; d++)
            {
                n = 2+math.randominteger(4);
                m = 2+math.randominteger(4);
                l = 2+math.randominteger(4);
                x = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x[i] = i;
                }
                y = new double[m];
                for(i=0; i<=m-1; i++)
                {
                    y[i] = i;
                }
                z = new double[l];
                for(i=0; i<=l-1; i++)
                {
                    z[i] = i;
                }
                vf = new double[l*m*n*d];
                offs = 0;
                ax = 2*math.randomreal()-1;
                ay = 2*math.randomreal()-1;
                az = 2*math.randomreal()-1;
                axy = 2*math.randomreal()-1;
                ayz = 2*math.randomreal()-1;
                for(k=0; k<=l-1; k++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(di=0; di<=d-1; di++)
                            {
                                vf[offs] = di+ax*i+ay*j+az*k+axy*i*j+ayz*j*k;
                                offs = offs+1;
                            }
                        }
                    }
                }
                spline3d.spline3dbuildtrilinearv(x, n, y, m, z, l, vf, d, c);
                vx = math.randomreal()*n;
                vy = math.randomreal()*m;
                vz = math.randomreal()*l;
                spline3d.spline3dcalcv(c, vx, vy, vz, ref vf);
                for(di=0; di<=d-1; di++)
                {
                    if( (double)(Math.Abs(di+ax*vx+ay*vy+az*vz+axy*vx*vy+ayz*vy*vz-vf[di]))>(double)(eps) )
                    {
                        result = true;
                        return result;
                    }
                }
                if( d==1 )
                {
                    vsf = spline3d.spline3dcalc(c, vx, vy, vz);
                    if( (double)(Math.Abs(ax*vx+ay*vy+az*vz+axy*vx*vy+ayz*vy*vz-vsf))>(double)(eps) )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            
            //
            // Generate random grid and test function.
            // Test spline ability to reproduce function values at grid nodes.
            //
            passcount = 20;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // Prepare a model and check that functions (Spline3DBuildTrilinear,
                // Spline3DCalc,Spline3DCalcV) work correctly and
                //
                buildrndgrid(true, true, ref n, ref m, ref l, ref d, ref x, ref y, ref z, ref vf);
                apserv.rvectorsetlengthatleast(ref sf, n*m*l);
                
                //
                // Check that the model's values are equal to the function's values
                // in grid points
                //
                spline3d.spline3dbuildtrilinearv(x, n, y, m, z, l, vf, d, c);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        for(k=0; k<=l-1; k++)
                        {
                            spline3d.spline3dcalcv(c, x[i], y[j], z[k], ref vvf);
                            for(di=0; di<=d-1; di++)
                            {
                                if( (double)(Math.Abs(vf[d*(n*(m*k+j)+i)+di]-vvf[di]))>(double)(eps) )
                                {
                                    result = true;
                                    return result;
                                }
                            }
                        }
                    }
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Unpack/UnpackV test
        *************************************************************************/
        private static bool testunpack()
        {
            bool result = new bool();
            spline3d.spline3dinterpolant c = new spline3d.spline3dinterpolant();
            double[,] tbl0 = new double[0,0];
            double[,] tbl1 = new double[0,0];
            int n = 0;
            int m = 0;
            int l = 0;
            int d = 0;
            int sz = 0;
            int un = 0;
            int um = 0;
            int ul = 0;
            int ud = 0;
            int ust = 0;
            int uvn = 0;
            int uvm = 0;
            int uvl = 0;
            int uvd = 0;
            int uvst = 0;
            int ci = 0;
            int cj = 0;
            int ck = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] z = new double[0];
            double[] sf = new double[0];
            double[] vf = new double[0];
            int p0 = 0;
            int p1 = 0;
            double tx = 0;
            double ty = 0;
            double tz = 0;
            double v1 = 0;
            double v2 = 0;
            double err = 0;
            int pass = 0;
            int passcount = 0;
            bool bperr = new bool();
            int i = 0;
            int j = 0;
            int k = 0;
            int di = 0;
            int i0 = 0;

            passcount = 20;
            err = 0;
            for(pass=1; pass<=passcount; pass++)
            {
                
                //
                // generate random grid.
                // NOTE: for this test we need ordered grid, i.e. grid
                //       with nodes in ascending order
                //
                buildrndgrid(true, false, ref n, ref m, ref l, ref d, ref x, ref y, ref z, ref vf);
                sz = n*m*l;
                apserv.rvectorsetlengthatleast(ref sf, sz);
                spline3d.spline3dbuildtrilinearv(x, n, y, m, z, l, vf, d, c);
                spline3d.spline3dunpackv(c, ref uvn, ref uvm, ref uvl, ref uvd, ref uvst, ref tbl0);
                for(di=0; di<=d-1; di++)
                {
                    
                    //
                    // DI-th component copy of a vector-function to
                    // a scalar function
                    //
                    for(i=0; i<=sz-1; i++)
                    {
                        sf[i] = vf[d*i+di];
                    }
                    spline3d.spline3dbuildtrilinearv(x, n, y, m, z, l, sf, 1, c);
                    spline3d.spline3dunpackv(c, ref un, ref um, ref ul, ref ud, ref ust, ref tbl1);
                    for(i=0; i<=n-2; i++)
                    {
                        for(j=0; j<=m-2; j++)
                        {
                            for(k=0; k<=l-2; k++)
                            {
                                p1 = (n-1)*((m-1)*k+j)+i;
                                p0 = d*p1+di;
                                
                                //
                                // Check that all components are correct:
                                //  *first check, that unpacked componets are equal
                                //   to packed components;
                                //
                                bperr = (((((((((((((((((un!=n || um!=m) || ul!=l) || (double)(tbl1[p1,0])!=(double)(x[i])) || (double)(tbl1[p1,1])!=(double)(x[i+1])) || (double)(tbl1[p1,2])!=(double)(y[j])) || (double)(tbl1[p1,3])!=(double)(y[j+1])) || (double)(tbl1[p1,4])!=(double)(z[k])) || (double)(tbl1[p1,5])!=(double)(z[k+1])) || uvn!=n) || uvm!=m) || uvl!=l) || uvd!=d) || (double)(tbl0[p0,0])!=(double)(x[i])) || (double)(tbl0[p0,1])!=(double)(x[i+1])) || (double)(tbl0[p0,2])!=(double)(y[j])) || (double)(tbl0[p0,3])!=(double)(y[j+1])) || (double)(tbl0[p0,4])!=(double)(z[k])) || (double)(tbl0[p0,5])!=(double)(z[k+1]);
                                
                                //
                                //  *check, that all components unpacked by Unpack
                                //   function are equal to all components unpacked
                                //   by UnpackV function.
                                //
                                for(i0=0; i0<=13; i0++)
                                {
                                    bperr = bperr || (double)(tbl0[p0,i0])!=(double)(tbl1[p1,i0]);
                                }
                                if( bperr )
                                {
                                    result = true;
                                    return result;
                                }
                                tx = (0.001+0.999*math.randomreal())*(tbl1[p1,1]-tbl1[p1,0]);
                                ty = (0.001+0.999*math.randomreal())*(tbl1[p1,3]-tbl1[p1,2]);
                                tz = (0.001+0.999*math.randomreal())*(tbl1[p1,5]-tbl1[p1,4]);
                                
                                //
                                // Interpolation properties for:
                                //  *scalar function;
                                //
                                v1 = 0;
                                for(ci=0; ci<=1; ci++)
                                {
                                    for(cj=0; cj<=1; cj++)
                                    {
                                        for(ck=0; ck<=1; ck++)
                                        {
                                            v1 = v1+tbl1[p1,6+2*(2*ck+cj)+ci]*Math.Pow(tx, ci)*Math.Pow(ty, cj)*Math.Pow(tz, ck);
                                        }
                                    }
                                }
                                v2 = spline3d.spline3dcalc(c, tbl1[p1,0]+tx, tbl1[p1,2]+ty, tbl1[p1,4]+tz);
                                err = Math.Max(err, Math.Abs(v1-v2));
                                
                                //
                                //  *component of vector function.
                                //
                                v1 = 0;
                                for(ci=0; ci<=1; ci++)
                                {
                                    for(cj=0; cj<=1; cj++)
                                    {
                                        for(ck=0; ck<=1; ck++)
                                        {
                                            v1 = v1+tbl0[p0,6+2*(2*ck+cj)+ci]*Math.Pow(tx, ci)*Math.Pow(ty, cj)*Math.Pow(tz, ck);
                                        }
                                    }
                                }
                                v2 = spline3d.spline3dcalc(c, tbl0[p0,0]+tx, tbl0[p0,2]+ty, tbl0[p0,4]+tz);
                                err = Math.Max(err, Math.Abs(v1-v2));
                            }
                        }
                    }
                }
            }
            result = (double)(err)>(double)(1.0E+5*math.machineepsilon);
            return result;
        }


        /*************************************************************************
        LinTrans test
        *************************************************************************/
        private static bool testlintrans()
        {
            bool result = new bool();
            spline3d.spline3dinterpolant c = new spline3d.spline3dinterpolant();
            spline3d.spline3dinterpolant c2 = new spline3d.spline3dinterpolant();
            int m = 0;
            int n = 0;
            int l = 0;
            int d = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] z = new double[0];
            double[] f = new double[0];
            double a1 = 0;
            double a2 = 0;
            double a3 = 0;
            double b1 = 0;
            double b2 = 0;
            double b3 = 0;
            double tx = 0;
            double ty = 0;
            double tz = 0;
            double vx = 0;
            double vy = 0;
            double vz = 0;
            double[] v1 = new double[0];
            double[] v2 = new double[0];
            int pass = 0;
            int passcount = 0;
            int xjob = 0;
            int yjob = 0;
            int zjob = 0;
            double err = 0;
            int i = 0;

            err = 0;
            passcount = 15;
            for(pass=1; pass<=passcount; pass++)
            {
                buildrndgrid(true, false, ref n, ref m, ref l, ref d, ref x, ref y, ref z, ref f);
                spline3d.spline3dbuildtrilinearv(x, n, y, m, z, l, f, d, c);
                for(xjob=0; xjob<=1; xjob++)
                {
                    for(yjob=0; yjob<=1; yjob++)
                    {
                        for(zjob=0; zjob<=1; zjob++)
                        {
                            
                            //
                            // Prepare
                            //
                            do
                            {
                                a1 = 2.0*math.randomreal()-1.0;
                            }
                            while( (double)(a1)==(double)(0) );
                            a1 = a1*xjob;
                            b1 = x[0]+math.randomreal()*(x[n-1]-x[0]+2.0)-1.0;
                            do
                            {
                                a2 = 2.0*math.randomreal()-1.0;
                            }
                            while( (double)(a2)==(double)(0) );
                            a2 = a2*yjob;
                            b2 = y[0]+math.randomreal()*(y[m-1]-y[0]+2.0)-1.0;
                            do
                            {
                                a3 = 2.0*math.randomreal()-1.0;
                            }
                            while( (double)(a3)==(double)(0) );
                            a3 = a3*zjob;
                            b3 = z[0]+math.randomreal()*(z[l-1]-z[0]+2.0)-1.0;
                            
                            //
                            // Test XYZ
                            //
                            spline3d.spline3dcopy(c, c2);
                            spline3d.spline3dlintransxyz(c2, a1, b1, a2, b2, a3, b3);
                            tx = x[0]+math.randomreal()*(x[n-1]-x[0]);
                            ty = y[0]+math.randomreal()*(y[m-1]-y[0]);
                            tz = z[0]+math.randomreal()*(z[l-1]-z[0]);
                            if( xjob==0 )
                            {
                                tx = b1;
                                vx = x[0]+math.randomreal()*(x[n-1]-x[0]);
                            }
                            else
                            {
                                vx = (tx-b1)/a1;
                            }
                            if( yjob==0 )
                            {
                                ty = b2;
                                vy = y[0]+math.randomreal()*(y[m-1]-y[0]);
                            }
                            else
                            {
                                vy = (ty-b2)/a2;
                            }
                            if( zjob==0 )
                            {
                                tz = b3;
                                vz = z[0]+math.randomreal()*(z[l-1]-z[0]);
                            }
                            else
                            {
                                vz = (tz-b3)/a3;
                            }
                            spline3d.spline3dcalcv(c, tx, ty, tz, ref v1);
                            spline3d.spline3dcalcv(c2, vx, vy, vz, ref v2);
                            for(i=0; i<=d-1; i++)
                            {
                                err = Math.Max(err, Math.Abs(v1[i]-v2[i]));
                            }
                            if( (double)(err)>(double)(1.0E+4*math.machineepsilon) )
                            {
                                result = true;
                                return result;
                            }
                            
                            //
                            // Test F
                            //
                            spline3d.spline3dcopy(c, c2);
                            spline3d.spline3dlintransf(c2, a1, b1);
                            tx = x[0]+math.randomreal()*(x[n-1]-x[0]);
                            ty = y[0]+math.randomreal()*(y[m-1]-y[0]);
                            tz = z[0]+math.randomreal()*(z[l-1]-z[0]);
                            spline3d.spline3dcalcv(c, tx, ty, tz, ref v1);
                            spline3d.spline3dcalcv(c2, tx, ty, tz, ref v2);
                            for(i=0; i<=d-1; i++)
                            {
                                err = Math.Max(err, Math.Abs(a1*v1[i]+b1-v2[i]));
                            }
                        }
                    }
                }
            }
            result = (double)(err)>(double)(1.0E+4*math.machineepsilon);
            return result;
        }


        /*************************************************************************
        Resample test
        *************************************************************************/
        private static bool testtrilinearresample()
        {
            bool result = new bool();
            spline3d.spline3dinterpolant c = new spline3d.spline3dinterpolant();
            int n = 0;
            int m = 0;
            int l = 0;
            int n2 = 0;
            int m2 = 0;
            int l2 = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] z = new double[0];
            double[] f = new double[0];
            double[] fr = new double[0];
            double v1 = 0;
            double v2 = 0;
            double err = 0;
            double mf = 0;
            int pass = 0;
            int passcount = 0;
            int i = 0;
            int j = 0;
            int k = 0;

            result = false;
            passcount = 20;
            for(pass=1; pass<=passcount; pass++)
            {
                n = math.randominteger(4)+2;
                m = math.randominteger(4)+2;
                l = math.randominteger(4)+2;
                n2 = math.randominteger(4)+2;
                m2 = math.randominteger(4)+2;
                l2 = math.randominteger(4)+2;
                apserv.rvectorsetlengthatleast(ref x, n);
                apserv.rvectorsetlengthatleast(ref y, m);
                apserv.rvectorsetlengthatleast(ref z, l);
                apserv.rvectorsetlengthatleast(ref f, n*m*l);
                for(i=0; i<=n-1; i++)
                {
                    x[i] = (double)i/(double)(n-1);
                }
                for(i=0; i<=m-1; i++)
                {
                    y[i] = (double)i/(double)(m-1);
                }
                for(i=0; i<=l-1; i++)
                {
                    z[i] = (double)i/(double)(l-1);
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        for(k=0; k<=l-1; k++)
                        {
                            f[n*(m*k+j)+i] = 2*math.randomreal()-1;
                        }
                    }
                }
                spline3d.spline3dresampletrilinear(f, l, m, n, l2, m2, n2, ref fr);
                spline3d.spline3dbuildtrilinearv(x, n, y, m, z, l, f, 1, c);
                err = 0;
                mf = 0;
                for(i=0; i<=n2-1; i++)
                {
                    for(j=0; j<=m2-1; j++)
                    {
                        for(k=0; k<=l2-1; k++)
                        {
                            v1 = spline3d.spline3dcalc(c, (double)i/(double)(n2-1), (double)j/(double)(m2-1), (double)k/(double)(l2-1));
                            v2 = fr[n2*(m2*k+j)+i];
                            err = Math.Max(err, Math.Abs(v1-v2));
                            mf = Math.Max(mf, Math.Abs(v1));
                        }
                    }
                }
                result = result || (double)(err/mf)>(double)(1.0E+4*math.machineepsilon);
                if( result )
                {
                    return result;
                }
            }
            return result;
        }


        /*************************************************************************
        The function does build random function on random grid with random number
        of points:
        * N, M, K   -   random from 2 to 5
        * D         -   1 in case IsVect=False, 1..3 in case IsVect=True
        * X, Y, Z   -   each variable spans from MinV to MaxV, with MinV is random
                        number from [-1.5,0.5] and MaxV is random number from
                        [0.5,1.5]. All nodes are well separated. All nodes are
                        randomly reordered in case Reorder=False. When Reorder=True,
                        nodes are returned in ascending order.
        * F         -   random values from [-1,+1]
        *************************************************************************/
        private static void buildrndgrid(bool isvect,
            bool reorder,
            ref int n,
            ref int m,
            ref int l,
            ref int d,
            ref double[] x,
            ref double[] y,
            ref double[] z,
            ref double[] f)
        {
            double st = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int di = 0;
            double v = 0;
            double mx = 0;
            double maxv = 0;
            double minv = 0;

            n = 0;
            m = 0;
            l = 0;
            d = 0;
            x = new double[0];
            y = new double[0];
            z = new double[0];
            f = new double[0];

            st = 0.3;
            m = math.randominteger(4)+2;
            n = math.randominteger(4)+2;
            l = math.randominteger(4)+2;
            if( isvect )
            {
                d = math.randominteger(3)+1;
            }
            else
            {
                d = 1;
            }
            apserv.rvectorsetlengthatleast(ref x, n);
            apserv.rvectorsetlengthatleast(ref y, m);
            apserv.rvectorsetlengthatleast(ref z, l);
            apserv.rvectorsetlengthatleast(ref f, n*m*l*d);
            
            //
            // Fill X
            //
            x[0] = 0;
            for(i=1; i<=n-1; i++)
            {
                x[i] = x[i-1]+st+math.randomreal();
            }
            minv = -0.5-math.randomreal();
            maxv = 0.5+math.randomreal();
            mx = x[n-1];
            for(i=0; i<=n-1; i++)
            {
                x[i] = x[i]/mx*(maxv-minv)+minv;
            }
            if( reorder )
            {
                for(i=0; i<=n-1; i++)
                {
                    k = math.randominteger(n);
                    v = x[i];
                    x[i] = x[k];
                    x[k] = v;
                }
            }
            
            //
            // Fill Y
            //
            y[0] = 0;
            for(i=1; i<=m-1; i++)
            {
                y[i] = y[i-1]+st+math.randomreal();
            }
            minv = -0.5-math.randomreal();
            maxv = 0.5+math.randomreal();
            mx = y[m-1];
            for(i=0; i<=m-1; i++)
            {
                y[i] = y[i]/mx*(maxv-minv)+minv;
            }
            if( reorder )
            {
                for(i=0; i<=m-1; i++)
                {
                    k = math.randominteger(m);
                    v = y[i];
                    y[i] = y[k];
                    y[k] = v;
                }
            }
            
            //
            // Fill Z
            //
            z[0] = 0;
            for(i=1; i<=l-1; i++)
            {
                z[i] = z[i-1]+st+math.randomreal();
            }
            minv = -0.5-math.randomreal();
            maxv = 0.5+math.randomreal();
            mx = z[l-1];
            for(i=0; i<=l-1; i++)
            {
                z[i] = z[i]/mx*(maxv-minv)+minv;
            }
            if( reorder )
            {
                for(i=0; i<=l-1; i++)
                {
                    k = math.randominteger(l);
                    v = z[i];
                    z[i] = z[k];
                    z[k] = v;
                }
            }
            
            //
            // Fill F
            //
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    for(k=0; k<=l-1; k++)
                    {
                        for(di=0; di<=d-1; di++)
                        {
                            f[d*(n*(m*k+j)+i)+di] = 2*math.randomreal()-1;
                        }
                    }
                }
            }
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testspdgevd(bool silent)
        {
            return testspdgevd(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testinverseupdate(bool silent)
        {
            return testinverseupdate(silent);
        }


        /*************************************************************************
        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];
            alglib.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+1<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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testschur(bool silent)
        {
            return testschur(silent);
        }


        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 testminnlcunit
    {
        public const double barriertolerance = 0.05;


        public static bool testminnlc(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            bool bcerr = new bool();
            bool lcerr = new bool();
            bool nlcerr = new bool();
            bool othererr = new bool();
            bool bugs = new bool();

            waserrors = false;
            bcerr = false;
            lcerr = false;
            nlcerr = false;
            othererr = false;
            bugs = false;
            testbugs(ref bugs);
            testbc(ref bcerr);
            testlc(ref lcerr);
            testnlc(ref nlcerr);
            testother(ref othererr);
            
            //
            // end
            //
            waserrors = (((bcerr || lcerr) || nlcerr) || othererr) || bugs;
            if( !silent )
            {
                System.Console.Write("TESTING MINNLC OPTIMIZATION");
                System.Console.WriteLine();
                System.Console.Write("TESTS:");
                System.Console.WriteLine();
                System.Console.Write("* BOUND CONSTRAINED                       ");
                if( bcerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* LINEARLY CONSTRAINED                    ");
                if( lcerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* NONLINEARLY CONSTRAINED                 ");
                if( nlcerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* OTHER PROPERTIES                        ");
                if( othererr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* FIXED BUGS:                             ");
                if( bugs )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testminnlc(bool silent)
        {
            return testminnlc(silent);
        }


        /*************************************************************************
        This function tests bound constrained quadratic programming algorithm.

        On failure sets error flag.
        *************************************************************************/
        private static void testbc(ref bool wereerrors)
        {
            minnlc.minnlcstate state = new minnlc.minnlcstate();
            minnlc.minnlcreport rep = new minnlc.minnlcreport();
            int n = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int aulits = 0;
            double tolx = 0;
            double tolg = 0;
            int scaletype = 0;
            double rho = 0;
            double[,] fulla = new double[0,0];
            double[] b = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] s = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double gnorm = 0;
            double g = 0;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();

            hqrnd.hqrndrandomize(rs);
            
            //
            // Convex test:
            // * N dimensions
            // * random number (0..N) of random boundary constraints
            // * positive-definite quadratic programming problem
            // * initial point is random (maybe infeasible!)
            // * random scale (unit or non-unit)
            //
            aulits = 50;
            rho = 200.0;
            tolx = 0.0005;
            tolg = 0.01;
            for(n=1; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate well-conditioned problem with unit scale
                    //
                    matgen.spdmatrixrndcond(n, 1.0E2, ref fulla);
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = hqrnd.hqrndnormal(rs);
                        bndl[i] = Double.NegativeInfinity;
                        bndu[i] = Double.PositiveInfinity;
                        x0[i] = hqrnd.hqrndnormal(rs);
                        j = hqrnd.hqrnduniformi(rs, 5);
                        if( j==0 )
                        {
                            bndl[i] = 0;
                        }
                        if( j==1 )
                        {
                            bndu[i] = 0;
                        }
                        if( j==2 )
                        {
                            bndl[i] = hqrnd.hqrndnormal(rs);
                            bndu[i] = bndl[i];
                        }
                        if( j==3 )
                        {
                            bndl[i] = -0.1;
                            bndu[i] = 0.1;
                        }
                    }
                    
                    //
                    // Apply scaling to quadratic/linear term, so problem becomes
                    // well-conditioned in the scaled coordinates.
                    //
                    scaletype = hqrnd.hqrnduniformi(rs, 2);
                    s = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        if( scaletype==0 )
                        {
                            s[i] = 1;
                        }
                        else
                        {
                            s[i] = Math.Exp(20*hqrnd.hqrndnormal(rs));
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = x0[i]*s[i];
                        bndl[i] = bndl[i]*s[i];
                        bndu[i] = bndu[i]*s[i];
                        b[i] = b[i]/s[i];
                        for(j=0; j<=n-1; j++)
                        {
                            fulla[i,j] = fulla[i,j]/(s[i]*s[j]);
                        }
                    }
                    
                    //
                    // Solve problem
                    //
                    minnlc.minnlccreate(n, x0, state);
                    minnlc.minnlcsetalgoaul(state, rho, aulits);
                    if( scaletype!=0 )
                    {
                        minnlc.minnlcsetscale(state, s);
                    }
                    minnlc.minnlcsetbc(state, bndl, bndu);
                    minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                    while( minnlc.minnlciteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+b[i]*state.x[i];
                                state.j[0,i] = b[i];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                    state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                                }
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minnlc.minnlcresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( wereerrors )
                    {
                        return;
                    }
                    
                    //
                    // Check feasibility properties
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref wereerrors, math.isfinite(bndl[i]) && (double)(x1[i])<=(double)(bndl[i]-tolx*s[i]));
                        apserv.seterrorflag(ref wereerrors, math.isfinite(bndu[i]) && (double)(x1[i])>=(double)(bndu[i]+tolx*s[i]));
                    }
                    
                    //
                    // Test - calculate scaled constrained gradient at solution,
                    // check its norm.
                    //
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g = b[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g = g+fulla[i,j]*x1[j];
                        }
                        g = s[i]*g;
                        if( (math.isfinite(bndl[i]) && (double)(Math.Abs(x1[i]-bndl[i]))<(double)(tolx*s[i])) && (double)(g)>(double)(0) )
                        {
                            g = 0;
                        }
                        if( (math.isfinite(bndu[i]) && (double)(Math.Abs(x1[i]-bndu[i]))<(double)(tolx*s[i])) && (double)(g)<(double)(0) )
                        {
                            g = 0;
                        }
                        gnorm = gnorm+math.sqr(g);
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref wereerrors, (double)(gnorm)>(double)(tolg));
                }
            }
            
            //
            // Non-convex test:
            // * N dimensions, N>=2
            // * box constraints, x[i] in [-1,+1]
            // * A is symmetric indefinite with condition number 50.0
            // * random B with normal entries
            // * initial point is random, feasible
            // * scale is always unit
            //
            // We check that constrained problem can be successfully solved.
            // We do not check ability to detect unboundedness of unconstrained
            // problem because there is such functionality in MinNLC.
            //
            aulits = 50;
            rho = 200.0;
            tolx = 0.0005;
            tolg = 0.01;
            for(n=2; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate problem
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            fulla[i,j] = 0.0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        fulla[i,i] = -1-hqrnd.hqrnduniformr(rs);
                    }
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = 0.05*hqrnd.hqrndnormal(rs);
                        bndl[i] = -1;
                        bndu[i] = 1;
                        x0[i] = 2*hqrnd.hqrnduniformr(rs)-1;
                    }
                    
                    //
                    // Solve problem:
                    // * without constraints we expect failure
                    // * with constraints algorithm must succeed
                    //
                    minnlc.minnlccreate(n, x0, state);
                    minnlc.minnlcsetalgoaul(state, rho, aulits);
                    minnlc.minnlcsetbc(state, bndl, bndu);
                    minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                    while( minnlc.minnlciteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+b[i]*state.x[i];
                                state.j[0,i] = b[i];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                    state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                                }
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minnlc.minnlcresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( wereerrors )
                    {
                        return;
                    }
                    
                    //
                    // Check feasibility properties
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref wereerrors, math.isfinite(bndl[i]) && (double)(x1[i])<=(double)(bndl[i]-tolx));
                        apserv.seterrorflag(ref wereerrors, math.isfinite(bndu[i]) && (double)(x1[i])>=(double)(bndu[i]+tolx));
                    }
                    
                    //
                    // Test - calculate scaled constrained gradient at solution,
                    // check its norm.
                    //
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g = b[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g = g+fulla[i,j]*x1[j];
                        }
                        g = g;
                        if( (math.isfinite(bndl[i]) && (double)(Math.Abs(x1[i]-bndl[i]))<(double)(tolx)) && (double)(g)>(double)(0) )
                        {
                            g = 0;
                        }
                        if( (math.isfinite(bndu[i]) && (double)(Math.Abs(x1[i]-bndu[i]))<(double)(tolx)) && (double)(g)<(double)(0) )
                        {
                            g = 0;
                        }
                        gnorm = gnorm+math.sqr(g);
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref wereerrors, (double)(gnorm)>(double)(tolg));
                }
            }
        }


        /*************************************************************************
        This function tests linearly constrained quadratic programming algorithm.

        Sets error flag on failure.
        *************************************************************************/
        private static void testlc(ref bool wereerrors)
        {
            int n = 0;
            int k = 0;
            int i = 0;
            int j = 0;
            int pass = 0;
            double[,] q = new double[0,0];
            double[,] fulla = new double[0,0];
            double v = 0;
            double vv = 0;
            double[] tmp = new double[0];
            double[] bl = new double[0];
            double[] bu = new double[0];
            double[] b = new double[0];
            double[] xs0 = new double[0];
            double[] xstart = new double[0];
            double[] x = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[] xm = new double[0];
            double[] s = new double[0];
            double[] g = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[,] a = new double[0,0];
            double[,] c = new double[0,0];
            double[,] ce = new double[0,0];
            int[] ct = new int[0];
            bool[] nonnegative = new bool[0];
            double tolx = 0;
            double tolg = 0;
            double tolf = 0;
            int aulits = 0;
            double rho = 0;
            minnlc.minnlcstate state = new minnlc.minnlcstate();
            minnlc.minnlcreport rep = new minnlc.minnlcreport();
            int scaletype = 0;
            double f0 = 0;
            double f1 = 0;
            double tolconstr = 0;
            int bscale = 0;
            int akind = 0;
            int ccnt = 0;
            int shiftkind = 0;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            snnls.snnlssolver nnls = new snnls.snnlssolver();
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            
            //
            // First test:
            // * K<N equality constraints Q*x = Q*x0, where Q is random
            //   orthogonal K*N matrix, x0 is some random vector
            // * quadratic programming problem with identity quadratic term A and
            //   linear term equal to xm*A, where xm is some random vector such
            //   that Q*xm=0. It is always possible to find such xm, because K<N
            //   Thus, optimization problem has form 0.5*x'*I*x-xm'*x.
            // * exact solution must be equal to x0
            //
            // NOTE: this test is important because it is the only linearly constrained one
            //       which uses non-unit scaling!
            //
            rho = 200.0;
            tolx = 0.0005;
            aulits = 50;
            for(n=2; n<=6; n++)
            {
                for(k=1; k<=n-1; k++)
                {
                    
                    //
                    // Generate problem: A, b, CMatrix, x0, XStart
                    //
                    matgen.rmatrixrndorthogonal(n, ref q);
                    b = new double[n];
                    x0 = new double[n];
                    xm = new double[n];
                    xstart = new double[n];
                    c = new double[k, n+1];
                    ct = new int[k];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 2*math.randomreal()-1;
                        xm[i] = x0[i];
                        xstart[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            c[i,i_] = q[i,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += q[i,i_]*x0[i_];
                        }
                        c[i,n] = v;
                        ct[i] = 0;
                        v = 2*math.randomreal()-1;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            xm[i_] = xm[i_] + v*q[i,i_];
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = -xm[i];
                    }
                    
                    //
                    // Apply scaling to linear term and known solution,
                    // so problem becomes well-conditioned in the scaled coordinates.
                    //
                    scaletype = hqrnd.hqrnduniformi(rs, 2);
                    s = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        if( scaletype==0 )
                        {
                            s[i] = 1;
                        }
                        else
                        {
                            s[i] = Math.Exp(20*hqrnd.hqrndnormal(rs));
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = x0[i]*s[i];
                        xstart[i] = xstart[i]*s[i];
                        b[i] = b[i]/s[i];
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            c[i,j] = c[i,j]/s[j];
                        }
                    }
                    
                    //
                    // Create optimizer, solve
                    //
                    minnlc.minnlccreate(n, xstart, state);
                    minnlc.minnlcsetalgoaul(state, rho, aulits);
                    minnlc.minnlcsetscale(state, s);
                    minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                    minnlc.minnlcsetlc(state, c, ct, k);
                    while( minnlc.minnlciteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+b[i]*state.x[i]+0.5*math.sqr(state.x[i])/math.sqr(s[i]);
                                state.j[0,i] = b[i]+state.x[i]/math.sqr(s[i]);
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minnlc.minnlcresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( wereerrors )
                    {
                        return;
                    }
                    
                    //
                    // Compare with analytic solution
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[i]-x0[i]))>(double)(tolx*s[i]));
                    }
                }
            }
            
            //
            // Inequality constrained problem:
            // * N*N diagonal A
            // * one inequality constraint q'*x>=0, where q is random unit vector
            // * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
            //   where x1 is some random vector
            // * either:
            //   a) x1 is feasible => we must stop at x1
            //   b) x1 is infeasible => we must stop at the boundary q'*x=0 and
            //      projection of gradient onto q*x=0 must be zero
            //
            // NOTE: we make several passes because some specific kind of errors is rarely
            //       caught by this test, so we need several repetitions.
            //
            rho = 200.0;
            tolx = 0.0005;
            tolg = 0.01;
            aulits = 50;
            for(n=2; n<=6; n++)
            {
                for(pass=0; pass<=4; pass++)
                {
                    
                    //
                    // Generate problem: A, b, CMatrix, x0, XStart
                    //
                    matgen.spdmatrixrndcond(n, 1.0E2, ref fulla);
                    b = new double[n];
                    xm = new double[n];
                    xstart = new double[n];
                    c = new double[1, n+1];
                    ct = new int[1];
                    for(i=0; i<=n-1; i++)
                    {
                        xm[i] = 2*math.randomreal()-1;
                        xstart[i] = 2*math.randomreal()-1;
                    }
                    do
                    {
                        v = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            c[0,i] = 2*math.randomreal()-1;
                            v = v+math.sqr(c[0,i]);
                        }
                        v = Math.Sqrt(v);
                    }
                    while( (double)(v)==(double)(0) );
                    for(i=0; i<=n-1; i++)
                    {
                        c[0,i] = c[0,i]/v;
                    }
                    c[0,n] = 0;
                    ct[0] = 1;
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += fulla[i,i_]*xm[i_];
                        }
                        b[i] = -v;
                    }
                    
                    //
                    // Apply scaling to linear term and known solution,
                    // so problem becomes well-conditioned in the scaled coordinates.
                    //
                    scaletype = hqrnd.hqrnduniformi(rs, 2);
                    s = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        if( scaletype==0 )
                        {
                            s[i] = 1;
                        }
                        else
                        {
                            s[i] = Math.Exp(20*hqrnd.hqrndnormal(rs));
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        xm[i] = xm[i]*s[i];
                        xstart[i] = xstart[i]*s[i];
                        b[i] = b[i]/s[i];
                        for(j=0; j<=n-1; j++)
                        {
                            fulla[i,j] = fulla[i,j]/(s[i]*s[j]);
                        }
                    }
                    for(j=0; j<=n-1; j++)
                    {
                        c[0,j] = c[0,j]/s[j];
                    }
                    
                    //
                    // Create optimizer, solve
                    //
                    minnlc.minnlccreate(n, xstart, state);
                    minnlc.minnlcsetalgoaul(state, rho, aulits);
                    minnlc.minnlcsetlc(state, c, ct, 1);
                    minnlc.minnlcsetscale(state, s);
                    while( minnlc.minnlciteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+b[i]*state.x[i];
                                state.j[0,i] = b[i];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                    state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                                }
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minnlc.minnlcresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( wereerrors )
                    {
                        return;
                    }
                    
                    //
                    // Test solution
                    //
                    g = new double[n];
                    for(i_=0; i_<=n-1;i_++)
                    {
                        g[i_] = b[i_];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += fulla[i,i_]*x1[i_];
                        }
                        g[i] = g[i]+v;
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += x1[i_]*c[0,i_];
                    }
                    apserv.seterrorflag(ref wereerrors, (double)(v)<(double)(-tolx));
                    if( (double)(v)<(double)(tolx) )
                    {
                        
                        //
                        // Point at the boundary, project gradient into
                        // equality-constrained subspace.
                        //
                        v = 0.0;
                        vv = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            v = v+g[i]*c[0,i];
                            vv = vv+c[0,i]*c[0,i];
                        }
                        v = v/vv;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            g[i_] = g[i_] - v*c[0,i_];
                        }
                    }
                    v = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+math.sqr(g[i]*s[i]);
                    }
                    apserv.seterrorflag(ref wereerrors, (double)(Math.Sqrt(v))>(double)(tolg));
                }
            }
            
            //
            // Equality-constrained test:
            // * N*N SPD A
            // * K<N equality constraints Q*x = Q*x0, where Q is random
            //   orthogonal K*N matrix, x0 is some random vector
            // * optimization problem has form 0.5*x'*A*x-(xm*A)*x,
            //   where xm is some random vector
            // * we check feasibility properties of the solution
            // * we do not know analytic form of the exact solution,
            //   but we know that projection of gradient into equality constrained
            //   subspace must be zero at the solution
            //
            rho = 200.0;
            tolx = 0.0005;
            tolg = 0.01;
            aulits = 50;
            for(n=2; n<=6; n++)
            {
                for(k=1; k<=n-1; k++)
                {
                    
                    //
                    // Generate problem: A, b, CMatrix, x0, XStart
                    //
                    matgen.rmatrixrndorthogonal(n, ref q);
                    matgen.spdmatrixrndcond(n, 1.0E2, ref fulla);
                    b = new double[n];
                    x0 = new double[n];
                    xm = new double[n];
                    xstart = new double[n];
                    c = new double[k, n+1];
                    ct = new int[k];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 2*math.randomreal()-1;
                        xm[i] = 2*math.randomreal()-1;
                        xstart[i] = 2*math.randomreal()-1;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        for(i_=0; i_<=n-1;i_++)
                        {
                            c[i,i_] = q[i,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += q[i,i_]*x0[i_];
                        }
                        c[i,n] = v;
                        ct[i] = 0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += fulla[i,i_]*xm[i_];
                        }
                        b[i] = -v;
                    }
                    
                    //
                    // Create optimizer, solve
                    //
                    minnlc.minnlccreate(n, xstart, state);
                    minnlc.minnlcsetalgoaul(state, rho, aulits);
                    minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                    minnlc.minnlcsetlc(state, c, ct, k);
                    while( minnlc.minnlciteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+b[i]*state.x[i];
                                state.j[0,i] = b[i];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                    state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                                }
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minnlc.minnlcresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( wereerrors )
                    {
                        return;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += x1[i_]*c[i,i_];
                        }
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(v-c[i,n]))>(double)(tolx));
                    }
                    g = new double[n];
                    for(i_=0; i_<=n-1;i_++)
                    {
                        g[i_] = b[i_];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += fulla[i,i_]*x1[i_];
                        }
                        g[i] = g[i]+v;
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += g[i_]*c[i,i_];
                        }
                        for(i_=0; i_<=n-1;i_++)
                        {
                            g[i_] = g[i_] - v*c[i,i_];
                        }
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += g[i_]*g[i_];
                    }
                    apserv.seterrorflag(ref wereerrors, (double)(Math.Sqrt(v))>(double)(tolg));
                }
            }
            
            //
            // Boundary constraints vs linear ones:
            // * N*N SPD A
            // * optimization problem has form 0.5*x'*A*x-(xm*A)*x,
            //   where xm is some random vector from [-1,+1]
            // * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
            //   with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
            // * initial point xstart is from [-1,+2]
            // * we solve two related QP problems:
            //   a) one with constraints posed as boundary ones
            //   b) another one with same constraints posed as general linear ones
            // both problems must have same solution.
            // Here we test that boundary constrained and linear inequality constrained
            // solvers give same results.
            //
            rho = 200.0;
            tolx = 0.0005;
            aulits = 50;
            for(n=1; n<=6; n++)
            {
                
                //
                // Generate problem: A, b, x0, XStart, C, CT
                //
                matgen.spdmatrixrndcond(n, 1.0E2, ref fulla);
                b = new double[n];
                xm = new double[n];
                x0 = new double[n];
                c = new double[2*n, n+1];
                ct = new int[2*n];
                bndl = new double[n];
                bndu = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    xm[i] = 2*math.randomreal()-1;
                    x0[i] = 3*math.randomreal()-1;
                    bndl[i] = -(0.1+0.9*math.randomreal());
                    bndu[i] = 0.1+0.9*math.randomreal();
                    for(j=0; j<=n-1; j++)
                    {
                        c[2*i+0,j] = 0;
                        c[2*i+1,j] = 0;
                    }
                    c[2*i+0,i] = 1;
                    c[2*i+0,n] = bndl[i];
                    ct[2*i+0] = 1;
                    c[2*i+1,i] = 1;
                    c[2*i+1,n] = bndu[i];
                    ct[2*i+1] = -1;
                }
                for(i=0; i<=n-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += fulla[i,i_]*xm[i_];
                    }
                    b[i] = -v;
                }
                
                //
                // Solve linear inequality constrained problem
                //
                minnlc.minnlccreate(n, x0, state);
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetlc(state, c, ct, 2*n);
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+b[i]*state.x[i];
                            state.j[0,i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                
                //
                // Solve boundary constrained problem
                //
                minnlc.minnlccreate(n, x0, state);
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetbc(state, bndl, bndu);
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+b[i]*state.x[i];
                            state.j[0,i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x2, rep);
                apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                
                //
                // Compare solutions
                //
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[i]-x2[i]))>(double)(tolx));
                }
            }
            
            //
            // Boundary and linear equality constrained QP problem with excessive
            // equality constraints:
            // * N*N SPD A with moderate condtion number (up to 100)
            // * boundary constraints 0<=x[i]<=1
            // * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix,
            //   x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
            // * optimization problem has form 0.5*x'*A*x-b*x,
            //   where b is some random vector
            // * because constraints are excessive, the main problem is to find
            //   feasible point; the only existing feasible point is solution,
            //   so we have to check only feasibility
            //
            rho = 200.0;
            tolx = 0.0005;
            aulits = 50;
            for(n=1; n<=6; n++)
            {
                
                //
                // Generate problem: A, b, BndL, BndU, CMatrix, x0, xm, XStart
                //
                k = 2*n;
                matgen.spdmatrixrndcond(n, 1.0E2, ref fulla);
                b = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                x0 = new double[n];
                xm = new double[n];
                xstart = new double[n];
                c = new double[k, n+1];
                ct = new int[k];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 0.1+0.8*math.randomreal();
                    xm[i] = 2*math.randomreal()-1;
                    bndl[i] = 0.0;
                    bndu[i] = 1.0;
                    xstart[i] = math.randominteger(2);
                }
                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_]*x0[i_];
                    }
                    c[i,n] = v;
                    ct[i] = 0;
                }
                for(i=0; i<=n-1; i++)
                {
                    b[i] = 2*math.randomreal()-1;
                }
                
                //
                // Create optimizer, solve
                //
                minnlc.minnlccreate(n, xstart, state);
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                minnlc.minnlcsetlc(state, c, ct, k);
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+b[i]*state.x[i];
                            state.j[0,i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                for(i=0; i<=k-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += x1[i_]*c[i,i_];
                    }
                    apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(v-c[i,n]))>(double)(tolx));
                }
            }
            
            //
            // Boundary and linear equality/inequality constrained QP problem with
            // excessive constraints:
            // * N*N SPD A with moderate condtion number (up to 100)
            // * boundary constraints 0<=x[i]<=1
            // * K=2*N equality/inequality constraints Q*x = Q*xm, where Q is random matrix,
            //   xm is some random vector from the feasible hypercube (0.1<=xm[i]<=0.9)
            // * optimization problem has form 0.5*x'*A*x-b*x,
            //   where b is some random vector
            // * because constraints are excessive, the main problem is to find
            //   feasible point; usually, the only existing feasible point is solution,
            //   so we have to check only feasibility
            //
            // NOTE: this problem is difficult one (estimates of Lagrange multipliers converge
            //       slowly), so we use relaxed tolerances - 0.01 instead of 0.0005
            //
            rho = 200.0;
            tolx = 0.01;
            aulits = 50;
            for(n=1; n<=6; n++)
            {
                
                //
                // Generate problem: A, b, BndL, BndU, CMatrix, xm, x1, XStart
                //
                k = 2*n;
                matgen.spdmatrixrndcond(n, 1.0E2, ref fulla);
                b = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                xm = new double[n];
                x0 = new double[n];
                c = new double[k, n+1];
                ct = new int[k];
                for(i=0; i<=n-1; i++)
                {
                    xm[i] = 0.1+0.8*math.randomreal();
                    bndl[i] = 0.0;
                    bndu[i] = 1.0;
                    x0[i] = math.randominteger(2);
                    b[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_]*xm[i_];
                    }
                    ct[i] = math.randominteger(3)-1;
                    if( ct[i]==0 )
                    {
                        c[i,n] = v;
                    }
                    if( ct[i]>0 )
                    {
                        c[i,n] = v;
                    }
                    if( ct[i]<0 )
                    {
                        c[i,n] = v;
                    }
                }
                
                //
                // Create optimizer, solve
                //
                minnlc.minnlccreate(n, x0, state);
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetbc(state, bndl, bndu);
                minnlc.minnlcsetlc(state, c, ct, k);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+b[i]*state.x[i];
                            state.j[0,i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                for(i=0; i<=k-1; i++)
                {
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += x1[i_]*c[i,i_];
                    }
                    if( ct[i]==0 )
                    {
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(v-c[i,n]))>(double)(tolx));
                    }
                    if( ct[i]>0 )
                    {
                        apserv.seterrorflag(ref wereerrors, (double)(v)<(double)(c[i,n]-tolx));
                    }
                    if( ct[i]<0 )
                    {
                        apserv.seterrorflag(ref wereerrors, (double)(v)>(double)(c[i,n]+tolx));
                    }
                }
            }
            
            //
            // Boundary and linear equality constrained QP problem,
            // test checks that different starting points yield same final point:
            // * random N from [1..6], random K from [1..N-1]
            // * N*N SPD A with moderate condtion number (important!)
            // * boundary constraints 0<=x[i]<=1
            // * K<N random linear equality constraints C*x = C*x0,
            //   where x0 is some random vector from the inner area of the
            //   feasible hypercube (0.1<=x0[i]<=0.9)
            // * optimization problem has form 0.5*x'*A*x+b*x,
            //   where b is some random vector with -5<=b[i]<=+5
            //
            // We solve this problem two times:
            // * each time from different initial point XStart in [-2,+2]
            // * we compare values of the target function (although final points
            //   may be slightly different, function values should match each other)
            // 
            // Both points should give same results; any significant difference is
            // evidence of some error in the QP implementation.
            //
            rho = 1000.0;
            tolf = 0.01;
            aulits = 50;
            for(pass=1; pass<=50; pass++)
            {
                
                //
                // Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, xm, XStart.
                //
                n = math.randominteger(5)+2;
                k = 1+math.randominteger(n-1);
                matgen.spdmatrixrndcond(n, 1.0E2, ref fulla);
                b = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                x0 = new double[n];
                xstart = new double[n];
                c = new double[k, n+1];
                ct = new int[k];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 0.1+0.8*math.randomreal();
                    b[i] = 2*math.randomreal()-1;
                    bndl[i] = 0.0;
                    bndu[i] = 1.0;
                }
                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_]*x0[i_];
                    }
                    c[i,n] = v;
                    ct[i] = 0;
                }
                
                //
                // Start from first point
                //
                for(i=0; i<=n-1; i++)
                {
                    xstart[i] = 4*math.randomreal()-2;
                }
                minnlc.minnlccreate(n, xstart, state);
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                minnlc.minnlcsetbc(state, bndl, bndu);
                minnlc.minnlcsetlc(state, c, ct, k);
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+b[i]*state.x[i];
                            state.j[0,i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x0, rep);
                apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x0, n));
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                
                //
                // Start from another point
                //
                for(i=0; i<=n-1; i++)
                {
                    xstart[i] = 4*math.randomreal()-2;
                }
                minnlc.minnlcrestartfrom(state, xstart);
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+b[i]*state.x[i];
                            state.j[0,i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                
                //
                // Calculate function value at X0 and X1, compare solutions
                //
                f0 = 0.0;
                f1 = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        f0 = f0+0.5*x0[i]*fulla[i,j]*x0[j];
                        f1 = f1+0.5*x1[i]*fulla[i,j]*x1[j];
                    }
                    f0 = f0+x0[i]*b[i];
                    f1 = f1+x1[i]*b[i];
                }
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(f0-f1))>(double)(tolf));
            }
            
            //
            // Convex/nonconvex optimization problem with excessive
            // (degenerate constraints):
            //
            // * N=2..5
            // * f = 0.5*x'*A*x+b'*x
            // * b has normally distributed entries with scale 10^BScale
            // * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank
            // * box constraints: x[i] in [-1,+1]
            // * 2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
            //   where v_k is one of 2^N vertices of feasible hypercube, v_shift is
            //   a shift parameter:
            //   * with zero v_shift such constraints are degenerate (each vertex has
            //     N box constraints and one "redundant" linear constraint)
            //   * with positive v_shift linear constraint is always inactive
            //   * with small (about machine epsilon) but negative v_shift,
            //     constraint is close to degenerate - but not exactly
            //
            // We check that constrained gradient is close to zero at solution.
            // Box constraint is considered active if distance to boundary is less
            // than TolConstr.
            //
            // NOTE: because AUL algorithm is less exact than its active set counterparts,
            //       VERY loose tolerances are used for this test.
            //
            tolconstr = 1.0E-2;
            for(n=2; n<=8; n++)
            {
                for(akind=0; akind<=3; akind++)
                {
                    for(shiftkind=-5; shiftkind<=1; shiftkind++)
                    {
                        for(bscale=0; bscale>=-2; bscale--)
                        {
                            
                            //
                            // Generate A, B and initial point
                            //
                            a = new double[n, n];
                            b = new double[n];
                            x = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                b[i] = Math.Pow(10, bscale)*hqrnd.hqrndnormal(rs);
                                x[i] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0.0;
                                }
                            }
                            if( akind==1 )
                            {
                                
                                //
                                // Dense well conditioned SPD
                                //
                                matgen.spdmatrixrndcond(n, 50.0, ref a);
                            }
                            if( akind==2 )
                            {
                                
                                //
                                // Dense well conditioned indefinite
                                //
                                matgen.smatrixrndcond(n, 50.0, ref a);
                            }
                            if( akind==3 )
                            {
                                
                                //
                                // Low rank
                                //
                                tmp = new double[n];
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        a[i,j] = 0.0;
                                    }
                                }
                                for(k=1; k<=Math.Min(3, n-1); k++)
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        tmp[i] = hqrnd.hqrndnormal(rs);
                                    }
                                    v = hqrnd.hqrndnormal(rs);
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=0; j<=n-1; j++)
                                        {
                                            a[i,j] = a[i,j]+v*tmp[i]*tmp[j];
                                        }
                                    }
                                }
                            }
                            
                            //
                            // Generate constraints
                            //
                            bl = new double[n];
                            bu = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                bl[i] = -1.0;
                                bu[i] = 1.0;
                            }
                            ccnt = (int)Math.Round(Math.Pow(2, n));
                            c = new double[ccnt, n+1];
                            ct = new int[ccnt];
                            for(i=0; i<=ccnt-1; i++)
                            {
                                ct[i] = -1;
                                k = i;
                                c[i,n] = Math.Sign(shiftkind)*Math.Pow(10, Math.Abs(shiftkind))*math.machineepsilon;
                                for(j=0; j<=n-1; j++)
                                {
                                    c[i,j] = 2*(k%2)-1;
                                    c[i,n] = c[i,n]+c[i,j]*c[i,j];
                                    k = k/2;
                                }
                            }
                            
                            //
                            // Create and optimize
                            //
                            minnlc.minnlccreate(n, x, state);
                            minnlc.minnlcsetbc(state, bl, bu);
                            minnlc.minnlcsetlc(state, c, ct, ccnt);
                            minnlc.minnlcsetcond(state, 1.0E-12, 0.0, 0.0, 0);
                            minnlc.minnlcsetalgoaul(state, 1000.0, 10);
                            while( minnlc.minnlciteration(state) )
                            {
                                alglib.ap.assert(state.needfij);
                                state.fi[0] = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    state.fi[0] = state.fi[0]+state.x[i]*b[i];
                                    state.j[0,i] = b[i];
                                }
                                for(i=0; i<=n-1; i++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += a[i,i_]*state.x[i_];
                                    }
                                    state.fi[0] = state.fi[0]+0.5*state.x[i]*v;
                                    state.j[0,i] = state.j[0,i]+v;
                                }
                            }
                            minnlc.minnlcresults(state, ref xs0, rep);
                            apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                            if( wereerrors )
                            {
                                return;
                            }
                            
                            //
                            // Evaluate gradient at solution and test
                            //
                            vv = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*xs0[i_];
                                }
                                v = v+b[i];
                                if( (double)(xs0[i])<=(double)(bl[i]+tolconstr) && (double)(v)>(double)(0) )
                                {
                                    v = 0.0;
                                }
                                if( (double)(xs0[i])>=(double)(bu[i]-tolconstr) && (double)(v)<(double)(0) )
                                {
                                    v = 0.0;
                                }
                                vv = vv+math.sqr(v);
                            }
                            vv = Math.Sqrt(vv);
                            apserv.seterrorflag(ref wereerrors, (double)(vv)>(double)(1.0E-3));
                        }
                    }
                }
            }
            
            //
            // Linear/convex optimization problem with combination of
            // box and linear constraints:
            //
            // * N=2..8
            // * f = 0.5*x'*A*x+b'*x
            // * b has normally distributed entries with scale 10^BScale
            // * several kinds of A are tried: zero, well conditioned SPD
            // * box constraints: x[i] in [-1,+1]
            // * initial point x0 = [0 0 ... 0 0]
            // * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
            //   random mix of equality/inequality constraints is tried.
            //   x0 is guaranteed to be feasible.
            //
            // We check that constrained gradient is close to zero at solution.
            // Inequality constraint is considered active if distance to boundary
            // is less than TolConstr. We use nonnegative least squares solver
            // in order to compute constrained gradient.
            //
            tolconstr = 1.0E-2;
            for(n=2; n<=8; n++)
            {
                for(akind=0; akind<=1; akind++)
                {
                    for(bscale=0; bscale>=-2; bscale--)
                    {
                        
                        //
                        // Generate A, B and initial point
                        //
                        a = new double[n, n];
                        b = new double[n];
                        x = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            b[i] = Math.Pow(10, bscale)*hqrnd.hqrndnormal(rs);
                            x[i] = 0.0;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a[i,j] = 0.0;
                            }
                        }
                        if( akind==1 )
                        {
                            
                            //
                            // Dense well conditioned SPD
                            //
                            matgen.spdmatrixrndcond(n, 50.0, ref a);
                        }
                        
                        //
                        // Generate constraints
                        //
                        bl = new double[n];
                        bu = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            bl[i] = -1.0;
                            bu[i] = 1.0;
                        }
                        ccnt = Math.Min(3, n-1);
                        c = new double[ccnt, n+1];
                        ct = new int[ccnt];
                        for(i=0; i<=ccnt-1; i++)
                        {
                            ct[i] = hqrnd.hqrnduniformi(rs, 3)-1;
                            c[i,n] = 0.0;
                            for(j=0; j<=n-1; j++)
                            {
                                c[i,j] = hqrnd.hqrnduniformr(rs)-0.5;
                            }
                        }
                        
                        //
                        // Create and optimize
                        //
                        minnlc.minnlccreate(n, x, state);
                        minnlc.minnlcsetbc(state, bl, bu);
                        minnlc.minnlcsetlc(state, c, ct, ccnt);
                        minnlc.minnlcsetcond(state, 1.0E-9, 0.0, 0.0, 0);
                        minnlc.minnlcsetalgoaul(state, 1000.0, 10);
                        while( minnlc.minnlciteration(state) )
                        {
                            alglib.ap.assert(state.needfij);
                            state.fi[0] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+state.x[i]*b[i];
                                state.j[0,i] = b[i];
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*state.x[i_];
                                }
                                state.fi[0] = state.fi[0]+0.5*state.x[i]*v;
                                state.j[0,i] = state.j[0,i]+v;
                            }
                        }
                        minnlc.minnlcresults(state, ref xs0, rep);
                        apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                        if( wereerrors )
                        {
                            return;
                        }
                        
                        //
                        // 1. evaluate unconstrained gradient at solution
                        //
                        // 2. calculate constrained gradient (NNLS solver is used
                        //    to evaluate gradient subject to active constraints).
                        //    In order to do this we form CE matrix, matrix of active
                        //    constraints (columns store constraint vectors). Then
                        //    we try to approximate gradient vector by columns of CE,
                        //    subject to non-negativity restriction placed on variables
                        //    corresponding to inequality constraints.
                        //
                        //    Residual from such regression is a constrained gradient vector.
                        //
                        g = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += a[i,i_]*xs0[i_];
                            }
                            g[i] = v+b[i];
                        }
                        ce = new double[n, n+ccnt];
                        nonnegative = new bool[n+ccnt];
                        k = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            apserv.seterrorflag(ref wereerrors, (double)(xs0[i])<(double)(bl[i]-tolconstr));
                            apserv.seterrorflag(ref wereerrors, (double)(xs0[i])>(double)(bu[i]+tolconstr));
                            if( (double)(xs0[i])<=(double)(bl[i]+tolconstr) )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = 0.0;
                                }
                                ce[i,k] = 1.0;
                                nonnegative[k] = true;
                                apserv.inc(ref k);
                                continue;
                            }
                            if( (double)(xs0[i])>=(double)(bu[i]-tolconstr) )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = 0.0;
                                }
                                ce[i,k] = -1.0;
                                nonnegative[k] = true;
                                apserv.inc(ref k);
                                continue;
                            }
                        }
                        for(i=0; i<=ccnt-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += c[i,i_]*xs0[i_];
                            }
                            v = v-c[i,n];
                            apserv.seterrorflag(ref wereerrors, ct[i]==0 && (double)(Math.Abs(v))>(double)(tolconstr));
                            apserv.seterrorflag(ref wereerrors, ct[i]>0 && (double)(v)<(double)(-tolconstr));
                            apserv.seterrorflag(ref wereerrors, ct[i]<0 && (double)(v)>(double)(tolconstr));
                            if( ct[i]==0 )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = c[i,j];
                                }
                                nonnegative[k] = false;
                                apserv.inc(ref k);
                                continue;
                            }
                            if( (ct[i]>0 && (double)(v)<=(double)(tolconstr)) || (ct[i]<0 && (double)(v)>=(double)(-tolconstr)) )
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    ce[j,k] = Math.Sign(ct[i])*c[i,j];
                                }
                                nonnegative[k] = true;
                                apserv.inc(ref k);
                                continue;
                            }
                        }
                        snnls.snnlsinit(0, 0, 0, nnls);
                        snnls.snnlssetproblem(nnls, ce, g, 0, k, n);
                        for(i=0; i<=k-1; i++)
                        {
                            if( !nonnegative[i] )
                            {
                                snnls.snnlsdropnnc(nnls, i);
                            }
                        }
                        snnls.snnlssolve(nnls, ref tmp);
                        for(i=0; i<=k-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                g[j] = g[j]-tmp[i]*ce[j,i];
                            }
                        }
                        vv = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            vv += g[i_]*g[i_];
                        }
                        vv = Math.Sqrt(vv);
                        apserv.seterrorflag(ref wereerrors, (double)(vv)>(double)(1.0E-3));
                    }
                }
            }
        }


        /*************************************************************************
        This function tests nonlinearly constrained quadratic programming algorithm.

        Sets error flag on failure.
        *************************************************************************/
        private static void testnlc(ref bool wereerrors)
        {
            int n = 0;
            int n2 = 0;
            double tolx = 0;
            double tolg = 0;
            int aulits = 0;
            double rho = 0;
            minnlc.minnlcstate state = new minnlc.minnlcstate();
            minnlc.minnlcreport rep = new minnlc.minnlcreport();
            int scaletype = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] b = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] s = new double[0];
            double[] g = new double[0];
            int[] ckind = new int[0];
            double[,] fulla = new double[0,0];
            double[,] c = new double[0,0];
            int[] ct = new int[0];
            int cntbc = 0;
            int cntlc = 0;
            int cntnlec = 0;
            int cntnlic = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int pass = 0;
            int klc = 0;
            int knlc = 0;
            int knlec = 0;
            int knlic = 0;
            double v = 0;
            double vv = 0;
            double vx = 0;
            double vy = 0;
            double gnorm = 0;
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();

            hqrnd.hqrndrandomize(rs);
            
            //
            // Basic test:
            // * 2-dimensional problem
            // * target function F(x0,x1) = (x0-1)^2 + (x1-1)^2
            // * one nonlinear constraint Z(x0,x1) = x0^2+x1^2-1,
            //   which is tried as equality and inequality one
            //
            rho = 200.0;
            tolx = 0.0005;
            aulits = 50;
            n = 2;
            x0 = new double[n];
            for(i=0; i<=n-1; i++)
            {
                x0[i] = 2*math.randomreal()-1;
            }
            minnlc.minnlccreate(n, x0, state);
            minnlc.minnlcsetalgoaul(state, rho, aulits);
            minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
            minnlc.minnlcsetnlc(state, 0, 1);
            while( minnlc.minnlciteration(state) )
            {
                if( state.needfij )
                {
                    state.fi[0] = math.sqr(state.x[0]-1)+math.sqr(state.x[1]-1);
                    state.j[0,0] = 2*(state.x[0]-1);
                    state.j[0,1] = 2*(state.x[1]-1);
                    state.fi[1] = math.sqr(state.x[0])+math.sqr(state.x[1])-1;
                    state.j[1,0] = 2*state.x[0];
                    state.j[1,1] = 2*state.x[1];
                    continue;
                }
                alglib.ap.assert(false);
            }
            minnlc.minnlcresults(state, ref x1, rep);
            apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
            apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
            if( wereerrors )
            {
                return;
            }
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[0]-Math.Sqrt(2)/2))>(double)(tolx));
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[1]-Math.Sqrt(2)/2))>(double)(tolx));
            minnlc.minnlcsetnlc(state, 1, 0);
            minnlc.minnlcrestartfrom(state, x0);
            while( minnlc.minnlciteration(state) )
            {
                if( state.needfij )
                {
                    state.fi[0] = math.sqr(state.x[0]-1)+math.sqr(state.x[1]-1);
                    state.j[0,0] = 2*(state.x[0]-1);
                    state.j[0,1] = 2*(state.x[1]-1);
                    state.fi[1] = math.sqr(state.x[0])+math.sqr(state.x[1])-1;
                    state.j[1,0] = 2*state.x[0];
                    state.j[1,1] = 2*state.x[1];
                    continue;
                }
                alglib.ap.assert(false);
            }
            minnlc.minnlcresults(state, ref x1, rep);
            apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
            apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
            if( wereerrors )
            {
                return;
            }
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[0]-Math.Sqrt(2)/2))>(double)(tolx));
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[1]-Math.Sqrt(2)/2))>(double)(tolx));
            
            //
            // This test checks correctness of scaling being applied to nonlinear
            // constraints. We solve bound constrained scaled problem and check
            // that solution is correct.
            //
            aulits = 50;
            rho = 200.0;
            tolx = 0.0005;
            tolg = 0.01;
            for(n=1; n<=10; n++)
            {
                for(pass=1; pass<=10; pass++)
                {
                    
                    //
                    // Generate well-conditioned problem with unit scale
                    //
                    matgen.spdmatrixrndcond(n, 1.0E2, ref fulla);
                    b = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    x0 = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        b[i] = hqrnd.hqrndnormal(rs);
                        x0[i] = hqrnd.hqrndnormal(rs);
                    }
                    cntnlec = hqrnd.hqrnduniformi(rs, n);
                    cntnlic = n-cntnlec;
                    for(i=0; i<=cntnlec-1; i++)
                    {
                        bndl[i] = hqrnd.hqrndnormal(rs);
                        bndu[i] = bndl[i];
                    }
                    for(i=cntnlec; i<=n-1; i++)
                    {
                        bndl[i] = hqrnd.hqrndnormal(rs);
                        bndu[i] = bndl[i]+0.5;
                    }
                    
                    //
                    // Apply scaling to quadratic/linear term, so problem becomes
                    // well-conditioned in the scaled coordinates.
                    //
                    scaletype = hqrnd.hqrnduniformi(rs, 2);
                    s = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        if( scaletype==0 )
                        {
                            s[i] = 1;
                        }
                        else
                        {
                            s[i] = Math.Exp(20*hqrnd.hqrndnormal(rs));
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = x0[i]*s[i];
                        bndl[i] = bndl[i]*s[i];
                        bndu[i] = bndu[i]*s[i];
                        b[i] = b[i]/s[i];
                        for(j=0; j<=n-1; j++)
                        {
                            fulla[i,j] = fulla[i,j]/(s[i]*s[j]);
                        }
                    }
                    
                    //
                    // Solve problem with boundary constraints posed as nonlinear ones
                    //
                    minnlc.minnlccreate(n, x0, state);
                    minnlc.minnlcsetalgoaul(state, rho, aulits);
                    minnlc.minnlcsetscale(state, s);
                    minnlc.minnlcsetnlc(state, cntnlec, 2*cntnlic);
                    minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                    while( minnlc.minnlciteration(state) )
                    {
                        if( state.needfij )
                        {
                            for(i=0; i<=cntnlec+2*cntnlic; i++)
                            {
                                state.fi[i] = 0;
                                for(j=0; j<=n-1; j++)
                                {
                                    state.j[i,j] = 0;
                                }
                            }
                            
                            //
                            // Function itself
                            //
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+b[i]*state.x[i];
                                state.j[0,i] = b[i];
                                for(j=0; j<=n-1; j++)
                                {
                                    state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                    state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                                }
                            }
                            
                            //
                            // Equality constraints
                            //
                            for(i=0; i<=cntnlec-1; i++)
                            {
                                state.fi[1+i] = (state.x[i]-bndl[i])/s[i];
                                state.j[1+i,i] = 1/s[i];
                            }
                            
                            //
                            // Inequality constraints
                            //
                            for(i=0; i<=cntnlic-1; i++)
                            {
                                k = cntnlec+i;
                                state.fi[1+cntnlec+2*i+0] = (bndl[k]-state.x[k])/s[k];
                                state.j[1+cntnlec+2*i+0,k] = -(1/s[k]);
                                state.fi[1+cntnlec+2*i+1] = (state.x[k]-bndu[k])/s[k];
                                state.j[1+cntnlec+2*i+1,k] = 1/s[k];
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minnlc.minnlcresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( wereerrors )
                    {
                        return;
                    }
                    
                    //
                    // Check feasibility properties
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref wereerrors, math.isfinite(bndl[i]) && (double)(x1[i])<=(double)(bndl[i]-tolx*s[i]));
                        apserv.seterrorflag(ref wereerrors, math.isfinite(bndu[i]) && (double)(x1[i])>=(double)(bndu[i]+tolx*s[i]));
                    }
                    
                    //
                    // Test - calculate scaled constrained gradient at solution,
                    // check its norm.
                    //
                    g = new double[n];
                    gnorm = 0.0;
                    for(i=0; i<=n-1; i++)
                    {
                        g[i] = b[i];
                        for(j=0; j<=n-1; j++)
                        {
                            g[i] = g[i]+fulla[i,j]*x1[j];
                        }
                        g[i] = s[i]*g[i];
                        if( (math.isfinite(bndl[i]) && (double)(Math.Abs(x1[i]-bndl[i]))<(double)(tolx*s[i])) && (double)(g[i])>(double)(0) )
                        {
                            g[i] = 0;
                        }
                        if( (math.isfinite(bndu[i]) && (double)(Math.Abs(x1[i]-bndu[i]))<(double)(tolx*s[i])) && (double)(g[i])<(double)(0) )
                        {
                            g[i] = 0;
                        }
                        gnorm = gnorm+math.sqr(g[i]);
                    }
                    gnorm = Math.Sqrt(gnorm);
                    apserv.seterrorflag(ref wereerrors, (double)(gnorm)>(double)(tolg));
                }
            }
            
            //
            // Complex problem with mix of boundary, linear and nonlinear constraints:
            // * quadratic target function f(x) = 0.5*x'*A*x + b'*x
            // * unit scaling is used
            // * problem size N is even
            // * all variables are divided into pairs: x[0] and x[1], x[2] and x[3], ...
            // * constraints are set for pairs of variables, i.e. each constraint involves
            //   only pair of adjacent variables (x0/x1, x2/x3, x4/x5 and so on), and each
            //   pair of variables has at most one constraint which binds them
            // * for variables u and v following kinds of constraints can be randomly set:
            //   * CKind=0      no constraint
            //   * CKind=1      boundary equality constraint:    u=a, v=b
            //   * CKind=2      boundary inequality constraint:  a0<=u<=b0, a1<=v<=b1
            //   * CKind=3      linear equality constraint:      a*u+b*v  = c
            //   * CKind=4      linear inequality constraint:    a*u+b*v <= c
            //   * CKind=5      nonlinear equality constraint:   u^2+v^2  = 1
            //   * CKind=6      nonlinear inequality constraint: u^2+v^2 <= 1
            // * it is relatively easy to calculated projected gradient for such problem
            //
            aulits = 50;
            rho = 200.0;
            tolx = 0.0005;
            tolg = 0.01;
            n = 20;
            n2 = n/2;
            for(pass=1; pass<=50; pass++)
            {
                
                //
                // Generate well-conditioned problem with unit scale
                //
                matgen.spdmatrixrndcond(n, 1.0E2, ref fulla);
                b = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                c = new double[n, n+1];
                ct = new int[n];
                x0 = new double[n];
                ckind = new int[n2];
                cntbc = 0;
                cntlc = 0;
                cntnlec = 0;
                cntnlic = 0;
                for(i=0; i<=n-1; i++)
                {
                    bndl[i] = Double.NegativeInfinity;
                    bndu[i] = Double.PositiveInfinity;
                    x0[i] = hqrnd.hqrndnormal(rs);
                    b[i] = hqrnd.hqrndnormal(rs);
                }
                for(i=0; i<=n2-1; i++)
                {
                    ckind[i] = hqrnd.hqrnduniformi(rs, 7);
                    if( ckind[i]==0 )
                    {
                        
                        //
                        // Unconstrained
                        //
                        continue;
                    }
                    if( ckind[i]==1 )
                    {
                        
                        //
                        // Bound equality constrained
                        //
                        bndl[2*i+0] = hqrnd.hqrnduniformr(rs)-0.5;
                        bndu[2*i+0] = bndl[2*i+0];
                        bndl[2*i+1] = hqrnd.hqrnduniformr(rs)-0.5;
                        bndu[2*i+1] = bndl[2*i+1];
                        apserv.inc(ref cntbc);
                        continue;
                    }
                    if( ckind[i]==2 )
                    {
                        
                        //
                        // Bound inequality constrained
                        //
                        bndl[2*i+0] = hqrnd.hqrnduniformr(rs)-0.5;
                        bndu[2*i+0] = bndl[2*i+0]+0.5;
                        bndl[2*i+1] = hqrnd.hqrnduniformr(rs)-0.5;
                        bndu[2*i+1] = bndl[2*i+1]+0.5;
                        apserv.inc(ref cntbc);
                        continue;
                    }
                    if( ckind[i]==3 )
                    {
                        
                        //
                        // Linear equality constrained
                        //
                        for(j=0; j<=n; j++)
                        {
                            c[cntlc,j] = 0.0;
                        }
                        vx = hqrnd.hqrnduniformr(rs)-0.5;
                        vy = hqrnd.hqrnduniformr(rs)-0.5;
                        c[cntlc,2*i+0] = vx;
                        c[cntlc,2*i+1] = vy;
                        c[cntlc,n] = hqrnd.hqrnduniformr(rs)-0.5;
                        ct[cntlc] = 0;
                        apserv.inc(ref cntlc);
                        continue;
                    }
                    if( ckind[i]==4 )
                    {
                        
                        //
                        // Linear inequality constrained
                        //
                        for(j=0; j<=n; j++)
                        {
                            c[cntlc,j] = 0.0;
                        }
                        vx = hqrnd.hqrnduniformr(rs)-0.5;
                        vy = hqrnd.hqrnduniformr(rs)-0.5;
                        c[cntlc,2*i+0] = vx;
                        c[cntlc,2*i+1] = vy;
                        c[cntlc,n] = hqrnd.hqrnduniformr(rs)-0.5;
                        ct[cntlc] = -1;
                        apserv.inc(ref cntlc);
                        continue;
                    }
                    if( ckind[i]==5 )
                    {
                        
                        //
                        // Nonlinear equality constrained
                        //
                        apserv.inc(ref cntnlec);
                        continue;
                    }
                    if( ckind[i]==6 )
                    {
                        
                        //
                        // Nonlinear inequality constrained
                        //
                        apserv.inc(ref cntnlic);
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                
                //
                // Solve problem
                //
                minnlc.minnlccreate(n, x0, state);
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetbc(state, bndl, bndu);
                minnlc.minnlcsetlc(state, c, ct, cntlc);
                minnlc.minnlcsetnlc(state, cntnlec, cntnlic);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        
                        //
                        // Evaluate target function
                        //
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+b[i]*state.x[i];
                            state.j[0,i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi[0] = state.fi[0]+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                            }
                        }
                        
                        //
                        // Evaluate constraint functions
                        //
                        knlec = 1;
                        knlic = 1+cntnlec;
                        for(i=0; i<=n2-1; i++)
                        {
                            if( ckind[i]==5 )
                            {
                                state.fi[knlec] = 0;
                                for(j=0; j<=n-1; j++)
                                {
                                    state.j[knlec,j] = 0.0;
                                }
                                state.fi[knlec] = math.sqr(state.x[2*i+0])+math.sqr(state.x[2*i+1])-1;
                                state.j[knlec,2*i+0] = 2*state.x[2*i+0];
                                state.j[knlec,2*i+1] = 2*state.x[2*i+1];
                                apserv.inc(ref knlec);
                                continue;
                            }
                            if( ckind[i]==6 )
                            {
                                state.fi[knlic] = 0;
                                for(j=0; j<=n-1; j++)
                                {
                                    state.j[knlic,j] = 0.0;
                                }
                                state.fi[knlic] = math.sqr(state.x[2*i+0])+math.sqr(state.x[2*i+1])-1;
                                state.j[knlic,2*i+0] = 2*state.x[2*i+0];
                                state.j[knlic,2*i+1] = 2*state.x[2*i+1];
                                apserv.inc(ref knlic);
                                continue;
                            }
                        }
                        alglib.ap.assert(knlec==1+cntnlec);
                        alglib.ap.assert(knlic==1+cntnlec+cntnlic);
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                
                //
                // Check feasibility properties
                //
                klc = 0;
                for(i=0; i<=n2-1; i++)
                {
                    if( ckind[i]==0 )
                    {
                        
                        //
                        // Unconstrained
                        //
                        continue;
                    }
                    if( ckind[i]==1 )
                    {
                        
                        //
                        // Bound equality constrained
                        //
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[2*i+0]-bndl[2*i+0]))>(double)(tolx));
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[2*i+1]-bndl[2*i+1]))>(double)(tolx));
                        continue;
                    }
                    if( ckind[i]==2 )
                    {
                        
                        //
                        // Bound inequality constrained
                        //
                        apserv.seterrorflag(ref wereerrors, (double)(x1[2*i+0])<(double)(bndl[2*i+0]-tolx));
                        apserv.seterrorflag(ref wereerrors, (double)(x1[2*i+0])>(double)(bndu[2*i+0]+tolx));
                        apserv.seterrorflag(ref wereerrors, (double)(x1[2*i+1])<(double)(bndl[2*i+1]-tolx));
                        apserv.seterrorflag(ref wereerrors, (double)(x1[2*i+1])>(double)(bndu[2*i+1]+tolx));
                        continue;
                    }
                    if( ckind[i]==3 )
                    {
                        
                        //
                        // Linear equality constrained
                        //
                        v = x1[2*i+0]*c[klc,2*i+0]+x1[2*i+1]*c[klc,2*i+1]-c[klc,n];
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(v))>(double)(tolx));
                        apserv.inc(ref klc);
                        continue;
                    }
                    if( ckind[i]==4 )
                    {
                        
                        //
                        // Linear inequality constrained
                        //
                        v = x1[2*i+0]*c[klc,2*i+0]+x1[2*i+1]*c[klc,2*i+1]-c[klc,n];
                        apserv.seterrorflag(ref wereerrors, (double)(v)>(double)(tolx));
                        apserv.inc(ref klc);
                        continue;
                    }
                    if( ckind[i]==5 )
                    {
                        
                        //
                        // Nonlinear equality constrained
                        //
                        v = math.sqr(x1[2*i+0])+math.sqr(x1[2*i+1])-1;
                        apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(v))>(double)(tolx));
                        continue;
                    }
                    if( ckind[i]==6 )
                    {
                        
                        //
                        // Nonlinear inequality constrained
                        //
                        v = math.sqr(x1[2*i+0])+math.sqr(x1[2*i+1])-1;
                        apserv.seterrorflag(ref wereerrors, (double)(v)>(double)(tolx));
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                
                //
                // Test - calculate scaled constrained gradient at solution,
                // check its norm.
                //
                g = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    v = b[i];
                    for(j=0; j<=n-1; j++)
                    {
                        v = v+fulla[i,j]*x1[j];
                    }
                    g[i] = v;
                }
                klc = 0;
                knlc = 0;
                for(i=0; i<=n2-1; i++)
                {
                    if( ckind[i]==0 )
                    {
                        
                        //
                        // Unconstrained
                        //
                        continue;
                    }
                    if( ckind[i]==1 )
                    {
                        
                        //
                        // Bound equality constrained, unconditionally set gradient to zero
                        //
                        g[2*i+0] = 0.0;
                        g[2*i+1] = 0.0;
                        continue;
                    }
                    if( ckind[i]==2 )
                    {
                        
                        //
                        // Bound inequality constrained, conditionally set gradient to zero
                        // (when constraint is active)
                        //
                        if( (double)(x1[2*i+0])<(double)(bndl[2*i+0]+tolx) || (double)(x1[2*i+0])>(double)(bndu[2*i+0]-tolx) )
                        {
                            g[2*i+0] = 0.0;
                        }
                        if( (double)(x1[2*i+1])<(double)(bndl[2*i+1]+tolx) || (double)(x1[2*i+1])>(double)(bndu[2*i+1]-tolx) )
                        {
                            g[2*i+1] = 0.0;
                        }
                        continue;
                    }
                    if( ckind[i]==3 )
                    {
                        
                        //
                        // Linear equality constrained, unconditionally project gradient into
                        // equality constrained subspace
                        //
                        v = g[2*i+0]*c[klc,2*i+0]+g[2*i+1]*c[klc,2*i+1];
                        vv = math.sqr(c[klc,2*i+0])+math.sqr(c[klc,2*i+1]);
                        g[2*i+0] = g[2*i+0]-c[klc,2*i+0]*(v/vv);
                        g[2*i+1] = g[2*i+1]-c[klc,2*i+1]*(v/vv);
                        apserv.inc(ref klc);
                        continue;
                    }
                    if( ckind[i]==4 )
                    {
                        
                        //
                        // Linear inequality constrained, conditionally project gradient
                        // (when constraint is active)
                        //
                        v = x1[2*i+0]*c[klc,2*i+0]+x1[2*i+1]*c[klc,2*i+1]-c[klc,n];
                        if( (double)(v)>(double)(-tolx) )
                        {
                            v = g[2*i+0]*c[klc,2*i+0]+g[2*i+1]*c[klc,2*i+1];
                            vv = math.sqr(c[klc,2*i+0])+math.sqr(c[klc,2*i+1]);
                            g[2*i+0] = g[2*i+0]-c[klc,2*i+0]*(v/vv);
                            g[2*i+1] = g[2*i+1]-c[klc,2*i+1]*(v/vv);
                        }
                        apserv.inc(ref klc);
                        continue;
                    }
                    if( ckind[i]==5 )
                    {
                        
                        //
                        // Nonlinear equality constrained, unconditionally project gradient
                        //
                        // NOTE: here we rely on the fact that corresponding components of X
                        //       sum to one.
                        //
                        v = g[2*i+0]*x1[2*i+0]+g[2*i+1]*x1[2*i+1];
                        g[2*i+0] = g[2*i+0]-x1[2*i+0]*v;
                        g[2*i+1] = g[2*i+1]-x1[2*i+1]*v;
                        continue;
                    }
                    if( ckind[i]==6 )
                    {
                        
                        //
                        // Nonlinear inequality constrained, conditionally project gradient
                        // (when constraint is active)
                        //
                        // NOTE: here we rely on the fact that corresponding components of X
                        //       sum to one.
                        //
                        v = math.sqr(x1[2*i+0])+math.sqr(x1[2*i+1])-1;
                        if( (double)(v)>(double)(-tolx) )
                        {
                            v = g[2*i+0]*x1[2*i+0]+g[2*i+1]*x1[2*i+1];
                            g[2*i+0] = g[2*i+0]-x1[2*i+0]*v;
                            g[2*i+1] = g[2*i+1]-x1[2*i+1]*v;
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                gnorm = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    gnorm = gnorm+math.sqr(g[i]);
                }
                gnorm = Math.Sqrt(gnorm);
                apserv.seterrorflag(ref wereerrors, (double)(gnorm)>(double)(tolg));
            }
        }


        /*************************************************************************
        This function performs additional tests

        On failure sets error flag.
        *************************************************************************/
        private static void testother(ref bool wereerrors)
        {
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            double v = 0;
            double h = 0;
            double fl = 0;
            double fr = 0;
            double fl2 = 0;
            double fr2 = 0;
            double dfl = 0;
            double dfr = 0;
            double dfl2 = 0;
            double dfr2 = 0;
            double d2fl = 0;
            double d2fr = 0;
            double d2fl2 = 0;
            double d2fr2 = 0;
            double f0 = 0;
            double df = 0;
            double d2f = 0;
            double ndf = 0;
            double nd2f = 0;
            double dtol = 0;
            double diffstep = 0;
            minnlc.minnlcstate state = new minnlc.minnlcstate();
            minnlc.minnlcreport rep = new minnlc.minnlcreport();
            double rho = 0;
            int aulits = 0;
            double tolx = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            double[] b = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[] x3 = new double[0];
            double[] xlast = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double condv = 0;
            double[,] a = new double[0,0];
            double[,] c = new double[0,0];
            double[,] fulla = new double[0,0];
            int[] ct = new int[0];
            int nlbfgs = 0;
            int nexact = 0;
            int nnone = 0;
            int prectype = 0;
            int ctype = 0;
            int trialidx = 0;
            int blocksize = 0;
            int blockcnt = 0;
            int maxits = 0;
            int spoiliteration = 0;
            int stopiteration = 0;
            int spoilvar = 0;
            double spoilval = 0;
            int pass = 0;
            int i_ = 0;

            hqrnd.hqrndrandomize(rs);
            
            //
            // Test equality penalty function (correctly calculated and smooth)
            //
            h = 1.0E-4;
            v = -0.98;
            dtol = 1.0E-3;
            while( (double)(v)<=(double)(0.98) )
            {
                
                //
                // Test numerical derivative; this test also checks continuity of the
                // function
                //
                minnlc.minnlcequalitypenaltyfunction(v-2*h, ref fl2, ref dfl2, ref d2fl2);
                minnlc.minnlcequalitypenaltyfunction(v-h, ref fl, ref dfl, ref d2fl);
                minnlc.minnlcequalitypenaltyfunction(v+h, ref fr, ref dfr, ref d2fr);
                minnlc.minnlcequalitypenaltyfunction(v+2*h, ref fr2, ref dfr2, ref d2fr2);
                minnlc.minnlcequalitypenaltyfunction(v, ref f0, ref df, ref d2f);
                ndf = (-fr2+8*fr-8*fl+fl2)/(12*h);
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(ndf-df))>(double)(dtol*Math.Max(Math.Abs(ndf), 1)));
                nd2f = (-dfr2+8*dfr-8*dfl+dfl2)/(12*h);
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(nd2f-d2f))>(double)(dtol*Math.Max(Math.Abs(nd2f), 1)));
                
                //
                // Next point
                //
                v = v+h;
            }
            minnlc.minnlcequalitypenaltyfunction(0.0, ref f0, ref df, ref d2f);
            apserv.seterrorflag(ref wereerrors, (double)(f0)!=(double)(0));
            apserv.seterrorflag(ref wereerrors, (double)(df)!=(double)(0));
            
            //
            // Test inequality penalty function (correctly calculated and smooth)
            //
            h = 1.0E-4;
            v = 0.02;
            dtol = 1.0E-3;
            while( (double)(v)<=(double)(2.00) )
            {
                
                //
                // Test numerical derivative; this test also checks continuity of the
                // function
                //
                minnlc.minnlcinequalityshiftfunction(v-2*h, ref fl2, ref dfl2, ref d2fl2);
                minnlc.minnlcinequalityshiftfunction(v-h, ref fl, ref dfl, ref d2fl);
                minnlc.minnlcinequalityshiftfunction(v+h, ref fr, ref dfr, ref d2fr);
                minnlc.minnlcinequalityshiftfunction(v+2*h, ref fr2, ref dfr2, ref d2fr2);
                minnlc.minnlcinequalityshiftfunction(v, ref f0, ref df, ref d2f);
                ndf = (-fr2+8*fr-8*fl+fl2)/(12*h);
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(ndf-df))>(double)(dtol*Math.Max(Math.Abs(ndf), 1)));
                nd2f = (-dfr2+8*dfr-8*dfl+dfl2)/(12*h);
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(nd2f-d2f))>(double)(dtol*Math.Max(Math.Abs(nd2f), 1)));
                
                //
                // Next point
                //
                v = v+h;
            }
            minnlc.minnlcinequalityshiftfunction(1.0, ref f0, ref df, ref d2f);
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(f0))>(double)(1.0E-6));
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(df+1))>(double)(1.0E-6));
            
            //
            // Test location reports
            //
            aulits = 50;
            rho = 200.0;
            tolx = 0.0005;
            n = 2;
            x0 = new double[n];
            xlast = new double[n];
            x0[0] = 0.1;
            x0[1] = 0.2;
            xlast[0] = 0;
            xlast[1] = 0;
            minnlc.minnlccreate(n, x0, state);
            minnlc.minnlcsetalgoaul(state, rho, aulits);
            minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
            minnlc.minnlcsetnlc(state, 0, 1);
            minnlc.minnlcsetxrep(state, true);
            while( minnlc.minnlciteration(state) )
            {
                if( state.needfij )
                {
                    state.fi[0] = math.sqr(state.x[0]-1)+math.sqr(state.x[1]-1);
                    state.j[0,0] = 2*(state.x[0]-1);
                    state.j[0,1] = 2*(state.x[1]-1);
                    state.fi[1] = math.sqr(state.x[0])+math.sqr(state.x[1])-1;
                    state.j[1,0] = 2*state.x[0];
                    state.j[1,1] = 2*state.x[1];
                    continue;
                }
                if( state.xupdated )
                {
                    
                    //
                    // Save last point
                    //
                    xlast[0] = state.x[0];
                    xlast[1] = state.x[1];
                    continue;
                }
                alglib.ap.assert(false);
            }
            minnlc.minnlcresults(state, ref x1, rep);
            apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
            apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
            if( wereerrors )
            {
                return;
            }
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[0]-xlast[0]))>(double)(1.0E4*math.machineepsilon));
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[1]-xlast[1]))>(double)(1.0E4*math.machineepsilon));
            
            //
            // Test numerical differentiation
            //
            aulits = 50;
            rho = 200.0;
            tolx = 0.0001;
            diffstep = 0.001;
            n = 2;
            x0 = new double[n];
            x0[0] = 0.1;
            x0[1] = 0.2;
            minnlc.minnlccreatef(n, x0, diffstep, state);
            minnlc.minnlcsetalgoaul(state, rho, aulits);
            minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
            minnlc.minnlcsetnlc(state, 0, 1);
            while( minnlc.minnlciteration(state) )
            {
                if( state.needfi )
                {
                    state.fi[0] = math.sqr(state.x[0]-1)+math.sqr(state.x[1]-1);
                    state.fi[1] = math.sqr(state.x[0])+math.sqr(state.x[1])-1;
                    continue;
                }
                alglib.ap.assert(false);
            }
            minnlc.minnlcresults(state, ref x1, rep);
            apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
            apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
            if( wereerrors )
            {
                return;
            }
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[0]-Math.Sqrt(2)/2))>(double)(tolx));
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[1]-Math.Sqrt(2)/2))>(double)(tolx));
            
            //
            // Test gradient checking
            //
            aulits = 50;
            rho = 200.0;
            tolx = 0.0001;
            diffstep = 0.001;
            n = 2;
            x0 = new double[n];
            x0[0] = 0.1;
            x0[1] = 0.2;
            minnlc.minnlccreate(n, x0, state);
            minnlc.minnlcsetalgoaul(state, rho, aulits);
            minnlc.minnlcsetgradientcheck(state, diffstep);
            minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
            minnlc.minnlcsetnlc(state, 0, 1);
            while( minnlc.minnlciteration(state) )
            {
                if( state.needfij )
                {
                    state.fi[0] = math.sqr(state.x[0]-1)+math.sqr(state.x[1]-1);
                    state.j[0,0] = 2*(state.x[0]-1);
                    state.j[0,1] = 2*(state.x[1]-1);
                    state.fi[1] = math.sqr(state.x[0])+math.sqr(state.x[1])-1;
                    state.j[1,0] = 2*state.x[0];
                    state.j[1,1] = 0;
                    continue;
                }
                alglib.ap.assert(false);
            }
            minnlc.minnlcresults(state, ref x1, rep);
            apserv.seterrorflag(ref wereerrors, rep.terminationtype!=-7);
            apserv.seterrorflag(ref wereerrors, rep.varidx!=1);
            apserv.seterrorflag(ref wereerrors, rep.funcidx!=1);
            
            //
            // Check handling of general linear constraints: solve linearly 
            // constrained twice, first time with constraints posed as linear
            // ones, second time with constraints posed as nonlinear ones.
            //
            // Linear constraints are normalized because we know that optimizer
            // normalizes them internally.
            //
            // We perform small amount of inner iterations - just 3 steps.
            // Only one outer iteration is performed. Such small number of
            // iterations allows to reduce influence of round-off errors
            // and compare results returned by different control paths within
            // optimizer (control path for linear constraints and one for
            // nonlinear constraints).
            //
            // We test following kinds of preconditioners:
            // * "none"
            // * "exact low rank", restart frequency is 1
            // Inexact LBFGS-based preconditioner is not tested because its
            // behavior greatly depends on order of equations.
            //
            n = 30;
            k = 5;
            rho = 1.0E3;
            aulits = 1;
            maxits = 3;
            x0 = new double[n];
            c = new double[k, n+1];
            ct = new int[k];
            for(prectype=0; prectype<=1; prectype++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = hqrnd.hqrndnormal(rs);
                }
                for(i=0; i<=k-1; i++)
                {
                    v = 0.0;
                    for(j=0; j<=n-1; j++)
                    {
                        c[i,j] = hqrnd.hqrndnormal(rs);
                        v = v+math.sqr(c[i,j]);
                    }
                    v = 1/Math.Sqrt(v);
                    for(i_=0; i_<=n-1;i_++)
                    {
                        c[i,i_] = v*c[i,i_];
                    }
                    v = 0.0;
                    for(i_=0; i_<=n-1;i_++)
                    {
                        v += c[i,i_]*x0[i_];
                    }
                    c[i,n] = v;
                    ct[i] = 0;
                }
                minnlc.minnlccreate(n, x0, state);
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 0.0, maxits);
                if( prectype==0 )
                {
                    minnlc.minnlcsetprecnone(state);
                }
                if( prectype==1 )
                {
                    minnlc.minnlcsetprecexactlowrank(state, 1);
                }
                minnlc.minnlcsetlc(state, c, ct, k);
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+math.sqr(state.x[i]);
                            state.j[0,i] = 2*state.x[i];
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                minnlc.minnlccreate(n, x0, state);
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 0.0, maxits);
                if( prectype==0 )
                {
                    minnlc.minnlcsetprecnone(state);
                }
                if( prectype==1 )
                {
                    minnlc.minnlcsetprecexactlowrank(state, 1);
                }
                minnlc.minnlcsetnlc(state, k, 0);
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+math.sqr(state.x[i]);
                            state.j[0,i] = 2*state.x[i];
                        }
                        for(i=0; i<=k-1; i++)
                        {
                            v = 0.0;
                            for(i_=0; i_<=n-1;i_++)
                            {
                                v += c[i,i_]*state.x[i_];
                            }
                            state.fi[1+i] = v-c[i,n];
                            for(i_=0; i_<=n-1;i_++)
                            {
                                state.j[1+i,i_] = c[i,i_];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x2, rep);
                apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x2, n));
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x1[i]-x2[i]))>(double)(1.0E-4));
                }
            }
            
            //
            // Test preconditioning:
            // * compare number of iterations required to solve problem with
            //   different preconditioners (LBFGS, exact, none)
            // * a set of trials is performed (100 trials)
            // * each trial is a solution of boundary/linearly constrained problem
            //   (linear constraints may be posed as nonlinear ones) with normalized
            //   constraint matrix. Normalization is essential for reproducibility
            //   of results .
            //
            // Outer loop checks handling of different types of constraints
            // (posed as linear or nonlinear ones)
            //
            n = 30;
            blocksize = 3;
            blockcnt = 3;
            rho = 1.0E3;
            aulits = 5;
            condv = 1.0E2;
            x0 = new double[n];
            bndl = new double[n];
            bndu = new double[n];
            c = new double[blocksize*blockcnt, n+1];
            ct = new int[blocksize*blockcnt];
            for(ctype=0; ctype<=1; ctype++)
            {
                
                //
                // First, initialize iteration counters
                //
                nlbfgs = 0;
                nexact = 0;
                nnone = 0;
                
                //
                // Perform trials
                //
                for(trialidx=0; trialidx<=99; trialidx++)
                {
                    
                    //
                    // Generate:
                    // * boundary constraints BndL/BndU and initial point X0
                    // * block-diagonal matrix of linear constraints C such
                    //   that X0 is feasible w.r.t. constraints given by C
                    //
                    for(i=0; i<=n-1; i++)
                    {
                        if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                        {
                            bndl[i] = 0;
                            bndu[i] = Double.PositiveInfinity;
                            x0[i] = hqrnd.hqrnduniformr(rs);
                        }
                        else
                        {
                            bndl[i] = 0;
                            bndu[i] = 0;
                            x0[i] = 0;
                        }
                    }
                    for(i=0; i<=blocksize*blockcnt-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            c[i,j] = 0.0;
                        }
                    }
                    for(k=0; k<=blockcnt-1; k++)
                    {
                        matgen.rmatrixrndcond(blocksize, condv, ref a);
                        for(i=0; i<=blocksize-1; i++)
                        {
                            for(j=0; j<=blocksize-1; j++)
                            {
                                c[k*blocksize+i,k*blocksize+j] = a[i,j];
                            }
                        }
                    }
                    for(i=0; i<=blocksize*blockcnt-1; i++)
                    {
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += c[i,i_]*c[i,i_];
                        }
                        v = 1/Math.Sqrt(v);
                        for(i_=0; i_<=n-1;i_++)
                        {
                            c[i,i_] = v*c[i,i_];
                        }
                        v = 0.0;
                        for(i_=0; i_<=n-1;i_++)
                        {
                            v += c[i,i_]*x0[i_];
                        }
                        c[i,n] = v;
                        ct[i] = hqrnd.hqrnduniformi(rs, 3)-1;
                    }
                    
                    //
                    // Test unpreconditioned iteration
                    //
                    minnlc.minnlccreate(n, x0, state);
                    minnlc.minnlcsetalgoaul(state, rho, aulits);
                    minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                    if( ctype==0 )
                    {
                        minnlc.minnlcsetlc(state, c, ct, blocksize*blockcnt);
                    }
                    else
                    {
                        minnlc.minnlcsetnlc(state, blocksize*blockcnt, 0);
                    }
                    minnlc.minnlcsetprecnone(state);
                    while( minnlc.minnlciteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+math.sqr(state.x[i]);
                                state.j[0,i] = 2*state.x[i];
                            }
                            if( ctype==1 )
                            {
                                for(i=0; i<=blocksize*blockcnt-1; i++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += c[i,i_]*state.x[i_];
                                    }
                                    state.fi[1+i] = v-c[i,n];
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        state.j[1+i,i_] = c[i,i_];
                                    }
                                }
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minnlc.minnlcresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( wereerrors )
                    {
                        return;
                    }
                    nnone = nnone+rep.iterationscount;
                    
                    //
                    // Test LBFGS preconditioned iteration
                    //
                    minnlc.minnlccreate(n, x0, state);
                    minnlc.minnlcsetalgoaul(state, rho, aulits);
                    minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                    if( ctype==0 )
                    {
                        minnlc.minnlcsetlc(state, c, ct, blocksize*blockcnt);
                    }
                    else
                    {
                        minnlc.minnlcsetnlc(state, blocksize*blockcnt, 0);
                    }
                    minnlc.minnlcsetprecinexact(state);
                    while( minnlc.minnlciteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+math.sqr(state.x[i]);
                                state.j[0,i] = 2*state.x[i];
                            }
                            if( ctype==1 )
                            {
                                for(i=0; i<=blocksize*blockcnt-1; i++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += c[i,i_]*state.x[i_];
                                    }
                                    state.fi[1+i] = v-c[i,n];
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        state.j[1+i,i_] = c[i,i_];
                                    }
                                }
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minnlc.minnlcresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( wereerrors )
                    {
                        return;
                    }
                    nlbfgs = nlbfgs+rep.iterationscount;
                    
                    //
                    // Test exact preconditioner
                    //
                    minnlc.minnlccreate(n, x0, state);
                    minnlc.minnlcsetalgoaul(state, rho, aulits);
                    minnlc.minnlcsetcond(state, 0.0, 0.0, 1.0E-9, 0);
                    if( ctype==0 )
                    {
                        minnlc.minnlcsetlc(state, c, ct, blocksize*blockcnt);
                    }
                    else
                    {
                        minnlc.minnlcsetnlc(state, blocksize*blockcnt, 0);
                    }
                    minnlc.minnlcsetprecexactlowrank(state, 3);
                    while( minnlc.minnlciteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+math.sqr(state.x[i]);
                                state.j[0,i] = 2*state.x[i];
                            }
                            if( ctype==1 )
                            {
                                for(i=0; i<=blocksize*blockcnt-1; i++)
                                {
                                    v = 0.0;
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        v += c[i,i_]*state.x[i_];
                                    }
                                    state.fi[1+i] = v-c[i,n];
                                    for(i_=0; i_<=n-1;i_++)
                                    {
                                        state.j[1+i,i_] = c[i,i_];
                                    }
                                }
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minnlc.minnlcresults(state, ref x1, rep);
                    apserv.seterrorflag(ref wereerrors, !apserv.isfinitevector(x1, n));
                    apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                    if( wereerrors )
                    {
                        return;
                    }
                    nexact = nexact+rep.iterationscount;
                }
                
                //
                // Compare.
                //
                // Preconditioners must be significantly different,
                // with exact being best one, inexact being second,
                // "none" being worst option.
                //
                apserv.seterrorflag(ref wereerrors, !((double)(nexact)<(double)(0.9*nlbfgs)));
                apserv.seterrorflag(ref wereerrors, !((double)(nlbfgs)<(double)(0.9*nnone)));
            }
            
            //
            // Test integrity checks for NAN/INF:
            // * algorithm solves optimization problem, which is normal for some time (quadratic)
            // * after 5-th step we choose random component of gradient and consistently spoil
            //   it by NAN or INF.
            // * we check that correct termination code is returned (-8)
            //
            n = 100;
            for(pass=1; pass<=10; pass++)
            {
                spoiliteration = 5;
                stopiteration = 8;
                if( (double)(hqrnd.hqrndnormal(rs))>(double)(0) )
                {
                    
                    //
                    // Gradient can be spoiled by +INF, -INF, NAN
                    //
                    spoilvar = hqrnd.hqrnduniformi(rs, n);
                    i = hqrnd.hqrnduniformi(rs, 3);
                    spoilval = Double.NaN;
                    if( i==0 )
                    {
                        spoilval = Double.NegativeInfinity;
                    }
                    if( i==1 )
                    {
                        spoilval = Double.PositiveInfinity;
                    }
                }
                else
                {
                    
                    //
                    // Function value can be spoiled only by NAN
                    // (+INF can be recognized as legitimate value during optimization)
                    //
                    spoilvar = -1;
                    spoilval = Double.NaN;
                }
                matgen.spdmatrixrndcond(n, 1.0E5, ref fulla);
                b = new double[n];
                x0 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    b[i] = hqrnd.hqrndnormal(rs);
                    x0[i] = hqrnd.hqrndnormal(rs);
                }
                minnlc.minnlccreate(n, x0, state);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 0.0, stopiteration);
                minnlc.minnlcsetxrep(state, true);
                k = -1;
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+b[i]*state.x[i];
                            state.j[0,i] = b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi[0] = state.f+0.5*state.x[i]*fulla[i,j]*state.x[j];
                                state.j[0,i] = state.j[0,i]+fulla[i,j]*state.x[j];
                            }
                        }
                        if( k>=spoiliteration )
                        {
                            if( spoilvar<0 )
                            {
                                state.fi[0] = spoilval;
                            }
                            else
                            {
                                state.j[0,spoilvar] = spoilval;
                            }
                        }
                        continue;
                    }
                    if( state.xupdated )
                    {
                        apserv.inc(ref k);
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, rep.terminationtype!=-8);
            }
        }


        /*************************************************************************
        This function performs tests for fixed bugs

        On failure sets error flag.
        *************************************************************************/
        private static void testbugs(ref bool wereerrors)
        {
            hqrnd.hqrndstate rs = new hqrnd.hqrndstate();
            int n = 0;
            int aulits = 0;
            int maxits = 0;
            double rho = 0;
            int ckind = 0;
            minnlc.minnlcstate state = new minnlc.minnlcstate();
            minnlc.minnlcreport rep = new minnlc.minnlcreport();
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            int[] ct = new int[0];
            double[,] c = new double[0,0];

            hqrnd.hqrndrandomize(rs);
            
            //
            // Bug description (fixed): sometimes on non-convex problems, when
            // Lagrange coefficient for inequality constraint becomes small,
            // algorithm performs VERY deep step into infeasible area (step is 1E50),
            // which de-stabilizes it and prevents from converging back to feasible area.
            //
            // Very rare situation, but must be fixed with additional "convexifying" term.
            // This test reproduces situation with convexified term turned off, then
            // checks that introduction of term solves issue.
            //
            // We perform three kinds of tests:
            // * with box inequality constraint
            // * with linear inequality constraint
            // * with nonlinear inequality constraint
            //
            // In all three cases we:
            // * first time solve non-convex problem with artificially moved stabilizing
            //   point and decreased initial value of Lagrange multiplier.
            // * second time we solve problem with good stabilizing point, but zero Lagrange multiplier
            // * last time solve same problem, but with default settings
            //
            aulits = 1;
            maxits = 1;
            rho = 100.0;
            n = 1;
            x0 = new double[n];
            x0[0] = 0.0;
            bndl = new double[n];
            bndu = new double[n];
            bndl[0] = 0.0;
            bndu[0] = Double.PositiveInfinity;
            c = new double[1, 2];
            ct = new int[1];
            c[0,0] = 1.0;
            c[0,1] = 0.0;
            ct[0] = 1;
            for(ckind=0; ckind<=2; ckind++)
            {
                minnlc.minnlccreate(n, x0, state);
                state.stabilizingpoint = -1.0E300;
                state.initialinequalitymultiplier = 1.0E-12;
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 0.0, maxits);
                if( ckind==0 )
                {
                    minnlc.minnlcsetbc(state, bndl, bndu);
                }
                if( ckind==1 )
                {
                    minnlc.minnlcsetlc(state, c, ct, 1);
                }
                if( ckind==2 )
                {
                    minnlc.minnlcsetnlc(state, 0, 1);
                }
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = state.x[0]-math.sqr(state.x[0]);
                        state.j[0,0] = 1-2*state.x[0];
                        if( ckind==2 )
                        {
                            state.fi[1] = -state.x[0];
                            state.j[1,0] = -1;
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                apserv.seterrorflag(ref wereerrors, (double)(x1[0])>(double)(-1.0E6));
                minnlc.minnlccreate(n, x0, state);
                state.stabilizingpoint = -1.0E2;
                state.initialinequalitymultiplier = 1.0E-12;
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 0.0, maxits);
                if( ckind==0 )
                {
                    minnlc.minnlcsetbc(state, bndl, bndu);
                }
                if( ckind==1 )
                {
                    minnlc.minnlcsetlc(state, c, ct, 1);
                }
                if( ckind==2 )
                {
                    minnlc.minnlcsetnlc(state, 0, 1);
                }
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = state.x[0]-math.sqr(state.x[0]);
                        state.j[0,0] = 1-2*state.x[0];
                        if( ckind==2 )
                        {
                            state.fi[1] = -state.x[0];
                            state.j[1,0] = -1;
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                apserv.seterrorflag(ref wereerrors, (double)(x1[0])<(double)(3*state.stabilizingpoint));
                minnlc.minnlccreate(n, x0, state);
                minnlc.minnlcsetalgoaul(state, rho, aulits);
                minnlc.minnlcsetcond(state, 0.0, 0.0, 0.0, maxits);
                if( ckind==0 )
                {
                    minnlc.minnlcsetbc(state, bndl, bndu);
                }
                if( ckind==1 )
                {
                    minnlc.minnlcsetlc(state, c, ct, 1);
                }
                if( ckind==2 )
                {
                    minnlc.minnlcsetnlc(state, 0, 1);
                }
                while( minnlc.minnlciteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = state.x[0]-math.sqr(state.x[0]);
                        state.j[0,0] = 1-2*state.x[0];
                        if( ckind==2 )
                        {
                            state.fi[1] = -state.x[0];
                            state.j[1,0] = -1;
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minnlc.minnlcresults(state, ref x1, rep);
                apserv.seterrorflag(ref wereerrors, rep.terminationtype<=0);
                if( wereerrors )
                {
                    return;
                }
                apserv.seterrorflag(ref wereerrors, (double)(x1[0])<(double)(3*state.stabilizingpoint));
            }
        }


    }
    public class testminnsunit
    {
        public const double scalingtesttol = 1.0E-6;
        public const int scalingtestcnt = 5;


        public static bool testminns(bool silent)
        {
            bool result = new bool();
            bool wereerrors = new bool();
            bool ucerrors = new bool();
            bool bcerrors = new bool();
            bool lcerrors = new bool();
            bool nlcerrors = new bool();
            bool othererrors = new bool();

            wereerrors = false;
            ucerrors = false;
            bcerrors = false;
            lcerrors = false;
            nlcerrors = false;
            othererrors = false;
            
            //
            // Basic tests
            //
            basictest0nlc(ref nlcerrors);
            basictest0uc(ref ucerrors);
            basictest1uc(ref ucerrors);
            basictest0bc(ref bcerrors);
            basictest1bc(ref bcerrors);
            basictest0lc(ref lcerrors);
            basictest1lc(ref lcerrors);
            
            //
            // Special tests
            //
            testother(ref othererrors);
            
            //
            // Full scale tests
            //
            testuc(ref ucerrors, ref othererrors);
            testbc(ref bcerrors, ref othererrors);
            testlc(ref lcerrors, ref othererrors);
            testnlc(ref nlcerrors, ref othererrors);
            
            //
            // end
            //
            wereerrors = (((ucerrors || bcerrors) || lcerrors) || nlcerrors) || othererrors;
            if( !silent )
            {
                System.Console.Write("TESTING MINNS OPTIMIZATION");
                System.Console.WriteLine();
                System.Console.Write("TESTS:");
                System.Console.WriteLine();
                System.Console.Write("* UNCONSTRAINED                           ");
                if( ucerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* BOUND CONSTRAINED                       ");
                if( bcerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* LINEARLY CONSTRAINED                    ");
                if( lcerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("* NONLINEARLY CONSTRAINED                 ");
                if( nlcerrors )
                {
                    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( wereerrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            result = !wereerrors;
            return result;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testminns(bool silent)
        {
            return testminns(silent);
        }


        /*************************************************************************
        Basic unconstrained test
        *************************************************************************/
        private static void basictest0uc(ref bool errors)
        {
            int n = 0;
            int i = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] d = new double[0];
            minns.minnsstate s = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();
            double sumits = 0;
            double sumnfev = 0;
            int pass = 0;
            int passcount = 0;

            n = 5;
            passcount = 10;
            sumits = 0;
            sumnfev = 0;
            x0 = new double[n];
            d = new double[n];
            for(pass=1; pass<=10; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 2*math.randomreal()-1;
                    d[i] = Math.Pow(10, 2*math.randomreal()-1);
                }
                minns.minnscreate(n, x0, s);
                minns.minnssetalgoags(s, 0.1, 0.0);
                while( minns.minnsiteration(s) )
                {
                    if( s.needfij )
                    {
                        s.fi[0] = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            s.fi[0] = s.fi[0]+d[i]*Math.Abs(s.x[i]);
                            s.j[0,i] = d[i]*Math.Sign(s.x[i]);
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minns.minnsresults(s, ref x1, rep);
                apserv.seterrorflag(ref errors, rep.terminationtype<=0);
                if( errors )
                {
                    return;
                }
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref errors, !math.isfinite(x1[i]) || (double)(Math.Abs(x1[i]))>(double)(0.001));
                }
                sumits = sumits+(double)rep.iterationscount/(double)passcount;
                sumnfev = sumnfev+(double)rep.nfev/(double)passcount;
            }
        }


        /*************************************************************************
        Basic unconstrained test: nonsmooth Rosenbrock posed as unconstrained problem.

                     [                                                                       ]
            minimize [ 10*|x0^2-x1| + (1-x0)^2 + 100*max(sqrt(2)*x0-1,0) + 100*max(2*x1-1,0) ]
                     [                                                                       ]

        It's exact solution is x0=1/sqrt(2), x1=1/2
        *************************************************************************/
        private static void basictest1uc(ref bool errors)
        {
            int n = 0;
            double v0 = 0;
            double v1 = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            minns.minnsstate s = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();

            n = 2;
            x0 = new double[n];
            x0[0] = 0;
            x0[1] = 0;
            minns.minnscreate(n, x0, s);
            minns.minnssetalgoags(s, 0.1, 0.0);
            while( minns.minnsiteration(s) )
            {
                if( s.needfij )
                {
                    v0 = s.x[0];
                    v1 = s.x[1];
                    s.fi[0] = 10*Math.Abs(math.sqr(v0)-v1)+math.sqr(v0-1);
                    s.j[0,0] = 10*Math.Sign(math.sqr(v0)-v1)*2*v0+2*(v0-1);
                    s.j[0,1] = 10*Math.Sign(math.sqr(v0)-v1)*-1;
                    if( (double)(Math.Sqrt(2)*v0-1)>(double)(0.0) )
                    {
                        s.fi[0] = s.fi[0]+100*(Math.Sqrt(2)*v0-1);
                        s.j[0,0] = s.j[0,0]+100*Math.Sqrt(2);
                    }
                    if( (double)(2*v1-1)>(double)(0.0) )
                    {
                        s.fi[0] = s.fi[0]+100*(2*v1-1);
                        s.j[0,1] = s.j[0,1]+100*2;
                    }
                    continue;
                }
                alglib.ap.assert(false);
            }
            minns.minnsresults(s, ref x1, rep);
            apserv.seterrorflag(ref errors, rep.terminationtype<=0);
            if( errors )
            {
                return;
            }
            apserv.seterrorflag(ref errors, !math.isfinite(x1[0]) || (double)(Math.Abs(x1[0]-1/Math.Sqrt(2)))>(double)(0.001));
            apserv.seterrorflag(ref errors, !math.isfinite(x1[1]) || (double)(Math.Abs(x1[1]-(double)1/(double)2))>(double)(0.001));
        }


        /*************************************************************************
        Basic box constrained test
        *************************************************************************/
        private static void basictest0bc(ref bool errors)
        {
            int n = 0;
            int i = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] d = new double[0];
            double[] bl = new double[0];
            double[] bu = new double[0];
            minns.minnsstate s = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();
            double sumits = 0;
            double sumnfev = 0;
            int pass = 0;
            int passcount = 0;
            double v0 = 0;
            double v1 = 0;

            n = 5;
            passcount = 10;
            sumits = 0;
            sumnfev = 0;
            x0 = new double[n];
            bl = new double[n];
            bu = new double[n];
            d = new double[n];
            for(pass=1; pass<=10; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 2*math.randomreal()-1;
                    d[i] = Math.Pow(10, 2*math.randomreal()-1);
                    v0 = 2*math.randomreal()-1;
                    v1 = 2*math.randomreal()-1;
                    bl[i] = Math.Min(v0, v1);
                    bu[i] = Math.Max(v0, v1);
                }
                minns.minnscreate(n, x0, s);
                minns.minnssetalgoags(s, 0.1, 0.0);
                minns.minnssetbc(s, bl, bu);
                while( minns.minnsiteration(s) )
                {
                    if( s.needfij )
                    {
                        s.fi[0] = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            s.fi[0] = s.fi[0]+d[i]*Math.Abs(s.x[i]);
                            s.j[0,i] = d[i]*Math.Sign(s.x[i]);
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minns.minnsresults(s, ref x1, rep);
                apserv.seterrorflag(ref errors, rep.terminationtype<=0);
                if( errors )
                {
                    return;
                }
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref errors, !math.isfinite(x1[i]) || (double)(Math.Abs(x1[i]-apserv.boundval(0.0, bl[i], bu[i])))>(double)(0.001));
                }
                sumits = sumits+(double)rep.iterationscount/(double)passcount;
                sumnfev = sumnfev+(double)rep.nfev/(double)passcount;
            }
        }


        /*************************************************************************
        Basic constrained test: nonsmooth Rosenbrock posed as box constrained problem.

                     [                         ]
            minimize [ 10*|x0^2-x1| + (1-x0)^2 ]
                     [                         ]
                     
            s.t. x0<=1/sqrt(2), x1<=0.5

        It's exact solution is x0=1/sqrt(2), x1=1/2
        *************************************************************************/
        private static void basictest1bc(ref bool errors)
        {
            int n = 0;
            double v0 = 0;
            double v1 = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            minns.minnsstate s = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();

            n = 2;
            x0 = new double[n];
            bndl = new double[n];
            bndu = new double[n];
            x0[0] = 0;
            x0[1] = 0;
            bndl[0] = Double.NegativeInfinity;
            bndl[1] = Double.NegativeInfinity;
            bndu[0] = 1/Math.Sqrt(2);
            bndu[1] = (double)1/(double)2;
            minns.minnscreate(n, x0, s);
            minns.minnssetbc(s, bndl, bndu);
            minns.minnssetalgoags(s, 0.1, 0.0);
            while( minns.minnsiteration(s) )
            {
                if( s.needfij )
                {
                    v0 = s.x[0];
                    v1 = s.x[1];
                    s.fi[0] = 10*Math.Abs(math.sqr(v0)-v1)+math.sqr(v0-1);
                    s.j[0,0] = 10*Math.Sign(math.sqr(v0)-v1)*2*v0+2*(v0-1);
                    s.j[0,1] = 10*Math.Sign(math.sqr(v0)-v1)*-1;
                    continue;
                }
                alglib.ap.assert(false);
            }
            minns.minnsresults(s, ref x1, rep);
            apserv.seterrorflag(ref errors, rep.terminationtype<=0);
            if( errors )
            {
                return;
            }
            apserv.seterrorflag(ref errors, !math.isfinite(x1[0]) || (double)(Math.Abs(x1[0]-1/Math.Sqrt(2)))>(double)(0.001));
            apserv.seterrorflag(ref errors, !math.isfinite(x1[1]) || (double)(Math.Abs(x1[1]-(double)1/(double)2))>(double)(0.001));
        }


        /*************************************************************************
        Basic linearly constrained test
        *************************************************************************/
        private static void basictest0lc(ref bool errors)
        {
            int n = 0;
            int i = 0;
            int j = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[,] c = new double[0,0];
            int[] ct = new int[0];
            double d = 0;
            minns.minnsstate s = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();
            double sumits = 0;
            double sumnfev = 0;
            int pass = 0;
            int passcount = 0;
            int nc = 0;

            d = -10.0;
            n = 5;
            passcount = 10;
            sumits = 0;
            sumnfev = 0;
            x0 = new double[n];
            c = new double[2*n, n+1];
            ct = new int[2*n];
            for(pass=1; pass<=10; pass++)
            {
                nc = 0;
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 2*math.randomreal()-1;
                    if( (double)(math.randomreal())<(double)(0.5) )
                    {
                        for(j=0; j<=n; j++)
                        {
                            c[nc,j] = 0.0;
                        }
                        c[nc,i] = 1.0+math.randomreal();
                        ct[nc] = 0;
                        apserv.inc(ref nc);
                    }
                    else
                    {
                        for(j=0; j<=n; j++)
                        {
                            c[nc+0,j] = 0.0;
                            c[nc+1,j] = 0.0;
                        }
                        c[nc+0,i] = 1.0+math.randomreal();
                        c[nc+1,i] = 1.0+math.randomreal();
                        ct[nc+0] = 1;
                        ct[nc+1] = -1;
                        nc = nc+2;
                    }
                }
                minns.minnscreate(n, x0, s);
                minns.minnssetalgoags(s, 0.1, 0.0);
                minns.minnssetlc(s, c, ct, nc);
                while( minns.minnsiteration(s) )
                {
                    if( s.needfij )
                    {
                        s.fi[0] = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            s.fi[0] = d*math.sqr(s.x[i]);
                            s.j[0,i] = d*2*s.x[i];
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minns.minnsresults(s, ref x1, rep);
                apserv.seterrorflag(ref errors, rep.terminationtype<=0);
                if( errors )
                {
                    return;
                }
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref errors, !math.isfinite(x1[i]) || (double)(Math.Abs(x1[i]))>(double)(0.001));
                }
                sumits = sumits+(double)rep.iterationscount/(double)passcount;
                sumnfev = sumnfev+(double)rep.nfev/(double)passcount;
            }
        }


        /*************************************************************************
        Basic constrained test: nonsmooth Rosenbrock posed as linearly constrained problem.

                     [                         ]
            minimize [ 10*|x0^2-x1| + (1-x0)^2 ]
                     [                         ]
                     
            s.t. x0<=1/sqrt(2), x1<=0.5

        It's exact solution is x0=1/sqrt(2), x1=1/2
        *************************************************************************/
        private static void basictest1lc(ref bool errors)
        {
            int n = 0;
            double v0 = 0;
            double v1 = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[,] c = new double[0,0];
            int[] ct = new int[0];
            minns.minnsstate s = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();

            n = 2;
            x0 = new double[n];
            c = new double[2, n+1];
            ct = new int[2];
            x0[0] = 0;
            x0[1] = 0;
            c[0,0] = 1.0;
            c[0,1] = 0.0;
            c[0,2] = 1/Math.Sqrt(2);
            c[1,0] = 0.0;
            c[1,1] = 1.0;
            c[1,2] = (double)1/(double)2;
            ct[0] = -1;
            ct[1] = -1;
            minns.minnscreate(n, x0, s);
            minns.minnssetlc(s, c, ct, 2);
            minns.minnssetalgoags(s, 0.1, 0.0);
            while( minns.minnsiteration(s) )
            {
                if( s.needfij )
                {
                    v0 = s.x[0];
                    v1 = s.x[1];
                    s.fi[0] = 10*Math.Abs(math.sqr(v0)-v1)+math.sqr(v0-1);
                    s.j[0,0] = 10*Math.Sign(math.sqr(v0)-v1)*2*v0+2*(v0-1);
                    s.j[0,1] = 10*Math.Sign(math.sqr(v0)-v1)*-1;
                    continue;
                }
                alglib.ap.assert(false);
            }
            minns.minnsresults(s, ref x1, rep);
            apserv.seterrorflag(ref errors, rep.terminationtype<=0);
            if( errors )
            {
                return;
            }
            apserv.seterrorflag(ref errors, !math.isfinite(x1[0]) || (double)(Math.Abs(x1[0]-1/Math.Sqrt(2)))>(double)(0.001));
            apserv.seterrorflag(ref errors, !math.isfinite(x1[1]) || (double)(Math.Abs(x1[1]-(double)1/(double)2))>(double)(0.001));
        }


        /*************************************************************************
        Basic nonlinearly constrained test
        *************************************************************************/
        private static void basictest0nlc(ref bool errors)
        {
            int n = 0;
            int i = 0;
            int j = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            double[,] ec = new double[0,0];
            double[,] ic = new double[0,0];
            int nec = 0;
            int nic = 0;
            double d = 0;
            minns.minnsstate s = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();
            double sumits = 0;
            double sumnfev = 0;
            int pass = 0;
            int passcount = 0;

            d = -10.0;
            n = 5;
            passcount = 10;
            sumits = 0;
            sumnfev = 0;
            x0 = new double[n];
            ec = new double[2*n, n+1];
            ic = new double[2*n, n+1];
            for(pass=1; pass<=10; pass++)
            {
                nec = 0;
                nic = 0;
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 2*math.randomreal()-1;
                    if( (double)(math.randomreal())<(double)(0.5) )
                    {
                        for(j=0; j<=n; j++)
                        {
                            ec[nec,j] = 0.0;
                        }
                        ec[nec,i] = 1.0+math.randomreal();
                        apserv.inc(ref nec);
                    }
                    else
                    {
                        for(j=0; j<=n; j++)
                        {
                            ic[nic+0,j] = 0.0;
                            ic[nic+1,j] = 0.0;
                        }
                        ic[nic+0,i] = 1.0+math.randomreal();
                        ic[nic+1,i] = -1.0-math.randomreal();
                        nic = nic+2;
                    }
                }
                minns.minnscreate(n, x0, s);
                minns.minnssetalgoags(s, 0.1, 100.0);
                minns.minnssetnlc(s, nec, nic);
                while( minns.minnsiteration(s) )
                {
                    if( s.needfij )
                    {
                        s.fi[0] = 0.0;
                        for(j=0; j<=n-1; j++)
                        {
                            s.fi[0] = d*math.sqr(s.x[j]);
                            s.j[0,j] = d*2*s.x[j];
                        }
                        for(i=0; i<=nec-1; i++)
                        {
                            s.fi[1+i] = -ec[i,n];
                            for(j=0; j<=n-1; j++)
                            {
                                s.fi[1+i] = s.fi[1+i]+s.x[j]*ec[i,j];
                                s.j[1+i,j] = ec[i,j];
                            }
                        }
                        for(i=0; i<=nic-1; i++)
                        {
                            s.fi[1+nec+i] = -ic[i,n];
                            for(j=0; j<=n-1; j++)
                            {
                                s.fi[1+nec+i] = s.fi[1+nec+i]+s.x[j]*ic[i,j];
                                s.j[1+nec+i,j] = ic[i,j];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minns.minnsresults(s, ref x1, rep);
                apserv.seterrorflag(ref errors, rep.terminationtype<=0);
                if( errors )
                {
                    return;
                }
                for(i=0; i<=n-1; i++)
                {
                    apserv.seterrorflag(ref errors, !math.isfinite(x1[i]) || (double)(Math.Abs(x1[i]))>(double)(0.001));
                }
                sumits = sumits+(double)rep.iterationscount/(double)passcount;
                sumnfev = sumnfev+(double)rep.nfev/(double)passcount;
            }
        }


        /*************************************************************************
        Unconstrained test
        *************************************************************************/
        private static void testuc(ref bool primaryerrors,
            ref bool othererrors)
        {
            int n = 0;
            int i = 0;
            double[] x0 = new double[0];
            double[] x0s = new double[0];
            double[] x1 = new double[0];
            double[] x1s = new double[0];
            double[] d = new double[0];
            double[] xc = new double[0];
            double[] s = new double[0];
            double[] xrfirst = new double[0];
            double[] xrlast = new double[0];
            minns.minnsstate state = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();
            double v = 0;
            int pass = 0;
            int passcount = 0;
            bool requirexrep = new bool();
            double epsrad = 0;
            bool werexreports = new bool();
            double repferr = 0;
            double xtol = 0;
            int i_ = 0;

            passcount = 10;
            for(pass=1; pass<=10; pass++)
            {
                for(n=1; n<=5; n++)
                {
                    
                    //
                    // First test:
                    // * test that problem is successfully solved
                    // * test that X-reports are performed correctly - present
                    //   when requested, return first and last points correctly,
                    //   not present by default, function value is reported
                    //   correctly.
                    // * we use non-unit scale, randomly chosen one, which results
                    //   in badly conditioned problems (to check robustness)
                    //
                    x0 = new double[n];
                    xc = new double[n];
                    d = new double[n];
                    s = new double[n];
                    xrfirst = new double[n];
                    xrlast = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 10*(2*math.randomreal()-1);
                        xc[i] = 2*math.randomreal()-1;
                        d[i] = Math.Pow(10, 2*(2*math.randomreal()-1));
                        s[i] = Math.Pow(10, 2*(2*math.randomreal()-1));
                    }
                    requirexrep = (double)(math.randomreal())>(double)(0.5);
                    epsrad = 0.01*Math.Pow(10, -(2*math.randomreal()));
                    xtol = 15.0*epsrad;
                    minns.minnscreate(n, x0, state);
                    minns.minnssetalgoags(state, 0.1, 0.0);
                    minns.minnssetcond(state, epsrad, 0);
                    minns.minnssetscale(state, s);
                    if( requirexrep )
                    {
                        minns.minnssetxrep(state, true);
                    }
                    werexreports = false;
                    repferr = 0.0;
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                                state.j[0,i] = d[i]*Math.Sign(state.x[i]-xc[i]);
                            }
                            continue;
                        }
                        if( state.xupdated )
                        {
                            if( !werexreports )
                            {
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    xrfirst[i_] = state.x[i_];
                                }
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xrlast[i_] = state.x[i_];
                            }
                            werexreports = true;
                            v = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                v = v+d[i]*Math.Abs(state.x[i]-xc[i]);
                            }
                            repferr = Math.Max(repferr, Math.Abs(v-state.f));
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    apserv.seterrorflag(ref othererrors, werexreports && !requirexrep);
                    apserv.seterrorflag(ref othererrors, requirexrep && !werexreports);
                    apserv.seterrorflag(ref othererrors, (double)(repferr)>(double)(10000*math.machineepsilon));
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref primaryerrors, !math.isfinite(x1[i]) || (double)(Math.Abs(x1[i]-xc[i])/s[i])>(double)(xtol));
                        if( requirexrep )
                        {
                            apserv.seterrorflag(ref othererrors, !math.isfinite(xrfirst[i]) || (double)(Math.Abs(x0[i]-xrfirst[i]))>(double)(100*math.machineepsilon));
                            apserv.seterrorflag(ref othererrors, !math.isfinite(xrlast[i]) || (double)(Math.Abs(x1[i]-xrlast[i]))>(double)(100*math.machineepsilon));
                        }
                    }
                    
                    //
                    // Test numerical differentiation:
                    // * test that problem is successfully solved
                    // * test that correct function value is reported
                    //
                    x0 = new double[n];
                    xc = new double[n];
                    d = new double[n];
                    s = new double[n];
                    xrlast = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 10*(2*math.randomreal()-1);
                        xc[i] = 2*math.randomreal()-1;
                        d[i] = Math.Pow(10, 2*(2*math.randomreal()-1));
                        s[i] = Math.Pow(10, 2*(2*math.randomreal()-1));
                    }
                    epsrad = 0.01*Math.Pow(10, -(2*math.randomreal()));
                    xtol = 15.0*epsrad;
                    minns.minnscreatef(n, x0, epsrad/100, state);
                    minns.minnssetalgoags(state, 0.1, 0.0);
                    minns.minnssetcond(state, epsrad, 0);
                    minns.minnssetscale(state, s);
                    minns.minnssetxrep(state, true);
                    repferr = 0.0;
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfi )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                            }
                            continue;
                        }
                        if( state.xupdated )
                        {
                            v = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                v = v+d[i]*Math.Abs(state.x[i]-xc[i]);
                            }
                            repferr = Math.Max(repferr, Math.Abs(v-state.f));
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    apserv.seterrorflag(ref othererrors, (double)(repferr)>(double)(10000*math.machineepsilon));
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref primaryerrors, !math.isfinite(x1[i]) || (double)(Math.Abs(x1[i]-xc[i])/s[i])>(double)(xtol));
                    }
                    
                    //
                    // Test scaling: we perform several steps on unit-scale problem,
                    // then we perform same amount of steps on re-scaled problem,
                    // starting from same point (but scaled according to chosen scale).
                    //
                    // Correctly written optimizer should perform essentially same steps
                    // (up to scale) on both problems. At least, it holds within first
                    // several steps, before rounding errors start to accumulate.
                    //
                    // NOTE: we also check that correctly scaled points are reported.
                    //       And, as side effect, we check MinNSRestartFrom().
                    //
                    // NOTE: we use moderate scale and diagonal coefficients in order
                    //       to have well-conditioned system. We test correctness of 
                    //       formulae here, not robustness of algorithm.
                    //
                    x0 = new double[n];
                    xc = new double[n];
                    x0s = new double[n];
                    d = new double[n];
                    s = new double[n];
                    xrlast = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        s[i] = Math.Pow(10, 2*math.randomreal()-1);
                        d[i] = Math.Pow(10, 2*math.randomreal()-1);
                        x0[i] = 2*math.randomreal()-1;
                        xc[i] = 2*math.randomreal()-1;
                        x0s[i] = x0[i]*s[i];
                    }
                    minns.minnscreate(n, x0, state);
                    minns.minnssetalgoags(state, 0.1, 0.0);
                    minns.minnssetcond(state, 0.0, scalingtestcnt);
                    minns.minnssetxrep(state, false);
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                                state.j[0,i] = d[i]*Math.Sign(state.x[i]-xc[i]);
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    minns.minnssetscale(state, s);
                    minns.minnssetxrep(state, true);
                    minns.minnsrestartfrom(state, x0s);
                    werexreports = false;
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]/s[i]-xc[i]);
                                state.j[0,i] = d[i]*Math.Sign(state.x[i]/s[i]-xc[i])/s[i];
                            }
                            continue;
                        }
                        if( state.xupdated )
                        {
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xrlast[i_] = state.x[i_];
                            }
                            werexreports = true;
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1s, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref primaryerrors, (!math.isfinite(x1[i]) || !math.isfinite(x1s[i])) || (double)(Math.Abs(x1[i]-x1s[i]/s[i]))>(double)(1.0E-4));
                        apserv.seterrorflag(ref othererrors, !math.isfinite(xrlast[i]) || (double)(Math.Abs(x1s[i]-xrlast[i]))>(double)(scalingtesttol));
                    }
                }
            }
        }


        /*************************************************************************
        Box constrained test
        *************************************************************************/
        private static void testbc(ref bool primaryerrors,
            ref bool othererrors)
        {
            int n = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double[] x0 = new double[0];
            double[] x0s = new double[0];
            double[] x1 = new double[0];
            double[] x1s = new double[0];
            double[] b = new double[0];
            double[] d = new double[0];
            double[] xc = new double[0];
            double[] s = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] scaledbndl = new double[0];
            double[] scaledbndu = new double[0];
            double[] xrfirst = new double[0];
            double[] xrlast = new double[0];
            double[,] a = new double[0,0];
            minns.minnsstate state = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();
            double v = 0;
            double v0 = 0;
            double v1 = 0;
            int pass = 0;
            int passcount = 0;
            bool requirexrep = new bool();
            double epsrad = 0;
            bool werexreports = new bool();
            double repferr = 0;
            double xtol = 0;
            int maxn = 0;
            double conda = 0;
            double gnorm = 0;
            int i_ = 0;

            passcount = 10;
            maxn = 5;
            
            //
            // First test:
            // * sparse function
            // * test that problem is successfully solved
            // * non-unit scale is used, which results in badly conditioned problem
            // * check that all iterates are feasible (box-constrained)
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    x0 = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    xc = new double[n];
                    d = new double[n];
                    s = new double[n];
                    xrfirst = new double[n];
                    xrlast = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 10*(2*math.randomreal()-1);
                        xc[i] = 2*math.randomreal()-1;
                        d[i] = Math.Pow(10, 2*(2*math.randomreal()-1));
                        s[i] = Math.Pow(10, 2*(2*math.randomreal()-1));
                        bndl[i] = Double.NegativeInfinity;
                        bndu[i] = Double.PositiveInfinity;
                        k = math.randominteger(5);
                        if( k==1 )
                        {
                            bndl[i] = 2*math.randomreal()-1;
                        }
                        if( k==2 )
                        {
                            bndu[i] = 2*math.randomreal()-1;
                        }
                        if( k==3 )
                        {
                            v0 = 2*math.randomreal()-1;
                            v1 = 2*math.randomreal()-1;
                            bndl[i] = Math.Min(v0, v1);
                            bndu[i] = Math.Max(v0, v1);
                        }
                        if( k==4 )
                        {
                            bndl[i] = 2*math.randomreal()-1;
                            bndu[i] = bndl[i];
                        }
                    }
                    requirexrep = (double)(math.randomreal())>(double)(0.5);
                    epsrad = 0.01*Math.Pow(10, -(2*math.randomreal()));
                    xtol = 15.0*epsrad;
                    minns.minnscreate(n, x0, state);
                    minns.minnssetalgoags(state, 0.1, 0.0);
                    minns.minnssetcond(state, epsrad, 0);
                    minns.minnssetbc(state, bndl, bndu);
                    minns.minnssetscale(state, s);
                    if( requirexrep )
                    {
                        minns.minnssetxrep(state, true);
                    }
                    werexreports = false;
                    repferr = 0.0;
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                                state.j[0,i] = d[i]*Math.Sign(state.x[i]-xc[i]);
                            }
                            continue;
                        }
                        if( state.xupdated )
                        {
                            if( !werexreports )
                            {
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    xrfirst[i_] = state.x[i_];
                                }
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                xrlast[i_] = state.x[i_];
                            }
                            werexreports = true;
                            v = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                v = v+d[i]*Math.Abs(state.x[i]-xc[i]);
                            }
                            repferr = Math.Max(repferr, Math.Abs(v-state.f));
                            for(i=0; i<=n-1; i++)
                            {
                                apserv.seterrorflag(ref primaryerrors, (double)(state.x[i])<(double)(bndl[i]));
                                apserv.seterrorflag(ref primaryerrors, (double)(state.x[i])>(double)(bndu[i]));
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    apserv.seterrorflag(ref othererrors, werexreports && !requirexrep);
                    apserv.seterrorflag(ref othererrors, requirexrep && !werexreports);
                    apserv.seterrorflag(ref othererrors, (double)(repferr)>(double)(10000*math.machineepsilon));
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref primaryerrors, !math.isfinite(x1[i]) || (double)(Math.Abs(x1[i]-apserv.boundval(xc[i], bndl[i], bndu[i]))/s[i])>(double)(xtol));
                        apserv.seterrorflag(ref primaryerrors, (double)(x1[i])<(double)(bndl[i]));
                        apserv.seterrorflag(ref primaryerrors, (double)(x1[i])>(double)(bndu[i]));
                        if( requirexrep )
                        {
                            apserv.seterrorflag(ref othererrors, !math.isfinite(xrfirst[i]) || (double)(Math.Abs(apserv.boundval(x0[i], bndl[i], bndu[i])-xrfirst[i]))>(double)(100*math.machineepsilon));
                            apserv.seterrorflag(ref othererrors, !math.isfinite(xrlast[i]) || (double)(Math.Abs(x1[i]-xrlast[i]))>(double)(100*math.machineepsilon));
                        }
                    }
                }
            }
            
            //
            // A bit harder test:
            // * dense quadratic function (smooth), may be prone to different
            //   rounding-related issues
            // * non-negativity box constraints
            // * unit scale is used
            // * extreme stopping criteria (EpsX=1.0E-12)
            // * single pass for each problem size
            // * check that constrained gradient at solution is small
            //
            conda = 1.0E3;
            epsrad = 1.0E-12;
            for(n=1; n<=10; n++)
            {
                x0 = new double[n];
                bndl = new double[n];
                bndu = new double[n];
                b = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 1.0;
                    b[i] = math.randomreal()-0.5;
                    bndl[i] = 0.0;
                    bndu[i] = Double.PositiveInfinity;
                }
                matgen.spdmatrixrndcond(n, conda, ref a);
                minns.minnscreate(n, x0, state);
                minns.minnssetalgoags(state, 0.1, 0.0);
                minns.minnssetcond(state, epsrad, 0);
                minns.minnssetbc(state, bndl, bndu);
                while( minns.minnsiteration(state) )
                {
                    if( state.needfij )
                    {
                        state.fi[0] = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.j[0,i] = 0.0;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            state.fi[0] = state.fi[0]+b[i]*state.x[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.fi[0] = state.fi[0]+0.5*state.x[i]*a[i,j]*state.x[j];
                            }
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            state.j[0,i] = state.j[0,i]+b[i];
                            for(j=0; j<=n-1; j++)
                            {
                                state.j[0,i] = state.j[0,i]+a[i,j]*state.x[j];
                            }
                        }
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minns.minnsresults(state, ref x1, rep);
                apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                if( primaryerrors || othererrors )
                {
                    return;
                }
                gnorm = 0.0;
                for(i=0; i<=n-1; i++)
                {
                    v = b[i];
                    for(j=0; j<=n-1; j++)
                    {
                        v = v+a[i,j]*x1[j];
                    }
                    if( (double)(x1[i])==(double)(bndl[i]) && (double)(v)>(double)(0) )
                    {
                        v = 0;
                    }
                    if( (double)(x1[i])==(double)(bndu[i]) && (double)(v)<(double)(0) )
                    {
                        v = 0;
                    }
                    gnorm = gnorm+math.sqr(v);
                    apserv.seterrorflag(ref primaryerrors, (double)(x1[i])<(double)(bndl[i]));
                    apserv.seterrorflag(ref primaryerrors, (double)(x1[i])>(double)(bndu[i]));
                }
                gnorm = Math.Sqrt(gnorm);
                apserv.seterrorflag(ref primaryerrors, (double)(gnorm)>(double)(1.0E-5));
            }
            
            //
            // Test on HIGHLY nonconvex bound constrained problem.
            // Algorithm should be able to stop.
            //
            // NOTE: because algorithm can be attracted to saddle points,
            //       x[i] may be -1, +1 or approximately zero.
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    x0 = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = math.randomreal()-0.5;
                        bndl[i] = -1.0;
                        bndu[i] = 1.0;
                    }
                    epsrad = 0.0001;
                    xtol = 15.0*epsrad;
                    minns.minnscreate(n, x0, state);
                    minns.minnssetalgoags(state, 0.1, 0.0);
                    minns.minnssetcond(state, epsrad, 0);
                    minns.minnssetbc(state, bndl, bndu);
                    v = -1000.0;
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                v0 = Math.Abs(state.x[i]);
                                v1 = Math.Sign(state.x[i]);
                                state.fi[0] = state.fi[0]+v*(v0+v0*v0);
                                state.j[0,i] = v*(v1+2*v0*v1);
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    for(i=0; i<=n-1; i++)
                    {
                        v = Math.Abs(x1[i]);
                        apserv.seterrorflag(ref primaryerrors, !math.isfinite(x1[i]));
                        apserv.seterrorflag(ref primaryerrors, (double)(v)!=(double)(1.0) && (double)(v)>(double)(xtol));
                    }
                }
            }
            
            //
            // Test numerical differentiation:
            // * test that problem is successfully solved
            // * test that correct function value is reported
            // * test that all iterates are within bound-constrained area
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    x0 = new double[n];
                    xc = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    d = new double[n];
                    s = new double[n];
                    xrlast = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 10*(2*math.randomreal()-1);
                        xc[i] = 2*math.randomreal()-1;
                        d[i] = Math.Pow(10, 2*(2*math.randomreal()-1));
                        s[i] = Math.Pow(10, 2*(2*math.randomreal()-1));
                        bndl[i] = Double.NegativeInfinity;
                        bndu[i] = Double.PositiveInfinity;
                        k = math.randominteger(5);
                        if( k==1 )
                        {
                            bndl[i] = 2*math.randomreal()-1;
                        }
                        if( k==2 )
                        {
                            bndu[i] = 2*math.randomreal()-1;
                        }
                        if( k==3 )
                        {
                            v0 = 2*math.randomreal()-1;
                            v1 = 2*math.randomreal()-1;
                            bndl[i] = Math.Min(v0, v1);
                            bndu[i] = Math.Max(v0, v1);
                        }
                        if( k==4 )
                        {
                            bndl[i] = 2*math.randomreal()-1;
                            bndu[i] = bndl[i];
                        }
                    }
                    epsrad = 0.01*Math.Pow(10, -(2*math.randomreal()));
                    xtol = 15.0*epsrad;
                    minns.minnscreatef(n, x0, epsrad/100, state);
                    minns.minnssetalgoags(state, 0.1, 0.0);
                    minns.minnssetcond(state, epsrad, 0);
                    minns.minnssetscale(state, s);
                    minns.minnssetbc(state, bndl, bndu);
                    minns.minnssetxrep(state, true);
                    repferr = 0.0;
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfi )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                                apserv.seterrorflag(ref primaryerrors, (double)(state.x[i])<(double)(bndl[i]));
                                apserv.seterrorflag(ref primaryerrors, (double)(state.x[i])>(double)(bndu[i]));
                            }
                            continue;
                        }
                        if( state.xupdated )
                        {
                            v = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                v = v+d[i]*Math.Abs(state.x[i]-xc[i]);
                                apserv.seterrorflag(ref primaryerrors, (double)(state.x[i])<(double)(bndl[i]));
                                apserv.seterrorflag(ref primaryerrors, (double)(state.x[i])>(double)(bndu[i]));
                            }
                            repferr = Math.Max(repferr, Math.Abs(v-state.f));
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    apserv.seterrorflag(ref othererrors, (double)(repferr)>(double)(10000*math.machineepsilon));
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref primaryerrors, !math.isfinite(x1[i]) || (double)(Math.Abs(x1[i]-apserv.boundval(xc[i], bndl[i], bndu[i]))/s[i])>(double)(xtol));
                    }
                }
            }
            
            //
            // Test scaling: we perform several steps on unit-scale problem,
            // then we perform same amount of steps on re-scaled problem,
            // starting from same point (but scaled according to chosen scale).
            //
            // Correctly written optimizer should perform essentially same steps
            // (up to scale) on both problems. At least, it holds within first
            // several steps, before rounding errors start to accumulate.
            //
            // NOTE: we also check that correctly scaled points are reported.
            //       And, as side effect, we check MinNSRestartFrom().
            //
            // NOTE: we use very low scale and diagonal coefficients in order
            //       to have well-conditioned system. We test correctness of 
            //       formulae here, not robustness of algorithm.
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    x0 = new double[n];
                    xc = new double[n];
                    x0s = new double[n];
                    d = new double[n];
                    s = new double[n];
                    xrlast = new double[n];
                    bndl = new double[n];
                    bndu = new double[n];
                    scaledbndl = new double[n];
                    scaledbndu = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        s[i] = Math.Pow(10, math.randomreal()-0.5);
                        d[i] = Math.Pow(10, math.randomreal()-0.5);
                        x0[i] = 2*math.randomreal()-1;
                        xc[i] = 2*math.randomreal()-1;
                        x0s[i] = x0[i]*s[i];
                        bndl[i] = Double.NegativeInfinity;
                        bndu[i] = Double.PositiveInfinity;
                        k = math.randominteger(5);
                        if( k==1 )
                        {
                            bndl[i] = 2*math.randomreal()-1;
                        }
                        if( k==2 )
                        {
                            bndu[i] = 2*math.randomreal()-1;
                        }
                        if( k==3 )
                        {
                            v0 = 2*math.randomreal()-1;
                            v1 = 2*math.randomreal()-1;
                            bndl[i] = Math.Min(v0, v1);
                            bndu[i] = Math.Max(v0, v1);
                        }
                        if( k==4 )
                        {
                            bndl[i] = 2*math.randomreal()-1;
                            bndu[i] = bndl[i];
                        }
                        scaledbndl[i] = bndl[i]*s[i];
                        scaledbndu[i] = bndu[i]*s[i];
                    }
                    minns.minnscreate(n, x0, state);
                    minns.minnssetalgoags(state, 0.01, 0.0);
                    minns.minnssetcond(state, 0.0, scalingtestcnt);
                    minns.minnssetbc(state, bndl, bndu);
                    minns.minnssetxrep(state, false);
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                                state.j[0,i] = d[i]*Math.Sign(state.x[i]-xc[i]);
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    minns.minnssetscale(state, s);
                    minns.minnssetbc(state, scaledbndl, scaledbndu);
                    minns.minnsrestartfrom(state, x0s);
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]/s[i]-xc[i]);
                                state.j[0,i] = d[i]*Math.Sign(state.x[i]/s[i]-xc[i])/s[i];
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1s, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref primaryerrors, (!math.isfinite(x1[i]) || !math.isfinite(x1s[i])) || (double)(Math.Abs(x1[i]-x1s[i]/s[i]))>(double)(scalingtesttol));
                    }
                }
            }
        }


        /*************************************************************************
        Linearly constrained test
        *************************************************************************/
        private static void testlc(ref bool primaryerrors,
            ref bool othererrors)
        {
            int n = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int nc = 0;
            double[] x0 = new double[0];
            double[] x0s = new double[0];
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[] x1s = new double[0];
            double[] d = new double[0];
            double[] xc = new double[0];
            double[] s = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[,] c = new double[0,0];
            double[,] scaledc = new double[0,0];
            int[] ct = new int[0];
            double[] scaledbndl = new double[0];
            double[] scaledbndu = new double[0];
            double[] xrfirst = new double[0];
            double[] xrlast = new double[0];
            minns.minnsstate state = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();
            double v = 0;
            double v0 = 0;
            double v1 = 0;
            double vv = 0;
            double flast0 = 0;
            double flast1 = 0;
            int pass = 0;
            int passcount = 0;
            double epsrad = 0;
            double repferr = 0;
            double xtol = 0;
            double ftol = 0;
            double rho = 0;
            int i_ = 0;

            passcount = 10;
            for(pass=1; pass<=10; pass++)
            {
                for(n=1; n<=5; n++)
                {
                    
                    //
                    // First test:
                    // * smooth problem
                    // * subject to random linear constraints
                    // * with non-unit scale
                    //
                    // We:
                    // * compare function value at constrained solution with function
                    //   value for penalized unconstrained problem. We do not compare
                    //   actual X-values returned, because they are highly unstable -
                    //   function values at minimum show better stability.
                    // * check that correct function values are reported
                    //
                    x0 = new double[n];
                    xc = new double[n];
                    d = new double[n];
                    s = new double[n];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = 10*(2*math.randomreal()-1);
                        xc[i] = 2*math.randomreal()-1;
                        d[i] = 1+math.randomreal();
                        s[i] = 1+math.randomreal();
                    }
                    nc = math.randominteger((n+1)/2);
                    if( nc>0 )
                    {
                        c = new double[nc, n+1];
                        ct = new int[nc];
                        for(i=0; i<=nc-1; i++)
                        {
                            ct[i] = math.randominteger(3)-1;
                            for(j=0; j<=n; j++)
                            {
                                c[i,j] = math.randomreal()-0.5;
                            }
                        }
                    }
                    epsrad = 0.00001;
                    ftol = 0.01;
                    minns.minnscreate(n, x0, state);
                    minns.minnssetalgoags(state, 0.1, 0.0);
                    minns.minnssetcond(state, epsrad, 0);
                    minns.minnssetscale(state, s);
                    minns.minnssetxrep(state, true);
                    minns.minnssetlc(state, c, ct, nc);
                    repferr = 0.0;
                    flast0 = Double.NaN;
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*math.sqr(state.x[i]-xc[i]);
                                state.j[0,i] = d[i]*(2*(state.x[i]-xc[i]));
                            }
                            continue;
                        }
                        if( state.xupdated )
                        {
                            flast0 = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                flast0 = flast0+d[i]*math.sqr(state.x[i]-xc[i]);
                            }
                            repferr = Math.Max(repferr, Math.Abs(flast0-state.f));
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    apserv.seterrorflag(ref primaryerrors, !math.isfinite(flast0));
                    apserv.seterrorflag(ref othererrors, (double)(repferr)>(double)(10000*math.machineepsilon));
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    minns.minnssetlc(state, c, ct, 0);
                    minns.minnsrestartfrom(state, x0);
                    rho = 1000.0;
                    repferr = 0.0;
                    flast1 = Double.NaN;
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*math.sqr(state.x[i]-xc[i]);
                                state.j[0,i] = d[i]*(2*(state.x[i]-xc[i]));
                            }
                            for(i=0; i<=nc-1; i++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += state.x[i_]*c[i,i_];
                                }
                                v = v-c[i,n];
                                vv = 0.0;
                                if( ct[i]<0 )
                                {
                                    vv = Math.Sign(Math.Max(v, 0.0));
                                    v = Math.Max(v, 0.0);
                                }
                                if( ct[i]==0 )
                                {
                                    vv = Math.Sign(v);
                                    v = Math.Abs(v);
                                }
                                if( ct[i]>0 )
                                {
                                    vv = -Math.Sign(Math.Max(-v, 0.0));
                                    v = Math.Max(-v, 0.0);
                                }
                                state.fi[0] = state.fi[0]+rho*v;
                                for(j=0; j<=n-1; j++)
                                {
                                    state.j[0,j] = state.j[0,j]+rho*vv*c[i,j];
                                }
                            }
                            continue;
                        }
                        if( state.xupdated )
                        {
                            flast1 = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                flast1 = flast1+d[i]*math.sqr(state.x[i]-xc[i]);
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x2, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    apserv.seterrorflag(ref primaryerrors, !math.isfinite(flast1));
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    apserv.seterrorflag(ref primaryerrors, (double)(Math.Abs(flast0-flast1))>(double)(ftol));
                    
                    //
                    // Test on HIGHLY nonconvex linearly constrained problem.
                    // Algorithm should be able to stop at the bounds.
                    //
                    x0 = new double[n];
                    c = new double[2*n, n+1];
                    ct = new int[2*n];
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = math.randomreal()-0.5;
                        for(j=0; j<=n-1; j++)
                        {
                            c[2*i+0,j] = 0.0;
                            c[2*i+1,j] = 0.0;
                        }
                        c[2*i+0,i] = 1.0;
                        c[2*i+0,n] = -1.0;
                        ct[2*i+0] = 1;
                        c[2*i+1,i] = 1.0;
                        c[2*i+1,n] = 1.0;
                        ct[2*i+1] = -1;
                    }
                    epsrad = 0.0001;
                    xtol = 15.0*epsrad;
                    minns.minnscreate(n, x0, state);
                    minns.minnssetalgoags(state, 0.1, 0.0);
                    minns.minnssetcond(state, epsrad, 0);
                    minns.minnssetlc(state, c, ct, 2*n);
                    v = -1000.0;
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                v0 = Math.Abs(state.x[i]);
                                v1 = Math.Sign(state.x[i]);
                                state.fi[0] = state.fi[0]+v*(v0+v0*v0);
                                state.j[0,i] = v*(v1+2*v0*v1);
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref primaryerrors, !math.isfinite(x1[i]));
                        apserv.seterrorflag(ref primaryerrors, ((double)(Math.Abs(x1[i]-1))>(double)(xtol) && (double)(Math.Abs(x1[i]))>(double)(xtol)) && (double)(Math.Abs(x1[i]+1))>(double)(xtol));
                    }
                    
                    //
                    // Test scaling: we perform several steps on unit-scale problem,
                    // then we perform same amount of steps on re-scaled problem,
                    // starting from same point (but scaled according to chosen scale).
                    //
                    // Correctly written optimizer should perform essentially same steps
                    // (up to scale) on both problems. At least, it holds within first
                    // several steps, before rounding errors start to accumulate.
                    //
                    // NOTE: we also check that correctly scaled points are reported.
                    //       And, as side effect, we check MinNSRestartFrom().
                    //
                    // NOTE: we use moderate scale and diagonal coefficients in order
                    //       to have well-conditioned system. We test correctness of 
                    //       formulae here, not robustness of algorithm.
                    //
                    x0 = new double[n];
                    xc = new double[n];
                    x0s = new double[n];
                    d = new double[n];
                    s = new double[n];
                    xrlast = new double[n];
                    c = new double[2*n, n+1];
                    scaledc = new double[2*n, n+1];
                    ct = new int[2*n];
                    for(i=0; i<=2*n-1; i++)
                    {
                        ct[i] = 0;
                        for(j=0; j<=n; j++)
                        {
                            c[i,j] = 0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        s[i] = Math.Pow(10, 2*math.randomreal()-1);
                        d[i] = Math.Pow(10, 2*math.randomreal()-1);
                        x0[i] = 2*math.randomreal()-1;
                        xc[i] = 2*math.randomreal()-1;
                        x0s[i] = x0[i]*s[i];
                        k = math.randominteger(5);
                        if( k==1 )
                        {
                            c[2*i+0,i] = 1.0;
                            c[2*i+0,n] = 2*math.randomreal()-1;
                            ct[2*i+0] = 1;
                        }
                        if( k==2 )
                        {
                            c[2*i+0,i] = 1.0;
                            c[2*i+0,n] = 2*math.randomreal()-1;
                            ct[2*i+0] = -1;
                        }
                        if( k==3 )
                        {
                            v0 = 2*math.randomreal()-1;
                            v1 = 2*math.randomreal()-1;
                            c[2*i+0,i] = 1.0;
                            c[2*i+0,n] = Math.Min(v0, v1);
                            c[2*i+1,i] = 1.0;
                            c[2*i+1,n] = Math.Max(v0, v1);
                            ct[2*i+0] = 1;
                            ct[2*i+1] = -1;
                        }
                        if( k==4 )
                        {
                            c[2*i+0,i] = 1.0;
                            c[2*i+0,n] = 2*math.randomreal()-1;
                            ct[2*i+0] = 0;
                        }
                    }
                    for(i=0; i<=2*n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            scaledc[i,j] = c[i,j]/s[j];
                        }
                        scaledc[i,n] = c[i,n];
                    }
                    minns.minnscreate(n, x0, state);
                    minns.minnssetalgoags(state, 0.1, 0.0);
                    minns.minnssetcond(state, 0.0, scalingtestcnt);
                    minns.minnssetlc(state, c, ct, 2*n);
                    minns.minnssetxrep(state, false);
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                                state.j[0,i] = d[i]*Math.Sign(state.x[i]-xc[i]);
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    minns.minnssetscale(state, s);
                    minns.minnssetlc(state, scaledc, ct, 2*n);
                    minns.minnsrestartfrom(state, x0s);
                    while( minns.minnsiteration(state) )
                    {
                        if( state.needfij )
                        {
                            state.fi[0] = 0.0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]/s[i]-xc[i]);
                                state.j[0,i] = d[i]*Math.Sign(state.x[i]/s[i]-xc[i])/s[i];
                            }
                            continue;
                        }
                        alglib.ap.assert(false);
                    }
                    minns.minnsresults(state, ref x1s, rep);
                    apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                    if( primaryerrors || othererrors )
                    {
                        return;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        apserv.seterrorflag(ref primaryerrors, (!math.isfinite(x1[i]) || !math.isfinite(x1s[i])) || (double)(Math.Abs(x1[i]-x1s[i]/s[i]))>(double)(scalingtesttol));
                    }
                }
            }
        }


        /*************************************************************************
        Nonlinearly constrained test
        *************************************************************************/
        private static void testnlc(ref bool primaryerrors,
            ref bool othererrors)
        {
            int n = 0;
            int i = 0;
            int j = 0;
            int nc = 0;
            int nec = 0;
            double[] x0 = new double[0];
            double[] x0s = new double[0];
            double[] x1 = new double[0];
            double[] x2 = new double[0];
            double[] x1s = new double[0];
            double[] d = new double[0];
            double[] xc = new double[0];
            double[] s = new double[0];
            double[] bndl = new double[0];
            double[] bndu = new double[0];
            double[] b = new double[0];
            double[] r = new double[0];
            double[,] c = new double[0,0];
            double[,] scaledc = new double[0,0];
            int[] ct = new int[0];
            double[] scaledbndl = new double[0];
            double[] scaledbndu = new double[0];
            double[] xrfirst = new double[0];
            double[] xrlast = new double[0];
            minns.minnsstate state = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();
            double v = 0;
            int pass = 0;
            int passcount = 0;
            double epsrad = 0;
            double xtol = 0;
            double rho = 0;
            int maxn = 0;
            double diffstep = 0;

            passcount = 10;
            maxn = 5;
            rho = 100.0;
            
            //
            // First test:
            // * simple problem
            // * subject to random nonlinear constraints of form r[i]*x[i] OPERATION 0.0,
            //   where OPERATION is <= or =
            // * with non-unit scale
            //
            // We:
            // * compare numerical solution with analytic one, which can be
            //   easily calculated
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    for(nc=1; nc<=n; nc++)
                    {
                        for(nec=0; nec<=nc; nec++)
                        {
                            x0 = new double[n];
                            xc = new double[n];
                            d = new double[n];
                            r = new double[n];
                            s = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                x0[i] = 2*math.randomreal()-1;
                                xc[i] = 2*math.randomreal()-1;
                                d[i] = Math.Pow(10, math.randomreal()-0.5);
                                s[i] = Math.Pow(10, math.randomreal()-0.5);
                                r[i] = (2*math.randominteger(2)-1)*(0.1+math.randomreal());
                            }
                            epsrad = 0.001;
                            xtol = 0.01;
                            minns.minnscreate(n, x0, state);
                            minns.minnssetalgoags(state, 0.1, rho);
                            minns.minnssetcond(state, epsrad, 0);
                            minns.minnssetscale(state, s);
                            minns.minnssetnlc(state, nec, nc-nec);
                            while( minns.minnsiteration(state) )
                            {
                                if( state.needfij )
                                {
                                    state.fi[0] = 0.0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                                        state.j[0,i] = d[i]*Math.Sign(state.x[i]-xc[i]);
                                    }
                                    for(i=1; i<=nc; i++)
                                    {
                                        state.fi[i] = state.x[i-1]*r[i-1];
                                        for(j=0; j<=n-1; j++)
                                        {
                                            state.j[i,j] = 0.0;
                                        }
                                        state.j[i,i-1] = r[i-1];
                                    }
                                    continue;
                                }
                                alglib.ap.assert(false);
                            }
                            minns.minnsresults(state, ref x1, rep);
                            apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                            if( primaryerrors || othererrors )
                            {
                                return;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                v = xc[i];
                                if( i<nec )
                                {
                                    v = 0.0;
                                }
                                if( i>=nec && i<nc )
                                {
                                    if( (double)(r[i])>(double)(0.0) )
                                    {
                                        v = Math.Min(v, 0.0);
                                    }
                                    if( (double)(r[i])<(double)(0.0) )
                                    {
                                        v = Math.Max(v, 0.0);
                                    }
                                }
                                apserv.seterrorflag(ref primaryerrors, !math.isfinite(x1[i]));
                                apserv.seterrorflag(ref primaryerrors, (double)(Math.Abs(x1[i]-v))>(double)(xtol*s[i]));
                            }
                        }
                    }
                }
            }
            
            //
            // Numerical differentiation test.
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    for(nc=1; nc<=n; nc++)
                    {
                        for(nec=0; nec<=nc; nec++)
                        {
                            x0 = new double[n];
                            xc = new double[n];
                            d = new double[n];
                            r = new double[n];
                            s = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                x0[i] = 2*math.randomreal()-1;
                                xc[i] = 2*math.randomreal()-1;
                                d[i] = Math.Pow(10, math.randomreal()-0.5);
                                s[i] = Math.Pow(10, math.randomreal()-0.5);
                                r[i] = (2*math.randominteger(2)-1)*(0.1+math.randomreal());
                            }
                            epsrad = 0.001;
                            xtol = 0.01;
                            diffstep = 0.001;
                            minns.minnscreatef(n, x0, diffstep, state);
                            minns.minnssetalgoags(state, 0.1, rho);
                            minns.minnssetcond(state, epsrad, 0);
                            minns.minnssetscale(state, s);
                            minns.minnssetnlc(state, nec, nc-nec);
                            while( minns.minnsiteration(state) )
                            {
                                if( state.needfi )
                                {
                                    state.fi[0] = 0.0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                                    }
                                    for(i=1; i<=nc; i++)
                                    {
                                        state.fi[i] = state.x[i-1]*r[i-1];
                                    }
                                    continue;
                                }
                                alglib.ap.assert(false);
                            }
                            minns.minnsresults(state, ref x1, rep);
                            apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                            if( primaryerrors || othererrors )
                            {
                                return;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                v = xc[i];
                                if( i<nec )
                                {
                                    v = 0.0;
                                }
                                if( i>=nec && i<nc )
                                {
                                    if( (double)(r[i])>(double)(0.0) )
                                    {
                                        v = Math.Min(v, 0.0);
                                    }
                                    if( (double)(r[i])<(double)(0.0) )
                                    {
                                        v = Math.Max(v, 0.0);
                                    }
                                }
                                apserv.seterrorflag(ref primaryerrors, !math.isfinite(x1[i]));
                                apserv.seterrorflag(ref primaryerrors, (double)(Math.Abs(x1[i]-v))>(double)(xtol*s[i]));
                            }
                        }
                    }
                }
            }
            
            //
            // Test scaling: we perform several steps on unit-scale problem,
            // then we perform same amount of steps on re-scaled problem,
            // starting from same point (but scaled according to chosen scale).
            //
            // Correctly written optimizer should perform essentially same steps
            // (up to scale) on both problems. At least, it holds within first
            // several steps, before rounding errors start to accumulate.
            //
            // NOTE: we use moderate scale and diagonal coefficients in order
            //       to have well-conditioned system. We test correctness of 
            //       formulae here, not robustness of algorithm.
            //
            // NOTE: we have to use very relaxed thresholds for this test because
            //       each AGS iteration involves solution of several nested QP
            //       subproblems, so rounding errors accumulate too quickly.
            //       It does not mean that algorithm is inexact, just that two
            //       almost identical optimization sessions diverge too fast to
            //       compare them.
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    for(nc=1; nc<=n; nc++)
                    {
                        for(nec=0; nec<=nc; nec++)
                        {
                            x0 = new double[n];
                            x0s = new double[n];
                            xc = new double[n];
                            d = new double[n];
                            r = new double[n];
                            s = new double[n];
                            for(i=0; i<=n-1; i++)
                            {
                                x0[i] = 2*math.randomreal()-1;
                                xc[i] = 2*math.randomreal()-1;
                                d[i] = Math.Pow(10, math.randomreal()-0.5);
                                s[i] = Math.Pow(10, math.randomreal()-0.5);
                                r[i] = (2*math.randominteger(2)-1)*(0.1+math.randomreal());
                                x0s[i] = x0[i]*s[i];
                            }
                            minns.minnscreate(n, x0, state);
                            minns.minnssetalgoags(state, 0.1, 1.0);
                            minns.minnssetcond(state, 0.0, scalingtestcnt);
                            minns.minnssetnlc(state, nec, nc-nec);
                            while( minns.minnsiteration(state) )
                            {
                                if( state.needfij )
                                {
                                    state.fi[0] = 0.0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]-xc[i]);
                                        state.j[0,i] = d[i]*Math.Sign(state.x[i]-xc[i]);
                                    }
                                    for(i=1; i<=nc; i++)
                                    {
                                        state.fi[i] = state.x[i-1]*r[i-1];
                                        for(j=0; j<=n-1; j++)
                                        {
                                            state.j[i,j] = 0.0;
                                        }
                                        state.j[i,i-1] = r[i-1];
                                    }
                                    continue;
                                }
                                alglib.ap.assert(false);
                            }
                            minns.minnsresults(state, ref x1, rep);
                            apserv.seterrorflag(ref primaryerrors, rep.terminationtype<=0);
                            if( primaryerrors || othererrors )
                            {
                                return;
                            }
                            minns.minnssetscale(state, s);
                            minns.minnsrestartfrom(state, x0s);
                            while( minns.minnsiteration(state) )
                            {
                                if( state.needfij )
                                {
                                    state.fi[0] = 0.0;
                                    for(i=0; i<=n-1; i++)
                                    {
                                        state.fi[0] = state.fi[0]+d[i]*Math.Abs(state.x[i]/s[i]-xc[i]);
                                        state.j[0,i] = d[i]*Math.Sign(state.x[i]/s[i]-xc[i])/s[i];
                                    }
                                    for(i=1; i<=nc; i++)
                                    {
                                        state.fi[i] = state.x[i-1]/s[i-1]*r[i-1];
                                        for(j=0; j<=n-1; j++)
                                        {
                                            state.j[i,j] = 0.0;
                                        }
                                        state.j[i,i-1] = r[i-1]/s[i-1];
                                    }
                                    continue;
                                }
                                alglib.ap.assert(false);
                            }
                            minns.minnsresults(state, ref x1s, rep);
                            for(i=0; i<=n-1; i++)
                            {
                                apserv.seterrorflag(ref primaryerrors, (!math.isfinite(x1[i]) || !math.isfinite(x1s[i])) || (double)(Math.Abs(x1[i]-x1s[i]/s[i]))>(double)(scalingtesttol));
                            }
                        }
                    }
                }
            }
        }


        /*************************************************************************
        Special tests
        *************************************************************************/
        private static void testother(ref bool othererrors)
        {
            int n = 0;
            int k = 0;
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            minns.minnsstate state = new minns.minnsstate();
            minns.minnsreport rep = new minns.minnsreport();
            double v0 = 0;
            double v1 = 0;
            double v = 0;
            double xtol = 0;
            double epsrad = 0;
            double rho = 0;

            
            //
            // First test:
            // * 2D problem, minimization of F(x0,x1)=x1
            // * two constraints, with wildly different magnitudes
            //   * G0(x0,x1)=Rho*Abs(x0+x1)=0
            //   * H0(x0,x1)=Rho*(x0-1000)<=0
            //   where Rho is some large value
            //
            // Optimizer should be able to deal with situation when
            // magnitude of Jacobian components is so wildly different.
            //
            n = 2;
            x0 = new double[n];
            x0[0] = 0.1;
            x0[1] = 1.0;
            epsrad = 0.00001;
            xtol = 0.01;
            for(k=0; k<=6; k++)
            {
                rho = Math.Pow(10, k);
                minns.minnscreate(n, x0, state);
                minns.minnssetalgoags(state, 0.1, 10.0);
                minns.minnssetcond(state, epsrad, 0);
                minns.minnssetnlc(state, 1, 1);
                v = 1000.0;
                while( minns.minnsiteration(state) )
                {
                    if( state.needfij )
                    {
                        v0 = state.x[0];
                        v1 = state.x[1];
                        state.fi[0] = v1;
                        state.j[0,0] = 0.0;
                        state.j[0,1] = 1.0;
                        state.fi[1] = rho*Math.Abs(v0+v1);
                        state.j[1,0] = rho*Math.Sign(v0+v1);
                        state.j[1,1] = rho*Math.Sign(v0+v1);
                        state.fi[2] = rho*(v0-v);
                        state.j[2,0] = rho;
                        state.j[2,1] = 0.0;
                        continue;
                    }
                    alglib.ap.assert(false);
                }
                minns.minnsresults(state, ref x1, rep);
                apserv.seterrorflag(ref othererrors, rep.terminationtype<=0);
                apserv.seterrorflag(ref othererrors, !math.isfinite(x1[0]));
                apserv.seterrorflag(ref othererrors, !math.isfinite(x1[1]));
                apserv.seterrorflag(ref othererrors, (double)(Math.Abs(x1[0]-v))>(double)(xtol));
                apserv.seterrorflag(ref othererrors, (double)(Math.Abs(x1[1]+v))>(double)(xtol));
            }
        }


    }
    public class testlincgunit
    {
        public const double e0 = 1.0E-6;
        public const double maxcond = 30;


        public static bool testlincg(bool silent)
        {
            bool result = new bool();
            bool basictestxerrors = new bool();
            bool basictestiterserr = new bool();
            bool complexreserrors = new bool();
            bool complexerrors = new bool();
            bool rcorrectness = new bool();
            bool krylovsubspaceerr = new bool();
            bool sparseerrors = new bool();
            bool preconderrors = new bool();
            bool waserrors = new bool();

            basictestxerrors = basictestx(true);
            basictestiterserr = basictestiters(true);
            complexreserrors = complexres(true);
            complexerrors = complextest(true);
            rcorrectness = testrcorrectness(true);
            krylovsubspaceerr = krylovsubspacetest(true);
            sparseerrors = sparsetest(true);
            preconderrors = precondtest(true);
            
            //
            // report
            //
            waserrors = ((((((basictestxerrors || complexreserrors) || complexerrors) || rcorrectness) || basictestiterserr) || krylovsubspaceerr) || sparseerrors) || preconderrors;
            if( !silent )
            {
                System.Console.Write("TESTING LinCG");
                System.Console.WriteLine();
                System.Console.Write("BasicTestX:                                   ");
                if( basictestxerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("BasicTestIters:                               ");
                if( basictestiterserr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("ComplexResTest:                               ");
                if( complexreserrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("ComplexTest:                                  ");
                if( complexerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("R2 correctness:                               ");
                if( rcorrectness )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("KrylovSubSpaceTest:                           ");
                if( krylovsubspaceerr )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("SparseTest:                                   ");
                if( sparseerrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                System.Console.Write("PrecondTest:                                  ");
                if( preconderrors )
                {
                    System.Console.Write("FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("OK");
                    System.Console.WriteLine();
                }
                
                //
                //was errors?
                //
                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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testlincg(bool silent)
        {
            return testlincg(silent);
        }


        /*************************************************************************
        Function for testing LinCGIteration function(custom option), which to solve
        Ax=b(here A is random positive definite matrix NxN, b is random vector). It
        uses  the  default  stopping criterion(RNorm<FEps=10^-6). If algorithm does
        more iterations than size  of  the problem, then  some errors are possible.
        The test verifies the following propirties:
            1. (A*pk,pm)=0 for any m<>k;
            2. (rk,rm)=0 for any m<>k;
            3. (rk,pm)=0 for any m<>k;

        INPUT:
            Silent   -   if true then function output report

          -- ALGLIB --
             Copyright 14.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool complextest(bool silent)
        {
            bool result = new bool();
            lincg.lincgstate state = new lincg.lincgstate();
            lincg.lincgreport rep = new lincg.lincgreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            int n = 0;
            double c = 0;
            double[] x0 = new double[0];
            double[] residual = new double[0];
            double normofresidual = 0;
            double sclr = 0;
            double na = 0;
            double nv0 = 0;
            double nv1 = 0;
            int sz = 0;
            double mx = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int l = 0;
            double tmp = 0;
            double[,] mtx = new double[0,0];
            double[,] mtp = new double[0,0];
            double[,] mtr = new double[0,0];
            double getrnorm = 0;
            int numofit = 0;

            mx = 100;
            n = 5;
            for(sz=1; sz<=n; sz++)
            {
                
                //
                // Generate:
                // * random A with norm NA (equal to 1.0),
                // * random right part B whose elements are uniformly distributed in [-MX,+MX]
                // * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
                //
                c = 15+15*math.randomreal();
                matgen.spdmatrixrndcond(sz, c, ref a);
                na = 1;
                b = new double[sz];
                for(i=0; i<=sz-1; i++)
                {
                    b[i] = mx*(2*math.randomreal()-1);
                }
                x0 = new double[sz];
                for(i=0; i<=sz-1; i++)
                {
                    x0[i] = mx*(2*math.randomreal()-1);
                }
                mtx = new double[sz+1, sz];
                
                //
                // Start optimization, record its progress for further analysis
                // NOTE: we set update frequency of R to 2 in order to test that R is updated correctly
                //
                lincg.lincgcreate(sz, state);
                lincg.lincgsetxrep(state, true);
                lincg.lincgsetb(state, b);
                lincg.lincgsetstartingpoint(state, x0);
                lincg.lincgsetcond(state, 0, sz);
                lincg.lincgsetrupdatefreq(state, 2);
                numofit = 0;
                getrnorm = math.maxrealnumber;
                while( lincg.lincgiteration(state) )
                {
                    if( state.needmv )
                    {
                        for(i=0; i<=sz-1; i++)
                        {
                            state.mv[i] = 0;
                            for(j=0; j<=sz-1; j++)
                            {
                                state.mv[i] = state.mv[i]+a[i,j]*state.x[j];
                            }
                        }
                    }
                    if( state.needvmv )
                    {
                        state.vmv = 0;
                        for(i=0; i<=sz-1; i++)
                        {
                            state.mv[i] = 0;
                            for(j=0; j<=sz-1; j++)
                            {
                                state.mv[i] = state.mv[i]+a[i,j]*state.x[j];
                            }
                            state.vmv = state.vmv+state.mv[i]*state.x[i];
                        }
                    }
                    if( state.needprec )
                    {
                        for(i=0; i<=sz-1; i++)
                        {
                            state.pv[i] = state.x[i];
                        }
                    }
                    if( state.xupdated )
                    {
                        
                        //
                        // Save current point to MtX, it will be used later for additional tests
                        //
                        if( numofit>=alglib.ap.rows(mtx) )
                        {
                            result = true;
                            return result;
                        }
                        for(i=0; i<=sz-1; i++)
                        {
                            mtx[numofit,i] = state.x[i];
                        }
                        getrnorm = state.r2;
                        numofit = numofit+1;
                    }
                }
                lincg.lincgresults(state, ref x0, rep);
                if( (double)(getrnorm)!=(double)(rep.r2) )
                {
                    if( !silent )
                    {
                        System.Console.Write("IterationsCount=");
                        System.Console.Write("{0,0:d}",rep.iterationscount);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("NMV=");
                        System.Console.Write("{0,0:d}",rep.nmv);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("TerminationType=");
                        System.Console.Write("{0,0:d}",rep.terminationtype);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("Size=");
                        System.Console.Write("{0,0:d}",sz);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("Cond=");
                        System.Console.Write("{0,0:F5}",c);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("ComplexTest::Fail::GetRNorm<>Rep.R2!(");
                        System.Console.Write("{0,0:E2}",getrnorm);
                        System.Console.Write("<>");
                        System.Console.Write("{0,0:E2}",rep.r2);
                        System.Console.Write(")");
                        System.Console.WriteLine();
                    }
                    result = true;
                    return result;
                }
                
                //
                // Calculate residual, check result
                //
                residual = new double[sz];
                for(i=0; i<=sz-1; i++)
                {
                    tmp = 0;
                    for(j=0; j<=sz-1; j++)
                    {
                        tmp = tmp+a[i,j]*x0[j];
                    }
                    residual[i] = b[i]-tmp;
                }
                normofresidual = 0;
                for(i=0; i<=sz-1; i++)
                {
                    if( (double)(Math.Abs(residual[i]))>(double)(e0) )
                    {
                        if( !silent )
                        {
                            System.Console.Write("IterationsCount=");
                            System.Console.Write("{0,0:d}",rep.iterationscount);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                            System.Console.Write("NMV=");
                            System.Console.Write("{0,0:d}",rep.nmv);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                            System.Console.Write("TerminationType=");
                            System.Console.Write("{0,0:d}",rep.terminationtype);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                            System.Console.Write("Size=");
                            System.Console.Write("{0,0:d}",sz);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                            System.Console.Write("Cond=");
                            System.Console.Write("{0,0:F5}",c);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                            System.Console.Write("ComplexTest::Fail::Discripancy[");
                            System.Console.Write("{0,0:d}",i);
                            System.Console.Write("]>E0!(");
                            System.Console.Write("{0,0:E2}",residual[i]);
                            System.Console.Write(">");
                            System.Console.Write("{0,0:E2}",e0);
                            System.Console.Write(")");
                            System.Console.WriteLine();
                        }
                        result = true;
                        return result;
                    }
                    normofresidual = normofresidual+residual[i]*residual[i];
                }
                if( (double)(Math.Abs(normofresidual-rep.r2))>(double)(e0) )
                {
                    if( !silent )
                    {
                        System.Console.Write("IterationsCount=");
                        System.Console.Write("{0,0:d}",rep.iterationscount);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("NMV=");
                        System.Console.Write("{0,0:d}",rep.nmv);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("TerminationType=");
                        System.Console.Write("{0,0:d}",rep.terminationtype);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("Size=");
                        System.Console.Write("{0,0:d}",sz);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("Cond=");
                        System.Console.Write("{0,0:F5}",c);
                        System.Console.Write(";");
                        System.Console.WriteLine();
                        System.Console.Write("ComplexTest::Fail::||NormOfResidual-Rep.R2||>E0!(");
                        System.Console.Write("{0,0:E2}",Math.Abs(normofresidual-rep.r2));
                        System.Console.Write(">");
                        System.Console.Write("{0,0:E2}",e0);
                        System.Console.Write(")");
                        System.Console.WriteLine();
                        System.Console.Write("NormOfResidual=");
                        System.Console.Write("{0,0:E2}",normofresidual);
                        System.Console.Write("; Rep.R2=");
                        System.Console.Write("{0,0:E2}",rep.r2);
                        System.Console.WriteLine();
                    }
                    result = true;
                    return result;
                }
                
                //
                // Check algorithm properties (conjugacy/orthogonality).
                // Here we use MtX which was filled during algorithm progress towards solution.
                //
                // NOTE: this test is skipped when algorithm converged in less than SZ iterations.
                //
                if( sz>1 && rep.iterationscount==sz )
                {
                    mtp = new double[sz, sz];
                    mtr = new double[sz, sz];
                    for(i=0; i<=sz-1; i++)
                    {
                        for(j=0; j<=sz-1; j++)
                        {
                            mtp[i,j] = mtx[i+1,j]-mtx[i,j];
                            tmp = 0;
                            for(k=0; k<=sz-1; k++)
                            {
                                tmp = tmp+a[j,k]*mtx[i,k];
                            }
                            mtr[i,j] = b[j]-tmp;
                        }
                    }
                    
                    //
                    //(Api,pj)=0?
                    //
                    sclr = 0;
                    nv0 = 0;
                    nv1 = 0;
                    for(i=0; i<=sz-1; i++)
                    {
                        for(j=0; j<=sz-1; j++)
                        {
                            if( i==j )
                            {
                                continue;
                            }
                            for(k=0; k<=sz-1; k++)
                            {
                                tmp = 0;
                                for(l=0; l<=sz-1; l++)
                                {
                                    tmp = tmp+a[k,l]*mtp[i,l];
                                }
                                sclr = sclr+tmp*mtp[j,k];
                                nv0 = nv0+mtp[i,k]*mtp[i,k];
                                nv1 = nv1+mtp[j,k]*mtp[j,k];
                            }
                            nv0 = Math.Sqrt(nv0);
                            nv1 = Math.Sqrt(nv1);
                            if( (double)(Math.Abs(sclr))>(double)(e0*na*nv0*nv1) )
                            {
                                if( !silent )
                                {
                                    System.Console.Write("IterationsCount=");
                                    System.Console.Write("{0,0:d}",rep.iterationscount);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("NMV=");
                                    System.Console.Write("{0,0:d}",rep.nmv);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("TerminationType=");
                                    System.Console.Write("{0,0:d}",rep.terminationtype);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("Size=");
                                    System.Console.Write("{0,0:d}",sz);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("Cond=");
                                    System.Console.Write("{0,0:F5}",c);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("ComplexTest::Fail::(Ap");
                                    System.Console.Write("{0,0:d}",i);
                                    System.Console.Write(",p");
                                    System.Console.Write("{0,0:d}",j);
                                    System.Console.Write(")!=0");
                                    System.Console.WriteLine();
                                    System.Console.Write("{{Sclr=");
                                    System.Console.Write("{0,0:F15}",sclr);
                                    System.Console.Write("; NA=");
                                    System.Console.Write("{0,0:F15}",na);
                                    System.Console.Write(" NV0=");
                                    System.Console.Write("{0,0:F15}",nv0);
                                    System.Console.Write(" NV1=");
                                    System.Console.Write("{0,0:F15}",nv1);
                                    System.Console.Write(";}}");
                                    System.Console.WriteLine();
                                }
                                result = true;
                                return result;
                            }
                        }
                    }
                    
                    //
                    //(ri,pj)=0?
                    //
                    for(i=1; i<=sz-1; i++)
                    {
                        for(j=0; j<=i-1; j++)
                        {
                            sclr = 0;
                            nv0 = 0;
                            nv1 = 0;
                            for(k=0; k<=sz-1; k++)
                            {
                                sclr = sclr+mtr[i,k]*mtp[j,k];
                                nv0 = nv0+mtr[i,k]*mtr[i,k];
                                nv1 = nv1+mtp[j,k]*mtp[j,k];
                            }
                            nv0 = Math.Sqrt(nv0);
                            nv1 = Math.Sqrt(nv1);
                            if( (double)(Math.Abs(sclr))>(double)(e0*nv0*nv1) )
                            {
                                if( !silent )
                                {
                                    System.Console.Write("IterationsCount=");
                                    System.Console.Write("{0,0:d}",rep.iterationscount);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("NMV=");
                                    System.Console.Write("{0,0:d}",rep.nmv);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("TerminationType=");
                                    System.Console.Write("{0,0:d}",rep.terminationtype);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("Size=");
                                    System.Console.Write("{0,0:d}",sz);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("Cond=");
                                    System.Console.Write("{0,0:F5}",c);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("ComplexTest::Fail::(r");
                                    System.Console.Write("{0,0:d}",i);
                                    System.Console.Write(",p");
                                    System.Console.Write("{0,0:d}",j);
                                    System.Console.Write(")!=0");
                                    System.Console.WriteLine();
                                    System.Console.Write("{{Sclr=");
                                    System.Console.Write("{0,0:F15}",sclr);
                                    System.Console.Write("; NV0=");
                                    System.Console.Write("{0,0:F15}",nv0);
                                    System.Console.Write(" NV1=");
                                    System.Console.Write("{0,0:F15}",nv1);
                                    System.Console.Write(";}}");
                                    System.Console.WriteLine();
                                }
                                result = true;
                                return result;
                            }
                        }
                    }
                    
                    //
                    //(ri,rj)=0?
                    //
                    for(i=0; i<=sz-1; i++)
                    {
                        for(j=i+1; j<=sz-1; j++)
                        {
                            sclr = 0;
                            nv0 = 0;
                            nv1 = 0;
                            for(k=0; k<=sz-1; k++)
                            {
                                sclr = sclr+mtr[i,k]*mtr[j,k];
                                nv0 = nv0+mtr[i,k]*mtr[i,k];
                                nv1 = nv1+mtr[j,k]*mtr[j,k];
                            }
                            nv0 = Math.Sqrt(nv0);
                            nv1 = Math.Sqrt(nv1);
                            if( (double)(Math.Abs(sclr))>(double)(e0*nv0*nv1) )
                            {
                                if( !silent )
                                {
                                    System.Console.Write("IterationsCount=");
                                    System.Console.Write("{0,0:d}",rep.iterationscount);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("NMV=");
                                    System.Console.Write("{0,0:d}",rep.nmv);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("TerminationType=");
                                    System.Console.Write("{0,0:d}",rep.terminationtype);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("Size=");
                                    System.Console.Write("{0,0:d}",sz);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("Cond=");
                                    System.Console.Write("{0,0:F5}",c);
                                    System.Console.Write(";");
                                    System.Console.WriteLine();
                                    System.Console.Write("ComplexTest::Fail::(rm,rk)!=0");
                                    System.Console.WriteLine();
                                    System.Console.Write("{{Sclr=");
                                    System.Console.Write("{0,0:F15}",sclr);
                                    System.Console.Write("; NV0=");
                                    System.Console.Write("{0,0:F15}",nv0);
                                    System.Console.Write(" NV1=");
                                    System.Console.Write("{0,0:F15}",nv1);
                                    System.Console.Write(";}}");
                                    System.Console.WriteLine();
                                }
                                result = true;
                                return result;
                            }
                        }
                    }
                }
            }
            if( !silent )
            {
                System.Console.Write("ComplexTest::Ok");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function prepare problem with a known solution 'Xs'(A*Xs-b=0). There
        b is A*Xs. After, function check algorithm result and 'Xs'.
        There used two stopping criterions:
            1. achieved the required precision(StCrit=0);
            2. execution of the required number of iterations(StCrit=1).

          -- ALGLIB --
             Copyright 14.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool complexres(bool silent)
        {
            bool result = new bool();
            lincg.lincgstate s = new lincg.lincgstate();
            lincg.lincgreport rep = new lincg.lincgreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double[] xs = new double[0];
            double[] x0 = new double[0];
            double err = 0;
            int n = 0;
            int sz = 0;
            double c = 0;
            int i = 0;
            int j = 0;
            int stcrit = 0;
            double mx = 0;
            double tmp = 0;
            double eps = 0;
            int xp = 0;
            int nxp = 0;

            sz = 5;
            mx = 100;
            nxp = 100;
            for(xp=0; xp<=nxp-1; xp++)
            {
                for(n=1; n<=sz; n++)
                {
                    for(stcrit=0; stcrit<=1; stcrit++)
                    {
                        
                        //
                        // Generate:
                        // * random A with norm NA (equal to 1.0),
                        // * random solution XS whose elements are uniformly distributed in [-MX,+MX]
                        // * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
                        // * B = A*Xs
                        //
                        c = (maxcond-1)*math.randomreal()+1;
                        matgen.spdmatrixrndcond(n, c, ref a);
                        b = new double[n];
                        x0 = new double[n];
                        xs = new double[n];
                        for(i=0; i<=n-1; i++)
                        {
                            x0[i] = mx*(2*math.randomreal()-1);
                            xs[i] = mx*(2*math.randomreal()-1);
                        }
                        eps = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            b[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                b[i] = b[i]+a[i,j]*xs[j];
                            }
                            eps = eps+b[i]*b[i];
                        }
                        eps = 1.0E-6*Math.Sqrt(eps);
                        
                        //
                        // Solve with different stopping criteria
                        //
                        lincg.lincgcreate(n, s);
                        lincg.lincgsetb(s, b);
                        lincg.lincgsetstartingpoint(s, x0);
                        lincg.lincgsetxrep(s, true);
                        if( stcrit==0 )
                        {
                            lincg.lincgsetcond(s, 1.0E-6, 0);
                        }
                        else
                        {
                            lincg.lincgsetcond(s, 0, n);
                        }
                        while( lincg.lincgiteration(s) )
                        {
                            if( s.needmv )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    s.mv[i] = 0;
                                    for(j=0; j<=n-1; j++)
                                    {
                                        s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                    }
                                }
                            }
                            if( s.needvmv )
                            {
                                s.vmv = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    s.mv[i] = 0;
                                    for(j=0; j<=n-1; j++)
                                    {
                                        s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                                    }
                                    s.vmv = s.vmv+s.mv[i]*s.x[i];
                                }
                            }
                            if( s.needprec )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    s.pv[i] = s.x[i];
                                }
                            }
                        }
                        lincg.lincgresults(s, ref x0, rep);
                        
                        //
                        // Check result
                        //
                        err = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            tmp = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                tmp = tmp+a[i,j]*x0[j];
                            }
                            err = err+math.sqr(b[i]-tmp);
                        }
                        err = Math.Sqrt(err);
                        if( (double)(err)>(double)(eps) )
                        {
                            if( !silent )
                            {
                                System.Console.Write("ComplexRes::fail");
                                System.Console.WriteLine();
                                System.Console.Write("IterationsCount=");
                                System.Console.Write("{0,0:d}",rep.iterationscount);
                                System.Console.WriteLine();
                                System.Console.Write("NMV=");
                                System.Console.Write("{0,0:d}",rep.nmv);
                                System.Console.WriteLine();
                                System.Console.Write("TerminationType=");
                                System.Console.Write("{0,0:d}",rep.terminationtype);
                                System.Console.WriteLine();
                                System.Console.Write("X and Xs...");
                                System.Console.WriteLine();
                                for(j=0; j<=n-1; j++)
                                {
                                    System.Console.Write("x[");
                                    System.Console.Write("{0,0:d}",j);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F10}",x0[j]);
                                    System.Console.Write("; xs[");
                                    System.Console.Write("{0,0:d}",j);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F10}",xs[j]);
                                    System.Console.WriteLine();
                                }
                            }
                            result = true;
                            return result;
                        }
                    }
                }
            }
            
            //
            //test has been passed
            //
            if( !silent )
            {
                System.Console.Write("ComplexRes::Ok");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function check, that XUpdated return State.X=X0 at zero iteration and
        State.X=X(algorithm result) at last.

          -- ALGLIB --
             Copyright 14.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool basictestx(bool silent)
        {
            bool result = new bool();
            lincg.lincgstate s = new lincg.lincgstate();
            lincg.lincgreport rep = new lincg.lincgreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double[] x0 = new double[0];
            double[] x00 = new double[0];
            double[] x01 = new double[0];
            int n = 0;
            int sz = 0;
            double c = 0;
            int i = 0;
            int j = 0;
            double mx = 0;
            int iters = 0;

            sz = 5;
            mx = 100;
            for(n=1; n<=sz; n++)
            {
                
                //
                // Generate:
                // * random A with norm NA (equal to 1.0),
                // * random right part B whose elements are uniformly distributed in [-MX,+MX]
                // * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
                //
                c = (maxcond-1)*math.randomreal()+1;
                matgen.spdmatrixrndcond(n, c, ref a);
                b = new double[n];
                x0 = new double[n];
                x00 = new double[n];
                x01 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = mx*(2*math.randomreal()-1);
                    b[i] = mx*(2*math.randomreal()-1);
                }
                
                //
                // Solve, save first and last reported points to x00 and x01
                //
                lincg.lincgcreate(n, s);
                lincg.lincgsetb(s, b);
                lincg.lincgsetstartingpoint(s, x0);
                lincg.lincgsetxrep(s, true);
                lincg.lincgsetcond(s, 0, n);
                iters = 0;
                while( lincg.lincgiteration(s) )
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                        }
                    }
                    if( s.needvmv )
                    {
                        s.vmv = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                            s.vmv = s.vmv+s.mv[i]*s.x[i];
                        }
                    }
                    if( s.needprec )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.pv[i] = s.x[i];
                        }
                    }
                    if( s.xupdated )
                    {
                        if( iters==0 )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                x00[i] = s.x[i];
                            }
                        }
                        if( iters==n )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                x01[i] = s.x[i];
                            }
                        }
                        iters = iters+1;
                    }
                }
                
                //
                // Check first and last points
                //
                for(i=0; i<=n-1; i++)
                {
                    if( (double)(x00[i])!=(double)(x0[i]) )
                    {
                        if( !silent )
                        {
                            System.Console.Write("BasicTestX::fail");
                            System.Console.WriteLine();
                            System.Console.Write("IterationsCount=");
                            System.Console.Write("{0,0:d}",rep.iterationscount);
                            System.Console.WriteLine();
                            System.Console.Write("NMV=");
                            System.Console.Write("{0,0:d}",rep.nmv);
                            System.Console.WriteLine();
                            System.Console.Write("TerminationType=");
                            System.Console.Write("{0,0:d}",rep.terminationtype);
                            System.Console.WriteLine();
                            for(j=0; j<=n-1; j++)
                            {
                                System.Console.Write("x0=");
                                System.Console.Write("{0,0:F5}",x0[j]);
                                System.Console.Write("; x00=");
                                System.Console.Write("{0,0:F5}",x00[j]);
                                System.Console.Write(";");
                                System.Console.WriteLine();
                            }
                        }
                        result = true;
                        return result;
                    }
                }
                lincg.lincgresults(s, ref x0, rep);
                for(i=0; i<=n-1; i++)
                {
                    if( (double)(x01[i])!=(double)(x0[i]) )
                    {
                        if( !silent )
                        {
                            System.Console.Write("BasicTestX::fail");
                            System.Console.WriteLine();
                            System.Console.Write("IterationsCount=");
                            System.Console.Write("{0,0:d}",rep.iterationscount);
                            System.Console.WriteLine();
                            System.Console.Write("NMV=");
                            System.Console.Write("{0,0:d}",rep.nmv);
                            System.Console.WriteLine();
                            System.Console.Write("TerminationType=");
                            System.Console.Write("{0,0:d}",rep.terminationtype);
                            System.Console.WriteLine();
                            for(j=0; j<=n-1; j++)
                            {
                                System.Console.Write("x0=");
                                System.Console.Write("{0,0:F5}",x0[j]);
                                System.Console.Write("; x01=");
                                System.Console.Write("{0,0:F5}",x01[j]);
                                System.Console.Write(";");
                                System.Console.WriteLine();
                            }
                        }
                        result = true;
                        return result;
                    }
                }
            }
            
            //
            //test has been passed
            //
            if( !silent )
            {
                System.Console.Write("BasicTestIters::Ok");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function checks that XUpdated returns correct State.R2. It creates
        large badly conditioned problem (N=50), which should be large enough and
        ill-conditioned enough to cause periodic recalculation of R.

          -- ALGLIB --
             Copyright 14.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool testrcorrectness(bool silent)
        {
            bool result = new bool();
            lincg.lincgstate s = new lincg.lincgstate();
            lincg.lincgreport rep = new lincg.lincgreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            int n = 0;
            double c = 0;
            int i = 0;
            int j = 0;
            double r2 = 0;
            double v = 0;
            double rtol = 0;
            int maxits = 0;

            rtol = 1.0E6*math.machineepsilon;
            n = 50;
            maxits = n/2;
            c = 10000;
            matgen.spdmatrixrndcond(n, c, ref a);
            b = new double[n];
            for(i=0; i<=n-1; i++)
            {
                b[i] = 2*math.randomreal()-1;
            }
            lincg.lincgcreate(n, s);
            lincg.lincgsetb(s, b);
            lincg.lincgsetxrep(s, true);
            lincg.lincgsetcond(s, 0, maxits);
            while( lincg.lincgiteration(s) )
            {
                if( s.needmv )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        s.mv[i] = 0;
                        for(j=0; j<=n-1; j++)
                        {
                            s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                        }
                    }
                }
                if( s.needvmv )
                {
                    s.vmv = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        s.mv[i] = 0;
                        for(j=0; j<=n-1; j++)
                        {
                            s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                        }
                        s.vmv = s.vmv+s.mv[i]*s.x[i];
                    }
                }
                if( s.needprec )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        s.pv[i] = s.x[i];
                    }
                }
                if( s.xupdated )
                {
                    
                    //
                    // calculate R2, compare with value returned in state.R2
                    //
                    r2 = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = 0;
                        for(j=0; j<=n-1; j++)
                        {
                            v = v+a[i,j]*s.x[j];
                        }
                        r2 = r2+math.sqr(v-b[i]);
                    }
                    if( (double)(Math.Abs(r2-s.r2))>(double)(rtol) )
                    {
                        result = true;
                        return result;
                    }
                }
            }
            lincg.lincgresults(s, ref b, rep);
            if( rep.iterationscount!=maxits )
            {
                result = true;
                return result;
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function check, that number of iterations are't more than MaxIts.

          -- ALGLIB --
             Copyright 14.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool basictestiters(bool silent)
        {
            bool result = new bool();
            lincg.lincgstate s = new lincg.lincgstate();
            lincg.lincgreport rep = new lincg.lincgreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double[] x0 = new double[0];
            int n = 0;
            int sz = 0;
            double c = 0;
            int i = 0;
            int j = 0;
            double mx = 0;
            int iters = 0;

            sz = 5;
            mx = 100;
            for(n=1; n<=sz; n++)
            {
                
                //
                // Generate:
                // * random A with norm NA (equal to 1.0),
                // * random right part B whose elements are uniformly distributed in [-MX,+MX]
                // * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
                //
                c = (maxcond-1)*math.randomreal()+1;
                matgen.spdmatrixrndcond(n, c, ref a);
                b = new double[n];
                x0 = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = mx*(2*math.randomreal()-1);
                    b[i] = mx*(2*math.randomreal()-1);
                }
                
                //
                // Solve
                //
                lincg.lincgcreate(n, s);
                lincg.lincgsetb(s, b);
                lincg.lincgsetstartingpoint(s, x0);
                lincg.lincgsetxrep(s, true);
                lincg.lincgsetcond(s, 0, n);
                iters = 0;
                while( lincg.lincgiteration(s) )
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                        }
                    }
                    if( s.needvmv )
                    {
                        s.vmv = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                            s.vmv = s.vmv+s.mv[i]*s.x[i];
                        }
                    }
                    if( s.needprec )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.pv[i] = s.x[i];
                        }
                    }
                    if( s.xupdated )
                    {
                        iters = iters+1;
                    }
                }
                lincg.lincgresults(s, ref x0, rep);
                
                //
                // Check
                //
                if( iters!=rep.iterationscount+1 || iters>n+1 )
                {
                    if( !silent )
                    {
                        System.Console.Write("BasicTestIters::fail");
                        System.Console.WriteLine();
                        System.Console.Write("IterationsCount=");
                        System.Console.Write("{0,0:d}",rep.iterationscount);
                        System.Console.WriteLine();
                        System.Console.Write("NMV=");
                        System.Console.Write("{0,0:d}",rep.nmv);
                        System.Console.WriteLine();
                        System.Console.Write("TerminationType=");
                        System.Console.Write("{0,0:d}",rep.terminationtype);
                        System.Console.WriteLine();
                        System.Console.Write("Iters=");
                        System.Console.Write("{0,0:d}",iters);
                        System.Console.WriteLine();
                    }
                    result = true;
                    return result;
                }
                
                //
                // Restart problem
                //
                c = (maxcond-1)*math.randomreal()+1;
                matgen.spdmatrixrndcond(n, c, ref a);
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = mx*(2*math.randomreal()-1);
                    b[i] = mx*(2*math.randomreal()-1);
                }
                lincg.lincgsetstartingpoint(s, x0);
                lincg.lincgrestart(s);
                lincg.lincgsetb(s, b);
                iters = 0;
                while( lincg.lincgiteration(s) )
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                        }
                    }
                    if( s.needvmv )
                    {
                        s.vmv = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                            s.vmv = s.vmv+s.mv[i]*s.x[i];
                        }
                    }
                    if( s.needprec )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.pv[i] = s.x[i];
                        }
                    }
                    if( s.xupdated )
                    {
                        iters = iters+1;
                    }
                }
                lincg.lincgresults(s, ref x0, rep);
                
                //
                //check
                //
                if( iters!=rep.iterationscount+1 || iters>n+1 )
                {
                    if( !silent )
                    {
                        System.Console.Write("BasicTestIters::fail");
                        System.Console.WriteLine();
                        System.Console.Write("IterationsCount=");
                        System.Console.Write("{0,0:d}",rep.iterationscount);
                        System.Console.WriteLine();
                        System.Console.Write("NMV=");
                        System.Console.Write("{0,0:d}",rep.nmv);
                        System.Console.WriteLine();
                        System.Console.Write("TerminationType=");
                        System.Console.Write("{0,0:d}",rep.terminationtype);
                        System.Console.WriteLine();
                        System.Console.Write("Iters=");
                        System.Console.Write("{0,0:d}",iters);
                        System.Console.WriteLine();
                    }
                    result = true;
                    return result;
                }
            }
            
            //
            //test has been passed
            //
            if( !silent )
            {
                System.Console.Write("BasicTestIters::Ok");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        This function check, that programmed method is Krylov subspace methed.

          -- ALGLIB --
             Copyright 14.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool krylovsubspacetest(bool silent)
        {
            bool result = new bool();
            lincg.lincgstate s = new lincg.lincgstate();
            lincg.lincgreport rep = new lincg.lincgreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double[] x0 = new double[0];
            double[,] ksr = new double[0,0];
            double[] r0 = new double[0];
            double[] tarray = new double[0];
            double[,] mtx = new double[0,0];
            int n = 0;
            int sz = 0;
            double c = 0;
            int i = 0;
            int j = 0;
            int l = 0;
            int m = 0;
            double mx = 0;
            double tmp = 0;
            double normr0 = 0;
            int numofit = 0;
            int maxits = 0;
            int k2 = 0;
            double eps = 0;

            eps = 1.0E-6;
            maxits = 3;
            sz = 5;
            mx = 100;
            for(n=1; n<=sz; n++)
            {
                
                //
                // Generate:
                // * random A with unit norm
                // * cond(A) in [0.5*MaxCond, 1.0*MaxCond]
                // * random x0 and b such that |A*x0-b| is large enough for algorithm to make at least one iteration.
                //
                // IMPORTANT: it is very important to have cond(A) both (1) not very large and
                //            (2) not very small. Large cond(A) will make our problem ill-conditioned,
                //            thus analytic properties won't hold. Small cond(A), from the other side,
                //            will give us rapid convergence of the algorithm - in fact, too rapid.
                //            Krylov basis will be dominated by numerical noise and test may fail.
                //
                c = maxcond*(0.5*math.randomreal()+0.5);
                matgen.spdmatrixrndcond(n, c, ref a);
                mtx = new double[n+1, n];
                ksr = new double[n, n];
                r0 = new double[n];
                tarray = new double[n];
                b = new double[n];
                x0 = new double[n];
                do
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x0[i] = mx*(2*math.randomreal()-1);
                        b[i] = mx*(2*math.randomreal()-1);
                    }
                    normr0 = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        tmp = 0;
                        for(j=0; j<=n-1; j++)
                        {
                            tmp = tmp+a[i,j]*x0[j];
                        }
                        r0[i] = b[i]-tmp;
                        normr0 = normr0+r0[i]*r0[i];
                    }
                }
                while( (double)(Math.Sqrt(normr0))<=(double)(eps) );
                
                //
                // Fill KSR by {r0, A*r0, A^2*r0, ... }
                //
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ksr[i,j] = r0[j];
                    }
                    for(j=0; j<=i-1; j++)
                    {
                        for(l=0; l<=n-1; l++)
                        {
                            tarray[l] = 0;
                            for(m=0; m<=n-1; m++)
                            {
                                tarray[l] = tarray[l]+a[l,m]*ksr[i,m];
                            }
                        }
                        for(l=0; l<=n-1; l++)
                        {
                            ksr[i,l] = tarray[l];
                        }
                    }
                }
                
                //
                // Solve system, record intermediate points for futher analysis.
                // NOTE: we set update frequency of R to 2 in order to test that R is updated correctly
                //
                lincg.lincgcreate(n, s);
                lincg.lincgsetb(s, b);
                lincg.lincgsetstartingpoint(s, x0);
                lincg.lincgsetxrep(s, true);
                lincg.lincgsetcond(s, 0, n);
                lincg.lincgsetrupdatefreq(s, 2);
                numofit = 0;
                while( lincg.lincgiteration(s) )
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                        }
                    }
                    if( s.needvmv )
                    {
                        s.vmv = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                            s.vmv = s.vmv+s.mv[i]*s.x[i];
                        }
                    }
                    if( s.needprec )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.pv[i] = s.x[i];
                        }
                    }
                    if( s.xupdated )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            mtx[numofit,i] = s.x[i];
                        }
                        numofit = numofit+1;
                    }
                }
                
                //
                // Check that I-th step S_i=X[I+1]-X[i] belongs to I-th Krylov subspace.
                // Checks are done for first K2 steps, with K2 small enough to avoid
                // numerical errors.
                //
                if( n<=maxits )
                {
                    k2 = n;
                }
                else
                {
                    k2 = maxits;
                }
                for(i=0; i<=k2-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        tarray[j] = mtx[i+1,j]-mtx[i,j];
                    }
                    if( !frombasis(tarray, ksr, n, i+1, e0) )
                    {
                        if( !silent )
                        {
                            System.Console.Write("KrylovSubspaceTest::FAIL");
                            System.Console.WriteLine();
                            System.Console.Write("Size=");
                            System.Console.Write("{0,0:d}",n);
                            System.Console.Write("; Iters=");
                            System.Console.Write("{0,0:d}",i);
                            System.Console.Write(";");
                            System.Console.WriteLine();
                        }
                        result = true;
                        return result;
                    }
                }
            }
            if( !silent )
            {
                System.Console.Write("KrylovSubspaceTest::OK");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing  LinCgSolveSparse. This function  prepare problem with
        a known solution 'Xs'(A*Xs-b=0). There b is A*Xs. After, function calculate
        result by LinCGSolveSparse and compares it with 'Xs'.

          -- ALGLIB --
             Copyright 14.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool sparsetest(bool silent)
        {
            bool result = new bool();
            lincg.lincgstate s = new lincg.lincgstate();
            lincg.lincgreport rep = new lincg.lincgreport();
            double[,] a = new double[0,0];
            double[] b = new double[0];
            double[] xs = new double[0];
            double[] x0 = new double[0];
            double[] x1 = new double[0];
            sparse.sparsematrix uppera = new sparse.sparsematrix();
            sparse.sparsematrix lowera = new sparse.sparsematrix();
            double err = 0;
            int n = 0;
            int sz = 0;
            double c = 0;
            int i = 0;
            int j = 0;
            double mx = 0;
            double eps = 0;

            sz = 5;
            mx = 100;
            for(n=1; n<=sz; n++)
            {
                
                //
                // Generate:
                // * random A with unit norm
                // * random X0 (starting point) and XS (known solution)
                // Copy dense A to sparse SA
                //
                c = (maxcond-1)*math.randomreal()+1;
                matgen.spdmatrixrndcond(n, c, ref a);
                b = new double[n];
                xs = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    xs[i] = mx*(2*math.randomreal()-1);
                }
                eps = 0;
                for(i=0; i<=n-1; i++)
                {
                    b[i] = 0;
                    for(j=0; j<=n-1; j++)
                    {
                        b[i] = b[i]+a[i,j]*xs[j];
                    }
                    eps = eps+b[i]*b[i];
                }
                eps = 1.0E-6*Math.Sqrt(eps);
                sparse.sparsecreate(n, n, 0, uppera);
                sparse.sparsecreate(n, n, 0, lowera);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( j>=i )
                        {
                            sparse.sparseset(uppera, i, j, a[i,j]);
                        }
                        if( j<=i )
                        {
                            sparse.sparseset(lowera, i, j, a[i,j]);
                        }
                    }
                }
                sparse.sparseconverttocrs(uppera);
                sparse.sparseconverttocrs(lowera);
                
                //
                // Test upper triangle
                //
                lincg.lincgcreate(n, s);
                lincg.lincgsetcond(s, 0, n);
                lincg.lincgsolvesparse(s, uppera, true, b);
                lincg.lincgresults(s, ref x0, rep);
                err = 0;
                for(i=0; i<=n-1; i++)
                {
                    err = err+math.sqr(x0[i]-xs[i]);
                }
                err = Math.Sqrt(err);
                if( (double)(err)>(double)(eps) )
                {
                    result = true;
                    return result;
                }
                
                //
                // Test lower triangle
                //
                lincg.lincgcreate(n, s);
                lincg.lincgsetcond(s, 0, n);
                lincg.lincgsolvesparse(s, lowera, false, b);
                lincg.lincgresults(s, ref x1, rep);
                err = 0;
                for(i=0; i<=n-1; i++)
                {
                    err = err+math.sqr(x1[i]-xs[i]);
                }
                err = Math.Sqrt(err);
                if( (double)(err)>(double)(eps) )
                {
                    result = true;
                    return result;
                }
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Function for testing  the preconditioned conjugate gradient method.

          -- ALGLIB --
             Copyright 14.11.2011 by Bochkanov Sergey
        *************************************************************************/
        private static bool precondtest(bool silent)
        {
            bool result = new bool();
            lincg.lincgstate s = new lincg.lincgstate();
            lincg.lincgreport rep = new lincg.lincgreport();
            double[,] a = new double[0,0];
            double[,] ta = new double[0,0];
            sparse.sparsematrix sa = new sparse.sparsematrix();
            double[] m = new double[0];
            double[,] mtx = new double[0,0];
            double[,] mtprex = new double[0,0];
            double[] de = new double[0];
            double[] rde = new double[0];
            double[] b = new double[0];
            double[] tb = new double[0];
            double[] d = new double[0];
            double[] xe = new double[0];
            double[] x0 = new double[0];
            double[] tx0 = new double[0];
            double[] err = new double[0];
            int n = 0;
            int sz = 0;
            int numofit = 0;
            double c = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double eps = 0;
            bool bflag = new bool();

            
            //
            // Test 1.
            // 
            // Preconditioned CG for A*x=b with preconditioner M=E*E' is algebraically 
            // equivalent to non-preconditioned CG for (inv(E)*A*inv(E'))*z = inv(E)*b
            // with z=E'*x.
            // 
            // We test it by generating random preconditioner, running algorithm twice -
            // one  time  for  original  problem  with  preconditioner , another one  for
            // modified problem without preconditioner.
            //
            sz = 5;
            for(n=1; n<=sz; n++)
            {
                
                //
                // Generate:
                // * random A with unit norm
                // * random positive definite diagonal preconditioner M
                // * dE=sqrt(M)
                // * rdE=dE^(-1)
                // * tA = rdE*A*rdE
                // * random x0 and b - for original preconditioned problem
                // * tx0 and tb - for modified problem
                //
                c = (maxcond-1)*math.randomreal()+1;
                matgen.spdmatrixrndcond(n, c, ref a);
                ta = new double[n, n];
                mtx = new double[n+1, n];
                mtprex = new double[n+1, n];
                m = new double[n];
                de = new double[n];
                rde = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    m[i] = math.randomreal()+0.5;
                    de[i] = Math.Sqrt(m[i]);
                    rde[i] = 1/de[i];
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ta[i,j] = rde[i]*a[i,j]*rde[j];
                    }
                }
                b = new double[n];
                tb = new double[n];
                x0 = new double[n];
                tx0 = new double[n];
                err = new double[n];
                for(i=0; i<=n-1; i++)
                {
                    x0[i] = 2*math.randomreal()-1;
                    b[i] = 2*math.randomreal()-1;
                }
                eps = 1.0E-5;
                for(i=0; i<=n-1; i++)
                {
                    tx0[i] = de[i]*x0[i];
                    tb[i] = rde[i]*b[i];
                }
                
                //
                // Solve two problems, intermediate points are saved to MtX and MtPreX
                //
                lincg.lincgcreate(n, s);
                lincg.lincgsetb(s, b);
                lincg.lincgsetstartingpoint(s, x0);
                lincg.lincgsetxrep(s, true);
                lincg.lincgsetcond(s, 0, n);
                numofit = 0;
                while( lincg.lincgiteration(s) )
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                        }
                    }
                    if( s.needvmv )
                    {
                        s.vmv = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+a[i,j]*s.x[j];
                            }
                            s.vmv = s.vmv+s.mv[i]*s.x[i];
                        }
                    }
                    if( s.needprec )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.pv[i] = s.x[i]/m[i];
                        }
                    }
                    if( s.xupdated )
                    {
                        if( numofit>=alglib.ap.rows(mtx) )
                        {
                            result = true;
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            mtx[numofit,i] = s.x[i];
                        }
                        numofit = numofit+1;
                    }
                }
                lincg.lincgsetstartingpoint(s, tx0);
                lincg.lincgsetb(s, tb);
                lincg.lincgrestart(s);
                numofit = 0;
                while( lincg.lincgiteration(s) )
                {
                    if( s.needmv )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+ta[i,j]*s.x[j];
                            }
                        }
                    }
                    if( s.needvmv )
                    {
                        s.vmv = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            s.mv[i] = 0;
                            for(j=0; j<=n-1; j++)
                            {
                                s.mv[i] = s.mv[i]+ta[i,j]*s.x[j];
                            }
                            s.vmv = s.vmv+s.mv[i]*s.x[i];
                        }
                    }
                    if( s.needprec )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            s.pv[i] = s.x[i];
                        }
                    }
                    if( s.xupdated )
                    {
                        if( numofit>=alglib.ap.rows(mtprex) )
                        {
                            result = true;
                            return result;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            mtprex[numofit,i] = s.x[i];
                        }
                        numofit = numofit+1;
                    }
                }
                
                //
                // Compare results - sequence of points generated when solving original problem with
                // points generated by modified problem.
                //
                for(i=0; i<=numofit-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (double)(Math.Abs(mtx[i,j]-rde[j]*mtprex[i,j]))>(double)(eps) )
                        {
                            if( !silent )
                            {
                                System.Console.Write("PrecondTest::fail");
                                System.Console.WriteLine();
                                System.Console.Write("Size=");
                                System.Console.Write("{0,0:d}",n);
                                System.Console.WriteLine();
                                System.Console.Write("IterationsCount=");
                                System.Console.Write("{0,0:d}",rep.iterationscount);
                                System.Console.WriteLine();
                                System.Console.Write("NMV=");
                                System.Console.Write("{0,0:d}",rep.nmv);
                                System.Console.WriteLine();
                                System.Console.Write("TerminationType=");
                                System.Console.Write("{0,0:d}",rep.terminationtype);
                                System.Console.WriteLine();
                                System.Console.Write("X and X^...");
                                System.Console.WriteLine();
                                for(k=0; k<=n-1; k++)
                                {
                                    System.Console.Write("I=");
                                    System.Console.Write("{0,0:d}",i);
                                    System.Console.Write("; mtx[");
                                    System.Console.Write("{0,0:d}",k);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F10}",mtx[i,k]);
                                    System.Console.Write("; mtx^[");
                                    System.Console.Write("{0,0:d}",k);
                                    System.Console.Write("]=");
                                    System.Console.Write("{0,0:F10}",mtprex[i,k]);
                                    System.Console.WriteLine();
                                }
                            }
                            result = true;
                            return result;
                        }
                    }
                }
            }
            
            //
            // Test 2.
            //
            // We test automatic diagonal preconditioning used by SolveSparse.
            // In order to do so we:
            // 1. generate 20*20 matrix A0 with condition number equal to 1.0E1
            // 2. generate random "exact" solution xe and right part b=A0*xe
            // 3. generate random ill-conditioned diagonal scaling matrix D with
            //    condition number equal to 1.0E50:
            // 4. transform A*x=b into badly scaled problem:
            //    A0*x0=b0
            //    A0*D*(inv(D)*x0)=b0
            //    (D*A0*D)*(inv(D)*x0)=(D*b0)
            //    finally we got new problem A*x=b with A=D*A0*D, b=D*b0, x=inv(D)*x0
            //
            // Then we solve A*x=b:
            // 1. with default preconditioner
            // 2. with explicitly activayed diagonal preconditioning
            // 3. with unit preconditioner.
            // 1st and 2nd solutions must be close to xe, 3rd solution must be very
            // far from the true one.
            //
            n = 20;
            matgen.spdmatrixrndcond(n, 1.0E1, ref ta);
            xe = new double[n];
            for(i=0; i<=n-1; i++)
            {
                xe[i] = apserv.randomnormal();
            }
            b = new double[n];
            for(i=0; i<=n-1; i++)
            {
                b[i] = 0;
                for(j=0; j<=n-1; j++)
                {
                    b[i] = b[i]+ta[i,j]*xe[j];
                }
            }
            d = new double[n];
            for(i=0; i<=n-1; i++)
            {
                d[i] = Math.Pow(10, 100*math.randomreal()-50);
            }
            a = new double[n, n];
            sparse.sparsecreate(n, n, n*n, sa);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a[i,j] = d[i]*ta[i,j]*d[j];
                    sparse.sparseset(sa, i, j, a[i,j]);
                }
                b[i] = b[i]*d[i];
                xe[i] = xe[i]/d[i];
            }
            sparse.sparseconverttocrs(sa);
            lincg.lincgcreate(n, s);
            lincg.lincgsetcond(s, 0, 2*n);
            lincg.lincgsolvesparse(s, sa, true, b);
            lincg.lincgresults(s, ref x0, rep);
            if( rep.terminationtype<=0 )
            {
                result = true;
                return result;
            }
            for(i=0; i<=n-1; i++)
            {
                if( (double)(Math.Abs(xe[i]-x0[i]))>(double)(5.0E-2/d[i]) )
                {
                    result = true;
                    return result;
                }
            }
            lincg.lincgsetprecunit(s);
            lincg.lincgsolvesparse(s, sa, true, b);
            lincg.lincgresults(s, ref x0, rep);
            if( rep.terminationtype>0 )
            {
                bflag = false;
                for(i=0; i<=n-1; i++)
                {
                    bflag = bflag || (double)(Math.Abs(xe[i]-x0[i]))>(double)(5.0E-2/d[i]);
                }
                if( !bflag )
                {
                    result = true;
                    return result;
                }
            }
            lincg.lincgsetprecdiag(s);
            lincg.lincgsolvesparse(s, sa, true, b);
            lincg.lincgresults(s, ref x0, rep);
            if( rep.terminationtype<=0 )
            {
                result = true;
                return result;
            }
            for(i=0; i<=n-1; i++)
            {
                if( (double)(Math.Abs(xe[i]-x0[i]))>(double)(5.0E-2/d[i]) )
                {
                    result = true;
                    return result;
                }
            }
            
            //
            //test has been passed
            //
            if( !silent )
            {
                System.Console.Write("PrecondTest::Ok");
                System.Console.WriteLine();
            }
            result = false;
            return result;
        }


        /*************************************************************************
        Orthogonalization by Gram-Shmidt method.
        *************************************************************************/
        private static void gramshmidtortnorm(double[,] a,
            int n,
            int k,
            double eps,
            ref double[,] b,
            ref int k2)
        {
            double scaling = 0;
            double tmp = 0;
            double e = 0;
            int i = 0;
            int j = 0;
            int l = 0;
            int m = 0;
            double sc = 0;

            b = new double[0,0];
            k2 = 0;

            k2 = 0;
            scaling = 0;
            b = new double[k, n];
            for(i=0; i<=k-1; i++)
            {
                tmp = 0;
                for(j=0; j<=n-1; j++)
                {
                    tmp = tmp+a[i,j]*a[i,j];
                }
                if( (double)(tmp)>(double)(scaling) )
                {
                    scaling = tmp;
                }
            }
            scaling = Math.Sqrt(scaling);
            e = eps*scaling;
            for(i=0; i<=k-1; i++)
            {
                tmp = 0;
                for(j=0; j<=n-1; j++)
                {
                    b[k2,j] = a[i,j];
                    tmp = tmp+a[i,j]*a[i,j];
                }
                tmp = Math.Sqrt(tmp);
                if( (double)(tmp)<=(double)(e) )
                {
                    continue;
                }
                for(j=0; j<=k2-1; j++)
                {
                    sc = 0;
                    for(m=0; m<=n-1; m++)
                    {
                        sc = sc+b[k2,m]*b[j,m];
                    }
                    for(l=0; l<=n-1; l++)
                    {
                        b[k2,l] = b[k2,l]-sc*b[j,l];
                    }
                }
                tmp = 0;
                for(j=0; j<=n-1; j++)
                {
                    tmp = tmp+b[k2,j]*b[k2,j];
                }
                tmp = Math.Sqrt(tmp);
                if( (double)(tmp)<=(double)(e) )
                {
                    continue;
                }
                else
                {
                    for(j=0; j<=n-1; j++)
                    {
                        b[k2,j] = b[k2,j]/tmp;
                    }
                }
                k2 = k2+1;
            }
        }


        /*************************************************************************
        Checks that a vector belongs to the basis.
        *************************************************************************/
        private static bool frombasis(double[] x,
            double[,] basis,
            int n,
            int k,
            double eps)
        {
            bool result = new bool();
            double normx = 0;
            double[,] ortnormbasis = new double[0,0];
            int k2 = 0;
            int i = 0;
            int j = 0;
            double alpha = 0;
            double[] alphas = new double[0];

            alphas = new double[k];
            
            //
            //calculating NORM for X
            //
            normx = 0;
            for(i=0; i<=n-1; i++)
            {
                normx = normx+x[i]*x[i];
            }
            normx = Math.Sqrt(normx);
            
            //
            //Gram-Shmidt method
            //
            gramshmidtortnorm(basis, n, k, eps, ref ortnormbasis, ref k2);
            for(i=0; i<=k2-1; i++)
            {
                alpha = 0;
                for(j=0; j<=n-1; j++)
                {
                    alpha = alpha+x[j]*ortnormbasis[i,j];
                }
                alphas[i] = alpha;
            }
            
            //
            //check
            //
            for(i=0; i<=n-1; i++)
            {
                alpha = 0;
                for(j=0; j<=k2-1; j++)
                {
                    alpha = alpha+alphas[j]*ortnormbasis[j,i];
                }
                if( (double)(Math.Abs(x[i]-alpha))>(double)(normx*eps) )
                {
                    result = false;
                    return result;
                }
            }
            result = true;
            return result;
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testnleq(bool silent)
        {
            return testnleq(silent);
        }


        /*************************************************************************
        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;

            alglib.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;

            alglib.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;

            alglib.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 testpolynomialsolverunit
    {
        /*************************************************************************
        Test
        *************************************************************************/
        public static bool testpolynomialsolver(bool silent)
        {
            bool result = new bool();
            bool wereerrors = new bool();
            double[] a = new double[0];
            complex[] x = new complex[0];
            double eps = 0;
            int n = 0;
            polynomialsolver.polynomialsolverreport rep = new polynomialsolver.polynomialsolverreport();

            wereerrors = false;
            
            //
            // Basic tests
            //
            eps = 1.0E-6;
            n = 1;
            a = new double[n+1];
            a[0] = 2;
            a[1] = 3;
            polynomialsolver.polynomialsolve(a, n, ref x, rep);
            apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x[0].x+(double)2/(double)3))>(double)(eps));
            apserv.seterrorflag(ref wereerrors, (double)(x[0].y)!=(double)(0));
            apserv.seterrorflag(ref wereerrors, (double)(rep.maxerr)>(double)(100*math.machineepsilon));
            n = 2;
            a = new double[n+1];
            a[0] = 1;
            a[1] = -2;
            a[2] = 1;
            polynomialsolver.polynomialsolve(a, n, ref x, rep);
            apserv.seterrorflag(ref wereerrors, (double)(math.abscomplex(x[0]-1))>(double)(eps));
            apserv.seterrorflag(ref wereerrors, (double)(math.abscomplex(x[1]-1))>(double)(eps));
            apserv.seterrorflag(ref wereerrors, (double)(rep.maxerr)>(double)(100*math.machineepsilon));
            n = 2;
            a = new double[n+1];
            a[0] = 2;
            a[1] = -3;
            a[2] = 1;
            polynomialsolver.polynomialsolve(a, n, ref x, rep);
            if( (double)(x[0].x)<(double)(x[1].x) )
            {
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x[0].x-1))>(double)(eps));
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x[1].x-2))>(double)(eps));
            }
            else
            {
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x[0].x-2))>(double)(eps));
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x[1].x-1))>(double)(eps));
            }
            apserv.seterrorflag(ref wereerrors, (double)(x[0].y)!=(double)(0));
            apserv.seterrorflag(ref wereerrors, (double)(x[1].y)!=(double)(0));
            apserv.seterrorflag(ref wereerrors, (double)(rep.maxerr)>(double)(100*math.machineepsilon));
            n = 2;
            a = new double[n+1];
            a[0] = 1;
            a[1] = 0;
            a[2] = 1;
            polynomialsolver.polynomialsolve(a, n, ref x, rep);
            apserv.seterrorflag(ref wereerrors, (double)(math.abscomplex(x[0]*x[0]+1))>(double)(eps));
            apserv.seterrorflag(ref wereerrors, (double)(rep.maxerr)>(double)(100*math.machineepsilon));
            n = 4;
            a = new double[n+1];
            a[0] = 0;
            a[1] = 0;
            a[2] = 0;
            a[3] = 0;
            a[4] = 1;
            polynomialsolver.polynomialsolve(a, n, ref x, rep);
            apserv.seterrorflag(ref wereerrors, x[0]!=0);
            apserv.seterrorflag(ref wereerrors, x[1]!=0);
            apserv.seterrorflag(ref wereerrors, x[2]!=0);
            apserv.seterrorflag(ref wereerrors, x[3]!=0);
            apserv.seterrorflag(ref wereerrors, (double)(rep.maxerr)>(double)(100*math.machineepsilon));
            n = 2;
            a = new double[n+1];
            a[0] = 0;
            a[1] = 3;
            a[2] = 2;
            polynomialsolver.polynomialsolve(a, n, ref x, rep);
            if( (double)(x[0].x)>(double)(x[1].x) )
            {
                apserv.seterrorflag(ref wereerrors, x[0]!=0);
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x[1].x+(double)3/(double)2))>(double)(eps));
                apserv.seterrorflag(ref wereerrors, (double)(x[1].y)!=(double)(0));
            }
            else
            {
                apserv.seterrorflag(ref wereerrors, x[1]!=0);
                apserv.seterrorflag(ref wereerrors, (double)(Math.Abs(x[0].x+(double)3/(double)2))>(double)(eps));
                apserv.seterrorflag(ref wereerrors, (double)(x[0].y)!=(double)(0));
            }
            apserv.seterrorflag(ref wereerrors, (double)(rep.maxerr)>(double)(100*math.machineepsilon));
            if( !silent )
            {
                System.Console.Write("TESTING POLYNOMIAL SOLVER");
                System.Console.WriteLine();
                if( wereerrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
            }
            result = !wereerrors;
            return result;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testpolynomialsolver(bool silent)
        {
            return testpolynomialsolver(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testchebyshev(bool silent)
        {
            return testchebyshev(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testhermite(bool silent)
        {
            return testhermite(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testlaguerre(bool silent)
        {
            return testlaguerre(silent);
        }


    }
    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testlegendre(bool silent)
        {
            return testlegendre(silent);
        }


    }
    public class teststestunit
    {
        public static bool teststest(bool silent)
        {
            bool result = new bool();
            double[] x = new double[0];
            double taill = 0;
            double tailr = 0;
            double tailb = 0;
            bool waserrors = new bool();
            double eps = 0;

            waserrors = false;
            eps = 1.0E-3;
            
            //
            // Test 1
            //
            x = new double[6];
            x[0] = -3;
            x[1] = -2;
            x[2] = -1;
            x[3] = 1;
            x[4] = 2;
            x[5] = 3;
            stest.onesamplesigntest(x, 6, 0.0, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(taill-0.65625))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.65625))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailb-1.00000))>(double)(eps);
            stest.onesamplesigntest(x, 6, -1.0, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(taill-0.81250))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.50000))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailb-1.00000))>(double)(eps);
            stest.onesamplesigntest(x, 6, -1.5, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(taill-0.89062))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.34375))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailb-0.68750))>(double)(eps);
            stest.onesamplesigntest(x, 6, -3.0, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(taill-1.00000))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.03125))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailb-0.06250))>(double)(eps);
            
            //
            // Test 2
            //
            x = new double[3];
            x[0] = 2;
            x[1] = 2;
            x[2] = 2;
            stest.onesamplesigntest(x, 3, 2.0, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            
            //
            // Final report
            //
            if( !silent )
            {
                System.Console.Write("SIGN TEST:                               ");
                if( !waserrors )
                {
                    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;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_teststest(bool silent)
        {
            return teststest(silent);
        }


    }
    public class teststudentttestsunit
    {
        public static bool teststudentttests(bool silent)
        {
            bool result = new bool();
            bool waserrors = new bool();
            double eps = 0;
            double[] x = new double[0];
            double[] y = new double[0];
            double[] xa = new double[0];
            double[] ya = new double[0];
            double[] xb = new double[0];
            double[] yb = new double[0];
            int n = 0;
            double taill = 0;
            double tailr = 0;
            double tailb = 0;
            double taill1 = 0;
            double tailr1 = 0;
            double tailb1 = 0;

            waserrors = false;
            eps = 0.001;
            
            //
            // 1-sample test
            //
            n = 8;
            x = new double[8];
            x[0] = -3.0;
            x[1] = -1.5;
            x[2] = -1.0;
            x[3] = -0.5;
            x[4] = 0.5;
            x[5] = 1.0;
            x[6] = 1.5;
            x[7] = 3.0;
            studentttests.studentttest1(x, n, 0.0, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(tailb-1.00000))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-0.50000))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.50000))>(double)(eps);
            studentttests.studentttest1(x, n, 1.0, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(tailb-0.17816))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-0.08908))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.91092))>(double)(eps);
            studentttests.studentttest1(x, n, -1.0, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(tailb-0.17816))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-0.91092))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.08908))>(double)(eps);
            x[0] = 1.1;
            x[1] = 1.1;
            x[2] = 1.1;
            x[3] = 1.1;
            x[4] = 1.1;
            x[5] = 1.1;
            x[6] = 1.1;
            x[7] = 1.1;
            studentttests.studentttest1(x, n, 1.1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest1(x, n, 0.0, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(0);
            x[7] = 1.1;
            studentttests.studentttest1(x, 1, 1.1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest1(x, 1, 0.0, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(0);
            
            //
            // 2-sample pooled (equal variance) test
            //
            n = 8;
            x = new double[8];
            y = new double[8];
            x[0] = -3.0;
            x[1] = -1.5;
            x[2] = -1.0;
            x[3] = -0.5;
            x[4] = 0.5;
            x[5] = 1.0;
            x[6] = 1.5;
            x[7] = 3.0;
            y[0] = -2.0;
            y[1] = -0.5;
            y[2] = 0.0;
            y[3] = 0.5;
            y[4] = 1.5;
            y[5] = 2.0;
            y[6] = 2.5;
            y[7] = 4.0;
            studentttests.studentttest2(x, n, y, n, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(tailb-0.30780))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-0.15390))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.84610))>(double)(eps);
            studentttests.studentttest2(x, n, y, n-1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(tailb-0.53853))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-0.26927))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.73074))>(double)(eps);
            studentttests.studentttest2(x, n-1, y, n, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(tailb-0.13829))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-0.06915))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.93086))>(double)(eps);
            x[0] = -1.0;
            x[1] = -1.0;
            x[2] = -1.0;
            x[3] = -1.0;
            x[4] = -1.0;
            x[5] = -1.0;
            x[6] = -1.0;
            x[7] = -1.0;
            y[0] = 1.0;
            y[1] = 1.0;
            y[2] = 1.0;
            y[3] = 1.0;
            y[4] = 1.0;
            y[5] = 1.0;
            y[6] = 1.0;
            y[7] = 1.0;
            studentttests.studentttest2(x, n, y, n, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(0);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, n, y, n-1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(0);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, n, y, 1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(0);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, n-1, y, n, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(0);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, 1, y, n, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(0);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, 1, y, 1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(0);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(y, 1, x, 1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(0);
            x[0] = 1.1;
            x[1] = 1.1;
            x[2] = 1.1;
            x[3] = 1.1;
            x[4] = 1.1;
            x[5] = 1.1;
            x[6] = 1.1;
            x[7] = 1.1;
            y[0] = 1.1;
            y[1] = 1.1;
            y[2] = 1.1;
            y[3] = 1.1;
            y[4] = 1.1;
            y[5] = 1.1;
            y[6] = 1.1;
            y[7] = 1.1;
            studentttests.studentttest2(x, n, y, n, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, n, y, n-1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, n, y, 1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, n-1, y, n, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, 1, y, n, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.studentttest2(x, 1, y, 1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            
            //
            // 2-sample unpooled (unequal variance) test:
            // * test on two non-constant samples
            // * tests on different combinations of non-constant and constant samples
            //
            n = 8;
            xa = new double[8];
            ya = new double[8];
            xb = new double[8];
            yb = new double[8];
            xa[0] = -3.0;
            xa[1] = -1.5;
            xa[2] = -1.0;
            xa[3] = -0.5;
            xa[4] = 0.5;
            xa[5] = 1.0;
            xa[6] = 1.5;
            xa[7] = 3.0;
            ya[0] = -1.0;
            ya[1] = -0.5;
            ya[2] = 0.0;
            ya[3] = 0.5;
            ya[4] = 1.5;
            ya[5] = 2.0;
            ya[6] = 2.5;
            ya[7] = 3.0;
            xb[0] = -1.1;
            xb[1] = -1.1;
            xb[2] = -1.1;
            xb[3] = -1.1;
            xb[4] = -1.1;
            xb[5] = -1.1;
            xb[6] = -1.1;
            xb[7] = -1.1;
            yb[0] = 1.1;
            yb[1] = 1.1;
            yb[2] = 1.1;
            yb[3] = 1.1;
            yb[4] = 1.1;
            yb[5] = 1.1;
            yb[6] = 1.1;
            yb[7] = 1.1;
            studentttests.unequalvariancettest(xa, n, ya, n, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(Math.Abs(tailb-0.25791))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-0.12896))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-0.87105))>(double)(eps);
            studentttests.unequalvariancettest(xa, n, yb, n, ref tailb, ref taill, ref tailr);
            studentttests.studentttest1(xa, n, 1.1, ref tailb1, ref taill1, ref tailr1);
            waserrors = waserrors || (double)(Math.Abs(tailb-tailb1))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-taill1))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-tailr1))>(double)(eps);
            studentttests.unequalvariancettest(xa, n, yb, 1, ref tailb, ref taill, ref tailr);
            studentttests.studentttest1(xa, n, 1.1, ref tailb1, ref taill1, ref tailr1);
            waserrors = waserrors || (double)(Math.Abs(tailb-tailb1))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-taill1))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-tailr1))>(double)(eps);
            studentttests.unequalvariancettest(xb, n, ya, n, ref tailb, ref taill, ref tailr);
            studentttests.studentttest1(ya, n, -1.1, ref tailb1, ref taill1, ref tailr1);
            waserrors = waserrors || (double)(Math.Abs(tailb-tailb1))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-tailr1))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-taill1))>(double)(eps);
            studentttests.unequalvariancettest(xb, 1, ya, n, ref tailb, ref taill, ref tailr);
            studentttests.studentttest1(ya, n, -1.1, ref tailb1, ref taill1, ref tailr1);
            waserrors = waserrors || (double)(Math.Abs(tailb-tailb1))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(taill-tailr1))>(double)(eps);
            waserrors = waserrors || (double)(Math.Abs(tailr-taill1))>(double)(eps);
            studentttests.unequalvariancettest(xb, 1, yb, 1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(0);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            studentttests.unequalvariancettest(yb, 1, xb, 1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(0);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(0);
            studentttests.unequalvariancettest(xb, 1, xb, 1, ref tailb, ref taill, ref tailr);
            waserrors = waserrors || (double)(tailb)!=(double)(1);
            waserrors = waserrors || (double)(taill)!=(double)(1);
            waserrors = waserrors || (double)(tailr)!=(double)(1);
            
            //
            //
            //
            if( !silent )
            {
                if( waserrors )
                {
                    System.Console.Write("TEST FAILED");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("TEST PASSED");
                    System.Console.WriteLine();
                }
            }
            result = !waserrors;
            return result;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_teststudentttests(bool silent)
        {
            return teststudentttests(silent);
        }


    }
    public class testalglibbasicsunit
    {
        public class rec1 : apobject
        {
            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()
            {
                init();
            }
            public override void init()
            {
                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 override alglib.apobject make_copy()
            {
                rec1 _result = new rec1();
                _result.bfield = bfield;
                _result.rfield = rfield;
                _result.ifield = ifield;
                _result.cfield = cfield;
                _result.b1field = (bool[])b1field.Clone();
                _result.r1field = (double[])r1field.Clone();
                _result.i1field = (int[])i1field.Clone();
                _result.c1field = (alglib.complex[])c1field.Clone();
                _result.b2field = (bool[,])b2field.Clone();
                _result.r2field = (double[,])r2field.Clone();
                _result.i2field = (int[,])i2field.Clone();
                _result.c2field = (alglib.complex[,])c2field.Clone();
                return _result;
            }
        };


        public class rec4serialization : apobject
        {
            public bool[] b;
            public int[] i;
            public double[] r;
            public rec4serialization()
            {
                init();
            }
            public override void init()
            {
                b = new bool[0];
                i = new int[0];
                r = new double[0];
            }
            public override alglib.apobject make_copy()
            {
                rec4serialization _result = new rec4serialization();
                _result.b = (bool[])b.Clone();
                _result.i = (int[])i.Clone();
                _result.r = (double[])r.Clone();
                return _result;
            }
        };


        public class poolrec1 : apobject
        {
            public complex cval;
            public double rval;
            public int ival;
            public bool bval;
            public int[] i1val;
            public poolrec1()
            {
                init();
            }
            public override void init()
            {
                i1val = new int[0];
            }
            public override alglib.apobject make_copy()
            {
                poolrec1 _result = new poolrec1();
                _result.cval = cval;
                _result.rval = rval;
                _result.ival = ival;
                _result.bval = bval;
                _result.i1val = (int[])i1val.Clone();
                return _result;
            }
        };


        public class poolrec2 : apobject
        {
            public bool bval;
            public poolrec1 recval;
            public alglib.smp.shared_pool pool;
            public poolrec2()
            {
                init();
            }
            public override void init()
            {
                recval = new poolrec1();
                pool = new alglib.smp.shared_pool();
            }
            public override alglib.apobject make_copy()
            {
                poolrec2 _result = new poolrec2();
                _result.bval = bval;
                _result.recval = (poolrec1)recval.make_copy();
                _result.pool = (alglib.smp.shared_pool)pool.make_copy();
                return _result;
            }
        };


        public class poolsummand : apobject
        {
            public int val;
            public poolsummand()
            {
                init();
            }
            public override void init()
            {
            }
            public override alglib.apobject make_copy()
            {
                poolsummand _result = new poolsummand();
                _result.val = val;
                return _result;
            }
        };




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

            
            //
            // boolean fields
            //
            s.alloc_entry();
            for(i=0; i<=alglib.ap.len(v.b)-1; i++)
            {
                s.alloc_entry();
            }
            
            //
            // integer fields
            //
            s.alloc_entry();
            for(i=0; i<=alglib.ap.len(v.i)-1; i++)
            {
                s.alloc_entry();
            }
            
            //
            // real fields
            //
            s.alloc_entry();
            for(i=0; i<=alglib.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(alglib.ap.len(v.b));
            for(i=0; i<=alglib.ap.len(v.b)-1; i++)
            {
                s.serialize_bool(v.b[i]);
            }
            
            //
            // integer fields
            //
            s.serialize_int(alglib.ap.len(v.i));
            for(i=0; i<=alglib.ap.len(v.i)-1; i++)
            {
                s.serialize_int(v.i[i]);
            }
            
            //
            // real fields
            //
            s.serialize_int(alglib.ap.len(v.r));
            for(i=0; i<=alglib.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 && teststandardfunctions(silent);
            result = result && testserializationfunctions(silent);
            result = result && testsharedpool(silent);
            result = result && testsmp(silent);
            if( !silent )
            {
                System.Console.WriteLine();
                System.Console.WriteLine();
            }
            return result;
        }


        /*************************************************************************
        Single-threaded stub. HPC ALGLIB replaces it by multithreaded code.
        *************************************************************************/
        public static bool _pexec_testalglibbasics(bool silent)
        {
            return testalglibbasics(silent);
        }


        /*************************************************************************
        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;
            alglib.ap.swap(ref b11, ref b12);
            if( alglib.ap.len(b11)==2 && alglib.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;
            alglib.ap.swap(ref i11, ref i12);
            if( alglib.ap.len(i11)==2 && alglib.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;
            alglib.ap.swap(ref r11, ref r12);
            if( alglib.ap.len(r11)==2 && alglib.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;
            alglib.ap.swap(ref c11, ref c12);
            if( alglib.ap.len(c11)==2 && alglib.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;
            alglib.ap.swap(ref b21, ref b22);
            if( ((alglib.ap.rows(b21)==2 && alglib.ap.cols(b21)==1) && alglib.ap.rows(b22)==1) && alglib.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;
            alglib.ap.swap(ref i21, ref i22);
            if( ((alglib.ap.rows(i21)==2 && alglib.ap.cols(i21)==1) && alglib.ap.rows(i22)==1) && alglib.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;
            alglib.ap.swap(ref r21, ref r22);
            if( ((alglib.ap.rows(r21)==2 && alglib.ap.cols(r21)==1) && alglib.ap.rows(r22)==1) && alglib.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;
            alglib.ap.swap(ref c21, ref c22);
            if( ((alglib.ap.rows(c21)==2 && alglib.ap.cols(c21)==1) && alglib.ap.rows(c22)==1) && alglib.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 standard functions
        *************************************************************************/
        private static bool teststandardfunctions(bool silent)
        {
            bool result = new bool();

            result = true;
            
            //
            // Test Sign()
            //
            result = result && Math.Sign(1.2)==1;
            result = result && Math.Sign(0)==0;
            result = result && Math.Sign(-1.2)==-1;
            
            //
            // summary
            //
            if( !silent )
            {
                if( result )
                {
                    System.Console.Write("STANDARD FUNCTIONS:                      OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("STANDARD FUNCTIONS:                      FAILED");
                    System.Console.WriteLine();
                }
            }
            return result;
        }


        /*************************************************************************
        Tests for serualization 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( (alglib.ap.len(r0.b)==alglib.ap.len(r1.b) && alglib.ap.len(r0.i)==alglib.ap.len(r1.i)) && alglib.ap.len(r0.r)==alglib.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;
        }


        /*************************************************************************
        Tests for pool functions
        *************************************************************************/
        private static void createpoolandrecords(poolrec2 seedrec2,
            poolrec2 seedrec2copy,
            alglib.smp.shared_pool pool)
        {
            seedrec2.bval = (double)(math.randomreal())>(double)(0.5);
            seedrec2.recval.bval = (double)(math.randomreal())>(double)(0.5);
            seedrec2.recval.ival = math.randominteger(10);
            seedrec2.recval.rval = math.randomreal();
            seedrec2.recval.cval.x = math.randomreal();
            seedrec2.recval.cval.y = math.randomreal();
            seedrec2.recval.i1val = new int[3];
            seedrec2.recval.i1val[0] = math.randominteger(10);
            seedrec2.recval.i1val[1] = math.randominteger(10);
            seedrec2.recval.i1val[2] = math.randominteger(10);
            seedrec2copy.bval = seedrec2.bval;
            seedrec2copy.recval.bval = seedrec2.recval.bval;
            seedrec2copy.recval.ival = seedrec2.recval.ival;
            seedrec2copy.recval.rval = seedrec2.recval.rval;
            seedrec2copy.recval.cval = seedrec2.recval.cval;
            seedrec2copy.recval.i1val = new int[3];
            seedrec2copy.recval.i1val[0] = seedrec2.recval.i1val[0];
            seedrec2copy.recval.i1val[1] = seedrec2.recval.i1val[1];
            seedrec2copy.recval.i1val[2] = seedrec2.recval.i1val[2];
            alglib.smp.ae_shared_pool_set_seed(pool, seedrec2);
        }


        private static bool sharedpoolerrors()
        {
            bool result = new bool();
            poolrec1 seedrec1 = new poolrec1();
            poolrec2 seedrec2 = new poolrec2();
            poolrec2 seedrec2copy = new poolrec2();
            alglib.smp.shared_pool pool = new alglib.smp.shared_pool();
            alglib.smp.shared_pool pool2 = new alglib.smp.shared_pool();
            poolrec2 prec2 = null;
            poolrec2 p0 = null;
            poolrec2 p1 = null;
            poolrec2 p2 = null;
            poolrec1 q0 = null;
            poolrec1 q1 = null;
            alglib.smp.shared_pool ppool0 = null;
            alglib.smp.shared_pool ppool1 = null;
            int val100cnt = 0;
            int val101cnt = 0;
            int val102cnt = 0;
            int tmpval = 0;

            result = true;
            
            //
            // Test 1: test that:
            // a) smart pointer is null by default
            // b) "conventional local" is valid by default
            // b) unitinitialized shared pool is "not initialized"
            //
            if( prec2!=null )
            {
                return result;
            }
            if( !(seedrec1!=null) )
            {
                return result;
            }
            if( alglib.smp.ae_shared_pool_is_initialized(pool) )
            {
                return result;
            }
            
            //
            // Test 2: basic copying of complex structures
            // * check that pool is recognized as "initialized"
            // * change original seed record,
            // * retrieve value from pool,
            // * check that it is valid
            // * and it is unchanged.
            //
            createpoolandrecords(seedrec2, seedrec2copy, pool);
            if( !alglib.smp.ae_shared_pool_is_initialized(pool) )
            {
                return result;
            }
            seedrec2.bval = !seedrec2.bval;
            seedrec2.recval.i1val[0] = seedrec2.recval.i1val[0]+1;
            alglib.smp.ae_shared_pool_retrieve(pool, ref prec2);
            if( !(prec2!=null) )
            {
                return result;
            }
            if( (seedrec2copy.bval && !prec2.bval) || (prec2.bval && !seedrec2copy.bval) )
            {
                return result;
            }
            if( seedrec2copy.recval.i1val[0]!=prec2.recval.i1val[0] )
            {
                return result;
            }
            
            //
            // Test 3: unrecycled values are lost
            // * retrieve value from pool,
            // * change it,
            // * retrieve one more time,
            // * check that it is unchanged.
            //
            createpoolandrecords(seedrec2, seedrec2copy, pool);
            alglib.smp.ae_shared_pool_retrieve(pool, ref prec2);
            prec2.recval.ival = prec2.recval.ival+1;
            alglib.smp.ae_shared_pool_retrieve(pool, ref prec2);
            if( prec2.recval.ival!=seedrec2copy.recval.ival )
            {
                return result;
            }
            
            //
            // Test 4: recycled values are reused, PoolClearRecycled() removes recycled values
            // * retrieve value from pool,
            // * change it,
            // * recycle,
            // * check that recycled pointer is null
            // * retrieve one more time,
            // * check that modified value was returned,
            // * recycle,
            // * clear pool,
            // * retrieve one more time,
            // * check that unmodified value was returned,
            //
            createpoolandrecords(seedrec2, seedrec2copy, pool);
            alglib.smp.ae_shared_pool_retrieve(pool, ref prec2);
            prec2.recval.ival = prec2.recval.ival+1;
            alglib.smp.ae_shared_pool_recycle(pool, ref prec2);
            if( prec2!=null )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_retrieve(pool, ref prec2);
            if( !(prec2!=null) )
            {
                return result;
            }
            if( prec2.recval.ival!=seedrec2copy.recval.ival+1 )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_recycle(pool, ref prec2);
            alglib.smp.ae_shared_pool_clear_recycled(pool);
            alglib.smp.ae_shared_pool_retrieve(pool, ref prec2);
            if( !(prec2!=null) )
            {
                return result;
            }
            if( prec2.recval.ival!=seedrec2copy.recval.ival )
            {
                return result;
            }
            
            //
            // Test 5: basic enumeration
            // * retrieve 3 values from pool
            // * fill RecVal.iVal by 100, 101, 102
            // * recycle values
            // * enumerate, check that each iVal occurs only once during enumeration
            // * repeat enumeration to make sure that it can be repeated
            //
            createpoolandrecords(seedrec2, seedrec2copy, pool);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p0);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p1);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p2);
            p0.recval.ival = 100;
            p1.recval.ival = 101;
            p2.recval.ival = 102;
            alglib.smp.ae_shared_pool_recycle(pool, ref p1);
            alglib.smp.ae_shared_pool_recycle(pool, ref p2);
            alglib.smp.ae_shared_pool_recycle(pool, ref p0);
            val100cnt = 0;
            val101cnt = 0;
            val102cnt = 0;
            alglib.smp.ae_shared_pool_first_recycled(pool, ref prec2);
            while( prec2!=null )
            {
                if( prec2.recval.ival==100 )
                {
                    val100cnt = val100cnt+1;
                }
                if( prec2.recval.ival==101 )
                {
                    val101cnt = val101cnt+1;
                }
                if( prec2.recval.ival==102 )
                {
                    val102cnt = val102cnt+1;
                }
                alglib.smp.ae_shared_pool_next_recycled(pool, ref prec2);
            }
            if( (val100cnt!=1 || val101cnt!=1) || val102cnt!=1 )
            {
                return result;
            }
            val100cnt = 0;
            val101cnt = 0;
            val102cnt = 0;
            alglib.smp.ae_shared_pool_first_recycled(pool, ref prec2);
            while( prec2!=null )
            {
                if( prec2.recval.ival==100 )
                {
                    val100cnt = val100cnt+1;
                }
                if( prec2.recval.ival==101 )
                {
                    val101cnt = val101cnt+1;
                }
                if( prec2.recval.ival==102 )
                {
                    val102cnt = val102cnt+1;
                }
                alglib.smp.ae_shared_pool_next_recycled(pool, ref prec2);
            }
            if( (val100cnt!=1 || val101cnt!=1) || val102cnt!=1 )
            {
                return result;
            }
            
            //
            // Test 6: pool itself can be pooled
            // * pool can be seeded with another pool
            // * smart pointers to pool are correctly handled
            // * pool correctly returns different references on "retrieve":
            //   * we retrieve, modify and recycle back to PPool0
            //   * we retrieve from PPool1 - unmodified value is returned
            //   * we retrievefrom PPool0  - modified value is returned
            //
            createpoolandrecords(seedrec2, seedrec2copy, pool);
            alglib.smp.ae_shared_pool_set_seed(pool2, pool);
            if( ppool0!=null || ppool1!=null )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_retrieve(pool2, ref ppool0);
            alglib.smp.ae_shared_pool_retrieve(pool2, ref ppool1);
            if( !(ppool0!=null && ppool1!=null) )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_retrieve(ppool0, ref p0);
            p0.recval.ival = p0.recval.ival+1;
            tmpval = p0.recval.ival;
            alglib.smp.ae_shared_pool_recycle(ppool0, ref p0);
            alglib.smp.ae_shared_pool_retrieve(ppool1, ref p1);
            if( p1.recval.ival==tmpval )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_recycle(ppool1, ref p1);
            alglib.smp.ae_shared_pool_retrieve(ppool0, ref p0);
            if( p0.recval.ival!=tmpval )
            {
                return result;
            }
            
            //
            // Test 7: pools which are fields of records are correctly handled
            // * pool can be seeded with record which has initialized pool as its field
            // * when record is retrieved from pool, its fields are correctly copied (including
            //   fields which are pools)
            //
            createpoolandrecords(seedrec2, seedrec2copy, pool);
            tmpval = 99;
            seedrec1.ival = tmpval;
            alglib.smp.ae_shared_pool_set_seed(seedrec2.pool, seedrec1);
            alglib.smp.ae_shared_pool_set_seed(pool, seedrec2);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p0);
            alglib.smp.ae_shared_pool_retrieve(p0.pool, ref q0);
            q0.ival = tmpval-1;
            alglib.smp.ae_shared_pool_recycle(p0.pool, ref q0);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p1);
            alglib.smp.ae_shared_pool_retrieve(p1.pool, ref q1);
            if( q1.ival!=tmpval )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_recycle(p1.pool, ref q1);
            alglib.smp.ae_shared_pool_retrieve(p0.pool, ref q0);
            if( q0.ival!=tmpval-1 )
            {
                return result;
            }
            
            //
            // Test 8: after call to PoolReset(), call to PoolFirstRecycled() returns null references
            //
            createpoolandrecords(seedrec2, seedrec2copy, pool);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p0);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p1);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p2);
            alglib.smp.ae_shared_pool_recycle(pool, ref p1);
            alglib.smp.ae_shared_pool_recycle(pool, ref p2);
            alglib.smp.ae_shared_pool_recycle(pool, ref p0);
            alglib.smp.ae_shared_pool_first_recycled(pool, ref p0);
            if( !(p0!=null) )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_next_recycled(pool, ref p0);
            if( !(p0!=null) )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_next_recycled(pool, ref p0);
            if( !(p0!=null) )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_next_recycled(pool, ref p0);
            if( p0!=null )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_reset(pool);
            alglib.smp.ae_shared_pool_first_recycled(pool, ref p0);
            if( p0!=null )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_next_recycled(pool, ref p0);
            if( p0!=null )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_next_recycled(pool, ref p0);
            if( p0!=null )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_next_recycled(pool, ref p0);
            if( p0!=null )
            {
                return result;
            }
            
            //
            // Test 9: invalid pointer is recognized as non-null (we do not reference it, just test)
            //
            createpoolandrecords(seedrec2, seedrec2copy, pool);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p0);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p1);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p2);
            alglib.smp.ae_shared_pool_recycle(pool, ref p1);
            alglib.smp.ae_shared_pool_recycle(pool, ref p2);
            alglib.smp.ae_shared_pool_recycle(pool, ref p0);
            alglib.smp.ae_shared_pool_first_recycled(pool, ref p0);
            if( !(p0!=null) )
            {
                return result;
            }
            alglib.smp.ae_shared_pool_clear_recycled(pool);
            if( !(p0!=null) )
            {
                return result;
            }
            
            //
            // Test 9: non-null pointer is nulled by calling SetNull()
            //
            createpoolandrecords(seedrec2, seedrec2copy, pool);
            alglib.smp.ae_shared_pool_retrieve(pool, ref p0);
            if( !(p0!=null) )
            {
                return result;
            }
            p0 = null;
            if( p0!=null )
            {
                return result;
            }
            result = false;
            return result;
        }


        private static bool testsharedpool(bool silent)
        {
            bool result = new bool();

            result = !sharedpoolerrors();
            if( !silent )
            {
                if( result )
                {
                    System.Console.Write("SHARED POOL:                             OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("SHARED POOL:                             FAILED");
                    System.Console.WriteLine();
                }
            }
            return result;
        }


        /*************************************************************************
        Tests for SMP functions

        testSort0: sort function
        *************************************************************************/
        private static void testsort0func(int[] a,
            int[] buf,
            int idx0,
            int idx2)
        {
            int idx1 = 0;

            if( idx2<=idx0+1 )
            {
                return;
            }
            idx1 = (idx0+idx2)/2;
            testsort0func(a, buf, idx0, idx1);
            testsort0func(a, buf, idx1, idx2);
            mergesortedarrays(a, buf, idx0, idx1, idx2);
        }


        /*************************************************************************
        testSort0: recursive sorting by splitting array into two subarrays.
        Returns True on success, False on failure.
        *************************************************************************/
        private static bool performtestsort0()
        {
            bool result = new bool();
            int[] a = new int[0];
            int[] buf = new int[0];
            int i = 0;
            int k = 0;
            int t = 0;
            int n = 0;

            n = 100000;
            a = new int[n];
            buf = new int[n];
            for(i=0; i<=n-1; i++)
            {
                a[i] = i;
            }
            for(i=0; i<=n-1; i++)
            {
                k = math.randominteger(n);
                if( k!=i )
                {
                    t = a[i];
                    a[i] = a[k];
                    a[k] = t;
                }
            }
            testsort0func(a, buf, 0, n);
            result = true;
            for(i=0; i<=n-1; i++)
            {
                result = result && a[i]==i;
            }
            return result;
        }


        /*************************************************************************
        TestSort0: sort function
        *************************************************************************/
        private static void testsort1func(int[] a,
            int[] buf,
            int idx0,
            int idx2,
            bool usesmp)
        {
            int idxa = 0;
            int idxb = 0;
            int idxc = 0;
            int cnt4 = 0;

            if( idx2<=idx0+1 )
            {
                return;
            }
            if( idx2==idx0+2 )
            {
                mergesortedarrays(a, buf, idx0, idx0+1, idx0+2);
                return;
            }
            if( idx2==idx0+3 )
            {
                mergesortedarrays(a, buf, idx0+0, idx0+1, idx0+2);
                mergesortedarrays(a, buf, idx0+0, idx0+2, idx0+3);
                return;
            }
            if( idx2==idx0+4 )
            {
                mergesortedarrays(a, buf, idx0+0, idx0+1, idx0+2);
                mergesortedarrays(a, buf, idx0+2, idx0+3, idx0+4);
                mergesortedarrays(a, buf, idx0+0, idx0+2, idx0+4);
                return;
            }
            cnt4 = (idx2-idx0)/4;
            idxa = idx0+cnt4;
            idxb = idx0+2*cnt4;
            idxc = idx0+3*cnt4;
            testsort1func(a, buf, idx0, idxa, usesmp);
            testsort1func(a, buf, idxa, idxb, usesmp);
            testsort1func(a, buf, idxb, idxc, usesmp);
            testsort1func(a, buf, idxc, idx2, usesmp);
            mergesortedarrays(a, buf, idx0, idxa, idxb);
            mergesortedarrays(a, buf, idxb, idxc, idx2);
            mergesortedarrays(a, buf, idx0, idxb, idx2);
        }


        /*************************************************************************
        TestSort0: recursive sorting by splitting array into 4 subarrays.

        Sorting is performed in three rounds:
        * parallel sorting of randomly permuted array
        * result is randomly shuffled and sequentially sorted
        * result is randomly shuffled (again) and sorted in parallel mode (again)

        The idea of such "multitry sort" is that we test ability of  SMP  core  to
        interleave highly parallel parts of code with long sequential parts.

        Returns True on success, False on failure.
        *************************************************************************/
        private static bool performtestsort1()
        {
            bool result = new bool();
            int[] a = new int[0];
            int[] buf = new int[0];
            int i = 0;
            int k = 0;
            int t = 0;
            int n = 0;

            
            //
            // Generate array
            //
            n = 100000;
            a = new int[n];
            buf = new int[n];
            for(i=0; i<=n-1; i++)
            {
                a[i] = i;
            }
            
            //
            // round 0: parallel sorting of randomly permuted array
            //
            for(i=0; i<=n-1; i++)
            {
                k = math.randominteger(n);
                if( k!=i )
                {
                    t = a[i];
                    a[i] = a[k];
                    a[k] = t;
                }
            }
            testsort1func(a, buf, 0, n, true);
            
            //
            // round 1: result is randomly shuffled and sequentially sorted
            //
            for(i=0; i<=n-1; i++)
            {
                k = math.randominteger(n);
                if( k!=i )
                {
                    t = a[i];
                    a[i] = a[k];
                    a[k] = t;
                }
            }
            testsort1func(a, buf, 0, n, false);
            
            //
            // round 2: result is randomly shuffled (again) and sorted in parallel mode (again)
            //
            for(i=0; i<=n-1; i++)
            {
                k = math.randominteger(n);
                if( k!=i )
                {
                    t = a[i];
                    a[i] = a[k];
                    a[k] = t;
                }
            }
            testsort1func(a, buf, 0, n, true);
            
            //
            // Test
            //
            result = true;
            for(i=0; i<=n-1; i++)
            {
                result = result && a[i]==i;
            }
            return result;
        }


        /*************************************************************************
        Tests for SMP functions

        testSort2: sort function
        *************************************************************************/
        private static void testsort2func(int[] a,
            int[] buf,
            int idx0,
            int idx2)
        {
            int idx1 = 0;

            if( idx2<=idx0+1 )
            {
                return;
            }
            idx1 = idx0+1+math.randominteger(idx2-idx0-1);
            testsort0func(a, buf, idx0, idx1);
            testsort0func(a, buf, idx1, idx2);
            mergesortedarrays(a, buf, idx0, idx1, idx2);
        }


        /*************************************************************************
        testSort2: recursive sorting by splitting array into two subarrays of
        different length (main difference from testsort0).
        Returns True on success, False on failure.
        *************************************************************************/
        private static bool performtestsort2()
        {
            bool result = new bool();
            int[] a = new int[0];
            int[] buf = new int[0];
            int i = 0;
            int k = 0;
            int t = 0;
            int n = 0;

            n = 100000;
            a = new int[n];
            buf = new int[n];
            for(i=0; i<=n-1; i++)
            {
                a[i] = i;
            }
            for(i=0; i<=n-1; i++)
            {
                k = math.randominteger(n);
                if( k!=i )
                {
                    t = a[i];
                    a[i] = a[k];
                    a[k] = t;
                }
            }
            testsort2func(a, buf, 0, n);
            result = true;
            for(i=0; i<=n-1; i++)
            {
                result = result && a[i]==i;
            }
            return result;
        }


        /*************************************************************************
        TestPoolSum: summation with pool

        We perform summation of 500000 numbers (each of them is equal to 1) in the
        recurrent manner, by accumulation of result in the pool.

        This test checks pool ability to handle continuous stream of operations.

        Returns True on success, False on failure.
        *************************************************************************/
        private static bool performtestpoolsum()
        {
            bool result = new bool();
            alglib.smp.shared_pool pool = new alglib.smp.shared_pool();
            poolsummand ptr = null;
            poolsummand seed = new poolsummand();
            int n = 0;
            int sum = 0;

            n = 500000;
            seed.val = 0;
            alglib.smp.ae_shared_pool_set_seed(pool, seed);
            parallelpoolsum(pool, 0, n);
            sum = 0;
            alglib.smp.ae_shared_pool_first_recycled(pool, ref ptr);
            while( ptr!=null )
            {
                sum = sum+ptr.val;
                alglib.smp.ae_shared_pool_next_recycled(pool, ref ptr);
            }
            result = sum==n;
            return result;
        }


        /*************************************************************************
        Summation routune for parallel summation test.
        *************************************************************************/
        private static void parallelpoolsum(alglib.smp.shared_pool sumpool,
            int ind0,
            int ind1)
        {
            int i = 0;
            poolsummand ptr = null;

            if( ind1-ind0<=2 )
            {
                alglib.smp.ae_shared_pool_retrieve(sumpool, ref ptr);
                ptr.val = ptr.val+ind1-ind0;
                alglib.smp.ae_shared_pool_recycle(sumpool, ref ptr);
            }
            else
            {
                i = (ind0+ind1)/2;
                parallelpoolsum(sumpool, ind0, i);
                parallelpoolsum(sumpool, i, ind1);
            }
        }


        /*************************************************************************
        This function merges sorted A[Idx0,Idx1) and A[Idx1,Idx2) into sorted array
        A[Idx0,Idx2) using corresponding elements of Buf.
        *************************************************************************/
        private static void mergesortedarrays(int[] a,
            int[] buf,
            int idx0,
            int idx1,
            int idx2)
        {
            int srcleft = 0;
            int srcright = 0;
            int dst = 0;

            srcleft = idx0;
            srcright = idx1;
            dst = idx0;
            while( true )
            {
                if( srcleft==idx1 && srcright==idx2 )
                {
                    break;
                }
                if( srcleft==idx1 )
                {
                    buf[dst] = a[srcright];
                    srcright = srcright+1;
                    dst = dst+1;
                    continue;
                }
                if( srcright==idx2 )
                {
                    buf[dst] = a[srcleft];
                    srcleft = srcleft+1;
                    dst = dst+1;
                    continue;
                }
                if( a[srcleft]<a[srcright] )
                {
                    buf[dst] = a[srcleft];
                    srcleft = srcleft+1;
                    dst = dst+1;
                }
                else
                {
                    buf[dst] = a[srcright];
                    srcright = srcright+1;
                    dst = dst+1;
                }
            }
            for(dst=idx0; dst<=idx2-1; dst++)
            {
                a[dst] = buf[dst];
            }
        }


        private static bool testsmp(bool silent)
        {
            bool result = new bool();
            bool t0 = new bool();
            bool t1 = new bool();
            bool t2 = new bool();
            bool ts = new bool();

            t0 = performtestsort0();
            t1 = performtestsort1();
            t2 = performtestsort2();
            ts = performtestpoolsum();
            result = ((t0 && t1) && t2) && ts;
            if( !silent )
            {
                if( result )
                {
                    System.Console.Write("SMP FUNCTIONS:                           OK");
                    System.Console.WriteLine();
                }
                else
                {
                    System.Console.Write("SMP FUNCTIONS:                           FAILED");
                    System.Console.WriteLine();
                    System.Console.Write("* TEST SORT0 (sorting, split-2)          ");
                    if( t0 )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* TEST SORT1 (sorting, split-4)          ");
                    if( t1 )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* TEST SORT2 (sorting, split-2, unequal) ");
                    if( t2 )
                    {
                        System.Console.Write("OK");
                        System.Console.WriteLine();
                    }
                    else
                    {
                        System.Console.Write("FAILED");
                        System.Console.WriteLine();
                    }
                    System.Console.Write("* TEST POOLSUM (accumulation with pool)  ");
                    if( ts )
                    {
                        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 seq_test, TestDelegate smp_test, bool use_smp, ref int sticky)
    {
        try
        {
            alglib.math.rndobject = new System.Random(seed);
            TestDelegate test = use_smp ? smp_test : seq_test;
            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;
        bool use_smp;
#if AE_TEST_SMP
        use_smp = true;
#else
        use_smp = false;
#endif
        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;
        
        System.Console.WriteLine("SEED: {0}", seed);
        System.Console.WriteLine("CORES: {0}", alglib.smp.cores_count);
        System.Console.WriteLine("TESTING MODE: {0}", use_smp ? "parallel" : "sequential");
    
        try
        {
            if( alglib.smpselftests.runtests() )
                System.Console.WriteLine("SMP self tests                   OK");
            else
            {
                System.Console.WriteLine("SMP self tests                   FAILED");
                result = 1;
            }
                        if( call_unittest(seed, new TestDelegate(alglib.testhqrndunit.testhqrnd), new TestDelegate(alglib.testhqrndunit._pexec_testhqrnd), use_smp, 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), new TestDelegate(alglib.testtsortunit._pexec_testtsort), use_smp, 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), new TestDelegate(alglib.testnearestneighborunit._pexec_testnearestneighbor), use_smp, 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), new TestDelegate(alglib.testablasunit._pexec_testablas), use_smp, 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), new TestDelegate(alglib.testbasestatunit._pexec_testbasestat), use_smp, 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), new TestDelegate(alglib.testbdssunit._pexec_testbdss), use_smp, 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.testblasunit.testblas), new TestDelegate(alglib.testblasunit._pexec_testblas), use_smp, 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.testclusteringunit.testclustering), new TestDelegate(alglib.testclusteringunit._pexec_testclustering), use_smp, ref result) )
                System.Console.WriteLine("clustering                       OK");
            else
            {
                System.Console.Write("clustering                       FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testdforestunit.testdforest), new TestDelegate(alglib.testdforestunit._pexec_testdforest), use_smp, 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.testgammafuncunit.testgammafunc), new TestDelegate(alglib.testgammafuncunit._pexec_testgammafunc), use_smp, 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.testhblasunit.testhblas), new TestDelegate(alglib.testhblasunit._pexec_testhblas), use_smp, 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), new TestDelegate(alglib.testreflectionsunit._pexec_testreflections), use_smp, 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), new TestDelegate(alglib.testcreflectionsunit._pexec_testcreflections), use_smp, 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), new TestDelegate(alglib.testsblasunit._pexec_testsblas), use_smp, 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), new TestDelegate(alglib.testortfacunit._pexec_testortfac), use_smp, 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.testbdsvdunit.testbdsvd), new TestDelegate(alglib.testbdsvdunit._pexec_testbdsvd), use_smp, 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), new TestDelegate(alglib.testsvdunit._pexec_testsvd), use_smp, 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), new TestDelegate(alglib.testlinregunit._pexec_testlinreg), use_smp, 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.testfiltersunit.testfilters), new TestDelegate(alglib.testfiltersunit._pexec_testfilters), use_smp, ref result) )
                System.Console.WriteLine("filters                          OK");
            else
            {
                System.Console.Write("filters                          FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testevdunit.testevd), new TestDelegate(alglib.testevdunit._pexec_testevd), use_smp, 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), new TestDelegate(alglib.testmatgenunit._pexec_testmatgen), use_smp, 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.testsparseunit.testsparse), new TestDelegate(alglib.testsparseunit._pexec_testsparse), use_smp, ref result) )
                System.Console.WriteLine("sparse                           OK");
            else
            {
                System.Console.Write("sparse                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testtrfacunit.testtrfac), new TestDelegate(alglib.testtrfacunit._pexec_testtrfac), use_smp, 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), new TestDelegate(alglib.testtrlinsolveunit._pexec_testtrlinsolve), use_smp, 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), new TestDelegate(alglib.testsafesolveunit._pexec_testsafesolve), use_smp, 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), new TestDelegate(alglib.testrcondunit._pexec_testrcond), use_smp, 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), new TestDelegate(alglib.testmatinvunit._pexec_testmatinv), use_smp, 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), new TestDelegate(alglib.testldaunit._pexec_testlda), use_smp, 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.testmlpbaseunit.testmlpbase), new TestDelegate(alglib.testmlpbaseunit._pexec_testmlpbase), use_smp, ref result) )
                System.Console.WriteLine("mlpbase                          OK");
            else
            {
                System.Console.Write("mlpbase                          FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testxblasunit.testxblas), new TestDelegate(alglib.testxblasunit._pexec_testxblas), use_smp, 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), new TestDelegate(alglib.testdensesolverunit._pexec_testdensesolver), use_smp, 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.testoptservunit.testoptserv), new TestDelegate(alglib.testoptservunit._pexec_testoptserv), use_smp, ref result) )
                System.Console.WriteLine("optserv                          OK");
            else
            {
                System.Console.Write("optserv                          FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testfblsunit.testfbls), new TestDelegate(alglib.testfblsunit._pexec_testfbls), use_smp, 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.testcqmodelsunit.testcqmodels), new TestDelegate(alglib.testcqmodelsunit._pexec_testcqmodels), use_smp, ref result) )
                System.Console.WriteLine("cqmodels                         OK");
            else
            {
                System.Console.Write("cqmodels                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testsnnlsunit.testsnnls), new TestDelegate(alglib.testsnnlsunit._pexec_testsnnls), use_smp, ref result) )
                System.Console.WriteLine("snnls                            OK");
            else
            {
                System.Console.Write("snnls                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testsactivesetsunit.testsactivesets), new TestDelegate(alglib.testsactivesetsunit._pexec_testsactivesets), use_smp, ref result) )
                System.Console.WriteLine("sactivesets                      OK");
            else
            {
                System.Console.Write("sactivesets                      FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testlinminunit.testlinmin), new TestDelegate(alglib.testlinminunit._pexec_testlinmin), use_smp, 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), new TestDelegate(alglib.testmincgunit._pexec_testmincg), use_smp, 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), new TestDelegate(alglib.testminbleicunit._pexec_testminbleic), use_smp, 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), new TestDelegate(alglib.testmcpdunit._pexec_testmcpd), use_smp, 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.testmlpeunit.testmlpe), new TestDelegate(alglib.testmlpeunit._pexec_testmlpe), use_smp, 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.testminlbfgsunit.testminlbfgs), new TestDelegate(alglib.testminlbfgsunit._pexec_testminlbfgs), use_smp, 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), new TestDelegate(alglib.testmlptrainunit._pexec_testmlptrain), use_smp, 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.testpcaunit.testpca), new TestDelegate(alglib.testpcaunit._pexec_testpca), use_smp, 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), new TestDelegate(alglib.testodesolverunit._pexec_testodesolver), use_smp, 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), new TestDelegate(alglib.testfftunit._pexec_testfft), use_smp, 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), new TestDelegate(alglib.testconvunit._pexec_testconv), use_smp, 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), new TestDelegate(alglib.testcorrunit._pexec_testcorr), use_smp, 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), new TestDelegate(alglib.testfhtunit._pexec_testfht), use_smp, 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), new TestDelegate(alglib.testgqunit._pexec_testgq), use_smp, 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), new TestDelegate(alglib.testgkqunit._pexec_testgkq), use_smp, 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), new TestDelegate(alglib.testautogkunit._pexec_testautogk), use_smp, 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), new TestDelegate(alglib.testidwintunit._pexec_testidwint), use_smp, 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), new TestDelegate(alglib.testratintunit._pexec_testratint), use_smp, 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), new TestDelegate(alglib.testpolintunit._pexec_testpolint), use_smp, 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), new TestDelegate(alglib.testspline1dunit._pexec_testspline1d), use_smp, 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.testnormestimatorunit.testnormestimator), new TestDelegate(alglib.testnormestimatorunit._pexec_testnormestimator), use_smp, ref result) )
                System.Console.WriteLine("normestimator                    OK");
            else
            {
                System.Console.Write("normestimator                    FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testminqpunit.testminqp), new TestDelegate(alglib.testminqpunit._pexec_testminqp), use_smp, ref result) )
                System.Console.WriteLine("minqp                            OK");
            else
            {
                System.Console.Write("minqp                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testminlmunit.testminlm), new TestDelegate(alglib.testminlmunit._pexec_testminlm), use_smp, 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), new TestDelegate(alglib.testlsfitunit._pexec_testlsfit), use_smp, 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.testparametricunit.testparametric), new TestDelegate(alglib.testparametricunit._pexec_testparametric), use_smp, ref result) )
                System.Console.WriteLine("parametric                       OK");
            else
            {
                System.Console.Write("parametric                       FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testlinlsqrunit.testlinlsqr), new TestDelegate(alglib.testlinlsqrunit._pexec_testlinlsqr), use_smp, ref result) )
                System.Console.WriteLine("linlsqr                          OK");
            else
            {
                System.Console.Write("linlsqr                          FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testrbfunit.testrbf), new TestDelegate(alglib.testrbfunit._pexec_testrbf), use_smp, ref result) )
                System.Console.WriteLine("rbf                              OK");
            else
            {
                System.Console.Write("rbf                              FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testspline2dunit.testspline2d), new TestDelegate(alglib.testspline2dunit._pexec_testspline2d), use_smp, 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.testspline3dunit.testspline3d), new TestDelegate(alglib.testspline3dunit._pexec_testspline3d), use_smp, ref result) )
                System.Console.WriteLine("spline3d                         OK");
            else
            {
                System.Console.Write("spline3d                         FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testspdgevdunit.testspdgevd), new TestDelegate(alglib.testspdgevdunit._pexec_testspdgevd), use_smp, 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), new TestDelegate(alglib.testinverseupdateunit._pexec_testinverseupdate), use_smp, 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), new TestDelegate(alglib.testschurunit._pexec_testschur), use_smp, 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.testminnlcunit.testminnlc), new TestDelegate(alglib.testminnlcunit._pexec_testminnlc), use_smp, ref result) )
                System.Console.WriteLine("minnlc                           OK");
            else
            {
                System.Console.Write("minnlc                           FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testminnsunit.testminns), new TestDelegate(alglib.testminnsunit._pexec_testminns), use_smp, ref result) )
                System.Console.WriteLine("minns                            OK");
            else
            {
                System.Console.Write("minns                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testlincgunit.testlincg), new TestDelegate(alglib.testlincgunit._pexec_testlincg), use_smp, ref result) )
                System.Console.WriteLine("lincg                            OK");
            else
            {
                System.Console.Write("lincg                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testnlequnit.testnleq), new TestDelegate(alglib.testnlequnit._pexec_testnleq), use_smp, 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.testpolynomialsolverunit.testpolynomialsolver), new TestDelegate(alglib.testpolynomialsolverunit._pexec_testpolynomialsolver), use_smp, ref result) )
                System.Console.WriteLine("polynomialsolver                 OK");
            else
            {
                System.Console.Write("polynomialsolver                 FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testchebyshevunit.testchebyshev), new TestDelegate(alglib.testchebyshevunit._pexec_testchebyshev), use_smp, 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), new TestDelegate(alglib.testhermiteunit._pexec_testhermite), use_smp, 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), new TestDelegate(alglib.testlaguerreunit._pexec_testlaguerre), use_smp, 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), new TestDelegate(alglib.testlegendreunit._pexec_testlegendre), use_smp, 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.teststestunit.teststest), new TestDelegate(alglib.teststestunit._pexec_teststest), use_smp, ref result) )
                System.Console.WriteLine("stest                            OK");
            else
            {
                System.Console.Write("stest                            FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.teststudentttestsunit.teststudentttests), new TestDelegate(alglib.teststudentttestsunit._pexec_teststudentttests), use_smp, ref result) )
                System.Console.WriteLine("studentttests                    OK");
            else
            {
                System.Console.Write("studentttests                    FAILED(seed=");
                System.Console.Write("{0,0:d}", seed);
                System.Console.WriteLine(")");
            }
            if( call_unittest(seed, new TestDelegate(alglib.testalglibbasicsunit.testalglibbasics), new TestDelegate(alglib.testalglibbasicsunit._pexec_testalglibbasics), use_smp, 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;
	}
}

