package math.equation;

import java.math.BigInteger;

import math.Maths;
import math.number.BigRational;

/**
 * Taylor series of<br />
 *    e^x     = 1 + x + x^2/2! + x^3/3! + x^4/4! + ...<br />
 *  sin x     = x - x^3/3! + x^5/5! - x^7/7! + ...<br />
 *  e^x sin x = x + x^2 + x^3/3 -x^5/30 -x^6/90 + ...<br />
 * <p>
 * n = 5<br />
 * e^x = 1/24 x^4 + 1/6 x^3 + 1/2 x^2 + 1 x + 1<br />
 * sin(x) = -1/6 x^3 + 1 x<br />
 * e^x sin(x) = -1/24 x^5 + 1/3 x^3 + 1 x^2 + 1 x<br />
 * @author Daniel Weschke
 */
public class Taylor{

    /**
     * Taylor series for e^x = 1 + x + x^2/2! + x^3/3! + x^4/4! + ...
     * @param n
     * @return e^x
     * @see Math#exp(double)
     */
    public static PolynomialRat e(int n){
        PolynomialRat e = PolynomialRat.ZERO;
        for (int i = 0; i < n; i++) {
            BigRational coef = new BigRational(BigInteger.ONE, Maths.factorial(i));
            PolynomialRat term = new PolynomialRat(coef, i);
            e = e.plus(term);
        }
        return e;
    }
    
    /**
     * Compute the e^x using the Taylor series:
     * e^x = 1 + x + x^2/2! + x^3/3! + ...
     * and also using Math.exp. Catastrophic cancellation
     * occurs if x is a large negative number.
     * 
     * x = 0
     * 1.0
     * 1.0
     * 
     * x = 1
     * 2.7182818284590455
     * 2.7182818284590455
     *
     * x = 50
     * 5.184705528587075E21
     * 5.184705528587072E21
     *
     * x = -1
     * 0.36787944117144245
     * 0.36787944117144233
     *
     * x = -10
     * 4.539992962303128E-5
     * 4.539992976248485E-5
     *
     * x = -30
     * -3.06681235635622E-5
     * 9.357622968840175E-14
     *
     * x = -50
     * 11072.93338289197
     * 1.9287498479639178E-22
     * @param x
     * @return e^x
     */
    public static double ed(double x){
        double sum  = 0.0;
        double term = 1.0;
        int i;
        for(i=1; sum!=sum+term; i++){
            sum  = sum + term;
            term = term * x / i;
//            System.out.println(i + " " + term + " " + sum);
        }
        System.out.println(sum+" ("+Math.exp(x)+")");
        return sum;
   }

    /**
     * Taylor series for sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ...
     * @param n
     * @return sin(x)
     */
    public static PolynomialRat sin(int n){
        PolynomialRat sin = PolynomialRat.ZERO;
        for (int i = 0; i < n; i++) {
            BigRational coef;
            if     (i % 4 == 0) coef = BigRational.ZERO;
            else if(i % 4 == 1) coef = new BigRational(BigInteger.ONE, Maths.factorial(i));
            else if(i % 4 == 2) coef = BigRational.ZERO;
            else                coef = new BigRational(new BigInteger("-1"), Maths.factorial(i));
            PolynomialRat term = new PolynomialRat(coef, i);
            sin = sin.plus(term);
        }
        return sin;
    }

    public static void main(String[] args) { 

        // number of terms
//        int N = Integer.parseInt(args[0]);
    	int N = 5;

        PolynomialRat e = e(N);
        System.out.println("e^x = " + e);

        PolynomialRat sin = sin(N);;
        System.out.println("sin(x) = " + sin);

        System.out.println("e^x sin(x) = " + e.times(sin).truncate(N));

   }

}