/*
 * ModeShape (http://www.modeshape.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.modeshape.common.math;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Random;
import org.modeshape.common.annotation.Immutable;

/**
 * The set of mathematic operations for a particular class of values. This is useful for generic classes that must work with one
 * of the {@link Number} subclasses.
 * 
 * @param <T> the numeric class, usually a subclass of {@link Number} (although this is not required)
 */
@Immutable
public interface MathOperations<T> {

    /**
     * Return the class that these operations operate upon.
     * 
     * @return the class
     */
    public Class<T> getOperandClass();

    /**
     * Add the two operands and return the sum. The {@link #createZeroValue() zero value} is used in place of any operand that is
     * null.
     * 
     * @param value1 the first operand
     * @param value2 the second operand
     * @return the sum of the two operands.
     */
    public T add( T value1,
                  T value2 );

    /**
     * Subtract the second operand from the first, and return the difference. The {@link #createZeroValue() zero value} is used in
     * place of any operand that is null.
     * 
     * @param value1 the first operand
     * @param value2 the second operand
     * @return the difference between the two operands.
     */
    public T subtract( T value1,
                       T value2 );

    /**
     * Multiply the two operands and return the product. The {@link #createZeroValue() zero value} is used in place of any operand
     * that is null.
     * 
     * @param value1 the first operand
     * @param value2 the second operand
     * @return the product of the two operands.
     */
    public T multiply( T value1,
                       T value2 );

    /**
     * Divide the first operand by the second, and return the result. The {@link #createZeroValue() zero value} is used in place
     * of any operand that is null.
     * 
     * @param value1 the first operand
     * @param value2 the second operand
     * @return the result of the division
     */
    public double divide( T value1,
                          T value2 );

    /**
     * Negate the supplied operand. The {@link #createZeroValue() zero value} is used in place of any operand that is null.
     * 
     * @param value the value that is to be negated
     * @return the result of the negation
     */
    public T negate( T value );

    /**
     * Increment the supplied operand by 1. (Note, the exact meaning of "1" is dependent upon the particular
     * {@link #getOperandClass() operand class}. The {@link #createZeroValue() zero value} is used in place of any operand that is
     * null.
     * 
     * @param value the value that is to be incremented
     * @return the incremented value
     */
    public T increment( T value );

    /**
     * Compare the two operands and return the one that is larger. A null value is considered smaller than non-null values
     * (including 0).
     * 
     * @param value1 the first operand
     * @param value2 the second operand
     * @return the larger of the two operands
     */
    public T maximum( T value1,
                      T value2 );

    /**
     * Compare the two operands and return the one that is smaller. A null value is considered larger than non-null values
     * (including 0).
     * 
     * @param value1 the first operand
     * @param value2 the second operand
     * @return the smaller of the two operands
     */
    public T minimum( T value1,
                      T value2 );

    /**
     * Compare the two operands and return an integer that describes whether the first value is larger, smaller or the same as the
     * second value. The semantics are identical to those of {@link Comparable}. The {@link #createZeroValue() zero value} is used
     * in place of any operand that is null.
     * 
     * @param value1 the first operand
     * @param value2 the second operand
     * @return -1 if the first value is smaller than the second, 1 if the first value is larger than the second, or 0 if they are
     *         equal.
     */
    public int compare( T value1,
                        T value2 );

    /**
     * Create a {@link BigDecimal} representation of the supplied value.
     * 
     * @param value the value that is to be converted to a BigDecimal
     * @return the BigDecimal representation, or null if <code>value</code> is null
     */
    public BigDecimal asBigDecimal( T value );

    /**
     * Convert the {@link BigDecimal} representation into the natural object representation. This may result in loss of some data
     * (e.g., converting a decimal to an integer results in the loss of the fractional part of the number).
     * 
     * @param value the BigDecimal value
     * @return the natural representation, or null if <code>value</code> is null
     */
    public T fromBigDecimal( BigDecimal value );

    /**
     * Convert the value to a double. This may result in a loss of information depending upon the {@link #getOperandClass()
     * operand class}.
     * 
     * @param value the value
     * @return the representation as a double
     */
    public double doubleValue( T value );

