package prefuse.util;
import java.util.Arrays;
import prefuse.Constants;
/**
* Library of mathematical constants and methods not included in the
* {@link java.lang.Math} class.
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class MathLib {
/** The value 2 * PI */
public static final double TWO_PI = 2*Math.PI;
/** The natural logarithm of 10 */
public static final double LOG10 = Math.log(10);
/** The natural logarithm of 2 */
public static final double LOG2 = Math.log(2);
private MathLib() {
// prevent instantiation
}
/**
* The base 2 logarithm of the input value
* @param x the input value
* @return the base 2 logarithm
*/
public static double log2(double x) {
return Math.log(x)/LOG2;
}
/**
* The base 10 logarithm of the input value
* @param x the input value
* @return the base 10 logarithm
*/
public static double log10(double x) {
return Math.log(x)/LOG10;
}
/**
* The "safe" base 10 logarithm of the input value, handling
* negative values by simply making them positive and then
* negating the return value.
* @param x the input value
* @return the "negative-safe" base 10 logarithm
*/
public static double safeLog10(double x) {
boolean neg = (x < 0.0);
if ( neg ) { x = -x; }
if ( x < 10.0 ) { x += (10.0-x) / 10; }
x = Math.log(x) / LOG10;
return neg ? -x : x;
}
/**
* The "safe" square root of the input value, handling
* negative values by simply making them positive and then
* negating the return value.
* @param x the input value
* @return the "negative-safe" square root
*/
public static double safeSqrt(double x) {
return ( x<0 ? -Math.sqrt(-x) : Math.sqrt(x) );
}
/**
* Interpolates a value within a range using a specified scale,
* returning the fractional position of the value within that scale.
* @param scale The scale on which to perform the interpolation, one of
* {@link prefuse.Constants#LINEAR_SCALE},
* {@link prefuse.Constants#LOG_SCALE},
* {@link prefuse.Constants#SQRT_SCALE}, or
* {@link prefuse.Constants#QUANTILE_SCALE}.
* @param val the interpolation value, a fraction between 0 and 1.0.
* @param dist a double array describing the distribution of the data.
* For the {@link prefuse.Constants#QUANTILE_SCALE} option, this should
* be a collection of quantile boundaries, as determined by the
* {@link #quantiles(int, double[])} method. For any other scale type,
* the first value of the array must contain the minimum value of the
* distribution and the last value of the array must contain the
* maximum value of the distribution; all values in between will be
* ignored.
* @return the fractional position of the value within the scale,
* a double between 0 and 1.
*/
public static double interp(int scale, double val, double dist[]) {
switch ( scale ) {
case Constants.LINEAR_SCALE:
return linearInterp(val, dist[0], dist[dist.length-1]);
case Constants.LOG_SCALE:
return logInterp(val, dist[0], dist[dist.length-1]);
case Constants.SQRT_SCALE:
return sqrtInterp(val, dist[0], dist[dist.length-1]);
case Constants.QUANTILE_SCALE:
return quantile(val, dist);
}
throw new IllegalArgumentException("Unrecognized scale value: "+scale);
}
/**
* Interpolates a value between a given minimum and maximum value using
* a linear scale.
* @param val the interpolation value, a fraction between 0 and 1.0.
* @param min the minimum value of the interpolation range
* @param max the maximum value of the interpolation range
* @return the resulting interpolated value
*/
public static double linearInterp(double val, double min, double max) {
double denominator = (max-min);
if ( denominator == 0 )
return 0;
return (val-min)/denominator;
}
/**
* Interpolates a value between a given minimum and maximum value using
* a base-10 logarithmic scale.
* @param val the interpolation value, a fraction between 0 and 1.0.
* @param min the minimum value of the interpolation range
* @param max the maximum value of the interpolation range
* @return the resulting interpolated value
*/
public static double logInterp(double val, double min, double max) {
double logMin = safeLog10(min);
double denominator = (safeLog10(max)-logMin);
if ( denominator == 0 )
return 0;
return (safeLog10(val)-logMin) / denominator;
}
/**
* Interpolates a value between a given minimum and maximum value using
* a square root scale.
* @param val the interpolation value, a fraction between 0 and 1.0.
* @param min the minimum value of the interpolation range
* @param max the maximum value of the interpolation range
* @return the resulting interpolated value
*/
public static double sqrtInterp(double val, double min, double max) {
double sqrtMin = safeSqrt(min);
double denominator = (safeSqrt(max)-sqrtMin);
if ( denominator == 0 )
return 0;
return (safeSqrt(val)-sqrtMin) / denominator;
}
/**
* Compute the n-quantile boundaries for a set of values. The result is
* an n+1 size array holding the minimum value in the first entry and
* then n quantile boundaries in the subsequent entries.
* @param n the number of quantile boundaries. For example, a value of 4
* will break up the values into quartiles, while a value of 100 will break
* up the values into percentiles.
* @param values the array of double values to divide into quantiles
* @return an n+1 array of doubles containing the minimum value and
* the quantile boundary values, in that order
*/
public static double[] quantiles(int n, double[] values) {
values = (double[])values.clone();
Arrays.sort(values);
double[] qtls = new double[n+1];
for ( int i=0; i<=n; ++i ) {
qtls[i] = values[((values.length-1)*i)/n];
}
return qtls;
}
/**
* Get the quantile measure, as a value between 0 and 1, for a given
* value and set of quantile boundaries. For example, if the input value
* is the median of the distribution described by the quantile boundaries,
* this method will return 0.5. As another example, if the quantile
* boundaries represent percentiles, this value will return the percentile
* ranking of the input value according to the given boundaries.
* @param val the value for which to return the quantile ranking
* @param quantiles an array of quantile boundaries of a distribution
* @return the quantile ranking, a value between 0 and 1
* @see #quantiles(int, double[])
*/
public static double quantile(double val, double[] quantiles) {
int x1 = 1;
int x2 = quantiles.length;
int i = x2 / 2;
while (x1 < x2) {
if (quantiles[i] == val) {
break;
} else if (quantiles[i] < val) {
x1 = i + 1;
} else {
x2 = i;
}
i = x1 + (x2 - x1) / 2;
}
return ((double)i)/(quantiles.length-1);
}
} // end of class MathLib