/// <summary>
        /// Evaluates the inverse complementary error function.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static double inverfc(double p)
        {
            if (p >= 2.0)
            {
                return(-100.0);
            }

            if (p <= 0.0)
            {
                return(100.0);
            }

            double pp = (p < 1.0) ? p : 2.0 - p;
            double t  = Math.Sqrt(-2.0 * Math.Log(pp / 2.0));
            double x  = -0.70711 * ((2.30753 + t * 0.27061) / (1.0 + t * (0.99229 + t * 0.04481)) - t);

            for (int j = 0; j < 2; j++)
            {
                double err = erfc(x) - pp;

                x += err / (1.12837916709551257 * Math.Exp(-MathTool.Sqr(x)) - x * err);
            }

            return(p < 1.0 ? x : -x);
        }
        public static Vector3D SphericalInterpolation(Vector3D start, Vector3D end, double p)
        {
            double cosΩ = MathTool.Clamp(Vector3D.DotProduct(start, end) / (start.Length * end.Length), -1.0, 1.0);
            double Ω    = Math.Acos(cosΩ);

            return(SphericalInterpolation(start, end, Ω, Math.Sin(Ω), p));
        }
Beispiel #3
0
        public static double[] FromAHSVInterpolation(double[] minimum, double p, double[] maximum)
        {
            double[] b = minimum;
            double[] e = maximum;

            double b1 = b[1] >= 0 ? b[1] : e[1];
            double e1 = e[1] >= 0 ? e[1] : b[1];

            if (b1 >= 0 && e1 >= 0 && System.Math.Abs(e1 - b1) > 180)
            {
                if (e1 > b1)
                {
                    b1 += 360;
                }
                else
                {
                    e1 += 360;
                }
            }

            p = MathTool.Clamp(p, 0.0, 1.0);

            double[] ahsv =
            {
                b[0] * (1.0 - p) + e[0] * p,
                b1 *(1.0 - p) + e1 * p,
                b[2] * (1.0 - p) + e[2] * p,
                b[3] * (1.0 - p) + e[3] * p
            };

            return(ahsv);
        }
Beispiel #4
0
        public static Color FromARGBInterpolation(Color min, double p, Color max)
        {
            double q = MathTool.Clamp(p, 0.0, 1.0);

#if false
            double amin = min.A / 255.0;
            double rmin = amin > 0 ? min.R * amin : min.R;
            double gmin = amin > 0 ? min.G * amin : min.G;
            double bmin = amin > 0 ? min.B * amin : min.B;

            double amax = max.A / 255.0;
            double rmax = amax > 0 ? max.R * amax : max.R;
            double gmax = amax > 0 ? max.G * amax : max.G;
            double bmax = amax > 0 ? max.B * amax : max.B;

            double a = amin * (1.0 - q) + amax * q;
            double r = (rmin * (1.0 - q) + rmax * q) / a;
            double g = (gmin * (1.0 - q) + gmax * q) / a;
            double b = (bmin * (1.0 - q) + bmax * q) / a;
            return(Color.FromArgb((byte)(a * 255.0), (byte)r, (byte)g, (byte)b));
#else
            double a = min.A * (1.0 - q) + max.A * q;
            double r = min.R * (1.0 - q) + max.R * q;
            double g = min.G * (1.0 - q) + max.G * q;
            double b = min.B * (1.0 - q) + max.B * q;

            return(Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b));
#endif
        }
