package plume;
import java.util.*;
/**
* Routines for doing approximate ('fuzzy') floating point comparisons.
* Those are comparisons that only require the floating point numbers to be
* relatively close to one another to be equal, rather than exactly
* equal. <p>
*
* Floating point numbers are compared for equality by dividing them by
* one another and comparing the ratio. By default they must be within
* 0.0001 (0.01%) to be considered equal; supply this value to the
* FuzzyFloat constructor, or set the value with the set_rel_diff method.
* Note that zero is never equal to a non-zero number using this method. <p>
*
* Two NaN floats are not considered equal (consistent with the == operator).
**/
public class FuzzyFloat {
/** Minimum ratio between two floats that will act as equal. */
double min_ratio = 0.9999;
/** Maximum ratio between two floats that will act as equal. */
double max_ratio = 1.0001;
/**
* True if ratio test turned off.
* This occurs exactly if the class is instantiated with the relative
* difference 0.
*/
boolean off = false;
public FuzzyFloat () {
}
/**
* Specify the specific relative difference allowed between two
* floats in order for them to be equal. The default is 0.0001
* a relative diff of zero, disables it (i.e., only exact matches work).
*/
public FuzzyFloat (double rel_diff) {
set_rel_diff (rel_diff);
}
/**
* Set the relative diff.
*
* @see #FuzzyFloat
*/
public void set_rel_diff (double rel_diff) {
min_ratio = 1 - rel_diff;
max_ratio = 1 + rel_diff;
off = (rel_diff == 0.0);
//System.out.println ("min_ratio = " + min_ratio + ", max_ratio = "
// + max_ratio);
}
/**
* Test d1 and d2 for equality using the current ratio. Two NaN floats
* are not considered equal (consistent with the == operator). <p>
*
* Note that if one of the numbers if 0.0, then the other number must
* be less than the square of the fuzzy ratio. This policy accomodates
* round off errors in floating point values.
*
* @return true if d1 and d2 are considered equal, false otherwise
*/
/* @ pure */ public boolean eq (double d1, double d2) {
// NaNs are not considered equal.
if (Double.isNaN(d1) && Double.isNaN(d2))
return (false);
// if zero was specified for a ratio, don't do the divide. You might
// get slightly different answers. And this should be faster.
if (off)
return (d1 == d2);
// slightly more efficient for matches and catches positive and negative
// infinity (which match in this test, but not below)
if (d1 == d2)
return (true);
// when one number is 0, check that the other is less than the square
// of the fuzzy ration (accomodates round off errors in floating point
// values)
if (d1 == 0.0 || d2 == 0.0) {
double zero_tolerance = Math.pow((max_ratio - 1), 2);
if (d1 == 0.0) {
return (Math.abs(d2) < zero_tolerance);
} else {
return (Math.abs(d1) < zero_tolerance);
}
}
double ratio = d1/d2;
return ((ratio >= min_ratio) && (ratio <= max_ratio));
}
/**
* Test d1 and d2 for non-equality using the current ratio.
*
* @see #eq
*/
/* @ pure */ public boolean ne (double d1, double d2) {
return (!eq (d1, d2));
}
/**
* Test d1 and d2 for d1 < d2. If d1 is equal to d2 using the current ratio
* this returns false.
*
* @see #eq
*/
/* @ pure */ public boolean lt (double d1, double d2) {
return ((d1 < d2) && ne (d1, d2));
}
/**
* test d1 and d2 for d1 <= d2. If d1 is equal to d2 using the current
* ratio, this returns true.
*
* @see #eq
*/
/* @ pure */ public boolean lte (double d1, double d2) {
return ((d1 <= d2) || eq (d1, d2));
}
/**
* test d1 and d2 for d1 > d2. IF d1 is equal to d2 using the current
* ratio, this returns false.
*
* @see #eq
*/
/* @ pure */ public boolean gt (double d1, double d2) {
return ((d1 > d2) && ne (d1, d2));
}
/**
* test d1 and d2 for d1 >= d2. If d1 is equal to d2 using the current
* ratio, this returns true.
*
* @see #eq
*/
/* @ pure */ public boolean gte (double d1, double d2) {
return ((d1 >= d2) || eq (d1, d2));
}
/**
* Searches for the first occurrence of elt in a. elt is considered
* equal to a[i] if it passes the {@link #eq} test.
*
* @return the first index containing the specified element,
* or -1 if the element is not found in the array.
* @see java.util.Vector#indexOf(java.lang.Object)
**/
/* @ pure */ public int indexOf (double[] a, double elt) {
for (int i=0; i<a.length; i++)
if (eq (elt, a[i]))
return i;
return -1;
}
/**
* Searches for the first subsequence of a that matches sub elementwise.
* Elements of sub are considered to match elements of a if they pass
* the {@link #eq} test.
*
* @return the first index whose subarray is equal to the specified array
* or -1 if no such subarray is found in the array.
* @see java.util.Vector#indexOf(java.lang.Object)
* @see java.lang.String#indexOf(java.lang.String)
**/
/* @ pure */ public int indexOf (double[] a, double[] sub) {
int a_index_max = a.length - sub.length;
outer: for (int i = 0; i <= a_index_max; i++) {
for (int j = 0; j < sub.length; j++) {
if (ne (a[i+j], sub[j])) {
continue outer;
}
}
return (i);
}
return (-1);
}
/**
* Determines whether or not a1 and a2 are set equivalent (contain only the
* same elements). Element comparison uses {@link #eq}. <p>
*
* Note that this implementation is optimized for cases where the
* elements are actually the same, since it does a sort of both arrays
* before starting the comparisons.
*
* @return true if a1 and a2 are set equivalent, false otherwise
*/
/* @ pure */ public boolean isElemMatch (double[] a1, double[] a2) {
//don't change our parameters
a1 = a1.clone();
a2 = a2.clone();
Arrays.sort (a1);
Arrays.sort (a2);
// look for elements of a2 in a1
int start = 0;
outer1: for (int i = 0; i < a2.length; i++) {
double val = a2[i];
for (int j = start; j < a1.length; j++) {
if (eq (val, a1[j])) {
start = j;
continue outer1;
}
if (val < a1[j]) {
// System.out.println ("isElemMatch: " + val + " " + a1[j]);
return (false);
}
}
// System.out.println ("isElemMatch: " + i);
return (false);
}
// look for elements of a1 in a2
start = 0;
outer2: for (int i = 0; i < a1.length; i++) {
double val = a1[i];
for (int j = start; j < a2.length; j++) {
if (eq (val, a2[j])) {
start = j;
continue outer2;
}
if (val < a2[j]) {
// System.out.println ("isElemMatch: " + val + " " + a2[j]);
return (false);
}
}
// System.out.println ("isElemMatch: " + i);
return (false);
}
return (true);
}
// Slightly more efficient method that will miss some matches
// int i = 0;
// int j = 0;
// while (i < a1.length && j < a2.length) {
// if (ne (a1[i], a2[j])) {
// System.out.println ("isElemMatch: " + a1[i] + " " + a2[j]);
// return (false);
// }
// double val = a1[i];
// i++;
// while ((i < a1.length) && (eq (a1[i], val))) {
// i++;
// }
// j++;
// while ((j < a2.length) && (eq (a2[j], val))) {
// j++;
// }
// }
// // if there are any elements left, then they don't match.
// if ((i != a1.length) || (j != a2.length)) {
// System.out.println ("isElemMatch: " + i + " " + j);
// return (false);
// }
// return (true);
// }
/**
* Lexically compares two double arrays.
*/
/* @ pure */ public class DoubleArrayComparatorLexical implements Comparator<double[]> {
/**
* Lexically compares o1 and o2 as double arrays.
*
* @return positive if o1 > 02, 0 if 01 == 02, negative if 01 < 02
*/
public int compare(double[] a1, double[] a2) {
if (a1 == a2)
return 0;
int len = Math.min(a1.length, a2.length);
for (int i=0; i<len; i++) {
if (ne (a1[i], a2[i])) {
return ((a1[i] > a2[i]) ? 1 : -1);
}
}
return a1.length - a2.length;
}
}
/**
* Determines whether smaller is a subset of bigger. Element
* comparison uses {@link #eq}. <p>
*
* Note that this implementation is optimized for cases where the
* elements are actually the same, since it does a sort of both
* arrays before starting the comparisons.
*
* @return true if smaller is a subset (each element of smaller is
* also a element of bigger) of bigger, false otherwise
*/
/* @ pure */ public boolean isSubset (double[] smaller, double[] bigger) {
//don't change our parameters
smaller = smaller.clone();
bigger = bigger.clone();
Arrays.sort (smaller);
Arrays.sort (bigger);
// look for elements of smaller in bigger
int start = 0;
outer1: for (int i = 0; i < smaller.length; i++) {
double val = smaller[i];
for (int j = start; j < bigger.length; j++) {
if (eq (val, bigger[j])) {
start = j;
continue outer1;
}
if (val < bigger[j]) {
return (false);
}
}
return (false);
}
return (true);
}
}