/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Struts", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */


package org.apache.velocity.tools.view.tools;


/**
 * Tool for performing floating point math in Velocity.
 * In this version, the methods simply return null if they 
 * cannot perform the operation with the given parameters.
 *
 * @author <a href="mailto:nathan@esha.com">Nathan Bubna</a>
 * @version $Revision: 1.1 $ $Date: 2002/02/11 16:54:27 $
 */

public class MathTool
{


    /**
     * @return the sum of the two numbers or null if they're invalid
     */
    public static Double add(Object num1, Object num2)
    {
        Double d1 = toDouble(num1);
        Double d2 = toDouble(num2);
        if (d1 == null || d2 == null)
        {
            return null;
        }
        return new Double(d1.doubleValue() + d2.doubleValue());
    }


    /**
     * @return the difference of the two numbers or null if they're invalid
     */
    public static Double sub(Object num1, Object num2)
    {
        Double d1 = toDouble(num1);
        Double d2 = toDouble(num2);
        if (d1 == null || d2 == null)
        {
            return null;
        }
        return new Double(d1.doubleValue() - d2.doubleValue());
    }


    /**
     * @return the product of the two numbers or null if they're invalid
     */
    public static Double mul(Object num1, Object num2)
    {
        Double d1 = toDouble(num1);
        Double d2 = toDouble(num2);
        if (d1 == null || d2 == null)
        {
            return null;
        }
        return new Double(d1.doubleValue() * d2.doubleValue());
    }


    /**
     * @return the factor of the two numbers or null if they're invalid
     */
    public static Double div(Object num1, Object num2)
    {
        Double d1 = toDouble(num1);
        Double d2 = toDouble(num2);
        if (d1 == null || d2 == null || d2.doubleValue() == 0.0)
        {
            return null;
        }
        return new Double(d1.doubleValue() / d2.doubleValue());
    }


    /**
     * @return the greater of the two numbers or null if they're invalid
     */
    public static Double max(Object num1, Object num2)
    {
        Double d1 = toDouble(num1);
        Double d2 = toDouble(num2);
        if (d1 == null || d2 == null)
        {
            return null;
        }
        return new Double(Math.max(d1.doubleValue(), d2.doubleValue()));
    }


    /**
     * @return the minimum of the two numbers or null if they're invalid
     */
    public static Double min(Object num1, Object num2)
    {
        Double d1 = toDouble(num1);
        Double d2 = toDouble(num2);
        if (d1 == null || d2 == null)
        {
            return null;
        }
        return new Double(Math.min(d1.doubleValue(), d2.doubleValue()));
    }


    /**
     * @return the first number raised to the second or null if they're invalid
     */
    public static Double pow(Object num1, Object num2)
    {
        Double d1 = toDouble(num1);
        Double d2 = toDouble(num2);
        if (d1 == null || d2 == null)
        {
            return null;
        }
        return new Double(Math.pow(d1.doubleValue(), d2.doubleValue()));
    }


    /**
     * @return the absolute value of the number or null if it's invalid
     */
    public static Double abs(Object num)
    {
        Double d = toDouble(num);
        if (d == null)
        {
            return null;
        }
        return new Double(Math.abs(d.doubleValue()));
    }


    /**
     * Converts an object with a numeric value into a double value or
     * returns null if the object does not contain a numeric value.
     */
    public static Double toDouble(Object num)
    {
        double value;
        try
        {
            if (num instanceof Number)
            {
                value = ((Number)num).doubleValue();
            }
            else if (num instanceof String)
            {
                value = Double.parseDouble((String)num);
            }
            else
            {
                String num_string = String.valueOf(num);
                value = Double.parseDouble(num_string);
            }
        }
        catch (NumberFormatException nfe)
        {
            return null;
        }
        return new Double(value);
    }


    /**
     * Converts an object with a numeric value into a integer value or
     * returns null if the object does not contain a numeric value.
     */
    public static Integer toInt(Object num)
    {
        int value;
        try
        {
            if (num instanceof Number)
            {
                value = ((Number)num).intValue();
            }
            else if (num instanceof String)
            {
                value = (int)Double.parseDouble((String)num);
            }
            else
            {
                String num_string = String.valueOf(num);
                value = (int)Double.parseDouble(num_string);
            }
        }
        catch (NumberFormatException nfe)
        {
            return null;
        }
        return new Integer(value);
    }


    /**
     * Rounds a double to the specified number of decimal places.
     */
    public static Double roundTo(int decimals, double value)
    {
        if (decimals == 0) 
        {
            value = (int)(value + .5);
        }
        else
        {
            double shift = Math.pow(10, decimals);
            value = value * shift;
            value = (int)(value + .5);
            value = value / shift;
        }
        return new Double(value);
    }


    /**
     * Rounds a number to the given decimal place
     */
    public static Double roundTo(Object decimals, Object num)
    {
        Integer i = toInt(decimals);
        Double d = toDouble(num);
        if (i == null || d == null)
        {
            return null;
        }
        return roundTo(i.intValue(), d.doubleValue());
    }


    /**
     * Rounds a number to the nearest whole Integer
     */
    public static Integer roundToInt(Object num)
    {
        Double d = toDouble(num);
        if (d == null)
        {
            return null;
        }
        return new Integer((int)Math.rint(d.doubleValue()));
    }


    /**
     * @return a pseudo-random Double from 0.0 to 1.0
     */
    public static Double getRandom()
    {
        return new Double(Math.random());
    }


    /**
     * @return a psuedo-random integer between the two given numbers
     */
    public static Integer random(Object num1, Object num2)
    {
        Integer i1 = toInt(num1);
        Integer i2 = toInt(num2);
        if (i1 == null || i2 == null)
        {
            return null;
        }
        //get the difference
        double diff = i2.intValue() - i1.intValue();
        //multiply the difference by a pseudo-random 
        //double from 0.0 to 1.0 and round to the nearest int
        int random = (int)Math.rint(diff * Math.random());
        //add the first value to the random int and return as an Integer
        return new Integer(random + i1.intValue());
    }


}