Beispiel #5
0
        public static void Clear(this WriteableBitmap bitmap, Color color, Rect rect)
        {
            int pixelRight  = Math.Max(bitmap.PixelWidth - 1, 0);
            int pixelBottom = Math.Max(bitmap.PixelHeight - 1, 0);
            int left        = (int)MathTool.Clamp(Math.Floor(rect.Left), 0.0, pixelRight);
            int right       = (int)MathTool.Clamp(Math.Floor(rect.Right), 0.0, pixelRight);
            int top         = (int)MathTool.Clamp(Math.Floor(rect.Top), 0.0, pixelBottom);
            int bottom      = (int)MathTool.Clamp(Math.Floor(rect.Left), 0.0, pixelBottom);

            int stride = bitmap.PixelWidth - (left + right);
            int a      = top * bitmap.PixelWidth + left;
            int c      = (color.A << 24) + (color.R << 16) + (color.G << 8) + color.B;

            for (int i = top; i <= bottom; ++i)
            {
                for (int j = left; j <= right; ++j)
                {
#if SILVERLIGHT
                    bitmap.Pixels[i] = c;
#else
                    //bitmap.BackBuffer[i]=c;
#endif
                }

                a += stride;
            }
        }
        /// <summary>
        /// Calculates the moving standard deviation.
        /// </summary>
        /// <remarks>
        /// NaN items in the sequence are ignores.
        /// </remarks>
        /// <param name="sequence"></param>
        /// <param name="period"></param>
        /// <param name="strict">Require at least period good values</param>
        /// <returns>The moving standard deviation</returns>
        public static IEnumerable <double> StandardDeviation(IEnumerable <double> sequence, int period, bool strict = false)
        {
            double[] buffer = new double[period];
            int      cursor = 0;
            int      count  = 0;

            double sum    = 0.0;
            double stddev = double.NaN;

            for (int i = 0; i < period; ++i)
            {
                buffer[i] = double.NaN;
            }

            foreach (double value in sequence)
            {
                if (!double.IsNaN(value))
                {
                    #region update buffer and sum
                    if (double.IsNaN(buffer[cursor]))
                    {
                        count = Math.Min(count + 1, period);
                    }
                    else
                    {
                        sum -= buffer[cursor];
                    }

                    buffer[cursor] = value;
                    cursor         = (cursor + 1) % period;
                    sum           += value;
                    #endregion

                    #region update stddev
                    if (count > period || !strict)
                    {
                        double mean = sum / (double)count;
                        double t    = 0.0;

                        for (int i = 0; i < period; ++i)
                        {
                            if (!double.IsNaN(buffer[i]))
                            {
                                t += MathTool.Sqr(buffer[i] - mean);
                            }
                        }

                        stddev = Math.Sqrt(t / (double)count);
                    }
                    else
                    {
                        stddev = double.NaN;
                    }
                    #endregion
                }

                yield return(stddev);
            }
        }
        public static List <int> Flatten(int index, int count, Func <int, double> X, Func <int, double> Y, double resolution)
        {
            List <int> ret = new List <int>();

            Flatten(ret, X, Y, index, count - 1, MathTool.Sqr(resolution));
            ret.Add(count - 1);

            return(ret);
        }
        public static double GreatCircleDistance(Point start, Point end)
        {
            double cosφs = Math.Cos(MathTool.Radians(start.Y));
            double sinφs = Math.Sin(MathTool.Radians(start.Y));

            double sinφf = Math.Sin(MathTool.Radians(end.Y));
            double cosφf = Math.Cos(MathTool.Radians(end.Y));

            double cosΔλ = Math.Cos(MathTool.Radians(end.X - start.X));
            double sinΔλ = Math.Sin(MathTool.Radians(end.X - start.X));

            return(Math.Atan2(MathTool.Sqr(cosφf * sinΔλ) + MathTool.Sqr(cosφs * sinφf - sinφs * cosφf * cosΔλ), sinφs * sinφf + cosφs * cosφf * cosΔλ));
        }
