/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2001-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.math;
import org.geotools.resources.XArray;
import static org.geotools.resources.XMath.next;
import static org.geotools.resources.XMath.previous;
/**
* Simple mathematical functions in addition to the ones provided in {@link Math}.
*
* @since 2.5
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*/
public final class XMath {
/**
* Table of some integer powers of 10. Used for fast computation of {@link #pow10(int)}.
*/
private static final double[] POW10 = {
1E+00, 1E+01, 1E+02, 1E+03, 1E+04, 1E+05, 1E+06, 1E+07, 1E+08, 1E+09,
1E+10, 1E+11, 1E+12, 1E+13, 1E+14, 1E+15, 1E+16, 1E+17, 1E+18, 1E+19,
1E+20, 1E+21, 1E+22
// Do not add more elements, unless we verified that 1/x is accurate.
// Last time we tried, it was not accurate anymore starting at 1E+23.
};
/**
* The sequence of prime numbers computed so far. Will be expanded as needed.
* We limit ourself to 16 bits numbers because they are suffisient for computing
* divisors of any 32 bits number.
*/
private static short[] primes = new short[] {2, 3};
/**
* Maximum length allowed for the {@link #primes} array. This is the index
* of the first prime number that can not be stored as 16 bits unsigned.
*/
private static final int MAX_PRIMES_LENGTH = 6542;
/**
* Do not allow instantiation of this class.
*/
private XMath() {
}
/**
* Computes 10 raised to the power of <var>x</var>. This method delegates to
* {@link #pow10(int)} if <var>x</var> is an integer, or to {@link Math#pow}
* otherwise.
*
* @param x The exponent.
* @return 10 raised to the given exponent.
*/
public static double pow10(final double x) {
final int ix = (int) x;
if (ix == x) {
return pow10(ix);
} else {
return Math.pow(10, x);
}
}
/**
* Computes 10 raised to the power of <var>x</var>. This method tries to be slightly more
* accurate than <code>{@linkplain Math#pow Math.pow}(10,x)</code>, sometime at the cost
* of performance.
* <p>
* The {@code Math.pow(10,x)} method doesn't always return the closest IEEE floating point
* representation. More accurate calculations are slower and usually not necessary, but the
* base 10 is a special case since it is used for scaling axes or formatting human-readable
* output, in which case the precision may matter.
*
* @param x The exponent.
* @return 10 raised to the given exponent.
*/
public static strictfp double pow10(final int x) {
if (x >= 0) {
if (x < POW10.length) {
return POW10[x];
}
} else if (x != Integer.MIN_VALUE) {
final int nx = -x;
if (nx < POW10.length) {
return 1 / POW10[nx];
}
}
try {
/*
* Double.parseDouble("1E"+x) give gives as good or better numbers than Math.pow(10,x)
* for ALL integer powers, but is slower. We hope that the current workaround is only
* temporary. See http://developer.java.sun.com/developer/bugParade/bugs/4358794.html
*/
return Double.parseDouble("1E" + x);
} catch (NumberFormatException exception) {
return StrictMath.pow(10, x);
}
}
/**
* Returns the sign of <var>x</var>. This method returns
* -1 if <var>x</var> is negative,
* 0 if <var>x</var> is zero or {@code NaN} and
* +1 if <var>x</var> is positive.
*
* @param x The number from which to get the sign.
* @return {@code +1} if <var>x</var> is positive, {@code -1} if negative, or 0 otherwise.
*
* @see Math#signum(double)
*/
public static int sgn(final double x) {
if (x > 0) return +1;
if (x < 0) return -1;
else return 0;
}
/**
* Returns the sign of <var>x</var>. This method returns
* -1 if <var>x</var> is negative,
* 0 if <var>x</var> is zero or {@code NaN} and
* +1 if <var>x</var> is positive.
*
* @param x The number from which to get the sign.
* @return {@code +1} if <var>x</var> is positive, {@code -1} if negative, or 0 otherwise.
*
* @see Math#signum(float)
*/
public static int sgn(final float x) {
if (x > 0) return +1;
if (x < 0) return -1;
else return 0;
}
/**
* Returns the sign of <var>x</var>. This method returns
* -1 if <var>x</var> is negative,
* 0 if <var>x</var> is zero and
* +1 if <var>x</var> is positive.
*
* @param x The number from which to get the sign.
* @return {@code +1} if <var>x</var> is positive, {@code -1} if negative, or 0 otherwise.
*/
public static int sgn(long x) {
if (x > 0) return +1;
if (x < 0) return -1;
else return 0;
}
/**
* Returns the sign of <var>x</var>. This method returns
* -1 if <var>x</var> is negative,
* 0 if <var>x</var> is zero and
* +1 if <var>x</var> is positive.
*
* @param x The number from which to get the sign.
* @return {@code +1} if <var>x</var> is positive, {@code -1} if negative, or 0 otherwise.
*/
public static int sgn(int x) {
if (x > 0) return +1;
if (x < 0) return -1;
else return 0;
}
/**
* Returns the sign of <var>x</var>. This method returns
* -1 if <var>x</var> is negative,
* 0 if <var>x</var> is zero and
* +1 if <var>x</var> is positive.
*
* @param x The number from which to get the sign.
* @return {@code +1} if <var>x</var> is positive, {@code -1} if negative, or 0 otherwise.
*/
public static short sgn(short x) {
if (x > 0) return (short) +1;
if (x < 0) return (short) -1;
else return (short) 0;
}
/**
* Returns the sign of <var>x</var>. This method returns
* -1 if <var>x</var> is negative,
* 0 if <var>x</var> is zero and
* +1 if <var>x</var> is positive.
*
* @param x The number from which to get the sign.
* @return {@code +1} if <var>x</var> is positive, {@code -1} if negative, or 0 otherwise.
*/
public static byte sgn(byte x) {
if (x > 0) return (byte) +1;
if (x < 0) return (byte) -1;
else return (byte) 0;
}
/**
* Rounds the specified value, providing that the difference between the original value and
* the rounded value is not greater than the specified amount of floating point units. This
* method can be used for hiding floating point error likes 2.9999999996.
*
* @param value The value to round.
* @param maxULP The maximal change allowed in ULPs (Unit in the Last Place).
* @return The rounded value, of {@code value} if it was not close enough to an integer.
*/
public static double roundIfAlmostInteger(final double value, int maxULP) {
final double target = Math.rint(value);
if (value != target) {
final boolean pos = (value < target);
double candidate = value;
while (--maxULP >= 0) {
candidate = pos ? next(candidate) : previous(candidate);
if (candidate == target) {
return target;
}
}
}
return value;
}
/**
* Tries to remove at least {@code n} fraction digits in the decimal representation of
* the specified value. This method tries small changes to {@code value}, by adding or
* substracting up to {@code maxULP} (Unit in the Last Place). If there is no small
* change that remove at least {@code n} fraction digits, then the value is returned
* unchanged. This method is used for hiding rounding errors, like in conversions from
* radians to degrees.
* <P>
* Example:
* {@code XMath.trimLastDecimalDigits(-61.500000000000014, 12, 4)} returns {@code -61.5}.
*
* @param value The value to fix.
* @param maxULP The maximal change allowed in ULPs (Unit in the Last Place).
* A typical value is 4.
* @param n The minimum amount of fraction digits.
* @return The trimmed value, or the unchanged {@code value} if there is no small change
* that remove at least {@code n} fraction digits.
*/
public static double trimDecimalFractionDigits(final double value, final int maxULP, int n) {
double lower = value;
double upper = value;
n = countDecimalFractionDigits(value) - n;
if (n > 0) {
for (int i=0; i<maxULP; i++) {
if (countDecimalFractionDigits(lower = previous(lower)) <= n) return lower;
if (countDecimalFractionDigits(upper = next (upper)) <= n) return upper;
}
}
return value;
}
/**
* Counts the fraction digits in the string representation of the specified value. This method
* is equivalent to a calling <code>{@linkplain Double#toString(double) Double.toString}(value)</code>
* and counting the number of digits after the decimal separator.
*
* @param value The value for which to count the fraction digits.
* @return The number of fraction digits.
*/
public static int countDecimalFractionDigits(final double value) {
final String asText = Double.toString(value);
final int exp = asText.indexOf('E');
int upper, power;
if (exp >= 0) {
upper = exp;
power = Integer.parseInt(asText.substring(exp+1));
} else {
upper = asText.length();
power = 0;
}
while ((asText.charAt(--upper)) == '0');
return Math.max(upper - asText.indexOf('.') - power, 0);
}
/**
* Returns a {@link Float#NaN NaN} number for the specified index. Valid NaN numbers have
* bit fields ranging from {@code 0x7f800001} through {@code 0x7fffffff} or {@code 0xff800001}
* through {@code 0xffffffff}. The standard {@link Float#NaN} has bit fields {@code 0x7fc00000}.
* See {@link Float#intBitsToFloat} for more details on NaN bit values.
*
* @param index The index, from -2097152 to 2097151 inclusive.
* @return One of the legal {@link Float#NaN NaN} values as a float.
* @throws IndexOutOfBoundsException if the specified index is out of bounds.
*
* @see Float#intBitsToFloat
*/
public static float toNaN(int index) throws IndexOutOfBoundsException {
index += 0x200000;
if (index>=0 && index<=0x3FFFFF) {
final float value = Float.intBitsToFloat(0x7FC00000 + index);
assert Float.isNaN(value) : value;
return value;
}
else {
throw new IndexOutOfBoundsException(Integer.toHexString(index));
}
}
/**
* Returns the <var>i</var><sup>th</sup> prime number. This method returns (2,3,5,7,11...)
* for index (0,1,2,3,4...). This method is designed for relatively small prime numbers only;
* don't use it for large values.
*
* @param index The prime number index, starting at index 0 for prime number 2.
* @return The prime number at the specified index.
* @throws IndexOutOfBoundsException if the specified index is too large.
*
* @see java.math.BigInteger#isProbablePrime
*/
public static synchronized int primeNumber(final int index) throws IndexOutOfBoundsException {
// 6541 is the largest index returning a 16 bits unsigned prime number.
if (index < 0 || index >= MAX_PRIMES_LENGTH) {
throw new IndexOutOfBoundsException(String.valueOf(index));
}
short[] primes = XMath.primes;
if (index >= primes.length) {
int i = primes.length;
int n = primes[i - 1] & 0xFFFF;
primes = XArray.resize(primes, Math.min((index | 0xF) + 1, MAX_PRIMES_LENGTH));
do {
next: while (true) {
n += 2;
for (int j=1; j<i; j++) {
if (n % (primes[j] & 0xFFFF) == 0) {
continue next;
}
// We could stop the search at the first value greater than sqrt(n), but
// given that the array is relatively short (because we limit ourself to
// 16 bits prime numbers), it probably doesn't worth.
}
assert n < 0xFFFF : i;
primes[i] = (short) n;
break;
}
} while (++i < primes.length);
XMath.primes = primes;
}
return primes[index] & 0xFFFF;
}
/**
* Returns the divisors of the specified number as positive integers. For any value other
* than {@code O} (which returns an empty array), the first element in the returned array
* is always {@code 1} and the last element is always the absolute value of {@code number}.
*
* @param number The number for which to compute the divisors.
* @return The divisors in strictly increasing order.
*/
public static int[] divisors(int number) {
if (number == 0) {
return new int[0];
}
number = Math.abs(number);
int[] divisors = new int[16];
divisors[0] = 1;
int count = 1;
/*
* Searchs for the first divisors among the prime numbers. We stop the search at the
* square root of 'n' because every values above that point can be inferred from the
* values before that point, i.e. if n=p1*p2 and p2 is greater than 'sqrt', than p1
* most be lower than 'sqrt'.
*/
final int sqrt = (int) (Math.sqrt(number) + 1E-6); // Really wants rounding toward 0.
for (int p,i=0; (p=primeNumber(i)) <= sqrt; i++) {
if (number % p == 0) {
if (count == divisors.length) {
divisors = XArray.resize(divisors, count*2);
}
divisors[count++] = p;
}
}
/*
* Completes the divisors past 'sqrt'. The numbers added here may or may not be prime
* numbers. Side note: checking that they are prime numbers would be costly, but this
* algorithm doesn't need that.
*/
int source = count;
if (count*2 > divisors.length) {
divisors = XArray.resize(divisors, count*2);
}
int d1 = divisors[--source];
int d2 = number / d1;
if (d1 != d2) {
divisors[count++] = d2;
}
while (--source >= 0) {
divisors[count++] = number / divisors[source];
}
/*
* Checks the products of divisors found so far. For example if 2 and 3 are divisors,
* checks if 6 is a divisor as well. The products found will themself be used for
* computing new products.
*/
for (int i=1; i<count; i++) {
d1 = divisors[i];
for (int j=i; j<count; j++) {
d2 = d1 * divisors[j];
if (number % d2 == 0) {
int p = org.geotools.resources.Java6.binarySearch(divisors, j, count, d2);
if (p < 0) {
p = ~p; // ~ operator, not minus
if (count == divisors.length) {
divisors = XArray.resize(divisors, count*2);
}
System.arraycopy(divisors, p, divisors, p+1, count-p);
divisors[p] = d2;
count++;
}
}
}
}
divisors = XArray.resize(divisors, count);
assert XArray.isStrictlySorted(divisors);
return divisors;
}
}