    /**
     * Convert the value to a float. This may result in a loss of information depending upon the {@link #getOperandClass() operand
     * class}.
     * 
     * @param value the value
     * @return the representation as a float
     */
    public float floatValue( T value );

    /**
     * Convert the value to an integer. This may result in a loss of information depending upon the {@link #getOperandClass()
     * operand class}.
     * 
     * @param value the value
     * @return the representation as an integer
     */
    public int intValue( T value );

    /**
     * Convert the value to a short. This may result in a loss of information depending upon the {@link #getOperandClass() operand
     * class}.
     * 
     * @param value the value
     * @return the representation as a short
     */
    public short shortValue( T value );

    /**
     * Convert the value to a long integer. This may result in a loss of information depending upon the {@link #getOperandClass()
     * operand class}.
     * 
     * @param value the value
     * @return the representation as a long
     */
    public long longValue( T value );

    /**
     * Create the object form of the "zero value". This is often used to create an uninitialized object.
     * 
     * @return the object that represents zero.
     */
    public T createZeroValue();

    /**
     * Convert the integer representation into the natural object representation.
     * 
     * @param value the integer value
     * @return the object representation of the integer
     */
    public T create( int value );

    /**
     * Convert the long representation into the natural object representation.
     * 
     * @param value the long value
     * @return the object representation of the long integer
     */
    public T create( long value );

    /**
     * Convert the double representation into the natural object representation.
     * 
     * @param value the double value
     * @return the object representation of the floating point number
     */
    public T create( double value );

    /**
     * Return the square root of the supplied operand.
     * 
     * @param value the value whose root is to be found; may not be null or 0
     * @return the square root of the value
     */
    public double sqrt( T value );

    /**
     * Return a {@link Comparator Comparator<T>} for this {@link #getOperandClass() operand class}. The implementation is free to
     * return the same comparator instance from multiple invocations of this method.
     * 
     * @return a comparator
     */
    public Comparator<T> getComparator();

    /**
     * Get the exponent if the number were written in exponential form.
     * 
     * @param value the value
     * @return the scale
     */
    public int getExponentInScientificNotation( T value );

    /**
     * Round up the supplied value to the desired scale. This process works (conceptually) by shifting the decimal point of the
     * value by <code>decimalShift</code> places, rounding, and then shifting the decimal point of the rounded value by
     * <code>-decimalShift</code>
     * <p>
     * For example, consider the number 10.000354. This can be rounded to 10.0004 by calling this method and supplying the value
     * and an "exponentToKeep" value of -4.
     * </p>
     * 
     * @param value the value to be rounded
     * @param decimalShift the number of places the decimal point should be shifted before rounding
     * @return the rounded value
     */
    public T roundUp( T value,
                      int decimalShift );

    /**
     * Round down the supplied value to the desired scale. This process works (conceptually) by shifting the decimal point of the
     * value by <code>decimalShift</code> places, rounding, and then shifting the decimal point of the rounded value by
     * <code>-decimalShift</code>
     * <p>
     * For example, consider the number 10.000354. This can be rounded to 10.0003 by calling this method and supplying the value
     * and an "exponentToKeep" value of -4.
     * </p>
     * 
     * @param value the value to be rounded
     * @param decimalShift the number of places the decimal point should be shifted before rounding
     * @return the rounded value
     */
    public T roundDown( T value,
                        int decimalShift );

    public T keepSignificantFigures( T value,
                                     int numSigFigs );

    /**
     * Generate a random instance within the specified range.
     * 
     * @param minimum the minimum value, or null if the {@link #createZeroValue() zero-value} should be used for the minimum
     * @param maximum the maximum value, or null if the {@link #createZeroValue() zero-value} should be used for the maximum
     * @param rng the random number generator to use
     * @return an instance of the {@link #getOperandClass() operand class} placed within the desired range using a random
     *         distribution, or null if this class does not support generating random instances
     */
    public T random( T minimum,
                     T maximum,
                     Random rng );
}