Beispiel #9
0
        public static Color Lightened(this Color color, double p)
        {
            double[] ahsl = color.AHSL();

            if (p < 0.0)
            {
                return(FromAHSL(ahsl[0], ahsl[1], ahsl[2], ahsl[3] * (1.0 - MathTool.Clamp(-p, 0.0, 1.0))));
            }
            else
            {
                return(FromAHSL(ahsl[0], ahsl[1], ahsl[2], ahsl[3] + MathTool.Clamp(p, 0.0, 1.0) * (1.0 - ahsl[3])));
            }
        }
        // outputs (p) cp cp p cp cp p cp cp p..
        public static void SplinePoints(IList <Point> ret, int count, Func <int, double> x, Func <int, double> y)
        {
            // flatten at resolution

            var    ea  = new Point(x(1) - x(0), y(1) - y(0));
            double eam = MathTool.Hypot(ea.X, ea.Y);

            ret.Add(new Point(x(0), y(0))); // output point[0]
            ret.Add(new Point(x(0), y(0))); // output a bogus control point

            for (int i = 2; i < count; ++i)
            {
                var    eb  = new Point(x(i) - x(i - 1), y(i) - y(i - 1));
                double ebm = MathTool.Hypot(eb.X, eb.Y);

                double dot = (ea.X * eb.X + ea.Y * eb.Y) / (eam * ebm);
                //double tension = 0.35*Math.Exp(0.5 * (dot + 1.0))/(Math.resolution-1);
                double tension = 0.5 * (dot + 1.0);
                tension = 1.0 + tension * (Math.E - 1.0);
                tension = 0.5 * Math.Log(tension);

                var d = new Point(x(i) - x(i - 2), y(i) - y(i - 2));

                double dm = MathTool.Hypot(d.X, d.Y);

                if (dm == 0)
                {
                    dm = double.PositiveInfinity;
                }

                ret.Add(new Point(x(i - 1) - eam * tension * d.X / dm, y(i - 1) - eam * tension * d.Y / dm));
                ret.Add(new Point(x(i - 1), y(i - 1)));
                ret.Add(new Point(x(i - 1) + ebm * tension * d.X / dm, y(i - 1) + ebm * tension * d.Y / dm));

                ea  = eb;
                eam = ebm;
            }

            ret.Add(new Point(x(count - 1), y(count - 1))); // output a bogus control point
            ret.Add(new Point(x(count - 1), y(count - 1))); // output point[0]
        }
