package JSci.maths;

/**
* The extra math library.
* Provides extra functions not in java.lang.Math class.
* This class cannot be subclassed or instantiated because all methods are static.
* @version 1.2
* @author Mark Hale
*/
public final class ExtraMath extends AbstractMath {
        private ExtraMath() {}

        /**
        * Rounds a number to so many significant figures.
        * @param x a number to be rounded.
        * @param significant number of significant figures to round to.
        */
        public static float round(float x, final int significant) {
                if(x == 0.0f)
                        return x;
                final float mag=Math.abs(x);
                int places;
                float tmp, factor;
                if(mag < 1.0f) {
                        tmp=10.0f*mag;
                        for(places=1; tmp<1.0f; places++)
                                tmp*=10.0f;
                        factor=(float)Math.pow(10.0f, places+significant-1);
                } else {
                        tmp=mag/10.0f;
                        for(places=1; tmp>1.0f; places++)
                                tmp/=10.0f;
                        factor=(float)Math.pow(10.0f, significant-places);
                }
                return Math.round(x*factor)/factor;
        }
        /**
        * Returns sqrt(x<sup>2</sup>+y<sup>2</sup>).
        */
        public static double hypot(final double x,final double y) {
                final double xAbs=Math.abs(x);
                final double yAbs=Math.abs(y);
                if(xAbs==0.0 && yAbs==0.0)
                        return 0.0;
                else if(xAbs<yAbs)
                        return yAbs*Math.sqrt(1.0+(x/y)*(x/y));
                else
                        return xAbs*Math.sqrt(1.0+(y/x)*(y/x));
        }
        /**
        * Returns a<sup>b</sup>.
        * @param a an integer.
        * @param b a positive integer.
        */
        public static int pow(int a, int b) {
                if(b < 0)
                        throw new IllegalArgumentException(b+" must be a positive integer.");
                else if(b == 0)
                        return 1;
                else {
                        if(a == 0)
                                return 0;
                        else if(a == 1)
                                return 1;
                        else if(a == 2)
                                return 1<<b;
                        else {
                                for(int i=1; i<b; i++)
                                        a *= a;
                                return a;
                        }
                }
        }
        /**
        * Returns 2<sup>a</sup>.
        * @param a a positive integer.
        */
        public static int pow2(int a) {
                return 1<<a;
        }
        /**
        * Returns the factorial.
        * (Wrapper for the gamma function).
        * @see SpecialMath#gamma
        * @param x a double.
        */
        public static double factorial(double x) {
                return SpecialMath.gamma(x+1.0);
        }
        /**
        * Returns the natural logarithm of the factorial.
        * (Wrapper for the log gamma function).
        * @see SpecialMath#logGamma
        * @param x a double.
        */
        public static double logFactorial(double x) {
                return SpecialMath.logGamma(x+1.0);
        }
        /**
        * Returns the binomial coefficient (n k).
        * Uses Pascal's recursion formula.
        * @planetmath PascalsRule
        * @param n an integer.
        * @param k an integer.
        */
        public static int binomial(int n,int k) {
                if(k == n || k ==0)
                        return 1;
                else if(n == 0)
                        return 1;
                else
                        return binomial(n-1, k-1)+binomial(n-1, k);
        }
        /**
        * Returns the binomial coefficient (n k).
        * Uses gamma functions.
        * @planetmath BinomialCoefficient
        * @param n a double.
        * @param k a double.
        */
        public static double binomial(double n,double k) {
                return Math.exp(SpecialMath.logGamma(n+1.0)-SpecialMath.logGamma(k+1.0)-SpecialMath.logGamma(n-k+1.0));
        }
        /**
        * Returns the hyperbolic sine of a double.
        * @param x a double.
        */
        public static double sinh(double x) {
                return (Math.exp(x)-Math.exp(-x))/2.0;
        }
        /**
        * Returns the hyperbolic cosine of a double.
        * @param x a double.
        */
        public static double cosh(double x) {
                return (Math.exp(x)+Math.exp(-x))/2.0;
        }
        /**
        * Returns the hyperbolic tangent of a double.
        * @param x a double.
        */
        public static double tanh(double x) {
                return sinh(x)/cosh(x);
        }
        /**
        * Returns the arc hyperbolic sine of a double,
        * in the range of -<img border=0 alt="infinity" src="doc-files/infinity.gif"> through <img border=0 alt="infinity" src="doc-files/infinity.gif">.
        * @param x a double.
        */
        public static double asinh(double x) {
                return Math.log(x+Math.sqrt(x*x+1.0));
        }
        /**
        * Returns the arc hyperbolic cosine of a double,
        * in the range of 0.0 through <img border=0 alt="infinity" src="doc-files/infinity.gif">.
        * @param x a double.
        */
        public static double acosh(double x) {
                return Math.log(x+Math.sqrt(x*x-1.0));
        }
        /**
        * Returns the arc hyperbolic tangent of a double,
        * in the range of -<img border=0 alt="infinity" src="doc-files/infinity.gif"> through <img border=0 alt="infinity" src="doc-files/infinity.gif">.
        * @param x a double.
        */
        public static double atanh(double x) {
                return Math.log((1.0+x)/(1.0-x))/2.0;
        }
}

