blob: e4634fd75050345a39d7ef84364ac514bbd755fa [file] [log] [blame]
/*
* Copyright 2003-2005 The Apache Software Foundation.
*
* 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.apache.commons.math.util;
import java.math.BigDecimal;
/**
* Some useful additions to the built-in functions in {@link Math}.
*
* @version $Revision$ $Date$
*/
public final class MathUtils {
/** 0.0 cast as a byte. */
private static final byte ZB = (byte) 0;
/** -1.0 cast as a byte. */
private static final byte NB = (byte) -1;
/** 1.0 cast as a byte. */
private static final byte PB = (byte) 1;
/** 0.0 cast as a short. */
private static final short ZS = (short) 0;
/** -1.0 cast as a short. */
private static final short NS = (short) -1;
/** 1.0 cast as a short. */
private static final short PS = (short) 1;
/**
* Private Constructor
*/
private MathUtils() {
}
/**
* Round the given value to the specified number of decimal places. The
* value is rounded using the {@link BigDecimal#ROUND_HALF_UP} method.
* @param x the value to round.
* @param scale the number of digits to the right of the decimal point.
* @return the rounded value.
* @since 1.1
*/
public static double round(double x, int scale) {
return round(x, scale, BigDecimal.ROUND_HALF_UP);
}
/**
* Round the given value to the specified number of decimal places. The
* value is rounded using the given method which is any method defined in
* {@link BigDecimal}.
* @param x the value to round.
* @param scale the number of digits to the right of the decimal point.
* @param roundingMethod the rounding method as defined in
* {@link BigDecimal}.
* @return the rounded value.
* @since 1.1
*/
public static double round(
double x, int scale, int roundingMethod)
{
return (new BigDecimal(x).setScale(scale, roundingMethod))
.doubleValue();
}
/**
* Round the given value to the specified number of decimal places. The
* value is rounding using the {@link BigDecimal#ROUND_HALF_UP} method.
* @param x the value to round.
* @param scale the number of digits to the right of the decimal point.
* @return the rounded value.
* @since 1.1
*/
public static float round(float x, int scale) {
return round(x, scale, BigDecimal.ROUND_HALF_UP);
}
/**
* Round the given value to the specified number of decimal places. The
* value is rounded using the given method which is any method defined in
* {@link BigDecimal}.
* @param x the value to round.
* @param scale the number of digits to the right of the decimal point.
* @param roundingMethod the rounding method as defined in
* {@link BigDecimal}.
* @return the rounded value.
* @since 1.1
*/
public static float round(float x, int scale, int roundingMethod) {
return (new BigDecimal(x).setScale(scale, roundingMethod)).floatValue();
}
/**
* Returns the <a href="http://mathworld.wolfram.com/Sign.html">
* sign</a> for double precision <code>x</code>.
*
* <p>
* For a double value <code>x</code>, this method returns <code>+1.0</code>
* if <code>x > 0</code>, <code>0.0</code> if <code>x = 0.0</code>,
* and <code>-1.0</code> if <code>x < 0</code>. Returns <code>NaN</code>
* if <code>x</code> is <code>NaN</code>.
*
* @param x the value, a double
* @return +1.0, 0.0, or -1.0, depending on the sign of x
*/
public static double sign(final double x) {
if (Double.isNaN(x)) {
return Double.NaN;
}
return (x == 0.0) ? 0.0 : (x > 0.0) ? 1.0 : -1.0;
}
/**
* Returns the <a href="http://mathworld.wolfram.com/Sign.html">
* sign</a> for float value <code>x</code>.
*
* <p>
* For a float value x, this method returns +1.0F if x > 0, 0.0F if
* x = 0.0F, and -1.0F if x < 0. Returns <code>NaN</code>
* if <code>x</code> is <code>NaN</code>.
*
* @param x the value, a float
* @return +1.0F, 0.0F, or -1.0F, depending on the sign of x
*/
public static float sign(final float x) {
if (Float.isNaN(x)) {
return Float.NaN;
}
return (x == 0.0F) ? 0.0F : (x > 0.0F) ? 1.0F : -1.0F;
}
/**
* Returns the <a href="http://mathworld.wolfram.com/Sign.html">
* sign</a> for byte value <code>x</code>.
*
* <p>
* For a byte value x, this method returns (byte)(+1) if x > 0, (byte)(0)
* if x = 0, and (byte)(-1) if x < 0.
*
* @param x the value, a byte
* @return (byte)(+1), (byte)(0), or (byte)(-1), depending on the sign of x
*/
public static byte sign(final byte x) {
return (x == ZB) ? ZB : (x > ZB) ? PB : NB;
}
/**
* Returns the <a href="http://mathworld.wolfram.com/Sign.html">
* sign</a> for short value <code>x</code>.
*
* <p>
* For a short value x, this method returns (short)(+1) if x > 0, (short)(0)
* if x = 0, and (short)(-1) if x < 0.
*
* @param x the value, a short
* @return (short)(+1), (short)(0), or (short)(-1), depending on the sign
* of x
*/
public static short sign(final short x) {
return (x == ZS) ? ZS : (x > ZS) ? PS : NS;
}
/**
* Returns the <a href="http://mathworld.wolfram.com/Sign.html">
* sign</a> for int value <code>x</code>.
*
* <p>
* For an int value x, this method returns +1 if x > 0, 0 if x = 0,
* and -1 if x < 0.
*
* @param x the value, an int
* @return +1, 0, or -1, depending on the sign of x
*/
public static int sign(final int x) {
return (x == 0) ? 0 : (x > 0) ? 1 : -1;
}
/**
* Returns the <a href="http://mathworld.wolfram.com/Sign.html">
* sign</a> for long value <code>x</code>.
*
* <p>
* For a long value x, this method returns +1L if x > 0, 0L if x = 0,
* and -1L if x < 0.
*
* @param x the value, a long
* @return +1L, 0L, or -1L, depending on the sign of x
*/
public static long sign(final long x) {
return (x == 0L) ? 0L : (x > 0L) ? 1L : -1L;
}
/**
* For a double precision value x, this method returns +1.0 if x >= 0
* and -1.0 if x < 0. Returns <code>NaN</code>
* if <code>x</code> is <code>NaN</code>.
*
* @param x the value, a double
* @return +1.0 or -1.0, depending on the sign of x
*/
public static double indicator(final double x) {
if (Double.isNaN(x)) {
return Double.NaN;
}
return (x >= 0.0) ? 1.0 : -1.0;
}
/**
* For a float value x, this method returns +1.0F if x >= 0
* and -1.0F if x < 0. Returns <code>NaN</code>
* if <code>x</code> is <code>NaN</code>.
*
* @param x the value, a float
* @return +1.0F or -1.0F, depending on the sign of x
*/
public static float indicator(final float x) {
if (Float.isNaN(x)) {
return Float.NaN;
}
return (x >= 0.0F) ? 1.0F : -1.0F;
}
/**
* For a byte value x, this method returns (byte)(+1) if x >= 0
* and (byte)(-1) if x < 0.
*
* @param x the value, a byte
* @return (byte)(+1) or (byte)(-1), depending on the sign of x
*/
public static byte indicator(final byte x) {
return (x >= ZB) ? PB : NB;
}
/**
* For a short value x, this method returns (short)(+1) if x >= 0
* and (short)(-1) if x < 0.
*
* @param x the value, a short
* @return (short)(+1) or (short)(-1), depending on the sign of x
*/
public static short indicator(final short x) {
return (x >= ZS) ? PS : NS;
}
/**
* For an int value x, this method returns +1 if x >= 0
* and -1 if x < 0.
*
* @param x the value, an int
* @return +1 or -1, depending on the sign of x
*/
public static int indicator(final int x) {
return (x >= 0) ? 1 : -1;
}
/**
* For a long value x, this method returns +1L if x >= 0
* and -1L if x < 0.
*
* @param x the value, a long
* @return +1L or -1L, depending on the sign of x
*/
public static long indicator(final long x) {
return (x >= 0L) ? 1L : -1L;
}
/**
* Returns an exact representation of the
* <a href="http://mathworld.wolfram.com/BinomialCoefficient.html">
* Binomial Coefficient</a>, "<code>n choose k</code>",
* the number of <code>k</code>-element subsets that can be selected from
* an <code>n</code>-element set.
* <p>
* <Strong>Preconditions</strong>:<ul>
* <li> <code>0 <= k <= n </code> (otherwise
* <code>IllegalArgumentException</code> is thrown)</li>
* <li> The result is small enough to fit into a <code>long</code>. The
* largest value of <code>n</code> for which all coefficients are
* <code> < Long.MAX_VALUE</code> is 66. If the computed value
* exceeds <code>Long.MAX_VALUE</code> an <code>ArithMeticException
* </code> is thrown.</li>
* </ul>
*
* @param n the size of the set
* @param k the size of the subsets to be counted
* @return <code>n choose k</code>
* @throws IllegalArgumentException if preconditions are not met.
* @throws ArithmeticException if the result is too large to be represented
* by a long integer.
*/
public static long binomialCoefficient(final int n, final int k) {
if (n < k) {
throw new IllegalArgumentException(
"must have n >= k for binomial coefficient (n,k)");
}
if (n < 0) {
throw new IllegalArgumentException(
"must have n >= 0 for binomial coefficient (n,k)");
}
if ((n == k) || (k == 0)) {
return 1;
}
if ((k == 1) || (k == n - 1)) {
return n;
}
long result = Math.round(binomialCoefficientDouble(n, k));
if (result == Long.MAX_VALUE) {
throw new ArithmeticException(
"result too large to represent in a long integer");
}
return result;
}
/**
* Returns a <code>double</code> representation of the
* <a href="http://mathworld.wolfram.com/BinomialCoefficient.html">
* Binomial Coefficient</a>, "<code>n choose k</code>",
* the number of <code>k</code>-element subsets that can be selected from
* an <code>n</code>-element set.
* <p>
* <Strong>Preconditions</strong>:<ul>
* <li> <code>0 <= k <= n </code> (otherwise
* <code>IllegalArgumentException</code> is thrown)</li>
* <li> The result is small enough to fit into a <code>double</code>.
* The largest value of <code>n</code> for which all coefficients are
* < Double.MAX_VALUE is 1029. If the computed value exceeds
* Double.MAX_VALUE, Double.POSITIVE_INFINITY is returned</li>
* </ul>
*
* @param n the size of the set
* @param k the size of the subsets to be counted
* @return <code>n choose k</code>
* @throws IllegalArgumentException if preconditions are not met.
*/
public static double binomialCoefficientDouble(final int n, final int k) {
return Math.floor(Math.exp(binomialCoefficientLog(n, k)) + 0.5);
}
/**
* Returns the natural <code>log</code> of the
* <a href="http://mathworld.wolfram.com/BinomialCoefficient.html">
* Binomial Coefficient</a>, "<code>n choose k</code>",
* the number of <code>k</code>-element subsets that can be selected from
* an <code>n</code>-element set.
* <p>
* <Strong>Preconditions</strong>:<ul>
* <li> <code>0 <= k <= n </code> (otherwise
* <code>IllegalArgumentException</code> is thrown)</li>
* </ul>
*
* @param n the size of the set
* @param k the size of the subsets to be counted
* @return <code>n choose k</code>
* @throws IllegalArgumentException if preconditions are not met.
*/
public static double binomialCoefficientLog(final int n, final int k) {
if (n < k) {
throw new IllegalArgumentException(
"must have n >= k for binomial coefficient (n,k)");
}
if (n < 0) {
throw new IllegalArgumentException(
"must have n >= 0 for binomial coefficient (n,k)");
}
if ((n == k) || (k == 0)) {
return 0;
}
if ((k == 1) || (k == n - 1)) {
return Math.log((double) n);
}
double logSum = 0;
// n!/k!
for (int i = k + 1; i <= n; i++) {
logSum += Math.log((double) i);
}
// divide by (n-k)!
for (int i = 2; i <= n - k; i++) {
logSum -= Math.log((double) i);
}
return logSum;
}
/**
* Returns n!. Shorthand for <code>n</code>
* <a href="http://mathworld.wolfram.com/Factorial.html">
* Factorial</a>, the product of the numbers <code>1,...,n</code>.
*
* <p>
* <Strong>Preconditions</strong>:<ul>
* <li> <code>n >= 0</code> (otherwise
* <code>IllegalArgumentException</code> is thrown)</li>
* <li> The result is small enough to fit into a <code>long</code>. The
* largest value of <code>n</code> for which <code>n!</code>
* < Long.MAX_VALUE</code> is 20. If the computed value
* exceeds <code>Long.MAX_VALUE</code> an <code>ArithMeticException
* </code> is thrown.</li>
* </ul>
* </p>
*
* @param n argument
* @return <code>n!</code>
* @throws ArithmeticException if the result is too large to be represented
* by a long integer.
* @throws IllegalArgumentException if n < 0
*/
public static long factorial(final int n) {
long result = Math.round(factorialDouble(n));
if (result == Long.MAX_VALUE) {
throw new ArithmeticException(
"result too large to represent in a long integer");
}
return result;
}
/**
* Returns n!. Shorthand for <code>n</code>
* <a href="http://mathworld.wolfram.com/Factorial.html">
* Factorial</a>, the product of the numbers <code>1,...,n</code> as a
* <code>double</code>.
*
* <p>
* <Strong>Preconditions</strong>:<ul>
* <li> <code>n >= 0</code> (otherwise
* <code>IllegalArgumentException</code> is thrown)</li>
* <li> The result is small enough to fit into a <code>double</code>. The
* largest value of <code>n</code> for which <code>n!</code>
* < Double.MAX_VALUE</code> is 170. If the computed value exceeds
* Double.MAX_VALUE, Double.POSITIVE_INFINITY is returned</li>
* </ul>
* </p>
*
* @param n argument
* @return <code>n!</code>
* @throws IllegalArgumentException if n < 0
*/
public static double factorialDouble(final int n) {
if (n < 0) {
throw new IllegalArgumentException("must have n >= 0 for n!");
}
return Math.floor(Math.exp(factorialLog(n)) + 0.5);
}
/**
* Returns the natural logarithm of n!.
* <p>
* <Strong>Preconditions</strong>:<ul>
* <li> <code>n >= 0</code> (otherwise
* <code>IllegalArgumentException</code> is thrown)</li>
* </ul>
*
* @param n argument
* @return <code>n!</code>
* @throws IllegalArgumentException if preconditions are not met.
*/
public static double factorialLog(final int n) {
if (n < 0) {
throw new IllegalArgumentException("must have n > 0 for n!");
}
double logSum = 0;
for (int i = 2; i <= n; i++) {
logSum += Math.log((double) i);
}
return logSum;
}
/**
* Returns the <a href="http://mathworld.wolfram.com/HyperbolicCosine.html">
* hyperbolic cosine</a> of x.
*
* @param x double value for which to find the hyperbolic cosine
* @return hyperbolic cosine of x
*/
public static double cosh(double x) {
return (Math.exp(x) + Math.exp(-x)) / 2.0;
}
/**
* Returns the <a href="http://mathworld.wolfram.com/HyperbolicSine.html">
* hyperbolic sine</a> of x.
*
* @param x double value for which to find the hyperbolic sine
* @return hyperbolic sine of x
*/
public static double sinh(double x) {
return (Math.exp(x) - Math.exp(-x)) / 2.0;
}
/**
* Returns an integer hash code representing the given double value.
*
* @param value the value to be hashed
* @return the hash code
*/
public static int hash(double value) {
long bits = Double.doubleToLongBits(value);
return (int)(bits ^ (bits >>> 32));
}
/**
* Returns true iff both arguments are NaN or
* neither is NaN and they are equal
*
* @param x first value
* @param y second value
* @return true if the values are equal or both are NaN
*/
public static boolean equals(double x, double y) {
return ((Double.isNaN(x) && Double.isNaN(y)) || x == y);
}
/**
* Returns the least common multiple between two integer values.
*
* @param a the first integer value.
* @param b the second integer value.
* @return the least common multiple between a and b.
* @throws ArithmeticException if the lcm is too large to store as an int
* @since 1.1
*/
public static int lcm(int a, int b) {
return Math.abs(mulAndCheck(a / gcd(a, b) , b));
}
/**
* <p>Gets the greatest common divisor of the absolute value of
* two numbers, using the "binary gcd" method which avoids
* division and modulo operations. See Knuth 4.5.2 algorithm B.
* This algorithm is due to Josef Stein (1961).</p>
*
* @param u a non-zero number
* @param v a non-zero number
* @return the greatest common divisor, never zero
* @since 1.1
*/
public static int gcd(int u, int v) {
if (u * v == 0) {
return (Math.abs(u) + Math.abs(v));
}
// keep u and v negative, as negative integers range down to
// -2^31, while positive numbers can only be as large as 2^31-1
// (i.e. we can't necessarily negate a negative number without
// overflow)
/* assert u!=0 && v!=0; */
if (u>0) { u=-u; } // make u negative
if (v>0) { v=-v; } // make v negative
// B1. [Find power of 2]
int k=0;
while ((u&1)==0 && (v&1)==0 && k<31) { // while u and v are both even...
u/=2; v/=2; k++; // cast out twos.
}
if (k==31) {
throw new ArithmeticException("overflow: gcd is 2^31");
}
// B2. Initialize: u and v have been divided by 2^k and at least
// one is odd.
int t = ((u&1)==1) ? v : -(u/2)/*B3*/;
// t negative: u was odd, v may be even (t replaces v)
// t positive: u was even, v is odd (t replaces u)
do {
/* assert u<0 && v<0; */
// B4/B3: cast out twos from t.
while ((t&1)==0) { // while t is even..
t/=2; // cast out twos
}
// B5 [reset max(u,v)]
if (t>0) {
u = -t;
} else {
v = t;
}
// B6/B3. at this point both u and v should be odd.
t = (v - u)/2;
// |u| larger: t positive (replace u)
// |v| larger: t negative (replace v)
} while (t!=0);
return -u*(1<<k); // gcd is u*2^k
}
/**
* Multiply two integers, checking for overflow.
*
* @param x a factor
* @param y a factor
* @return the product <code>x*y</code>
* @throws ArithmeticException if the result can not be represented as
* an int
* @since 1.1
*/
public static int mulAndCheck(int x, int y) {
long m = ((long)x)*((long)y);
if (m < Integer.MIN_VALUE ||
m > Integer.MAX_VALUE) {
throw new ArithmeticException("overflow: mul");
}
return (int)m;
}
/**
* Add two integers, checking for overflow.
*
* @param x an addend
* @param y an addend
* @return the sum <code>x+y</code>
* @throws ArithmeticException if the result can not be represented as
* an int
* @since 1.1
*/
public static int addAndCheck(int x, int y) {
long s = (long)x+(long)y;
if (s < Integer.MIN_VALUE ||
s > Integer.MAX_VALUE) {
throw new ArithmeticException("overflow: add");
}
return (int)s;
}
/**
* Subtract two integers, checking for overflow.
*
* @param x the minuend
* @param y the subtrahend
* @return the difference <code>x-y</code>
* @throws ArithmeticException if the result can not be represented as
* an int
* @since 1.1
*/
public static int subAndCheck(int x, int y) {
long s = (long)x-(long)y;
if (s < Integer.MIN_VALUE ||
s > Integer.MAX_VALUE) {
throw new ArithmeticException("overflow: add");
}
return (int)s;
}
}