Beispiel #11
0
        public static IList <int> StripCoincident(int count, Func <int, double> x, Func <int, double> y, double resolution)
        {
            IList <int> ret = new List <int>();

            for (int i = 0; i < count;)
            {
                double cx = x(i);
                double cy = y(i);

                int j = i;
                ret.Add(j);

                for (++i; i < count && MathTool.Hypot(x(j) - cx, y(j) - cy) < resolution; ++i)
                {
                    cx = (cx * (double)(i - j) + x(i)) / (double)(i - j + 1);
                    cy = (cy * (double)(i - j) + y(i)) / (double)(i - j + 1);
                }
            }

            return(ret);
        }
        /// <summary>
        /// Smooth a line into a PathFigure of quadratic and cubic bezier splines.
        /// </summary>
        /// <param name="count">Number of equator in line.</param>
        /// <param name="x">x coordinate of ith point.</param>
        /// <param name="y">y coordinate of ith point.</param>
        /// <returns>Smooth line.</returns>
        public static PathFigure Smooth(int count, Func <int, double> x, Func <int, double> y)
        {
            var pathFigure = new PathFigure()
            {
                StartPoint = new Point(x(0), y(0))
            };

            int n = count - 1;

            Point pa;
            Point pb = new Point(x(0), y(0));
            Point pc = new Point(x(0 + 1), y(0 + 1));
            Point pd = new Point(x(0 + 2), y(0 + 2));

            Point  eab;
            double mab;

            Point  ebc = new Point(pc.X - pb.X, pc.Y - pb.Y);
            double mbc = MathTool.Hypot(ebc.X, ebc.Y);

            Point  ecd = new Point(pd.X - pc.X, pd.Y - pc.Y);
            double mcd = MathTool.Hypot(ecd.X, ecd.Y);

            Point  tc;
            double sc;

            double alpha = 0.15;
            double beta  = 0.45;

            {
                // first quadratic patch

                tc = new Point(pd.X - pb.X, pd.Y - pb.Y); { double m = MathTool.Hypot(tc.X, tc.Y); tc.X /= m; tc.Y /= m; }
                sc = 0.5 + (ebc.X * ecd.X + ebc.Y * ecd.Y) / (2.0 * mbc * mcd);

                QuadraticBezierSegment segment = new QuadraticBezierSegment()
                {
                    Point1 = new Point(pc.X - tc.X * (alpha + beta * sc) * mbc, pc.Y - tc.Y * (alpha + beta * sc) * mbc),
                    Point2 = pc
                };

                if (double.IsNaN(segment.Point1.X) || double.IsNaN(segment.Point1.Y))
                {
                }

                if (double.IsNaN(segment.Point2.X) || double.IsNaN(segment.Point2.Y))
                {
                }

                pathFigure.Segments.Add(segment);
            }

            for (int i = 1; i < n - 1; ++i)
            {
                // intermediate cubic patches

                pa = pb;
                pb = pc;
                pc = pd;
                pd = new Point(x(i + 2), y(i + 2));

                eab = ebc;
                mab = mbc;

                ebc = ecd;
                mbc = mcd;

                ecd = new Point(pd.X - pc.X, pd.Y - pc.Y);
                mcd = MathTool.Hypot(ecd.X, ecd.Y);

                Point  tb = tc;
                double sb = sc;

                tc = new Point(pd.X - pb.X, pd.Y - pb.Y); { double m = MathTool.Hypot(tc.X, tc.Y); tc.X /= m; tc.Y /= m; }
                sc = 0.5 + (ebc.X * ecd.X + ebc.Y * ecd.Y) / (2.0 * mbc * mcd);

                BezierSegment segment = new BezierSegment()
                {
                    Point1 = new Point(pb.X + tb.X * (alpha + beta * sb) * mbc, pb.Y + tb.Y * (alpha + beta * sb) * mbc),
                    Point2 = new Point(pc.X - tc.X * (alpha + beta * sc) * mbc, pc.Y - tc.Y * (alpha + beta * sc) * mbc),
                    Point3 = pc
                };
                pathFigure.Segments.Add(segment);

                if (double.IsNaN(segment.Point1.X) || double.IsNaN(segment.Point1.Y))
                {
                }

                if (double.IsNaN(segment.Point2.X) || double.IsNaN(segment.Point2.Y))
                {
                }

                if (double.IsNaN(segment.Point3.X) || double.IsNaN(segment.Point3.Y))
                {
                }
            }

            {
                // last quadratic patch

                pa = pb;
                pb = pc;
                pc = pd;
                // pd
                eab = ebc;
                mab = mbc;

                ebc = ecd;
                mbc = mcd;

                Point  tb = tc;
                double sb = sc;

                QuadraticBezierSegment segment = new QuadraticBezierSegment()
                {
                    Point1 = new Point(pb.X + tb.X * (alpha + beta * sb) * mbc, pb.Y + tb.Y * (alpha + beta * sb) * mbc),
                    Point2 = pc
                };
                pathFigure.Segments.Add(segment);


                if (double.IsNaN(segment.Point1.X) || double.IsNaN(segment.Point1.Y))
                {
                }

                if (double.IsNaN(segment.Point2.X) || double.IsNaN(segment.Point2.Y))
                {
                }
            }

            return(pathFigure);
        }
        private static int clipBezierCurve(Rect rc, Point p0, Point p1, Point p2, Point p3)
        {
            #region trivial inclusion and exclusion by axis aligned bounding rectangle
            double xmin = MathTool.Min(p0.X, p1.X, p2.X, p3.X);

            if (xmin > rc.Right)
            {
                return(-1);
            }

            double ymin = MathTool.Min(p0.Y, p1.Y, p2.Y, p3.Y);

            if (ymin > rc.Bottom)
            {
                return(-1);
            }

            double xmax = MathTool.Max(p0.X, p1.X, p2.X, p3.X);

            if (xmax < rc.Left)
            {
                return(-1);
            }

            double ymax = MathTool.Max(p0.Y, p1.Y, p2.Y, p3.Y);

            if (ymax < rc.Top)
            {
                return(-1);
            }

            if (xmin >= rc.Left && ymin <= rc.Bottom && xmax <= rc.Right && ymin >= rc.Top)
            {
                return(0);
            }
            #endregion

            double p30x = p3.X - p0.X;
            double p30y = p3.Y - p0.Y;
            double s1   = p30x * (p1.Y - p0.Y) - p30y * (p1.X - p0.X);
            double s2   = p30x * (p2.Y - p0.Y) - p30y * (p2.X - p0.X);

            if (Math.Sign(s1) == Math.Sign(s2) /* control points are on the same side of the support */)
            {
                if (rc.Contains(p0) || rc.Contains(p1) || rc.Contains(p2) || rc.Contains(p3))
                {
                    if (rc.Contains(p0) && rc.Contains(p1) && rc.Contains(p2) && rc.Contains(p3))
                    {
                        return(0);   // entire convex hull is contained in bounding rect
                    }

                    return(1);   // some points are inside, sone points are outside
                }

                Point[]            polygon = { p0, p1, p2, p3 };
                Func <int, double> X       = (i) => polygon[i].X;
                Func <int, double> Y       = (i) => polygon[i].Y;

                if (GeometryTool.PolygonContains(4, X, Y, new Point(rc.Left, rc.Top)) ||
                    GeometryTool.PolygonContains(4, X, Y, new Point(rc.Left, rc.Bottom)) ||
                    GeometryTool.PolygonContains(4, X, Y, new Point(rc.Right, rc.Bottom)) ||
                    GeometryTool.PolygonContains(4, X, Y, new Point(rc.Right, rc.Top)))
                {
                    return(1);       // rc intersects curve - keep on clipping
                }

                return(-1);          // rc does not intersect curve
            }
            else
            {
                // need the convex hull to work out which points to test, but for the moment, just
                // assume that the whole curve is clipped


                return(1);
            }
        }
