public static void TestIntegration()
        {
            PZMath_integration_workspace w = new PZMath_integration_workspace();
            w.Alloc(1000);

            double result = 0, error = 0;
            double expected = -4.0;
            double alpha = 1.0;

            TestIntegrationParms parms = new TestIntegrationParms(alpha);

            PZMath_f F = new PZMath_f();
            F.f = new PZMath_delegate_f(f);
            F.Parms = parms;

            PZMath_integration.Qags(F, 0, 1, 0, 1e-7, 1000, w, ref result, ref error);

            System.Console.WriteLine ("result          = " + result);
            System.Console.WriteLine ("exact result    = " + expected);
            System.Console.WriteLine  ("estimated error = " + error);
            System.Console.WriteLine  ("actual error    = " + (result - expected));
            System.Console.WriteLine  ("intervals = " + w.size);
            System.Console.ReadLine();
            return;
        }
        public static int Qags(PZMath_f f, double a, double b,
            double epsabs, double epsrel, int limit,PZMath_integration_workspace workspace,
            ref double result, ref double abserr)
        {
            PZMath_integration_rules q = new PZMath_integration_rules(PZMath_integration.QK21);
            double area, errsum;
            double res_ext, err_ext;
            double result0 = 0.0, abserr0 = 0.0, resabs0 = 0.0, resasc0 = 0.0;
            double tolerance;

            double ertest = 0;
            double error_over_large_intervals = 0;
            double reseps = 0, abseps = 0, correc = 0;
            int ktmin = 0;
            int roundoff_type1 = 0, roundoff_type2 = 0, roundoff_type3 = 0;
            int error_type = 0, error_type2 = 0;

            int iteration = 0;

            int positive_integrand = 0;
            int extrapolate = 0;
            int disallow_extrapolation = 0;

            ExtrapolationTable table = new ExtrapolationTable();

            /* Initialize results */
            workspace.Initialise(a, b);

            result = 0;
            abserr = 0;

            if (limit > workspace.limit)
            {
                //PZMath_errno.ERROR ("iteration limit exceeds available workspace", PZMath_errno.PZMath_EINVAL) ;
                //return 0;
                return PZMath_errno.PZMath_EINVAL;
            }

            /* Test on accuracy */

            if (epsabs <= 0 && (epsrel < 50 * PZMath_machine.PZMath_DBL_EPSILON || epsrel < 0.5e-28))
            {
                //PZMath_errno.ERROR ("tolerance cannot be acheived with given epsabs and epsrel",
                //    PZMath_errno.PZMath_EBADTOL);
                //return 0;
                return PZMath_errno.PZMath_EBADTOL;
            }
            /* Perform the first integration */

            q (f, a, b, ref result0, ref abserr0, ref resabs0, ref resasc0);

            workspace.SetInitialResult (result0, abserr0);

            tolerance = System.Math.Max (epsabs, epsrel * System.Math.Abs (result0));

            if (abserr0 <= 100 * PZMath_machine.PZMath_DBL_EPSILON * resabs0 && abserr0 > tolerance)
            {
                result = result0;
                abserr = abserr0;

                //PZMath_errno.ERROR ("cannot reach tolerance because of roundoff error on first attempt", PZMath_errno.PZMath_EROUND);
                //return 0;
                return PZMath_errno.PZMath_EROUND;
            }
            else if ((abserr0 <= tolerance && abserr0 != resasc0) || abserr0 == 0.0)
            {
                result = result0;
                abserr = abserr0;

                return PZMath_errno.PZMath_SUCCESS;
            }
            else if (limit == 1)
            {
                result = result0;
                abserr = abserr0;

                //PZMath_errno.ERROR ("a maximum of one iteration was insufficient", PZMath_errno.PZMath_EMAXITER);
                //return 0;
                return PZMath_errno.PZMath_EMAXITER;
            }

            /* Initialization */

            table.InitialiseTable();
            table.AppendTable(result0);

            area = result0;
            errsum = abserr0;

            res_ext = result0;
            err_ext = PZMath_machine.PZMath_DBL_MAX;

            positive_integrand = TestPositivity (result0, resabs0);
            iteration = 1;

            do
            {
                int current_level;
                double a1, b1, a2, b2;
                double a_i = 0.0, b_i = 0.0, r_i = 0.0, e_i = 0.0;
                double area1 = 0, area2 = 0, area12 = 0;
                double error1 = 0, error2 = 0, error12 = 0;
                double resasc1 = 0.0, resasc2 = 0.0;
                double resabs1 = 0.0, resabs2 = 0.0;
                double last_e_i;

                /* Bisect the subinterval with the largest error estimate */

                workspace.Retrieve (ref a_i, ref b_i, ref r_i, ref e_i);

                current_level = workspace.level[workspace.i] + 1;

                a1 = a_i;
                b1 = 0.5 * (a_i + b_i);
                a2 = b1;
                b2 = b_i;

                iteration ++;

                q (f, a1, b1, ref area1, ref error1, ref resabs1, ref resasc1);
                q (f, a2, b2, ref area2, ref error2, ref resabs2, ref resasc2);

                area12 = area1 + area2;
                error12 = error1 + error2;
                last_e_i = e_i;

                /* Improve previous approximations to the integral and test for
                    accuracy.

                    We write these expressions in the same way as the original
                    QUADPACK code so that the rounding errors are the same, which
                    makes testing easier. */

                errsum = errsum + error12 - e_i;
                area = area + area12 - r_i;

                tolerance = System.Math.Max (epsabs, epsrel * System.Math.Abs (area));

                if (resasc1 != error1 && resasc2 != error2)
                {
                    double delta = r_i - area12;

                    if (System.Math.Abs (delta) <= 1.0e-5 * System.Math.Abs (area12) && error12 >= 0.99 * e_i)
                    {
                        if (extrapolate == 0)
                            roundoff_type1++;
                        else
                            roundoff_type2++;
                    }
                    if (iteration > 10 && error12 > e_i)
                        roundoff_type3++;
                }

                /* Test for roundoff and eventually set error flag */

                if (roundoff_type1 + roundoff_type2 >= 10 || roundoff_type3 >= 20)
                    error_type = 2;       /* round off error */

                if (roundoff_type2 >= 5)
                    error_type2 = 1;

                /* set error flag in the case of bad integrand behaviour at
                    a point of the integration range */

                if (SubintervalTooSmall (a1, a2, b2))
                    error_type = 4;

                /* append the newly-created intervals to the list */

                workspace.Update (a1, b1, area1, error1, a2, b2, area2, error2);

                if (errsum <= tolerance)
                    goto compute_result;

                if (error_type >= 1)
                    break;

                if (iteration >= limit - 1)
                {
                    error_type = 1;
                    break;
                }

                if (iteration == 2)       /* set up variables on first iteration */
                {
                    error_over_large_intervals = errsum;
                    ertest = tolerance;
                    table.AppendTable (area);
                    continue;
                }

                if (disallow_extrapolation >= 1)
                    continue;

                error_over_large_intervals += -last_e_i;

                if (current_level < workspace.maximum_level)
                    error_over_large_intervals += error12;

                if (extrapolate == 0)
                {
                    /* test whether the interval to be bisected next is the
                        smallest interval. */

                    if (workspace.LargeInterval ())
                        continue;

                    extrapolate = 1;
                    workspace.nrmax = 1;
                }

                if (error_type2 == 0 && error_over_large_intervals > ertest)
                {
                    if (workspace.IncreaseNrmax())
                        continue;
                }

                /* Perform extrapolation */

                table.AppendTable (area);

                table.Qelg(ref reseps, ref abseps);

                ktmin ++;

                if (ktmin > 5 && err_ext < 0.001 * errsum)
                    error_type = 5;

                if (abseps < err_ext)
                {
                    ktmin = 0;
                    err_ext = abseps;
                    res_ext = reseps;
                    correc = error_over_large_intervals;
                    ertest = System.Math.Max (epsabs, epsrel * System.Math.Abs (reseps));
                    if (err_ext <= ertest)
                        break;
                }

                /* Prepare bisection of the smallest interval. */

                if (table.n == 1)
                    disallow_extrapolation = 1;

                if (error_type == 5)
                    break;

                /* work on interval with largest error */

                workspace.ResetNrmax();
                extrapolate = 0;
                error_over_large_intervals = errsum;

            }
            while (iteration < limit);

            result = res_ext;
            abserr = err_ext;

            if (err_ext == PZMath_machine.PZMath_DBL_MAX)
                goto compute_result;

            if (error_type >=1 || error_type2 >= 1)
            {
                if (error_type2 >= 1)
                    err_ext += correc;

                if (error_type == 0)
                    error_type = 3;

                if (res_ext != 0.0 && area != 0.0)
                {
                    if (err_ext / System.Math.Abs (res_ext) > errsum / System.Math.Abs (area))
                        goto compute_result;
                }
                else if (err_ext > errsum)
                    goto compute_result;
                else if (area == 0.0)
                    goto return_error;
            }

            /*  Test on divergence. */

            {
                double max_area = System.Math.Max (System.Math.Abs (res_ext), System.Math.Abs (area));

                if (positive_integrand  == 0 && max_area < 0.01 * resabs0)
                    goto return_error;
            }

            {
                double ratio = res_ext / area;

                if (ratio < 0.01 || ratio > 100.0 || errsum > System.Math.Abs (area))
                error_type = 6;
            }

            goto return_error;

            compute_result:

            result = workspace.SumResults();
            abserr = errsum;

            return_error:

            if (error_type > 2)
                error_type--;

            if (error_type == 0)
                return PZMath_errno.PZMath_SUCCESS;
            else if (error_type == 1)
            {
                //PZMath_errno.ERROR("number of iterations was insufficient", PZMath_errno.PZMath_EMAXITER);
                //return 0;
                return PZMath_errno.PZMath_EMAXITER;
            }
            else if (error_type == 2)
            {
                //PZMath_errno.ERROR ("cannot reach tolerance because of roundoff error",
                //	PZMath_errno.PZMath_EROUND);
                //return 0;
                return PZMath_errno.PZMath_EROUND;
            }
            else if (error_type == 3)
            {
                //PZMath_errno.ERROR ("bad integrand behavior found in the integration interval",
                //    PZMath_errno.PZMath_ESING);
                //return 0;
                return PZMath_errno.PZMath_ESING;
            }
            else if (error_type == 4)
            {
                //PZMath_errno.ERROR ("roundoff error detected in the extrapolation table",
                //    PZMath_errno.PZMath_EROUND);
                //return 0;
                return PZMath_errno.PZMath_EROUND;
            }
            else if (error_type == 5)
            {
                //PZMath_errno.ERROR ("integral is divergent, or slowly convergent",
                //    PZMath_errno.PZMath_EDIVERGE);
                //return 0;
                return PZMath_errno.PZMath_EDIVERGE;
            }
            else
            {
                //PZMath_errno.ERROR ("could not integrate function", PZMath_errno.PZMath_EFAILED);
                //return 0;
                return PZMath_errno.PZMath_EFAILED;
            }
        }