mirror of
https://github.com/processing/processing4.git
synced 2026-06-16 04:26:26 +02:00
change Sort to use int for comparisons for accuracy, add DoubleDict and DoubleList
This commit is contained in:
@@ -0,0 +1,823 @@
|
||||
package processing.data;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import processing.core.PApplet;
|
||||
|
||||
|
||||
/**
|
||||
* A simple table class to use a String as a lookup for an double value.
|
||||
*
|
||||
* @webref data:composite
|
||||
* @see IntDict
|
||||
* @see StringDict
|
||||
*/
|
||||
public class DoubleDict {
|
||||
|
||||
/** Number of elements in the table */
|
||||
protected int count;
|
||||
|
||||
protected String[] keys;
|
||||
protected double[] values;
|
||||
|
||||
/** Internal implementation for faster lookups */
|
||||
private HashMap<String, Integer> indices = new HashMap<>();
|
||||
|
||||
|
||||
public DoubleDict() {
|
||||
count = 0;
|
||||
keys = new String[10];
|
||||
values = new double[10];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new lookup with a specific size. This is more efficient than not
|
||||
* specifying a size. Use it when you know the rough size of the thing you're creating.
|
||||
*
|
||||
* @nowebref
|
||||
*/
|
||||
public DoubleDict(int length) {
|
||||
count = 0;
|
||||
keys = new String[length];
|
||||
values = new double[length];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a set of entries from a Reader that has each key/value pair on
|
||||
* a single line, separated by a tab.
|
||||
*
|
||||
* @nowebref
|
||||
*/
|
||||
public DoubleDict(BufferedReader reader) {
|
||||
String[] lines = PApplet.loadStrings(reader);
|
||||
keys = new String[lines.length];
|
||||
values = new double[lines.length];
|
||||
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
String[] pieces = PApplet.split(lines[i], '\t');
|
||||
if (pieces.length == 2) {
|
||||
keys[count] = pieces[0];
|
||||
values[count] = PApplet.parseFloat(pieces[1]);
|
||||
indices.put(pieces[0], count);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @nowebref
|
||||
*/
|
||||
public DoubleDict(String[] keys, double[] values) {
|
||||
if (keys.length != values.length) {
|
||||
throw new IllegalArgumentException("key and value arrays must be the same length");
|
||||
}
|
||||
this.keys = keys;
|
||||
this.values = values;
|
||||
count = keys.length;
|
||||
for (int i = 0; i < count; i++) {
|
||||
indices.put(keys[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor to allow (more intuitive) inline initialization, e.g.:
|
||||
* <pre>
|
||||
* new FloatDict(new Object[][] {
|
||||
* { "key1", 1 },
|
||||
* { "key2", 2 }
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
public DoubleDict(Object[][] pairs) {
|
||||
count = pairs.length;
|
||||
this.keys = new String[count];
|
||||
this.values = new double[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
keys[i] = (String) pairs[i][0];
|
||||
values[i] = (Float) pairs[i][1];
|
||||
indices.put(keys[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Returns the number of key/value pairs
|
||||
*/
|
||||
public int size() {
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resize the internal data, this can only be used to shrink the list.
|
||||
* Helpful for situations like sorting and then grabbing the top 50 entries.
|
||||
*/
|
||||
public void resize(int length) {
|
||||
if (length == count) return;
|
||||
|
||||
if (length > count) {
|
||||
throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
|
||||
}
|
||||
if (length < 1) {
|
||||
throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
|
||||
}
|
||||
|
||||
String[] newKeys = new String[length];
|
||||
double[] newValues = new double[length];
|
||||
PApplet.arrayCopy(keys, newKeys, length);
|
||||
PApplet.arrayCopy(values, newValues, length);
|
||||
keys = newKeys;
|
||||
values = newValues;
|
||||
count = length;
|
||||
resetIndices();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all entries.
|
||||
*
|
||||
* @webref doubledict:method
|
||||
* @brief Remove all entries
|
||||
*/
|
||||
public void clear() {
|
||||
count = 0;
|
||||
indices = new HashMap<>();
|
||||
}
|
||||
|
||||
|
||||
private void resetIndices() {
|
||||
indices = new HashMap<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
indices.put(keys[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
|
||||
|
||||
public class Entry {
|
||||
public String key;
|
||||
public double value;
|
||||
|
||||
Entry(String key, double value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Iterable<Entry> entries() {
|
||||
return new Iterable<Entry>() {
|
||||
|
||||
public Iterator<Entry> iterator() {
|
||||
return entryIterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public Iterator<Entry> entryIterator() {
|
||||
return new Iterator<Entry>() {
|
||||
int index = -1;
|
||||
|
||||
public void remove() {
|
||||
removeIndex(index);
|
||||
index--;
|
||||
}
|
||||
|
||||
public Entry next() {
|
||||
++index;
|
||||
Entry e = new Entry(keys[index], values[index]);
|
||||
return e;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return index+1 < size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
|
||||
|
||||
public String key(int index) {
|
||||
return keys[index];
|
||||
}
|
||||
|
||||
|
||||
protected void crop() {
|
||||
if (count != keys.length) {
|
||||
keys = PApplet.subset(keys, 0, count);
|
||||
values = PApplet.subset(values, 0, count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Iterable<String> keys() {
|
||||
return new Iterable<String>() {
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return keyIterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Use this to iterate when you want to be able to remove elements along the way
|
||||
public Iterator<String> keyIterator() {
|
||||
return new Iterator<String>() {
|
||||
int index = -1;
|
||||
|
||||
public void remove() {
|
||||
removeIndex(index);
|
||||
index--;
|
||||
}
|
||||
|
||||
public String next() {
|
||||
return key(++index);
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return index+1 < size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a copy of the internal keys array. This array can be modified.
|
||||
*
|
||||
* @webref doubledict:method
|
||||
* @brief Return a copy of the internal keys array
|
||||
*/
|
||||
public String[] keyArray() {
|
||||
crop();
|
||||
return keyArray(null);
|
||||
}
|
||||
|
||||
|
||||
public String[] keyArray(String[] outgoing) {
|
||||
if (outgoing == null || outgoing.length != count) {
|
||||
outgoing = new String[count];
|
||||
}
|
||||
System.arraycopy(keys, 0, outgoing, 0, count);
|
||||
return outgoing;
|
||||
}
|
||||
|
||||
|
||||
public double value(int index) {
|
||||
return values[index];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Return the internal array being used to store the values
|
||||
*/
|
||||
public Iterable<Double> values() {
|
||||
return new Iterable<Double>() {
|
||||
|
||||
@Override
|
||||
public Iterator<Double> iterator() {
|
||||
return valueIterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public Iterator<Double> valueIterator() {
|
||||
return new Iterator<Double>() {
|
||||
int index = -1;
|
||||
|
||||
public void remove() {
|
||||
removeIndex(index);
|
||||
index--;
|
||||
}
|
||||
|
||||
public Double next() {
|
||||
return value(++index);
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return index+1 < size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new array and copy each of the values into it.
|
||||
*
|
||||
* @webref doubledict:method
|
||||
* @brief Create a new array and copy each of the values into it
|
||||
*/
|
||||
public double[] valueArray() {
|
||||
crop();
|
||||
return valueArray(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fill an already-allocated array with the values (more efficient than
|
||||
* creating a new array each time). If 'array' is null, or not the same
|
||||
* size as the number of values, a new array will be allocated and returned.
|
||||
*/
|
||||
public double[] valueArray(double[] array) {
|
||||
if (array == null || array.length != size()) {
|
||||
array = new double[count];
|
||||
}
|
||||
System.arraycopy(values, 0, array, 0, count);
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a value for the specified key.
|
||||
*
|
||||
* @webref doubledict:method
|
||||
* @brief Return a value for the specified key
|
||||
*/
|
||||
public double get(String key) {
|
||||
int index = index(key);
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("No key named '" + key + "'");
|
||||
}
|
||||
return values[index];
|
||||
}
|
||||
|
||||
|
||||
public double get(String key, double alternate) {
|
||||
int index = index(key);
|
||||
if (index == -1) {
|
||||
return alternate;
|
||||
}
|
||||
return values[index];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Create a new key/value pair or change the value of one
|
||||
*/
|
||||
public void set(String key, double amount) {
|
||||
int index = index(key);
|
||||
if (index == -1) {
|
||||
create(key, amount);
|
||||
} else {
|
||||
values[index] = amount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setIndex(int index, String key, double value) {
|
||||
if (index < 0 || index >= count) {
|
||||
throw new ArrayIndexOutOfBoundsException(index);
|
||||
}
|
||||
keys[index] = key;
|
||||
values[index] = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Check if a key is a part of the data structure
|
||||
*/
|
||||
public boolean hasKey(String key) {
|
||||
return index(key) != -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Add to a value
|
||||
*/
|
||||
public void add(String key, double amount) {
|
||||
int index = index(key);
|
||||
if (index == -1) {
|
||||
create(key, amount);
|
||||
} else {
|
||||
values[index] += amount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Subtract from a value
|
||||
*/
|
||||
public void sub(String key, double amount) {
|
||||
add(key, -amount);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Multiply a value
|
||||
*/
|
||||
public void mult(String key, double amount) {
|
||||
int index = index(key);
|
||||
if (index != -1) {
|
||||
values[index] *= amount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Divide a value
|
||||
*/
|
||||
public void div(String key, double amount) {
|
||||
int index = index(key);
|
||||
if (index != -1) {
|
||||
values[index] /= amount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void checkMinMax(String functionName) {
|
||||
if (count == 0) {
|
||||
String msg =
|
||||
String.format("Cannot use %s() on an empty %s.",
|
||||
functionName, getClass().getSimpleName());
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Return the smallest value
|
||||
*/
|
||||
public int minIndex() {
|
||||
//checkMinMax("minIndex");
|
||||
if (count == 0) return -1;
|
||||
|
||||
// Will still return NaN if there are 1 or more entries, and they're all NaN
|
||||
double m = Float.NaN;
|
||||
int mi = -1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
// find one good value to start
|
||||
if (values[i] == values[i]) {
|
||||
m = values[i];
|
||||
mi = i;
|
||||
|
||||
// calculate the rest
|
||||
for (int j = i+1; j < count; j++) {
|
||||
double d = values[j];
|
||||
if ((d == d) && (d < m)) {
|
||||
m = values[j];
|
||||
mi = j;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mi;
|
||||
}
|
||||
|
||||
|
||||
// return the key for the minimum value
|
||||
public String minKey() {
|
||||
checkMinMax("minKey");
|
||||
int index = minIndex();
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
return keys[index];
|
||||
}
|
||||
|
||||
|
||||
// return the minimum value, or throw an error if there are no values
|
||||
public double minValue() {
|
||||
checkMinMax("minValue");
|
||||
int index = minIndex();
|
||||
if (index == -1) {
|
||||
return Float.NaN;
|
||||
}
|
||||
return values[index];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Return the largest value
|
||||
*/
|
||||
// The index of the entry that has the max value. Reference above is incorrect.
|
||||
public int maxIndex() {
|
||||
//checkMinMax("maxIndex");
|
||||
if (count == 0) {
|
||||
return -1;
|
||||
}
|
||||
// Will still return NaN if there is 1 or more entries, and they're all NaN
|
||||
double m = Double.NaN;
|
||||
int mi = -1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
// find one good value to start
|
||||
if (values[i] == values[i]) {
|
||||
m = values[i];
|
||||
mi = i;
|
||||
|
||||
// calculate the rest
|
||||
for (int j = i+1; j < count; j++) {
|
||||
double d = values[j];
|
||||
if (!Double.isNaN(d) && (d > m)) {
|
||||
m = values[j];
|
||||
mi = j;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mi;
|
||||
}
|
||||
|
||||
|
||||
/** The key for a max value; null if empty or everything is NaN (no max). */
|
||||
public String maxKey() {
|
||||
//checkMinMax("maxKey");
|
||||
int index = maxIndex();
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
return keys[index];
|
||||
}
|
||||
|
||||
|
||||
/** The max value. (Or NaN if no entries or they're all NaN.) */
|
||||
public double maxValue() {
|
||||
//checkMinMax("maxValue");
|
||||
int index = maxIndex();
|
||||
if (index == -1) {
|
||||
return Float.NaN;
|
||||
}
|
||||
return values[index];
|
||||
}
|
||||
|
||||
|
||||
public double sum() {
|
||||
double sum = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
sum += values[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
public int index(String what) {
|
||||
Integer found = indices.get(what);
|
||||
return (found == null) ? -1 : found.intValue();
|
||||
}
|
||||
|
||||
|
||||
protected void create(String what, double much) {
|
||||
if (count == keys.length) {
|
||||
keys = PApplet.expand(keys);
|
||||
values = PApplet.expand(values);
|
||||
}
|
||||
indices.put(what, Integer.valueOf(count));
|
||||
keys[count] = what;
|
||||
values[count] = much;
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Remove a key/value pair
|
||||
*/
|
||||
public int remove(String key) {
|
||||
int index = index(key);
|
||||
if (index != -1) {
|
||||
removeIndex(index);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
public String removeIndex(int index) {
|
||||
if (index < 0 || index >= count) {
|
||||
throw new ArrayIndexOutOfBoundsException(index);
|
||||
}
|
||||
String key = keys[index];
|
||||
//System.out.println("index is " + which + " and " + keys[which]);
|
||||
indices.remove(keys[index]);
|
||||
for (int i = index; i < count-1; i++) {
|
||||
keys[i] = keys[i+1];
|
||||
values[i] = values[i+1];
|
||||
indices.put(keys[i], i);
|
||||
}
|
||||
count--;
|
||||
keys[count] = null;
|
||||
values[count] = 0;
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
public void swap(int a, int b) {
|
||||
String tkey = keys[a];
|
||||
double tvalue = values[a];
|
||||
keys[a] = keys[b];
|
||||
values[a] = values[b];
|
||||
keys[b] = tkey;
|
||||
values[b] = tvalue;
|
||||
|
||||
// indices.put(keys[a], Integer.valueOf(a));
|
||||
// indices.put(keys[b], Integer.valueOf(b));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort the keys alphabetically (ignoring case). Uses the value as a
|
||||
* tie-breaker (only really possible with a key that has a case change).
|
||||
*
|
||||
* @webref doubledict:method
|
||||
* @brief Sort the keys alphabetically
|
||||
*/
|
||||
public void sortKeys() {
|
||||
sortImpl(true, false, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Sort the keys alphabetically in reverse
|
||||
*/
|
||||
public void sortKeysReverse() {
|
||||
sortImpl(true, true, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort by values in descending order (largest value will be at [0]).
|
||||
*
|
||||
* @webref doubledict:method
|
||||
* @brief Sort by values in ascending order
|
||||
*/
|
||||
public void sortValues() {
|
||||
sortValues(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set true to ensure that the order returned is identical. Slightly
|
||||
* slower because the tie-breaker for identical values compares the keys.
|
||||
* @param stable
|
||||
*/
|
||||
public void sortValues(boolean stable) {
|
||||
sortImpl(false, false, stable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doubledict:method
|
||||
* @brief Sort by values in descending order
|
||||
*/
|
||||
public void sortValuesReverse() {
|
||||
sortValuesReverse(true);
|
||||
}
|
||||
|
||||
|
||||
public void sortValuesReverse(boolean stable) {
|
||||
sortImpl(false, true, stable);
|
||||
}
|
||||
|
||||
|
||||
protected void sortImpl(final boolean useKeys, final boolean reverse,
|
||||
final boolean stable) {
|
||||
Sort s = new Sort() {
|
||||
@Override
|
||||
public int size() {
|
||||
if (useKeys) {
|
||||
return count; // don't worry about NaN values
|
||||
|
||||
} else if (count == 0) { // skip the NaN check, it'll AIOOBE
|
||||
return 0;
|
||||
|
||||
} else { // first move NaN values to the end of the list
|
||||
int right = count - 1;
|
||||
while (values[right] != values[right]) {
|
||||
right--;
|
||||
if (right == -1) {
|
||||
return 0; // all values are NaN
|
||||
}
|
||||
}
|
||||
for (int i = right; i >= 0; --i) {
|
||||
if (Double.isNaN(values[i])) {
|
||||
swap(i, right);
|
||||
--right;
|
||||
}
|
||||
}
|
||||
return right + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(int a, int b) {
|
||||
double diff = 0;
|
||||
if (useKeys) {
|
||||
diff = keys[a].compareToIgnoreCase(keys[b]);
|
||||
if (diff == 0) {
|
||||
diff = values[a] - values[b];
|
||||
}
|
||||
} else { // sort values
|
||||
diff = values[a] - values[b];
|
||||
if (diff == 0 && stable) {
|
||||
diff = keys[a].compareToIgnoreCase(keys[b]);
|
||||
}
|
||||
}
|
||||
if (diff == 0) {
|
||||
return 0;
|
||||
} else if (reverse) {
|
||||
return diff < 0 ? 1 : -1;
|
||||
} else {
|
||||
return diff < 0 ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swap(int a, int b) {
|
||||
DoubleDict.this.swap(a, b);
|
||||
}
|
||||
};
|
||||
s.run();
|
||||
|
||||
// Set the indices after sort/swaps (performance fix 160411)
|
||||
resetIndices();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sum all of the values in this dictionary, then return a new FloatDict of
|
||||
* each key, divided by the total sum. The total for all values will be ~1.0.
|
||||
* @return a FloatDict with the original keys, mapped to their pct of the total
|
||||
*/
|
||||
public DoubleDict getPercent() {
|
||||
double sum = sum();
|
||||
DoubleDict outgoing = new DoubleDict();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
double percent = value(i) / sum;
|
||||
outgoing.set(key(i), percent);
|
||||
}
|
||||
return outgoing;
|
||||
}
|
||||
|
||||
|
||||
/** Returns a duplicate copy of this object. */
|
||||
public DoubleDict copy() {
|
||||
DoubleDict outgoing = new DoubleDict(count);
|
||||
System.arraycopy(keys, 0, outgoing.keys, 0, count);
|
||||
System.arraycopy(values, 0, outgoing.values, 0, count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
outgoing.indices.put(keys[i], i);
|
||||
}
|
||||
outgoing.count = count;
|
||||
return outgoing;
|
||||
}
|
||||
|
||||
|
||||
public void print() {
|
||||
for (int i = 0; i < size(); i++) {
|
||||
System.out.println(keys[i] + " = " + values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write tab-delimited entries out to
|
||||
* @param writer
|
||||
*/
|
||||
public void write(PrintWriter writer) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
writer.println(keys[i] + "\t" + values[i]);
|
||||
}
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return this dictionary as a String in JSON format.
|
||||
*/
|
||||
public String toJSON() {
|
||||
StringList items = new StringList();
|
||||
for (int i = 0; i < count; i++) {
|
||||
items.append(JSONObject.quote(keys[i])+ ": " + values[i]);
|
||||
}
|
||||
return "{ " + items.join(", ") + " }";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,905 @@
|
||||
package processing.data;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
|
||||
import processing.core.PApplet;
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for a list of floats. Lists are designed to have some of the
|
||||
* features of ArrayLists, but to maintain the simplicity and efficiency of
|
||||
* working with arrays.
|
||||
*
|
||||
* Functions like sort() and shuffle() always act on the list itself. To get
|
||||
* a sorted copy, use list.copy().sort().
|
||||
*
|
||||
* @webref data:composite
|
||||
* @see IntList
|
||||
* @see StringList
|
||||
*/
|
||||
public class DoubleList implements Iterable<Double> {
|
||||
int count;
|
||||
double[] data;
|
||||
|
||||
|
||||
public DoubleList() {
|
||||
data = new double[10];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @nowebref
|
||||
*/
|
||||
public DoubleList(int length) {
|
||||
data = new double[length];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @nowebref
|
||||
*/
|
||||
public DoubleList(double[] list) {
|
||||
count = list.length;
|
||||
data = new double[count];
|
||||
System.arraycopy(list, 0, data, 0, count);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct an FloatList from an iterable pile of objects.
|
||||
* For instance, a double array, an array of strings, who knows).
|
||||
* Un-parseable or null values will be set to NaN.
|
||||
* @nowebref
|
||||
*/
|
||||
public DoubleList(Iterable<Object> iter) {
|
||||
this(10);
|
||||
for (Object o : iter) {
|
||||
if (o == null) {
|
||||
append(Double.NaN);
|
||||
} else if (o instanceof Number) {
|
||||
append(((Number) o).doubleValue());
|
||||
} else {
|
||||
append(PApplet.parseFloat(o.toString().trim()));
|
||||
}
|
||||
}
|
||||
crop();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct an FloatList from a random pile of objects.
|
||||
* Un-parseable or null values will be set to NaN.
|
||||
*/
|
||||
public DoubleList(Object... items) {
|
||||
// nuts, no good way to pass missingValue to this fn (varargs must be last)
|
||||
final double missingValue = Double.NaN;
|
||||
|
||||
count = items.length;
|
||||
data = new double[count];
|
||||
int index = 0;
|
||||
for (Object o : items) {
|
||||
double value = missingValue;
|
||||
if (o != null) {
|
||||
if (o instanceof Number) {
|
||||
value = ((Number) o).doubleValue();
|
||||
} else {
|
||||
try {
|
||||
value = Double.parseDouble(o.toString().trim());
|
||||
} catch (NumberFormatException nfe) {
|
||||
value = missingValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
data[index++] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Improve efficiency by removing allocated but unused entries from the
|
||||
* internal array used to store the data. Set to private, though it could
|
||||
* be useful to have this public if lists are frequently making drastic
|
||||
* size changes (from very large to very small).
|
||||
*/
|
||||
private void crop() {
|
||||
if (count != data.length) {
|
||||
data = PApplet.subset(data, 0, count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the length of the list.
|
||||
*
|
||||
* @webref doublelist:method
|
||||
* @brief Get the length of the list
|
||||
*/
|
||||
public int size() {
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
public void resize(int length) {
|
||||
if (length > data.length) {
|
||||
double[] temp = new double[length];
|
||||
System.arraycopy(data, 0, temp, 0, count);
|
||||
data = temp;
|
||||
|
||||
} else if (length > count) {
|
||||
Arrays.fill(data, count, length, 0);
|
||||
}
|
||||
count = length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all entries from the list.
|
||||
*
|
||||
* @webref doublelist:method
|
||||
* @brief Remove all entries from the list
|
||||
*/
|
||||
public void clear() {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an entry at a particular index.
|
||||
*
|
||||
* @webref doublelist:method
|
||||
* @brief Get an entry at a particular index
|
||||
*/
|
||||
public double get(int index) {
|
||||
if (index >= count) {
|
||||
throw new ArrayIndexOutOfBoundsException(index);
|
||||
}
|
||||
return data[index];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the entry at a particular index. If the index is past the length of
|
||||
* the list, it'll expand the list to accommodate, and fill the intermediate
|
||||
* entries with 0s.
|
||||
*
|
||||
* @webref doublelist:method
|
||||
* @brief Set the entry at a particular index
|
||||
*/
|
||||
public void set(int index, double what) {
|
||||
if (index >= count) {
|
||||
data = PApplet.expand(data, index+1);
|
||||
for (int i = count; i < index; i++) {
|
||||
data[i] = 0;
|
||||
}
|
||||
count = index+1;
|
||||
}
|
||||
data[index] = what;
|
||||
}
|
||||
|
||||
|
||||
/** Just an alias for append(), but matches pop() */
|
||||
public void push(double value) {
|
||||
append(value);
|
||||
}
|
||||
|
||||
|
||||
public double pop() {
|
||||
if (count == 0) {
|
||||
throw new RuntimeException("Can't call pop() on an empty list");
|
||||
}
|
||||
double value = get(count-1);
|
||||
count--;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an element from the specified index.
|
||||
*
|
||||
* @webref doublelist:method
|
||||
* @brief Remove an element from the specified index
|
||||
*/
|
||||
public double remove(int index) {
|
||||
if (index < 0 || index >= count) {
|
||||
throw new ArrayIndexOutOfBoundsException(index);
|
||||
}
|
||||
double entry = data[index];
|
||||
// int[] outgoing = new int[count - 1];
|
||||
// System.arraycopy(data, 0, outgoing, 0, index);
|
||||
// count--;
|
||||
// System.arraycopy(data, index + 1, outgoing, 0, count - index);
|
||||
// data = outgoing;
|
||||
// For most cases, this actually appears to be faster
|
||||
// than arraycopy() on an array copying into itself.
|
||||
for (int i = index; i < count-1; i++) {
|
||||
data[i] = data[i+1];
|
||||
}
|
||||
count--;
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
// Remove the first instance of a particular value,
|
||||
// and return the index at which it was found.
|
||||
public int removeValue(int value) {
|
||||
int index = index(value);
|
||||
if (index != -1) {
|
||||
remove(index);
|
||||
return index;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Remove all instances of a particular value,
|
||||
// and return the number of values found and removed
|
||||
public int removeValues(int value) {
|
||||
int ii = 0;
|
||||
if (Double.isNaN(value)) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!Double.isNaN(data[i])) {
|
||||
data[ii++] = data[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (data[i] != value) {
|
||||
data[ii++] = data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
int removed = count - ii;
|
||||
count = ii;
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
||||
/** Replace the first instance of a particular value */
|
||||
public boolean replaceValue(double value, double newValue) {
|
||||
if (Double.isNaN(value)) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (Double.isNaN(data[i])) {
|
||||
data[i] = newValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int index = index(value);
|
||||
if (index != -1) {
|
||||
data[index] = newValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Replace all instances of a particular value */
|
||||
public boolean replaceValues(double value, double newValue) {
|
||||
boolean changed = false;
|
||||
if (Double.isNaN(value)) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (Double.isNaN(data[i])) {
|
||||
data[i] = newValue;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (data[i] == value) {
|
||||
data[i] = newValue;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add a new entry to the list.
|
||||
*
|
||||
* @webref doublelist:method
|
||||
* @brief Add a new entry to the list
|
||||
*/
|
||||
public void append(double value) {
|
||||
if (count == data.length) {
|
||||
data = PApplet.expand(data);
|
||||
}
|
||||
data[count++] = value;
|
||||
}
|
||||
|
||||
|
||||
public void append(double[] values) {
|
||||
for (double v : values) {
|
||||
append(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void append(DoubleList list) {
|
||||
for (double v : list.values()) { // will concat the list...
|
||||
append(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Add this value, but only if it's not already in the list. */
|
||||
public void appendUnique(double value) {
|
||||
if (!hasValue(value)) {
|
||||
append(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public void insert(int index, int value) {
|
||||
// if (index+1 > count) {
|
||||
// if (index+1 < data.length) {
|
||||
// }
|
||||
// }
|
||||
// if (index >= data.length) {
|
||||
// data = PApplet.expand(data, index+1);
|
||||
// data[index] = value;
|
||||
// count = index+1;
|
||||
//
|
||||
// } else if (count == data.length) {
|
||||
// if (index >= count) {
|
||||
// //int[] temp = new int[count << 1];
|
||||
// System.arraycopy(data, 0, temp, 0, index);
|
||||
// temp[index] = value;
|
||||
// System.arraycopy(data, index, temp, index+1, count - index);
|
||||
// data = temp;
|
||||
//
|
||||
// } else {
|
||||
// // data[] has room to grow
|
||||
// // for() loop believed to be faster than System.arraycopy over itself
|
||||
// for (int i = count; i > index; --i) {
|
||||
// data[i] = data[i-1];
|
||||
// }
|
||||
// data[index] = value;
|
||||
// count++;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
public void insert(int index, double value) {
|
||||
insert(index, new double[] { value });
|
||||
}
|
||||
|
||||
|
||||
// same as splice
|
||||
public void insert(int index, double[] values) {
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("insert() index cannot be negative: it was " + index);
|
||||
}
|
||||
if (index >= data.length) {
|
||||
throw new IllegalArgumentException("insert() index " + index + " is past the end of this list");
|
||||
}
|
||||
|
||||
double[] temp = new double[count + values.length];
|
||||
|
||||
// Copy the old values, but not more than already exist
|
||||
System.arraycopy(data, 0, temp, 0, Math.min(count, index));
|
||||
|
||||
// Copy the new values into the proper place
|
||||
System.arraycopy(values, 0, temp, index, values.length);
|
||||
|
||||
// if (index < count) {
|
||||
// The index was inside count, so it's a true splice/insert
|
||||
System.arraycopy(data, index, temp, index+values.length, count - index);
|
||||
count = count + values.length;
|
||||
// } else {
|
||||
// // The index was past 'count', so the new count is weirder
|
||||
// count = index + values.length;
|
||||
// }
|
||||
data = temp;
|
||||
}
|
||||
|
||||
|
||||
public void insert(int index, DoubleList list) {
|
||||
insert(index, list.values());
|
||||
}
|
||||
|
||||
|
||||
// below are aborted attempts at more optimized versions of the code
|
||||
// that are harder to read and debug...
|
||||
|
||||
// if (index + values.length >= count) {
|
||||
// // We're past the current 'count', check to see if we're still allocated
|
||||
// // index 9, data.length = 10, values.length = 1
|
||||
// if (index + values.length < data.length) {
|
||||
// // There's still room for these entries, even though it's past 'count'.
|
||||
// // First clear out the entries leading up to it, however.
|
||||
// for (int i = count; i < index; i++) {
|
||||
// data[i] = 0;
|
||||
// }
|
||||
// data[index] =
|
||||
// }
|
||||
// if (index >= data.length) {
|
||||
// int length = index + values.length;
|
||||
// int[] temp = new int[length];
|
||||
// System.arraycopy(data, 0, temp, 0, count);
|
||||
// System.arraycopy(values, 0, temp, index, values.length);
|
||||
// data = temp;
|
||||
// count = data.length;
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// } else if (count == data.length) {
|
||||
// int[] temp = new int[count << 1];
|
||||
// System.arraycopy(data, 0, temp, 0, index);
|
||||
// temp[index] = value;
|
||||
// System.arraycopy(data, index, temp, index+1, count - index);
|
||||
// data = temp;
|
||||
//
|
||||
// } else {
|
||||
// // data[] has room to grow
|
||||
// // for() loop believed to be faster than System.arraycopy over itself
|
||||
// for (int i = count; i > index; --i) {
|
||||
// data[i] = data[i-1];
|
||||
// }
|
||||
// data[index] = value;
|
||||
// count++;
|
||||
// }
|
||||
|
||||
|
||||
/** Return the first index of a particular value. */
|
||||
public int index(double what) {
|
||||
/*
|
||||
if (indexCache != null) {
|
||||
try {
|
||||
return indexCache.get(what);
|
||||
} catch (Exception e) { // not there
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (data[i] == what) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Check if a number is a part of the list
|
||||
*/
|
||||
public boolean hasValue(double value) {
|
||||
if (Double.isNaN(value)) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (Double.isNaN(data[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (data[i] == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void boundsProblem(int index, String method) {
|
||||
final String msg = String.format("The list size is %d. " +
|
||||
"You cannot %s() to element %d.", count, method, index);
|
||||
throw new ArrayIndexOutOfBoundsException(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Add to a value
|
||||
*/
|
||||
public void add(int index, double amount) {
|
||||
if (index < count) {
|
||||
data[index] += amount;
|
||||
} else {
|
||||
boundsProblem(index, "add");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Subtract from a value
|
||||
*/
|
||||
public void sub(int index, double amount) {
|
||||
if (index < count) {
|
||||
data[index] -= amount;
|
||||
} else {
|
||||
boundsProblem(index, "sub");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Multiply a value
|
||||
*/
|
||||
public void mult(int index, double amount) {
|
||||
if (index < count) {
|
||||
data[index] *= amount;
|
||||
} else {
|
||||
boundsProblem(index, "mult");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Divide a value
|
||||
*/
|
||||
public void div(int index, double amount) {
|
||||
if (index < count) {
|
||||
data[index] /= amount;
|
||||
} else {
|
||||
boundsProblem(index, "div");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void checkMinMax(String functionName) {
|
||||
if (count == 0) {
|
||||
String msg =
|
||||
String.format("Cannot use %s() on an empty %s.",
|
||||
functionName, getClass().getSimpleName());
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Return the smallest value
|
||||
*/
|
||||
public double min() {
|
||||
checkMinMax("min");
|
||||
int index = minIndex();
|
||||
return index == -1 ? Double.NaN : data[index];
|
||||
}
|
||||
|
||||
|
||||
public int minIndex() {
|
||||
checkMinMax("minIndex");
|
||||
double m = Double.NaN;
|
||||
int mi = -1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
// find one good value to start
|
||||
if (data[i] == data[i]) {
|
||||
m = data[i];
|
||||
mi = i;
|
||||
|
||||
// calculate the rest
|
||||
for (int j = i+1; j < count; j++) {
|
||||
double d = data[j];
|
||||
if (!Double.isNaN(d) && (d < m)) {
|
||||
m = data[j];
|
||||
mi = j;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mi;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Return the largest value
|
||||
*/
|
||||
public double max() {
|
||||
checkMinMax("max");
|
||||
int index = maxIndex();
|
||||
return index == -1 ? Double.NaN : data[index];
|
||||
}
|
||||
|
||||
|
||||
public int maxIndex() {
|
||||
checkMinMax("maxIndex");
|
||||
double m = Double.NaN;
|
||||
int mi = -1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
// find one good value to start
|
||||
if (data[i] == data[i]) {
|
||||
m = data[i];
|
||||
mi = i;
|
||||
|
||||
// calculate the rest
|
||||
for (int j = i+1; j < count; j++) {
|
||||
double d = data[j];
|
||||
if (!Double.isNaN(d) && (d > m)) {
|
||||
m = data[j];
|
||||
mi = j;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mi;
|
||||
}
|
||||
|
||||
|
||||
public double sum() {
|
||||
double sum = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
sum += data[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sorts the array in place.
|
||||
*
|
||||
* @webref doublelist:method
|
||||
* @brief Sorts an array, lowest to highest
|
||||
*/
|
||||
public void sort() {
|
||||
Arrays.sort(data, 0, count);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse sort, orders values from highest to lowest
|
||||
*
|
||||
* @webref doublelist:method
|
||||
* @brief Reverse sort, orders values from highest to lowest
|
||||
*/
|
||||
public void sortReverse() {
|
||||
new Sort() {
|
||||
@Override
|
||||
public int size() {
|
||||
// if empty, don't even mess with the NaN check, it'll AIOOBE
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
// move NaN values to the end of the list and don't sort them
|
||||
int right = count - 1;
|
||||
while (data[right] != data[right]) {
|
||||
right--;
|
||||
if (right == -1) { // all values are NaN
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (int i = right; i >= 0; --i) {
|
||||
double v = data[i];
|
||||
if (v != v) {
|
||||
data[i] = data[right];
|
||||
data[right] = v;
|
||||
--right;
|
||||
}
|
||||
}
|
||||
return right + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(int a, int b) {
|
||||
double diff = data[b] - data[a];
|
||||
return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swap(int a, int b) {
|
||||
double temp = data[a];
|
||||
data[a] = data[b];
|
||||
data[b] = temp;
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
|
||||
// use insert()
|
||||
// public void splice(int index, int value) {
|
||||
// }
|
||||
|
||||
|
||||
// public void subset(int start) {
|
||||
// subset(start, count - start);
|
||||
// }
|
||||
|
||||
|
||||
// public void subset(int start, int num) {
|
||||
// for (int i = 0; i < num; i++) {
|
||||
// data[i] = data[i+start];
|
||||
// }
|
||||
// count = num;
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* @webref doublelist:method
|
||||
* @brief Reverse the order of the list elements
|
||||
*/
|
||||
public void reverse() {
|
||||
int ii = count - 1;
|
||||
for (int i = 0; i < count/2; i++) {
|
||||
double t = data[i];
|
||||
data[i] = data[ii];
|
||||
data[ii] = t;
|
||||
--ii;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Randomize the order of the list elements. Note that this does not
|
||||
* obey the randomSeed() function in PApplet.
|
||||
*
|
||||
* @webref doublelist:method
|
||||
* @brief Randomize the order of the list elements
|
||||
*/
|
||||
public void shuffle() {
|
||||
Random r = new Random();
|
||||
int num = count;
|
||||
while (num > 1) {
|
||||
int value = r.nextInt(num);
|
||||
num--;
|
||||
double temp = data[num];
|
||||
data[num] = data[value];
|
||||
data[value] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Randomize the list order using the random() function from the specified
|
||||
* sketch, allowing shuffle() to use its current randomSeed() setting.
|
||||
*/
|
||||
public void shuffle(PApplet sketch) {
|
||||
int num = count;
|
||||
while (num > 1) {
|
||||
int value = (int) sketch.random(num);
|
||||
num--;
|
||||
double temp = data[num];
|
||||
data[num] = data[value];
|
||||
data[value] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public DoubleList copy() {
|
||||
DoubleList outgoing = new DoubleList(data);
|
||||
outgoing.count = count;
|
||||
return outgoing;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the actual array being used to store the data. For advanced users,
|
||||
* this is the fastest way to access a large list. Suitable for iterating
|
||||
* with a for() loop, but modifying the list will have terrible consequences.
|
||||
*/
|
||||
public double[] values() {
|
||||
crop();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/** Implemented this way so that we can use a FloatList in a for loop. */
|
||||
@Override
|
||||
public Iterator<Double> iterator() {
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public Iterator<Float> valueIterator() {
|
||||
return new Iterator<Double>() {
|
||||
int index = -1;
|
||||
|
||||
public void remove() {
|
||||
DoubleList.this.remove(index);
|
||||
index--;
|
||||
}
|
||||
|
||||
public Double next() {
|
||||
return data[++index];
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return index+1 < count;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new array with a copy of all the values.
|
||||
* @return an array sized by the length of the list with each of the values.
|
||||
* @webref doublelist:method
|
||||
* @brief Create a new array with a copy of all the values
|
||||
*/
|
||||
public double[] array() {
|
||||
return array(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy values into the specified array. If the specified array is null or
|
||||
* not the same size, a new array will be allocated.
|
||||
* @param array
|
||||
*/
|
||||
public double[] array(double[] array) {
|
||||
if (array == null || array.length != count) {
|
||||
array = new double[count];
|
||||
}
|
||||
System.arraycopy(data, 0, array, 0, count);
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a normalized version of this array. Called getPercent() for
|
||||
* consistency with the Dict classes. It's a getter method because it needs
|
||||
* to returns a new list (because IntList/Dict can't do percentages or
|
||||
* normalization in place on int values).
|
||||
*/
|
||||
public DoubleList getPercent() {
|
||||
double sum = 0;
|
||||
for (double value : array()) {
|
||||
sum += value;
|
||||
}
|
||||
DoubleList outgoing = new DoubleList(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
double percent = data[i] / sum;
|
||||
outgoing.set(i, percent);
|
||||
}
|
||||
return outgoing;
|
||||
}
|
||||
|
||||
|
||||
public DoubleList getSubset(int start) {
|
||||
return getSubset(start, count - start);
|
||||
}
|
||||
|
||||
|
||||
public DoubleList getSubset(int start, int num) {
|
||||
double[] subset = new double[num];
|
||||
System.arraycopy(data, start, subset, 0, num);
|
||||
return new DoubleList(subset);
|
||||
}
|
||||
|
||||
|
||||
public String join(String separator) {
|
||||
if (count == 0) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(data[0]);
|
||||
for (int i = 1; i < count; i++) {
|
||||
sb.append(separator);
|
||||
sb.append(data[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public void print() {
|
||||
for (int i = 0; i < count; i++) {
|
||||
System.out.format("[%d] %f%n", i, data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return this dictionary as a String in JSON format.
|
||||
*/
|
||||
public String toJSON() {
|
||||
return "[ " + join(", ") + " ]";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
|
||||
}
|
||||
}
|
||||
@@ -734,7 +734,7 @@ public class FloatDict {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float compare(int a, int b) {
|
||||
public int compare(int a, int b) {
|
||||
float diff = 0;
|
||||
if (useKeys) {
|
||||
diff = keys[a].compareToIgnoreCase(keys[b]);
|
||||
@@ -747,7 +747,13 @@ public class FloatDict {
|
||||
diff = keys[a].compareToIgnoreCase(keys[b]);
|
||||
}
|
||||
}
|
||||
return reverse ? -diff : diff;
|
||||
if (diff == 0) {
|
||||
return 0;
|
||||
} else if (reverse) {
|
||||
return diff < 0 ? 1 : -1;
|
||||
} else {
|
||||
return diff < 0 ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -692,8 +692,9 @@ public class FloatList implements Iterable<Float> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float compare(int a, int b) {
|
||||
return data[b] - data[a];
|
||||
public int compare(int a, int b) {
|
||||
float diff = data[b] - data[a];
|
||||
return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -701,7 +701,7 @@ public class IntDict {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float compare(int a, int b) {
|
||||
public int compare(int a, int b) {
|
||||
int diff = 0;
|
||||
if (useKeys) {
|
||||
diff = keys[a].compareToIgnoreCase(keys[b]);
|
||||
|
||||
@@ -641,7 +641,7 @@ public class IntList implements Iterable<Integer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float compare(int a, int b) {
|
||||
public int compare(int a, int b) {
|
||||
return data[b] - data[a];
|
||||
}
|
||||
|
||||
|
||||
@@ -690,7 +690,7 @@ public class LongDict {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float compare(int a, int b) {
|
||||
public int compare(int a, int b) {
|
||||
long diff = 0;
|
||||
if (useKeys) {
|
||||
diff = keys[a].compareToIgnoreCase(keys[b]);
|
||||
@@ -703,7 +703,13 @@ public class LongDict {
|
||||
diff = keys[a].compareToIgnoreCase(keys[b]);
|
||||
}
|
||||
}
|
||||
return reverse ? -diff : diff;
|
||||
if (diff == 0) {
|
||||
return 0;
|
||||
} else if (reverse) {
|
||||
return diff < 0 ? 1 : -1;
|
||||
} else {
|
||||
return diff < 0 ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -641,8 +641,9 @@ public class LongList implements Iterable<Long> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float compare(int a, int b) {
|
||||
return data[b] - data[a];
|
||||
public int compare(int a, int b) {
|
||||
long diff = data[b] - data[a];
|
||||
return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,6 +41,6 @@ public abstract class Sort implements Runnable {
|
||||
|
||||
|
||||
abstract public int size();
|
||||
abstract public float compare(int a, int b);
|
||||
abstract public int compare(int a, int b);
|
||||
abstract public void swap(int a, int b);
|
||||
}
|
||||
@@ -522,7 +522,7 @@ public class StringDict {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float compare(int a, int b) {
|
||||
public int compare(int a, int b) {
|
||||
int diff = 0;
|
||||
if (useKeys) {
|
||||
diff = keys[a].compareToIgnoreCase(keys[b]);
|
||||
|
||||
@@ -514,8 +514,8 @@ public class StringList implements Iterable<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float compare(int a, int b) {
|
||||
float diff = data[a].compareToIgnoreCase(data[b]);
|
||||
public int compare(int a, int b) {
|
||||
int diff = data[a].compareToIgnoreCase(data[b]);
|
||||
return reverse ? -diff : diff;
|
||||
}
|
||||
|
||||
|
||||
@@ -4333,7 +4333,7 @@ public class Table {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float compare(int index1, int index2) {
|
||||
public int compare(int index1, int index2) {
|
||||
int a = reverse ? order[index2] : order[index1];
|
||||
int b = reverse ? order[index1] : order[index2];
|
||||
|
||||
@@ -4341,11 +4341,14 @@ public class Table {
|
||||
case INT:
|
||||
return getInt(a, column) - getInt(b, column);
|
||||
case LONG:
|
||||
return getLong(a, column) - getLong(b, column);
|
||||
long diffl = getLong(a, column) - getLong(b, column);
|
||||
return diffl == 0 ? 0 : (diffl < 0 ? -1 : 1);
|
||||
case FLOAT:
|
||||
return getFloat(a, column) - getFloat(b, column);
|
||||
float difff = getFloat(a, column) - getFloat(b, column);
|
||||
return difff == 0 ? 0 : (difff < 0 ? -1 : 1);
|
||||
case DOUBLE:
|
||||
return (float) (getDouble(a, column) - getDouble(b, column));
|
||||
double diffd = getDouble(a, column) - getDouble(b, column);
|
||||
return diffd == 0 ? 0 : (diffd < 0 ? -1 : 1);
|
||||
case STRING:
|
||||
return getString(a, column).compareToIgnoreCase(getString(b, column));
|
||||
case CATEGORY:
|
||||
|
||||
Reference in New Issue
Block a user