Beispiel #14
0
            public Brent(Func <double, double> function, double xa, double xb, double xc, double tolerance)
            {
                const int    ITMAX = 100;
                const double CGOLD = 0.3819660;
                const double ZEPS  = 0.00001;// Double.Epsilon;// *1.0e-3;

                double a = (xa < xc ? xa : xc);
                double b = (xa > xc ? xa : xc);
                double d = 0.0;
                double e = 0.0;

                double u;
                double fu;

                double v  = xb;
                double fv = function(v);

                double w  = v;
                double fw = fv;

                double x  = v;
                double fx = fv;

                for (int i = 0; i < ITMAX; ++i)
                {
                    double xm   = 0.5 * (a + b);
                    double tol1 = tolerance * Math.Abs(x) + ZEPS;
                    double tol2 = 2.0 * tol1;

                    if (Math.Abs(x - xm) <= (tol2 - 0.5 * (b - a)))
                    {
                        xmin = x;
                        fmin = fx;

                        return;
                    }

                    if (Math.Abs(e) > tol1)
                    {
                        double r = (x - w) * (fx - fv);
                        double q = (x - v) * (fx - fw);
                        double p = (x - v) * q - (x - w) * r;

                        q = 2.0 * (q - r);

                        if (q > 0.0)
                        {
                            p = -p;
                        }

                        q = Math.Abs(q);
                        double etemp = e;
                        e = d;

                        if (Math.Abs(p) >= Math.Abs(0.5 * q * etemp) || p <= q * (a - x) || p >= q * (b - x))
                        {
                            d = CGOLD * (e = (x >= xm ? a - x : b - x));
                        }
                        else
                        {
                            d = p / q;
                            u = x + d;

                            if (u - a < tol2 || b - u < tol2)
                            {
                                d = MathTool.CopySign(tol1, xm - x);
                            }
                        }
                    }
                    else
                    {
                        d = CGOLD * (e = (x >= xm ? a - x : b - x));
                    }

                    u  = (Math.Abs(d) >= tol1 ? x + d : x + MathTool.CopySign(tol1, d));
                    fu = function(u);

                    if (fu <= fx)
                    {
                        if (u >= x)
                        {
                            a = x;
                        }
                        else
                        {
                            b = x;
                        }

                        shift(ref v, ref w, ref x, u);
                        shift(ref fv, ref fw, ref fx, fu);
                    }
                    else
                    {
                        if (u < x)
                        {
                            a = u;
                        }
                        else
                        {
                            b = u;
                        }

                        if (fu <= fw || w == x)
                        {
                            v  = w;
                            w  = u;
                            fv = fw;
                            fw = fu;
                        }
                        else
                        {
                            if (fu <= fv || v == x || v == w)
                            {
                                v  = u;
                                fv = fu;
                            }
                        }
                    }
                }

                //throw("Too many iterations in brent");
                xmin = double.NaN;
                fmin = double.NaN;
            }
 private static double Distance2(Point p0, Point p1)
 {
     return(MathTool.Sqr(p1.X - p0.X) + MathTool.Sqr(p1.Y - p0.Y));
 }
