import java.util.*;

/**
 *
 * @author Bobby Martin
 * @copyright 2000
 * @version 0.1
 * @since 0.1
 */
public class MathUtil
{
    /**
     * Returns a number with the same sign as num.  Will only return
     * -1, 0, or 1
     */
    public static short sign(double num)
    {
        return (num < 0) ? (short)-1 : ((num > 0) ? (short)1 : (short)0);
    }

    /**
     * Solves quadratic equations of the form a*x^2 + b*x + c.
     * @param a the factor of x^2
     * @param b the factor of x
     * @param c the constant
     * @returns double[] array of solutions (2 elements)
     */
    public double[] quadraticSolutions(double a, double b, double c)
    {
        double bFactor = -b/2*a;
        double otherFactor = (b*b - 4*a*c)/2*a;
        double[] solutions = new double[] {bFactor + otherFactor,
                                           bFactor - otherFactor};

        return solutions;
    }

    /**
     * If num is within tolerance of an integer, the integer is returned.
     * Otherwise, num is returned.
     */
    public static double snap(double num, double tolerance)
    {
        if( Math.abs(Math.round(num) - num) < tolerance )
            return Math.round(num);
        else
            return num;
    }

    /**
     * Retrieves an integer as 4 bytes in a buffer, with the first byte
     * being at index start.
     * Uses the standard serialization format for an integer.
     */
    public static int asInt(byte[] buffer, int start)
    {
        return ((buffer[start] & 0xff) << 24) +
            ((buffer[start+1] & 0xff) << 16) +
            ((buffer[start+2] & 0xff) << 8) +
            ( buffer[start+3] & 0xff);
    }

    public static boolean equalsTolerance(double num1, double num2,
                                          double tolerance)
    {
        return Math.abs(num1 - num2) < tolerance;
    }

    /**
     * If num is within tolerance of snapNum, snapNum is returned.
     * Otherwise, num is returned.
     */
    public static double snapTo(double num, double tolerance, double snapNum)
    {
        if( Math.abs(snapNum - num) < tolerance )
            return snapNum;
        else
            return num;
    }

    public static Random getRandom()
    {
        return unsyncedRandom_;
    }

    public static double mod(double val, double modval)
    {
        double retval;
        //retval + n*modval = val, 0 <= retval < modval
        //retval = val - n*modval
        //n = (retval - val)/modval
        double n = Math.floor(val/modval);
        retval = val - n*modval;
        return retval;
    }

    protected static Random unsyncedRandom_ = new Random();

    //test code follows
    public static void main(String[] args)
    {
        testMod(47.9, 2.4, 2.3);
        testMod(-47.9, -2.4, -2.3);
        testMod(-47.9, 2.4, .1);
        testMod(-2.3, 2.4, .1);
    }

    public static boolean testMod(double val, double modval, double realretval)
    {
        final double tolerance = 0.0001;

        double retval = mod(val, modval);
        if( !equalsTolerance(retval, realretval, tolerance) )
        {
            System.err.println("Expected retval " + realretval +
                               ", got retval " + retval);
            return false;
        }

        return true;
    }
}
