// <copyright file="Evaluate.cs" company="Math.NET">
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
//
// Copyright (c) 2009-2013 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>

// <contribution>
//    CERN - European Laboratory for Particle Physics
//        http://www.docjar.com/html/api/cern/jet/math/Bessel.java.html
//        Copyright 1999 CERN - European Laboratory for Particle Physics.
//        Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose
//        is hereby granted without fee, provided that the above copyright notice appear in all copies and
//        that both that copyright notice and this permission notice appear in supporting documentation.
//        CERN makes no representations about the suitability of this software for any purpose.
//        It is provided "as is" without expressed or implied warranty.
//    TOMS757 - Uncommon Special Functions (Fortran77) by Allan McLeod
//        http://people.sc.fsu.edu/~jburkardt/f77_src/toms757/toms757.html
//    Wei Wu
//    Cephes Math Library, Stephen L. Moshier
//    ALGLIB 2.0.1, Sergey Bochkanov
// </contribution>

using System;
using Complex = System.Numerics.Complex;

// ReSharper disable CheckNamespace
namespace MathNet.Numerics
// ReSharper restore CheckNamespace
{
    /// <summary>
    /// Evaluation functions, useful for function approximation.
    /// </summary>
    public static class Evaluate
    {
        /// <summary>
        /// Evaluate a polynomial at point x.
        /// Coefficients are ordered by power with power k at index k.
        /// Example: coefficients [3,-1,2] represent y=2x^2-x+3.
        /// </summary>
        /// <param name="z">The location where to evaluate the polynomial at.</param>
        /// <param name="coefficients">The coefficients of the polynomial, coefficient for power k at index k.</param>
        [Obsolete("Use Polynomial.Evaluate instead. Will be removed in the next major version.")]
        public static double Polynomial(double z, params double[] coefficients)
        {
            return Numerics.Polynomial.Evaluate(z, coefficients);
        }

        /// <summary>
        /// Evaluate a polynomial at point x.
        /// Coefficients are ordered by power with power k at index k.
        /// Example: coefficients [3,-1,2] represent y=2x^2-x+3.
        /// </summary>
        /// <param name="z">The location where to evaluate the polynomial at.</param>
        /// <param name="coefficients">The coefficients of the polynomial, coefficient for power k at index k.</param>
        [Obsolete("Use Polynomial.Evaluate instead. Will be removed in the next major version.")]
        public static Complex Polynomial(Complex z, params double[] coefficients)
        {
            return Numerics.Polynomial.Evaluate(z, coefficients);
        }

        /// <summary>
        /// Evaluate a polynomial at point x.
        /// Coefficients are ordered by power with power k at index k.
        /// Example: coefficients [3,-1,2] represent y=2x^2-x+3.
        /// </summary>
        /// <param name="z">The location where to evaluate the polynomial at.</param>
        /// <param name="coefficients">The coefficients of the polynomial, coefficient for power k at index k.</param>
        [Obsolete("Use Polynomial.Evaluate instead. Will be removed in the next major version.")]
        public static Complex Polynomial(Complex z, params Complex[] coefficients)
        {
            return Numerics.Polynomial.Evaluate(z, coefficients);
        }

        /// <summary>
        /// Numerically stable series summation
        /// </summary>
        /// <param name="nextSummand">provides the summands sequentially</param>
        /// <returns>Sum</returns>
        internal static double Series(Func<double> nextSummand)
        {
            double compensation = 0.0;
            double current;
            const double factor = 1 << 16;

            double sum = nextSummand();

            do
            {
                // Kahan Summation
                // NOTE (ruegg): do NOT optimize. Now, how to tell that the compiler?
                current = nextSummand();
                double y = current - compensation;
                double t = sum + y;
                compensation = t - sum;
                compensation -= y;
                sum = t;
            }
            while (Math.Abs(sum) < Math.Abs(factor*current));

            return sum;
        }

        /// <summary> Evaluates the series of Chebyshev polynomials Ti at argument x/2.
        /// The series is given by
        /// <pre>
        ///       N-1
        ///        - '
        /// y  =   >   coef[i] T (x/2)
        ///        -            i
        ///       i=0
        /// </pre>
        /// Coefficients are stored in reverse order, i.e. the zero
        /// order term is last in the array.  Note N is the number of
        /// coefficients, not the order.
        /// <p/>
        /// If coefficients are for the interval a to b, x must
        /// have been transformed to x -> 2(2x - b - a)/(b-a) before
        /// entering the routine.  This maps x from (a, b) to (-1, 1),
        /// over which the Chebyshev polynomials are defined.
        /// <p/>
        /// If the coefficients are for the inverted interval, in
        /// which (a, b) is mapped to (1/b, 1/a), the transformation
        /// required is x -> 2(2ab/x - b - a)/(b-a).  If b is infinity,
        /// this becomes x -> 4a/x - 1.
        /// <p/>
        /// SPEED:
        /// <p/>
        /// Taking advantage of the recurrence properties of the
        /// Chebyshev polynomials, the routine requires one more
        /// addition per loop than evaluating a nested polynomial of
        /// the same degree.
        /// </summary>
        /// <param name="coefficients">The coefficients of the polynomial.</param>
        /// <param name="x">Argument to the polynomial.</param>
        /// <remarks>
        /// Reference: https://bpm2.svn.codeplex.com/svn/Common.Numeric/Arithmetic.cs
        /// <p/>
        /// Marked as Deprecated in
        /// http://people.apache.org/~isabel/mahout_site/mahout-matrix/apidocs/org/apache/mahout/jet/math/Arithmetic.html
        /// </remarks>
        internal static double ChebyshevA(double[] coefficients, double x)
        {
            // TODO: Unify, normalize, then make public
            double b2;

            int p = 0;

            double b0 = coefficients[p++];
            double b1 = 0.0;
            int i = coefficients.Length - 1;

            do
            {
                b2 = b1;
                b1 = b0;
                b0 = x*b1 - b2 + coefficients[p++];
            }
            while (--i > 0);

            return 0.5*(b0 - b2);
        }

        /// <summary>
        /// Summation of Chebyshev polynomials, using the Clenshaw method with Reinsch modification.
        /// </summary>
        /// <param name="n">The no. of terms in the sequence.</param>
        /// <param name="coefficients">The coefficients of the Chebyshev series, length n+1.</param>
        /// <param name="x">The value at which the series is to be evaluated.</param>
        /// <remarks>
        /// ORIGINAL AUTHOR:
        ///    Dr. Allan J. MacLeod; Dept. of Mathematics and Statistics, University of Paisley; High St., PAISLEY, SCOTLAND
        /// REFERENCES:
        ///    "An error analysis of the modified Clenshaw method for evaluating Chebyshev and Fourier series"
        ///    J. Oliver, J.I.M.A., vol. 20, 1977, pp379-391
        /// </remarks>
        internal static double ChebyshevSum(int n, double[] coefficients, double x)
        {
            // TODO: Unify, normalize, then make public

            // If |x|  < 0.6 use the standard Clenshaw method
            if (Math.Abs(x) < 0.6)
            {
                double u0 = 0.0;
                double u1 = 0.0;
                double u2 = 0.0;
                double xx = x + x;

                for (int i = n; i >= 0; i--)
                {
                    u2 = u1;
                    u1 = u0;
                    u0 = xx*u1 + coefficients[i] - u2;
                }

                return (u0 - u2)/2.0;
            }

            // If ABS ( T )  > =  0.6 use the Reinsch modification
            // T > =  0.6 code
            if (x > 0.0)
            {
                double u1 = 0.0;
                double d1 = 0.0;
                double d2 = 0.0;
                double xx = (x - 0.5) - 0.5;
                xx = xx + xx;

                for (int i = n; i >= 0; i--)
                {
                    d2 = d1;
                    double u2 = u1;
                    d1 = xx*u2 + coefficients[i] + d2;
                    u1 = d1 + u2;
                }

                return (d1 + d2)/2.0;
            }
            else
            {
                // T < =  -0.6 code
                double u1 = 0.0;
                double d1 = 0.0;
                double d2 = 0.0;
                double xx = (x + 0.5) + 0.5;
                xx = xx + xx;

                for (int i = n; i >= 0; i--)
                {
                    d2 = d1;
                    double u2 = u1;
                    d1 = xx*u2 + coefficients[i] - d2;
                    u1 = d1 - u2;
                }

                return (d1 - d2)/2.0;
            }
        }
    }
}