Beispiel #16
0
        public static double[] Minimise(Func <double[], double> function, double[] pp, double tolerance)
        {
            int    n     = pp.Length;
            int    ITMAX = 200;
            double TINY  = 1.0e-25;

            double[,] ximat = new double[n, n];

            for (int i = 0; i < n; i++)
            {
                ximat[i, i] = 1.0;
            }

            double[] p   = new double[n];
            double[] pt  = new double[n];
            double[] ptt = new double[n];

            double[] xi   = new double[n];
            double   fret = function(p);

            for (int i = 0; i < n; ++i)
            {
                p[i]  = pp[i];
                pt[i] = p[i];
            }

            for (int i = 0; ; ++i)
            {
                double fp = fret;
                double fptt;
                int    ibig = 0;
                double del  = 0.0;

                for (int j = 0; j < n; ++j)
                {
                    for (int k = 0; k < n; ++k)
                    {
                        xi[k] = ximat[k, j];
                    }

                    fptt = fret;
                    fret = linmin(function, p, xi);

                    if (fptt - fret > del)
                    {
                        del  = fptt - fret;
                        ibig = j + 1;
                    }
                }

                if (2.0 * (fp - fret) <= tolerance * (Math.Abs(fp) + Math.Abs(fret)) + TINY)
                {
                    return(p);
                }

                if (i == ITMAX)
                {
                    return(null);    //throw ("powell exceeding maximum iterations.");
                }

                for (int j = 0; j < n; j++)
                {
                    ptt[j] = 2.0 * p[j] - pt[j];
                    xi[j]  = p[j] - pt[j];
                    pt[j]  = p[j];
                }

                fptt = function(ptt);

                if (fptt < fp)
                {
                    double t = 2.0 * (fp - 2.0 * fret + fptt) * MathTool.Sqr(fp - fret - del) - del * MathTool.Sqr(fp - fptt);

                    if (t < 0.0)
                    {
                        fret = linmin(function, p, xi);

                        for (int j = 0; j < n; j++)
                        {
                            ximat[j, ibig - 1] = ximat[j, n - 1];
                            ximat[j, n - 1]    = xi[j];
                        }
                    }
                }
            }
        }
        private static IList <int> Flatten(IList <int> result, Func <int, double> X, Func <int, double> Y, int b, int e, double E2)
        {
            if (b >= e)
            {
                if (b == e)
                {
                    result.Add(b);
                }

                return(result);
            }

            double Xb = X(b);
            double Yb = Y(b);
            double Xe = X(e);
            double Ye = Y(e);

            int    si = -1;
            double se = E2;
            double L  = MathTool.Hypot2(Xe - Xb, Ye - Yb);

            if (L == 0.0)
            {
                for (int i = b + 1; i < e; ++i)
                {
                    double Xi = X(i);
                    double Yi = Y(i);

                    if (!double.IsNaN(Xi) && !double.IsNaN(Yi))
                    {
                        double err = MathTool.Hypot2(Xe - X(i), Ye - Y(i));

                        if (err >= se)
                        {
                            se = err;
                            si = i;
                        }
                    }
                }
            }
            else
            {
                double vx = Xe - Xb;
                double vy = Ye - Yb;

                for (int i = b + 1; i < e; ++i)
                {
                    double Xi = X(i);
                    double Yi = Y(i);

                    if (!double.IsNaN(Xi) && !double.IsNaN(Yi))
                    {
                        double err = double.NaN;
                        double wx  = Xi - Xb;
                        double wy  = Yi - Yb;

                        double c1 = vx * wx + vy * wy;

                        if (c1 <= 0.0)
                        {
                            err = MathTool.Hypot2(Xb - Xi, Yb - Yi);
                        }
                        else
                        {
                            double c2 = vx * vx + vy * vy;

                            if (c2 <= c1)
                            {
                                err = MathTool.Hypot2(Xe - Xi, Ye - Yi);
                            }
                            else
                            {
                                double p = c1 / c2;
                                err = MathTool.Hypot2(Xb + p * vx - Xi, Yb + p * vy - Yi);
                            }
                        }

                        if (err >= se)
                        {
                            se = err;
                            si = i;
                        }
                    }
                }
            }

            if (si == -1)
            {
                result.Add(b);
                //               result.Add(e);
            }
            else
            {
                // the error point cannot be the first or last point - that wouldn't make any sense

                Debug.Assert(si != b);
                Debug.Assert(si != e);

                Flatten(result, X, Y, b, si, E2);    // big error - flatten from b to si
                Flatten(result, X, Y, si, e, E2);    // big error - flatten from si+1 to e
            }

            return(result);
        }
