/*
 * @(#)F.java    2.5.0   5 September 2006
 *
 * Copyright 2006
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs;

import edu.neu.ccs.util.*;

/**
 * <p>Class <code>F</code> is a collection of static methods
 * that make it easy to create and manipulate functions of one
 * argument, that is, objects that implement the interface
 * <code>Function.OneArg</code>.  Such function objects must
 * have a method of the form:</p>
 *
 * <p><code>public double evaluate(double x)</code></p>
 *
 * <p>This class defines methods that make all functions of
 * one argument in the following packages into objects:</p>
 *
 * <ul>
 *   <li><code>java.lang.Math</code></li>
 *   <li><code>edu.neu.ccs.util.MathUtilities</code></li>
 * </ul>
 *
 * <p>In addition, the class defines methods to add, subtract,
 * multiply, divide, and compose functions of one argument.</p>
 *
 * <p>Finally, some additional useful methods are defined.</p>
 *
 * <p>Class <code>F</code> cannot be instantiated.</p>
 *
 * <p>In 2.5.0, made a slight change to the random function
 * and removed the related functional method randomDouble.</p>
 * 
 * @author  Richard Rasala
 * @version 2.5.0
 * @since   2.5.0
 */
public class F {

    // Prevent instantiation
    private F() {}
    
    
    /** The constant log(2). */
    private static final double logOf2 = Math.log(2);
    
    
    /** The constant log(10). */
    private static final double logOf10 = Math.log(10);
    
    
    /** The identity function x --> x. */
    public static final Function.OneArg identity = new Function.OneArg() {
        public double evaluate(double x) { return x; }
    };
    
    
    /** The function x --> abs(x). */
    public static final Function.OneArg abs = new Function.OneArg() {
        public double evaluate(double x) { return Math.abs(x); }
    };
    
    
    /** The function x --> ceil(x). */
    public static final Function.OneArg ceil = new Function.OneArg() {
        public double evaluate(double x) { return Math.ceil(x); }
    };
    
    
    /** The function x --> floor(x). */
    public static final Function.OneArg floor = new Function.OneArg() {
        public double evaluate(double x) { return Math.floor(x); }
    };
    
    
    /** The function x --> rint(x). */
    public static final Function.OneArg rint = new Function.OneArg() {
        public double evaluate(double x) { return Math.rint(x); }
    };
    
    
    /** The function x --> sin(x). */
    public static final Function.OneArg sin = new Function.OneArg() {
        public double evaluate(double x) { return Math.sin(x); }
    };
    
    
    /** The function x --> cos(x). */
    public static final Function.OneArg cos = new Function.OneArg() {
        public double evaluate(double x) { return Math.cos(x); }
    };
    
    
    /** The function x --> tan(x). */
    public static final Function.OneArg tan = new Function.OneArg() {
        public double evaluate(double x) { return Math.tan(x); }
    };
    
    
    /** The function x --> asin(x). */
    public static final Function.OneArg asin = new Function.OneArg() {
        public double evaluate(double x) { return Math.asin(x); }
    };
    
    
    /** The function x --> acos(x). */
    public static final Function.OneArg acos = new Function.OneArg() {
        public double evaluate(double x) { return Math.acos(x); }
    };
    
    
    /** The function x --> atan(x). */
    public static final Function.OneArg atan = new Function.OneArg() {
        public double evaluate(double x) { return Math.atan(x); }
    };
    
    
    /** The function x --> sindeg(x). */
    public static final Function.OneArg sindeg = new Function.OneArg() {
        public double evaluate(double x) { return MathUtilities.sindeg(x); }
    };
    
    
    /** The function x --> cosdeg(x). */
    public static final Function.OneArg cosdeg = new Function.OneArg() {
        public double evaluate(double x) { return MathUtilities.cosdeg(x); }
    };
    
    
    /** The function x --> tandeg(x). */
    public static final Function.OneArg tandeg = new Function.OneArg() {
        public double evaluate(double x) { return MathUtilities.tandeg(x); }
    };
    
    
    /** The function x --> asindeg(x). */
    public static final Function.OneArg asindeg = new Function.OneArg() {
        public double evaluate(double x) { return MathUtilities.asindeg(x); }
    };
    
    
    /** The function x --> acosdeg(x). */
    public static final Function.OneArg acosdeg = new Function.OneArg() {
        public double evaluate(double x) { return MathUtilities.acosdeg(x); }
    };
    
    
    /** The function x --> atandeg(x). */
    public static final Function.OneArg atandeg = new Function.OneArg() {
        public double evaluate(double x) { return MathUtilities.atandeg(x); }
    };
    
    
    /** The function x --> toDegrees(x). */
    public static final Function.OneArg toDegrees = new Function.OneArg() {
        public double evaluate(double x) { return Math.toDegrees(x); }
    };
    
    
    /** The function x --> toRadians(x). */
    public static final Function.OneArg toRadians = new Function.OneArg() {
        public double evaluate(double x) { return Math.toRadians(x); }
    };
    
    
    /** The function x --> exp(x). */
    public static final Function.OneArg exp = new Function.OneArg() {
        public double evaluate(double x) { return Math.exp(x); }
    };
    
    
    /** The function x --> log(x). */
    public static final Function.OneArg log = new Function.OneArg() {
        public double evaluate(double x) { return Math.log(x); }
    };
    
    
    /** The function x --> 2-to-the-power-x = exp(x * log(2)). */
    public static final Function.OneArg exp2 = new Function.OneArg() {
        public double evaluate(double x) { return Math.exp(x * logOf2); }
    };
    
    
    /** The function x --> log-base-2(x) = log(x) / log(2). */
    public static final Function.OneArg log2 = new Function.OneArg() {
        public double evaluate(double x) { return Math.log(x) / logOf2; }
    };
    
    
    /** The function x --> 10-to-the-power-x = exp(x * log(10)). */
    public static final Function.OneArg exp10 = new Function.OneArg() {
        public double evaluate(double x) { return Math.exp(x * logOf10); }
    };
    
    
    /** The function x --> log-base-10(x) = log(x) / log(10). */
    public static final Function.OneArg log10 = new Function.OneArg() {
        public double evaluate(double x) { return Math.log(x) / logOf10; }
    };
    
    
    /** The function x --> sqrt(x). */
    public static final Function.OneArg sqrt = new Function.OneArg() {
        public double evaluate(double x) { return Math.sqrt(x); }
    };
    
    
    /**
     * <p>The function x --> Math.random() * x.</p>
     * 
     * <p>In effect, returns a random number between 0 and x.</p>
     */
    public static final Function.OneArg random = new Function.OneArg() {
        public double evaluate(double x) { return Math.random() * x; }
    };
    
    
    /**
     * The constant function x --> c.
     *
     * @param c the constant.
     */
    public static Function.OneArg constant(final double c) {
        return new Function.OneArg() {
            public double evaluate(double x) { return c; }
        };
    }
    
    
    /**
     * The scale function x --> a * x.
     *
     * @param a the scale.
     */
    public static Function.OneArg scale(final double a) {
        return new Function.OneArg() {
            public double evaluate(double x) { return a * x; }
        };
    }
    
    
    /**
     * The translate function x --> x + b.
     *
     * @param b the translate.
     */
    public static Function.OneArg translate(final double b) {
        return new Function.OneArg() {
            public double evaluate(double x) { return x + b; }
        };
    }
    
    
    /**
     * The linear function x --> a * x + b.
     *
     * @param a the scale.
     * @param b the translate.
     */
    public static Function.OneArg linear(final double a, final double b) {
        return new Function.OneArg() {
            public double evaluate(double x) { return a * x + b; }
        };
    }
    
    
    /**
     * The function x --> x-to-the-power-n
     * = <code>MathUtilities.power(x,n)</code>.
     */
    public static Function.OneArg xToPowerN(final int n) {
        return new Function.OneArg() {
            public double evaluate(double x)
                { return MathUtilities.power(x, n); }
        };
    }
    
    
    /** The function x --> x-to-the-power-a = Math.pow(x, a). */
    public static Function.OneArg xToPowerA(final double a) {
        return new Function.OneArg() {
            public double evaluate(double x)
                { return Math.pow(x, a); }
        };
    }
    
    
    /** The function x --> a-to-the-power-x = Math.pow(a, x). */
    public static Function.OneArg aToPowerX(final double a) {
        return new Function.OneArg() {
            public double evaluate(double x)
                { return Math.pow(a, x); }
        };
    }
    
    
    /** The function to add functions f,g: f + g. */
    public static Function.OneArg add
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("add", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return f.evaluate(x) + g.evaluate(x);
            }
        };
    }
    
    
    /** The function to subtract functions f,g: f - g. */
    public static Function.OneArg subtract
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("subtract", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return f.evaluate(x) - g.evaluate(x);
            }
        };
    }
    
    
    /** The function to multiply functions f,g: f * g. */
    public static Function.OneArg multiply
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("multiply", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return f.evaluate(x) * g.evaluate(x);
            }
        };
    }
    
    
    /** The function to divide functions f,g: f / g. */
    public static Function.OneArg divide
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("divide", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return f.evaluate(x) / g.evaluate(x);
            }
        };
    }
    
    
    /** The function to compose functions f,g: x --> f(g(x)). */
    public static Function.OneArg compose
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("compose", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return f.evaluate(g.evaluate(x));
            }
        };
    }
    
    
    /** The function to compute the function min(f, g). */
    public static Function.OneArg min
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("min", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return Math.min(f.evaluate(x), g.evaluate(x));
            }
        };
    }
    
    
    /** The function to compute the function max(f, g). */
    public static Function.OneArg max
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("max", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return Math.max(f.evaluate(x), g.evaluate(x));
            }
        };
    }
    
    
    /**
     * The function to compute the function f-to-the-power-n
     * = <code>MathUtilities.power(f.evaluate(x),n)</code>.
     */
    public static Function.OneArg power
        (final Function.OneArg f, final int n)
    {
        checkNull("power", f);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return MathUtilities.power(f.evaluate(x), n);
            }
        };
    }
    
    
    /**
     * The function to compute the function f-to-the-power-a
     * = <code>Math.pow(f.evaluate(x),a)</code>.
     */
    public static Function.OneArg pow
        (final Function.OneArg f, final double a)
    {
        checkNull("pow", f);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return Math.pow(f.evaluate(x), a);
            }
        };
    }
    
    
    /**
     * The function to compute the function a-to-the-power-f
     * = <code>Math.pow(a,f.evaluate(x))</code>.
     */
    public static Function.OneArg pow
        (final double a, final Function.OneArg f)
    {
        checkNull("pow", f);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return Math.pow(a, f.evaluate(x));
            }
        };
    }
    
    
    /**
     * The function to compute the function f-to-the-power-g
     * = <code>Math.pow(f.evaluate(x),g.evaluate(x))</code>.
     */
    public static Function.OneArg pow
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("pow", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return Math.pow(f.evaluate(x), g.evaluate(x));
            }
        };
    }
    
    
    /** The function to compute the function atan2(f, g). */
    public static Function.OneArg atan2
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("atan2", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return Math.atan2(f.evaluate(x), g.evaluate(x));
            }
        };
    }
    
    
    /** The function to compute the function atan2deg(f, g). */
    public static Function.OneArg atan2deg
        (final Function.OneArg f, final Function.OneArg g)
    {
        checkNull("atan2deg", f, g);
        
        return new Function.OneArg() {
            public double evaluate(double x) {
                return MathUtilities.atan2deg(f.evaluate(x), g.evaluate(x));
            }
        };
    }
    
    
    /**
     * Check for <code>null</code> function arguments and throw a
     * <code>NullPointerException</code> if necessary.</p>
     *
     * @param method the string name of the method
     * @param f the function argument
     */
    private static void checkNull
        (String method, Function.OneArg f)
    {
        if (f == null)
            throw new NullPointerException("Null function f in F." + method);
    }
    
    
    /**
     * Check for <code>null</code> function arguments and throw a
     * <code>NullPointerException</code> if necessary.</p>
     *
     * @param method the string name of the method
     * @param f the first  function argument
     * @param g the second function argument
     */
    private static void checkNull
        (String method, Function.OneArg f, Function.OneArg g)
    {
        if (f == null)
            throw new NullPointerException("Null function f in F." + method);
        
        if (g == null)
            throw new NullPointerException("Null function g in F." + method);
    }
    
}