Beispiel #18
0
        public static double[] Bracket(Func <double, double> function, double a, double b)
        {
            double xa;
            double xb;
            double xc;
            double fa;
            double fb;
            double fc;

            double GLIMIT = 100.0;
            double TINY   = 1.0e-20;

            xa = a;
            fa = function(xa);

            xb = b;
            fb = function(xb);

            if (fb > fa)
            {
                swap(ref xa, ref xb);
                swap(ref fa, ref fb);
            }

            xc = xb + MathTool.PHI * (xb - xa);
            fc = function(xc);

            double fu;

            while (fb > fc)
            {
                double r    = (xb - xa) * (fb - fc);
                double q    = (xb - xc) * (fb - fa);
                double u    = xb - ((xb - xc) * q - (xb - xa) * r) / (2.0 * MathTool.CopySign(Math.Max(Math.Abs(q - r), TINY), q - r));
                double ulim = xb + GLIMIT * (xc - xb);

                if ((xb - u) * (u - xc) > 0.0)
                {
                    fu = function(u);

                    if (fu < fc)
                    {
                        xa = xb;
                        fa = fb;

                        xb = u;
                        fb = fu;

                        return(new double[] { xa, fa, xb, fb, xc, fc });
                    }

                    if (fu > fb)
                    {
                        xc = u;
                        fc = fu;

                        return(new double[] { xa, fa, xb, fb, xc, fc });
                    }

                    u  = xc + MathTool.PHI * (xc - xb);
                    fu = function(u);
                }
                else
                {
                    if ((xc - u) * (u - ulim) > 0.0)
                    {
                        fu = function(u);

                        if (fu < fc)
                        {
                            shift(ref xb, ref xc, ref u, u + MathTool.PHI * (u - xc));
                            shift(ref fb, ref fc, ref fu, function(u));
                        }
                    }
                    else
                    {
                        if ((u - ulim) * (ulim - xc) >= 0.0)
                        {
                            u  = ulim;
                            fu = function(u);
                        }
                        else
                        {
                            u  = xc + MathTool.PHI * (xc - xb);
                            fu = function(u);
                        }
                    }
                }

                shift(ref xa, ref xb, ref xc, u);
                shift(ref fa, ref fb, ref fc, fu);
            }

            return(new double[] { xa, fa, xb, fb, xc, fc });
        }
        /// <summary>
        /// Creates coefficients for the polynomial least squares fit y=a0+a1x+a2x^2..
        /// </summary>
        /// <param name="n">Number of items in the categorySeries.</param>
        /// <param name="k">Polynomial order.</param>
        /// <param name="x">Delegate which returns the nth x value.</param>
        /// <param name="y">Delegate which returns the nth y value.</param>
        /// <remarks>
        /// If either of the x or y for a given point are NaN, the point is not taken
        /// into account for the fitting operation. Infinite values are valid, but
        /// are likely to result in significant numerical instability.
        /// <para>
        /// Weisstein, Eric W. "Least Squares Fitting--Polynomial." From MathWorld--A Wolfram Web Resource.
        /// http://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html
        /// </para>
        /// </remarks>
        /// <returns>Polynomial coefficients a0, a1, .. ak as an array of doubles or null if no solution.</returns>
        public static double[] PolynomialFit(int n, int k, Func <int, double> x, Func <int, double> y)
        {
            double[] ps = new double[1 + 2 * k];
            double[,] A = new double[k + 1, k + 1];
            double[] B = new double[k + 1];

            int N = 0;

            for (int i = 0; i < n; ++i)
            {
                double s  = 1.0;
                double xi = x(i);

                if (!double.IsNaN(xi))
                {
                    for (int p = 0; p < ps.Length; ++p)
                    {
                        ps[p] += s;
                        s     *= xi;
                    }

                    if (!double.IsNaN(y(i)))
                    {
                        ++N;
                    }
                }
            }

            if (N < k)
            {
                return(null);
            }

            for (int i = 0; i <= k; ++i)
            {
                for (int j = 0; j <= k; ++j)
                {
                    A[i, j] = ps[i + j];
                }
            }

            for (int i = 0; i < n; ++i)
            {
                double xi = x(i);

                if (!double.IsNaN(xi))
                {
                    for (int j = 0; j <= k; ++j)
                    {
                        double yi = y(i);

                        if (!double.IsNaN(yi))
                        {
                            B[j] += (Math.Pow(xi, j) * yi);
                        }
                    }
                }
            }

            return(MathTool.Solve(A, B) ? B : null);
        }