propane 3.11.0-java → 4.0.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.mvn/extensions.xml +1 -1
  3. data/CHANGELOG.md +2 -0
  4. data/README.md +5 -5
  5. data/Rakefile +1 -1
  6. data/lib/propane/app.rb +2 -2
  7. data/lib/propane/version.rb +1 -1
  8. data/lib/propane-4.0.0.jar +0 -0
  9. data/library/slider/slider.rb +1 -1
  10. data/pom.rb +8 -8
  11. data/pom.xml +8 -8
  12. data/propane.gemspec +3 -3
  13. data/src/main/java/monkstone/ColorUtil.java +1 -1
  14. data/src/main/java/monkstone/MathToolModule.java +1 -1
  15. data/src/main/java/monkstone/PropaneLibrary.java +1 -1
  16. data/src/main/java/monkstone/fastmath/DegLutTables.java +10 -11
  17. data/src/main/java/monkstone/fastmath/Deglut.java +1 -1
  18. data/src/main/java/monkstone/filechooser/Chooser.java +1 -1
  19. data/src/main/java/monkstone/noise/LICENSE +121 -0
  20. data/src/main/java/monkstone/slider/CustomHorizontalSlider.java +1 -1
  21. data/src/main/java/monkstone/slider/CustomVerticalSlider.java +1 -1
  22. data/src/main/java/monkstone/slider/SimpleHorizontalSlider.java +1 -1
  23. data/src/main/java/monkstone/slider/SimpleVerticalSlider.java +1 -1
  24. data/src/main/java/monkstone/slider/SliderBar.java +1 -1
  25. data/src/main/java/monkstone/slider/SliderGroup.java +1 -1
  26. data/src/main/java/monkstone/slider/WheelHandler.java +1 -1
  27. data/src/main/java/monkstone/vecmath/package-info.java +1 -1
  28. data/src/main/java/monkstone/vecmath/vec2/Vec2.java +92 -68
  29. data/src/main/java/monkstone/vecmath/vec3/Vec3.java +1 -1
  30. data/src/main/java/monkstone/videoevent/CaptureEvent.java +1 -1
  31. data/src/main/java/monkstone/videoevent/MovieEvent.java +1 -1
  32. data/src/main/java/monkstone/videoevent/package-info.java +1 -1
  33. data/src/main/java/processing/awt/PGraphicsJava2D.java +0 -1
  34. data/src/main/java/processing/awt/PImageAWT.java +2 -4
  35. data/src/main/java/processing/core/PApplet.java +4 -4
  36. data/src/main/java/processing/core/PImage.java +3025 -3047
  37. data/src/main/java/processing/core/PMatrix.java +5 -2
  38. data/src/main/java/processing/data/DoubleDict.java +72 -43
  39. data/src/main/java/processing/data/DoubleList.java +6 -2
  40. data/src/main/java/processing/data/FloatDict.java +744 -756
  41. data/src/main/java/processing/data/FloatList.java +68 -26
  42. data/src/main/java/processing/data/IntDict.java +72 -45
  43. data/src/main/java/processing/data/IntList.java +63 -26
  44. data/src/main/java/processing/data/JSONArray.java +892 -931
  45. data/src/main/java/processing/data/JSONObject.java +1169 -1262
  46. data/src/main/java/processing/data/JSONTokener.java +30 -49
  47. data/src/main/java/processing/data/LongDict.java +699 -712
  48. data/src/main/java/processing/data/LongList.java +676 -700
  49. data/src/main/java/processing/data/Sort.java +1 -0
  50. data/src/main/java/processing/data/Table.java +4040 -3661
  51. data/src/main/java/processing/data/TableRow.java +16 -0
  52. data/src/main/java/processing/data/XML.java +1041 -956
  53. data/src/main/java/processing/event/TouchEvent.java +1 -1
  54. data/src/main/java/processing/opengl/FontTexture.java +2 -2
  55. data/src/main/java/processing/opengl/PGraphicsOpenGL.java +15 -18
  56. data/src/main/java/processing/opengl/PJOGL.java +2 -2
  57. data/src/main/java/processing/opengl/PShapeOpenGL.java +23 -24
  58. data/test/vecmath_spec_test.rb +14 -3
  59. data/vendors/Rakefile +1 -1
  60. metadata +9 -8
  61. data/lib/propane-3.11.0.jar +0 -0
@@ -7,7 +7,6 @@ import java.util.NoSuchElementException;
7
7
 
8
8
  import processing.core.PApplet;
9
9
 
10
-
11
10
  /**
12
11
  * A simple table class to use a String as a lookup for an float value.
13
12
  *
@@ -17,831 +16,820 @@ import processing.core.PApplet;
17
16
  */
18
17
  public class FloatDict {
19
18
 
20
- /** Number of elements in the table */
21
- protected int count;
22
-
23
- protected String[] keys;
24
- protected float[] values;
25
-
26
- /** Internal implementation for faster lookups */
27
- private HashMap<String, Integer> indices = new HashMap<>();
28
-
29
-
30
- public FloatDict() {
31
- count = 0;
32
- keys = new String[10];
33
- values = new float[10];
34
- }
35
-
36
-
37
- /**
38
- * Create a new lookup with a specific size. This is more efficient than not
39
- * specifying a size. Use it when you know the rough size of the thing you're creating.
40
- *
41
- * @nowebref
42
- */
43
- public FloatDict(int length) {
44
- count = 0;
45
- keys = new String[length];
46
- values = new float[length];
47
- }
48
-
49
-
50
- /**
51
- * Read a set of entries from a Reader that has each key/value pair on
52
- * a single line, separated by a tab.
53
- *
54
- * @nowebref
55
- */
56
- public FloatDict(BufferedReader reader) {
57
- String[] lines = PApplet.loadStrings(reader);
58
- keys = new String[lines.length];
59
- values = new float[lines.length];
60
-
61
- for (int i = 0; i < lines.length; i++) {
62
- String[] pieces = PApplet.split(lines[i], '\t');
63
- if (pieces.length == 2) {
64
- keys[count] = pieces[0];
65
- values[count] = PApplet.parseFloat(pieces[1]);
66
- indices.put(pieces[0], count);
67
- count++;
68
- }
19
+ /**
20
+ * Number of elements in the table
21
+ */
22
+ protected int count;
23
+
24
+ protected String[] keys;
25
+ protected float[] values;
26
+
27
+ /**
28
+ * Internal implementation for faster lookups
29
+ */
30
+ private HashMap<String, Integer> indices = new HashMap<>();
31
+
32
+ public FloatDict() {
33
+ count = 0;
34
+ keys = new String[10];
35
+ values = new float[10];
36
+ }
37
+
38
+ /**
39
+ * Create a new lookup with a specific size.This is more efficient than not
40
+ * specifying a size. Use it when you know the rough size of the thing
41
+ * you're creating.
42
+ *
43
+ * @param length
44
+ * @nowebref
45
+ */
46
+ public FloatDict(int length) {
47
+ count = 0;
48
+ keys = new String[length];
49
+ values = new float[length];
50
+ }
51
+
52
+ /**
53
+ * Read a set of entries from a Reader that has each key/value pair on a
54
+ * single line, separated by a tab.
55
+ *
56
+ * @param reader
57
+ * @nowebref
58
+ */
59
+ public FloatDict(BufferedReader reader) {
60
+ String[] lines = PApplet.loadStrings(reader);
61
+ keys = new String[lines.length];
62
+ values = new float[lines.length];
63
+
64
+ for (String line : lines) {
65
+ String[] pieces = PApplet.split(line, '\t');
66
+ if (pieces.length == 2) {
67
+ keys[count] = pieces[0];
68
+ values[count] = PApplet.parseFloat(pieces[1]);
69
+ indices.put(pieces[0], count);
70
+ count++;
71
+ }
72
+ }
69
73
  }
70
- }
71
74
 
72
-
73
- /**
74
- * @nowebref
75
- */
76
- public FloatDict(String[] keys, float[] values) {
77
- if (keys.length != values.length) {
78
- throw new IllegalArgumentException("key and value arrays must be the same length");
79
- }
80
- this.keys = keys;
81
- this.values = values;
82
- count = keys.length;
83
- for (int i = 0; i < count; i++) {
84
- indices.put(keys[i], i);
75
+ /**
76
+ * @param keys
77
+ * @param values
78
+ * @nowebref
79
+ */
80
+ public FloatDict(String[] keys, float[] values) {
81
+ if (keys.length != values.length) {
82
+ throw new IllegalArgumentException("key and value arrays must be the same length");
83
+ }
84
+ this.keys = keys;
85
+ this.values = values;
86
+ count = keys.length;
87
+ for (int i = 0; i < count; i++) {
88
+ indices.put(keys[i], i);
89
+ }
85
90
  }
86
- }
87
91
 
88
-
89
- /**
90
- * Constructor to allow (more intuitive) inline initialization, e.g.:
91
- * <pre>
92
- * new FloatDict(new Object[][] {
93
- * { "key1", 1 },
94
- * { "key2", 2 }
95
- * });
96
- * </pre>
97
- */
98
- public FloatDict(Object[][] pairs) {
99
- count = pairs.length;
100
- this.keys = new String[count];
101
- this.values = new float[count];
102
- for (int i = 0; i < count; i++) {
103
- keys[i] = (String) pairs[i][0];
104
- values[i] = (Float) pairs[i][1];
105
- indices.put(keys[i], i);
92
+ /**
93
+ * Constructor to allow (more intuitive) inline initialization, e.g.:
94
+ * <pre>
95
+ * new FloatDict(new Object[][] {
96
+ * { "key1", 1 },
97
+ * { "key2", 2 }
98
+ * });
99
+ * </pre>
100
+ *
101
+ * @param pairs
102
+ */
103
+ public FloatDict(Object[][] pairs) {
104
+ count = pairs.length;
105
+ this.keys = new String[count];
106
+ this.values = new float[count];
107
+ for (int i = 0; i < count; i++) {
108
+ keys[i] = (String) pairs[i][0];
109
+ values[i] = (Float) pairs[i][1];
110
+ indices.put(keys[i], i);
111
+ }
106
112
  }
107
- }
108
-
109
-
110
- /**
111
- * @webref floatdict:method
112
- * @brief Returns the number of key/value pairs
113
- */
114
- public int size() {
115
- return count;
116
- }
117
113
 
114
+ /**
115
+ * @return @webref floatdict:method
116
+ * @brief Returns the number of key/value pairs
117
+ */
118
+ public int size() {
119
+ return count;
120
+ }
121
+
122
+ /**
123
+ * Resize the internal data, this can only be used to shrink the
124
+ * list.Helpful for situations like sorting and then grabbing the top 50
125
+ * entries.
126
+ *
127
+ * @param length
128
+ */
129
+ public void resize(int length) {
130
+ if (length == count) {
131
+ return;
132
+ }
118
133
 
119
- /**
120
- * Resize the internal data, this can only be used to shrink the list.
121
- * Helpful for situations like sorting and then grabbing the top 50 entries.
122
- */
123
- public void resize(int length) {
124
- if (length == count) return;
134
+ if (length > count) {
135
+ throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
136
+ }
137
+ if (length < 1) {
138
+ throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
139
+ }
125
140
 
126
- if (length > count) {
127
- throw new IllegalArgumentException("resize() can only be used to shrink the dictionary");
128
- }
129
- if (length < 1) {
130
- throw new IllegalArgumentException("resize(" + length + ") is too small, use 1 or higher");
141
+ String[] newKeys = new String[length];
142
+ float[] newValues = new float[length];
143
+ PApplet.arrayCopy(keys, newKeys, length);
144
+ PApplet.arrayCopy(values, newValues, length);
145
+ keys = newKeys;
146
+ values = newValues;
147
+ count = length;
148
+ resetIndices();
149
+ }
150
+
151
+ /**
152
+ * Remove all entries.
153
+ *
154
+ * @webref floatdict:method
155
+ * @brief Remove all entries
156
+ */
157
+ public void clear() {
158
+ count = 0;
159
+ indices = new HashMap<>();
160
+ }
161
+
162
+ private void resetIndices() {
163
+ indices = new HashMap<>(count);
164
+ for (int i = 0; i < count; i++) {
165
+ indices.put(keys[i], i);
166
+ }
131
167
  }
132
168
 
133
- String[] newKeys = new String[length];
134
- float[] newValues = new float[length];
135
- PApplet.arrayCopy(keys, newKeys, length);
136
- PApplet.arrayCopy(values, newValues, length);
137
- keys = newKeys;
138
- values = newValues;
139
- count = length;
140
- resetIndices();
141
- }
142
-
169
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
170
+ public class Entry {
143
171
 
144
- /**
145
- * Remove all entries.
146
- *
147
- * @webref floatdict:method
148
- * @brief Remove all entries
149
- */
150
- public void clear() {
151
- count = 0;
152
- indices = new HashMap<>();
153
- }
172
+ public String key;
173
+ public float value;
154
174
 
155
-
156
- private void resetIndices() {
157
- indices = new HashMap<>(count);
158
- for (int i = 0; i < count; i++) {
159
- indices.put(keys[i], i);
175
+ Entry(String key, float value) {
176
+ this.key = key;
177
+ this.value = value;
178
+ }
160
179
  }
161
- }
162
-
163
-
164
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
165
-
166
-
167
- public class Entry {
168
- public String key;
169
- public float value;
170
180
 
171
- Entry(String key, float value) {
172
- this.key = key;
173
- this.value = value;
181
+ public Iterable<Entry> entries() {
182
+ return () -> entryIterator();
174
183
  }
175
- }
176
-
177
-
178
- public Iterable<Entry> entries() {
179
- return new Iterable<Entry>() {
180
-
181
- public Iterator<Entry> iterator() {
182
- return entryIterator();
183
- }
184
- };
185
- }
186
-
187
-
188
- public Iterator<Entry> entryIterator() {
189
- return new Iterator<Entry>() {
190
- int index = -1;
191
-
192
- public void remove() {
193
- removeIndex(index);
194
- index--;
195
- }
196
184
 
197
- public Entry next() {
198
- ++index;
199
- Entry e = new Entry(keys[index], values[index]);
200
- return e;
201
- }
185
+ public Iterator<Entry> entryIterator() {
186
+ return new Iterator<Entry>() {
187
+ int index = -1;
202
188
 
203
- public boolean hasNext() {
204
- return index+1 < size();
205
- }
206
- };
207
- }
208
-
209
-
210
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
211
-
212
-
213
- public String key(int index) {
214
- return keys[index];
215
- }
189
+ @Override
190
+ public void remove() {
191
+ removeIndex(index);
192
+ index--;
193
+ }
216
194
 
195
+ @Override
196
+ public Entry next() {
197
+ ++index;
198
+ Entry e = new Entry(keys[index], values[index]);
199
+ return e;
200
+ }
217
201
 
218
- protected void crop() {
219
- if (count != keys.length) {
220
- keys = PApplet.subset(keys, 0, count);
221
- values = PApplet.subset(values, 0, count);
202
+ @Override
203
+ public boolean hasNext() {
204
+ return index + 1 < size();
205
+ }
206
+ };
222
207
  }
223
- }
224
-
225
208
 
226
- public Iterable<String> keys() {
227
- return new Iterable<String>() {
228
-
229
- @Override
230
- public Iterator<String> iterator() {
231
- return keyIterator();
232
- }
233
- };
234
- }
235
-
236
-
237
- // Use this to iterate when you want to be able to remove elements along the way
238
- public Iterator<String> keyIterator() {
239
- return new Iterator<String>() {
240
- int index = -1;
241
-
242
- public void remove() {
243
- removeIndex(index);
244
- index--;
245
- }
209
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
210
+ public String key(int index) {
211
+ return keys[index];
212
+ }
246
213
 
247
- public String next() {
248
- return key(++index);
249
- }
214
+ protected void crop() {
215
+ if (count != keys.length) {
216
+ keys = PApplet.subset(keys, 0, count);
217
+ values = PApplet.subset(values, 0, count);
218
+ }
219
+ }
250
220
 
251
- public boolean hasNext() {
252
- return index+1 < size();
253
- }
254
- };
255
- }
221
+ public Iterable<String> keys() {
222
+ return () -> keyIterator();
223
+ }
256
224
 
225
+ // Use this to iterate when you want to be able to remove elements along the way
226
+ public Iterator<String> keyIterator() {
227
+ return new Iterator<String>() {
228
+ int index = -1;
257
229
 
258
- /**
259
- * Return a copy of the internal keys array. This array can be modified.
260
- *
261
- * @webref floatdict:method
262
- * @brief Return a copy of the internal keys array
263
- */
264
- public String[] keyArray() {
265
- crop();
266
- return keyArray(null);
267
- }
230
+ @Override
231
+ public void remove() {
232
+ removeIndex(index);
233
+ index--;
234
+ }
268
235
 
236
+ @Override
237
+ public String next() {
238
+ return key(++index);
239
+ }
269
240
 
270
- public String[] keyArray(String[] outgoing) {
271
- if (outgoing == null || outgoing.length != count) {
272
- outgoing = new String[count];
241
+ @Override
242
+ public boolean hasNext() {
243
+ return index + 1 < size();
244
+ }
245
+ };
273
246
  }
274
- System.arraycopy(keys, 0, outgoing, 0, count);
275
- return outgoing;
276
- }
277
247
 
248
+ /**
249
+ * Return a copy of the internal keys array.This array can be modified.
250
+ *
251
+ * @return
252
+ * @webref floatdict:method
253
+ * @brief Return a copy of the internal keys array
254
+ */
255
+ public String[] keyArray() {
256
+ crop();
257
+ return keyArray(null);
258
+ }
278
259
 
279
- public float value(int index) {
280
- return values[index];
281
- }
282
-
260
+ public String[] keyArray(String[] outgoing) {
261
+ if (outgoing == null || outgoing.length != count) {
262
+ outgoing = new String[count];
263
+ }
264
+ System.arraycopy(keys, 0, outgoing, 0, count);
265
+ return outgoing;
266
+ }
283
267
 
284
- /**
285
- * @webref floatdict:method
286
- * @brief Return the internal array being used to store the values
287
- */
288
- public Iterable<Float> values() {
289
- return new Iterable<Float>() {
268
+ public float value(int index) {
269
+ return values[index];
270
+ }
290
271
 
291
- @Override
292
- public Iterator<Float> iterator() {
293
- return valueIterator();
294
- }
295
- };
296
- }
272
+ /**
273
+ * @return @webref floatdict:method
274
+ * @brief Return the internal array being used to store the values
275
+ */
276
+ public Iterable<Float> values() {
277
+ return () -> valueIterator();
278
+ }
297
279
 
280
+ public Iterator<Float> valueIterator() {
281
+ return new Iterator<Float>() {
282
+ int index = -1;
298
283
 
299
- public Iterator<Float> valueIterator() {
300
- return new Iterator<Float>() {
301
- int index = -1;
284
+ @Override
285
+ public void remove() {
286
+ removeIndex(index);
287
+ index--;
288
+ }
302
289
 
303
- public void remove() {
304
- removeIndex(index);
305
- index--;
306
- }
307
-
308
- public Float next() {
309
- return value(++index);
310
- }
311
-
312
- public boolean hasNext() {
313
- return index+1 < size();
314
- }
315
- };
316
- }
317
-
318
-
319
- /**
320
- * Create a new array and copy each of the values into it.
321
- *
322
- * @webref floatdict:method
323
- * @brief Create a new array and copy each of the values into it
324
- */
325
- public float[] valueArray() {
326
- crop();
327
- return valueArray(null);
328
- }
329
-
330
-
331
- /**
332
- * Fill an already-allocated array with the values (more efficient than
333
- * creating a new array each time). If 'array' is null, or not the same
334
- * size as the number of values, a new array will be allocated and returned.
335
- */
336
- public float[] valueArray(float[] array) {
337
- if (array == null || array.length != size()) {
338
- array = new float[count];
339
- }
340
- System.arraycopy(values, 0, array, 0, count);
341
- return array;
342
- }
343
-
344
-
345
- /**
346
- * Return a value for the specified key.
347
- *
348
- * @webref floatdict:method
349
- * @brief Return a value for the specified key
350
- */
351
- public float get(String key) {
352
- int index = index(key);
353
- if (index == -1) {
354
- throw new IllegalArgumentException("No key named '" + key + "'");
355
- }
356
- return values[index];
357
- }
358
-
359
-
360
- public float get(String key, float alternate) {
361
- int index = index(key);
362
- if (index == -1) {
363
- return alternate;
364
- }
365
- return values[index];
366
- }
367
-
368
-
369
- /**
370
- * @webref floatdict:method
371
- * @brief Create a new key/value pair or change the value of one
372
- */
373
- public void set(String key, float amount) {
374
- int index = index(key);
375
- if (index == -1) {
376
- create(key, amount);
377
- } else {
378
- values[index] = amount;
379
- }
380
- }
381
-
382
-
383
- public void setIndex(int index, String key, float value) {
384
- if (index < 0 || index >= count) {
385
- throw new ArrayIndexOutOfBoundsException(index);
386
- }
387
- keys[index] = key;
388
- values[index] = value;
389
- }
390
-
391
-
392
- /**
393
- * @webref floatdict:method
394
- * @brief Check if a key is a part of the data structure
395
- */
396
- public boolean hasKey(String key) {
397
- return index(key) != -1;
398
- }
399
-
400
-
401
- /**
402
- * @webref floatdict:method
403
- * @brief Add to a value
404
- */
405
- public void add(String key, float amount) {
406
- int index = index(key);
407
- if (index == -1) {
408
- create(key, amount);
409
- } else {
410
- values[index] += amount;
411
- }
412
- }
413
-
414
-
415
- /**
416
- * @webref floatdict:method
417
- * @brief Subtract from a value
418
- */
419
- public void sub(String key, float amount) {
420
- add(key, -amount);
421
- }
422
-
423
-
424
- /**
425
- * @webref floatdict:method
426
- * @brief Multiply a value
427
- */
428
- public void mult(String key, float amount) {
429
- int index = index(key);
430
- if (index != -1) {
431
- values[index] *= amount;
432
- }
433
- }
434
-
435
-
436
- /**
437
- * @webref floatdict:method
438
- * @brief Divide a value
439
- */
440
- public void div(String key, float amount) {
441
- int index = index(key);
442
- if (index != -1) {
443
- values[index] /= amount;
444
- }
445
- }
446
-
447
-
448
- private void checkMinMax(String functionName) {
449
- if (count == 0) {
450
- String msg =
451
- String.format("Cannot use %s() on an empty %s.",
452
- functionName, getClass().getSimpleName());
453
- throw new RuntimeException(msg);
454
- }
455
- }
456
-
457
-
458
- /**
459
- * @webref floatlist:method
460
- * @brief Return the smallest value
461
- */
462
- public int minIndex() {
463
- //checkMinMax("minIndex");
464
- if (count == 0) return -1;
465
-
466
- // Will still return NaN if there are 1 or more entries, and they're all NaN
467
- float m = Float.NaN;
468
- int mi = -1;
469
- for (int i = 0; i < count; i++) {
470
- // find one good value to start
471
- if (values[i] == values[i]) {
472
- m = values[i];
473
- mi = i;
474
-
475
- // calculate the rest
476
- for (int j = i+1; j < count; j++) {
477
- float d = values[j];
478
- if ((d == d) && (d < m)) {
479
- m = values[j];
480
- mi = j;
481
- }
482
- }
483
- break;
484
- }
485
- }
486
- return mi;
487
- }
488
-
489
-
490
- // return the key for the minimum value
491
- public String minKey() {
492
- checkMinMax("minKey");
493
- int index = minIndex();
494
- if (index == -1) {
495
- return null;
496
- }
497
- return keys[index];
498
- }
290
+ @Override
291
+ public Float next() {
292
+ return value(++index);
293
+ }
499
294
 
500
-
501
- // return the minimum value, or throw an error if there are no values
502
- public float minValue() {
503
- checkMinMax("minValue");
504
- int index = minIndex();
505
- if (index == -1) {
506
- return Float.NaN;
295
+ @Override
296
+ public boolean hasNext() {
297
+ return index + 1 < size();
298
+ }
299
+ };
300
+ }
301
+
302
+ /**
303
+ * Create a new array and copy each of the values into it.
304
+ *
305
+ * @return
306
+ * @webref floatdict:method
307
+ * @brief Create a new array and copy each of the values into it
308
+ */
309
+ public float[] valueArray() {
310
+ crop();
311
+ return valueArray(null);
312
+ }
313
+
314
+ /**
315
+ * Fill an already-allocated array with the values (more efficient than
316
+ * creating a new array each time).If 'array' is null, or not the same size
317
+ * as the number of values, a new array will be allocated and returned.
318
+ *
319
+ * @param array
320
+ * @return
321
+ */
322
+ public float[] valueArray(float[] array) {
323
+ if (array == null || array.length != size()) {
324
+ array = new float[count];
325
+ }
326
+ System.arraycopy(values, 0, array, 0, count);
327
+ return array;
328
+ }
329
+
330
+ /**
331
+ * Return a value for the specified key.
332
+ *
333
+ * @param key
334
+ * @return
335
+ * @webref floatdict:method
336
+ * @brief Return a value for the specified key
337
+ */
338
+ public float get(String key) {
339
+ int index = index(key);
340
+ if (index == -1) {
341
+ throw new IllegalArgumentException("No key named '" + key + "'");
342
+ }
343
+ return values[index];
507
344
  }
508
- return values[index];
509
- }
510
-
511
345
 
512
- /**
513
- * @webref floatlist:method
514
- * @brief Return the largest value
515
- */
516
- // The index of the entry that has the max value. Reference above is incorrect.
517
- public int maxIndex() {
518
- //checkMinMax("maxIndex");
519
- if (count == 0) {
520
- return -1;
346
+ public float get(String key, float alternate) {
347
+ int index = index(key);
348
+ if (index == -1) {
349
+ return alternate;
350
+ }
351
+ return values[index];
352
+ }
353
+
354
+ /**
355
+ * @param key
356
+ * @param amount
357
+ * @webref floatdict:method
358
+ * @brief Create a new key/value pair or change the value of one
359
+ */
360
+ public void set(String key, float amount) {
361
+ int index = index(key);
362
+ if (index == -1) {
363
+ create(key, amount);
364
+ } else {
365
+ values[index] = amount;
366
+ }
521
367
  }
522
- // Will still return NaN if there is 1 or more entries, and they're all NaN
523
- float m = Float.NaN;
524
- int mi = -1;
525
- for (int i = 0; i < count; i++) {
526
- // find one good value to start
527
- if (values[i] == values[i]) {
528
- m = values[i];
529
- mi = i;
530
-
531
- // calculate the rest
532
- for (int j = i+1; j < count; j++) {
533
- float d = values[j];
534
- if (!Float.isNaN(d) && (d > m)) {
535
- m = values[j];
536
- mi = j;
537
- }
538
- }
539
- break;
540
- }
541
- }
542
- return mi;
543
- }
544
-
545
-
546
- /** The key for a max value; null if empty or everything is NaN (no max). */
547
- public String maxKey() {
548
- //checkMinMax("maxKey");
549
- int index = maxIndex();
550
- if (index == -1) {
551
- return null;
552
- }
553
- return keys[index];
554
- }
555
-
556
-
557
- /** The max value. (Or NaN if no entries or they're all NaN.) */
558
- public float maxValue() {
559
- //checkMinMax("maxValue");
560
- int index = maxIndex();
561
- if (index == -1) {
562
- return Float.NaN;
563
- }
564
- return values[index];
565
- }
566
-
567
-
568
- public float sum() {
569
- double amount = sumDouble();
570
- if (amount > Float.MAX_VALUE) {
571
- throw new RuntimeException("sum() exceeds " + Float.MAX_VALUE + ", use sumDouble()");
572
- }
573
- if (amount < -Float.MAX_VALUE) {
574
- throw new RuntimeException("sum() lower than " + -Float.MAX_VALUE + ", use sumDouble()");
575
- }
576
- return (float) amount;
577
- }
578
-
579
368
 
580
- public double sumDouble() {
581
- double sum = 0;
582
- for (int i = 0; i < count; i++) {
583
- sum += values[i];
369
+ public void setIndex(int index, String key, float value) {
370
+ if (index < 0 || index >= count) {
371
+ throw new ArrayIndexOutOfBoundsException(index);
372
+ }
373
+ keys[index] = key;
374
+ values[index] = value;
375
+ }
376
+
377
+ /**
378
+ * @param key
379
+ * @return
380
+ * @webref floatdict:method
381
+ * @brief Check if a key is a part of the data structure
382
+ */
383
+ public boolean hasKey(String key) {
384
+ return index(key) != -1;
385
+ }
386
+
387
+ /**
388
+ * @param key
389
+ * @param amount
390
+ * @webref floatdict:method
391
+ * @brief Add to a value
392
+ */
393
+ public void add(String key, float amount) {
394
+ int index = index(key);
395
+ if (index == -1) {
396
+ create(key, amount);
397
+ } else {
398
+ values[index] += amount;
399
+ }
584
400
  }
585
- return sum;
586
- }
587
-
588
-
589
- public int index(String what) {
590
- Integer found = indices.get(what);
591
- return (found == null) ? -1 : found.intValue();
592
- }
593
-
594
401
 
595
- protected void create(String what, float much) {
596
- if (count == keys.length) {
597
- keys = PApplet.expand(keys);
598
- values = PApplet.expand(values);
402
+ /**
403
+ * @param key
404
+ * @param amount
405
+ * @webref floatdict:method
406
+ * @brief Subtract from a value
407
+ */
408
+ public void sub(String key, float amount) {
409
+ add(key, -amount);
410
+ }
411
+
412
+ /**
413
+ * @param key
414
+ * @param amount
415
+ * @webref floatdict:method
416
+ * @brief Multiply a value
417
+ */
418
+ public void mult(String key, float amount) {
419
+ int index = index(key);
420
+ if (index != -1) {
421
+ values[index] *= amount;
422
+ }
599
423
  }
600
- indices.put(what, Integer.valueOf(count));
601
- keys[count] = what;
602
- values[count] = much;
603
- count++;
604
- }
605
424
 
425
+ /**
426
+ * @param key
427
+ * @param amount
428
+ * @webref floatdict:method
429
+ * @brief Divide a value
430
+ */
431
+ public void div(String key, float amount) {
432
+ int index = index(key);
433
+ if (index != -1) {
434
+ values[index] /= amount;
435
+ }
436
+ }
606
437
 
607
- /**
608
- * @webref floatdict:method
609
- * @brief Remove a key/value pair
610
- */
611
- public float remove(String key) {
612
- int index = index(key);
613
- if (index == -1) {
614
- throw new NoSuchElementException("'" + key + "' not found");
615
- }
616
- float value = values[index];
617
- removeIndex(index);
618
- return value;
619
- }
438
+ private void checkMinMax(String functionName) {
439
+ if (count == 0) {
440
+ String msg
441
+ = String.format("Cannot use %s() on an empty %s.",
442
+ functionName, getClass().getSimpleName());
443
+ throw new RuntimeException(msg);
444
+ }
445
+ }
620
446
 
447
+ /**
448
+ * @return @webref floatlist:method
449
+ * @brief Return the smallest value
450
+ */
451
+ public int minIndex() {
452
+ //checkMinMax("minIndex");
453
+ if (count == 0) {
454
+ return -1;
455
+ }
621
456
 
622
- public float removeIndex(int index) {
623
- if (index < 0 || index >= count) {
624
- throw new ArrayIndexOutOfBoundsException(index);
625
- }
626
- float value = values[index];
627
- indices.remove(keys[index]);
628
- for (int i = index; i < count-1; i++) {
629
- keys[i] = keys[i+1];
630
- values[i] = values[i+1];
631
- indices.put(keys[i], i);
457
+ // Will still return NaN if there are 1 or more entries, and they're all NaN
458
+ float m = Float.NaN;
459
+ int mi = -1;
460
+ for (int i = 0; i < count; i++) {
461
+ // find one good value to start
462
+ if (values[i] == values[i]) {
463
+ m = values[i];
464
+ mi = i;
465
+
466
+ // calculate the rest
467
+ for (int j = i + 1; j < count; j++) {
468
+ float d = values[j];
469
+ if ((d == d) && (d < m)) {
470
+ m = values[j];
471
+ mi = j;
472
+ }
473
+ }
474
+ break;
475
+ }
476
+ }
477
+ return mi;
632
478
  }
633
- count--;
634
- keys[count] = null;
635
- values[count] = 0;
636
- return value;
637
- }
638
-
639
479
 
640
- public void swap(int a, int b) {
641
- String tkey = keys[a];
642
- float tvalue = values[a];
643
- keys[a] = keys[b];
644
- values[a] = values[b];
645
- keys[b] = tkey;
646
- values[b] = tvalue;
480
+ // return the key for the minimum value
481
+ public String minKey() {
482
+ checkMinMax("minKey");
483
+ int index = minIndex();
484
+ if (index == -1) {
485
+ return null;
486
+ }
487
+ return keys[index];
488
+ }
647
489
 
648
- // indices.put(keys[a], Integer.valueOf(a));
649
- // indices.put(keys[b], Integer.valueOf(b));
650
- }
651
-
652
-
653
- /**
654
- * Sort the keys alphabetically (ignoring case). Uses the value as a
655
- * tie-breaker (only really possible with a key that has a case change).
656
- *
657
- * @webref floatdict:method
658
- * @brief Sort the keys alphabetically
659
- */
660
- public void sortKeys() {
661
- sortImpl(true, false, true);
662
- }
663
-
664
-
665
- /**
666
- * @webref floatdict:method
667
- * @brief Sort the keys alphabetically in reverse
668
- */
669
- public void sortKeysReverse() {
670
- sortImpl(true, true, true);
671
- }
672
-
673
-
674
- /**
675
- * Sort by values in descending order (largest value will be at [0]).
676
- *
677
- * @webref floatdict:method
678
- * @brief Sort by values in ascending order
679
- */
680
- public void sortValues() {
681
- sortValues(true);
682
- }
683
-
684
-
685
- /**
686
- * Set true to ensure that the order returned is identical. Slightly
687
- * slower because the tie-breaker for identical values compares the keys.
688
- * @param stable
689
- */
690
- public void sortValues(boolean stable) {
691
- sortImpl(false, false, stable);
692
- }
693
-
694
-
695
- /**
696
- * @webref floatdict:method
697
- * @brief Sort by values in descending order
698
- */
699
- public void sortValuesReverse() {
700
- sortValuesReverse(true);
701
- }
702
-
703
-
704
- public void sortValuesReverse(boolean stable) {
705
- sortImpl(false, true, stable);
706
- }
707
-
708
-
709
- protected void sortImpl(final boolean useKeys, final boolean reverse,
710
- final boolean stable) {
711
- Sort s = new Sort() {
712
- @Override
713
- public int size() {
714
- if (useKeys) {
715
- return count; // don't worry about NaN values
716
-
717
- } else if (count == 0) { // skip the NaN check, it'll AIOOBE
718
- return 0;
719
-
720
- } else { // first move NaN values to the end of the list
721
- int right = count - 1;
722
- while (values[right] != values[right]) {
723
- right--;
724
- if (right == -1) {
725
- return 0; // all values are NaN
726
- }
727
- }
728
- for (int i = right; i >= 0; --i) {
729
- if (Float.isNaN(values[i])) {
730
- swap(i, right);
731
- --right;
490
+ // return the minimum value, or throw an error if there are no values
491
+ public float minValue() {
492
+ checkMinMax("minValue");
493
+ int index = minIndex();
494
+ if (index == -1) {
495
+ return Float.NaN;
496
+ }
497
+ return values[index];
498
+ }
499
+
500
+ /**
501
+ * @return @webref floatlist:method
502
+ * @brief Return the largest value
503
+ */
504
+ // The index of the entry that has the max value. Reference above is incorrect.
505
+ public int maxIndex() {
506
+ //checkMinMax("maxIndex");
507
+ if (count == 0) {
508
+ return -1;
509
+ }
510
+ // Will still return NaN if there is 1 or more entries, and they're all NaN
511
+ float m = Float.NaN;
512
+ int mi = -1;
513
+ for (int i = 0; i < count; i++) {
514
+ // find one good value to start
515
+ if (values[i] == values[i]) {
516
+ m = values[i];
517
+ mi = i;
518
+
519
+ // calculate the rest
520
+ for (int j = i + 1; j < count; j++) {
521
+ float d = values[j];
522
+ if (!Float.isNaN(d) && (d > m)) {
523
+ m = values[j];
524
+ mi = j;
525
+ }
526
+ }
527
+ break;
732
528
  }
733
- }
734
- return right + 1;
735
- }
736
- }
737
-
738
- @Override
739
- public int compare(int a, int b) {
740
- float diff = 0;
741
- if (useKeys) {
742
- diff = keys[a].compareToIgnoreCase(keys[b]);
743
- if (diff == 0) {
744
- diff = values[a] - values[b];
745
- }
746
- } else { // sort values
747
- diff = values[a] - values[b];
748
- if (diff == 0 && stable) {
749
- diff = keys[a].compareToIgnoreCase(keys[b]);
750
- }
751
- }
752
- if (diff == 0) {
753
- return 0;
754
- } else if (reverse) {
755
- return diff < 0 ? 1 : -1;
756
- } else {
757
- return diff < 0 ? -1 : 1;
758
529
  }
759
- }
530
+ return mi;
531
+ }
532
+
533
+ /**
534
+ * The key for a max value; null if empty or everything is NaN (no max).
535
+ *
536
+ * @return
537
+ */
538
+ public String maxKey() {
539
+ //checkMinMax("maxKey");
540
+ int index = maxIndex();
541
+ if (index == -1) {
542
+ return null;
543
+ }
544
+ return keys[index];
545
+ }
546
+
547
+ /**
548
+ * * The max value.(Or NaN if no entries or they're all NaN.)
549
+ *
550
+ * @return
551
+ */
552
+ public float maxValue() {
553
+ //checkMinMax("maxValue");
554
+ int index = maxIndex();
555
+ if (index == -1) {
556
+ return Float.NaN;
557
+ }
558
+ return values[index];
559
+ }
760
560
 
761
- @Override
762
- public void swap(int a, int b) {
763
- FloatDict.this.swap(a, b);
764
- }
765
- };
766
- s.run();
561
+ public float sum() {
562
+ double amount = sumDouble();
563
+ if (amount > Float.MAX_VALUE) {
564
+ throw new RuntimeException("sum() exceeds " + Float.MAX_VALUE + ", use sumDouble()");
565
+ }
566
+ if (amount < -Float.MAX_VALUE) {
567
+ throw new RuntimeException("sum() lower than " + -Float.MAX_VALUE + ", use sumDouble()");
568
+ }
569
+ return (float) amount;
570
+ }
767
571
 
768
- // Set the indices after sort/swaps (performance fix 160411)
769
- resetIndices();
770
- }
572
+ public double sumDouble() {
573
+ double sum = 0;
574
+ for (int i = 0; i < count; i++) {
575
+ sum += values[i];
576
+ }
577
+ return sum;
578
+ }
771
579
 
580
+ public int index(String what) {
581
+ Integer found = indices.get(what);
582
+ return (found == null) ? -1 : found;
583
+ }
772
584
 
773
- /**
774
- * Sum all of the values in this dictionary, then return a new FloatDict of
775
- * each key, divided by the total sum. The total for all values will be ~1.0.
776
- * @return a FloatDict with the original keys, mapped to their pct of the total
777
- */
778
- public FloatDict getPercent() {
779
- double sum = sum();
780
- FloatDict outgoing = new FloatDict();
781
- for (int i = 0; i < size(); i++) {
782
- double percent = value(i) / sum;
783
- outgoing.set(key(i), (float) percent);
585
+ protected void create(String what, float much) {
586
+ if (count == keys.length) {
587
+ keys = PApplet.expand(keys);
588
+ values = PApplet.expand(values);
589
+ }
590
+ indices.put(what, count);
591
+ keys[count] = what;
592
+ values[count] = much;
593
+ count++;
784
594
  }
785
- return outgoing;
786
- }
787
595
 
596
+ /**
597
+ * @param key
598
+ * @return
599
+ * @webref floatdict:method
600
+ * @brief Remove a key/value pair
601
+ */
602
+ public float remove(String key) {
603
+ int index = index(key);
604
+ if (index == -1) {
605
+ throw new NoSuchElementException("'" + key + "' not found");
606
+ }
607
+ float value = values[index];
608
+ removeIndex(index);
609
+ return value;
610
+ }
788
611
 
789
- /** Returns a duplicate copy of this object. */
790
- public FloatDict copy() {
791
- FloatDict outgoing = new FloatDict(count);
792
- System.arraycopy(keys, 0, outgoing.keys, 0, count);
793
- System.arraycopy(values, 0, outgoing.values, 0, count);
794
- for (int i = 0; i < count; i++) {
795
- outgoing.indices.put(keys[i], i);
612
+ public float removeIndex(int index) {
613
+ if (index < 0 || index >= count) {
614
+ throw new ArrayIndexOutOfBoundsException(index);
615
+ }
616
+ float value = values[index];
617
+ indices.remove(keys[index]);
618
+ for (int i = index; i < count - 1; i++) {
619
+ keys[i] = keys[i + 1];
620
+ values[i] = values[i + 1];
621
+ indices.put(keys[i], i);
622
+ }
623
+ count--;
624
+ keys[count] = null;
625
+ values[count] = 0;
626
+ return value;
796
627
  }
797
- outgoing.count = count;
798
- return outgoing;
799
- }
800
628
 
629
+ public void swap(int a, int b) {
630
+ String tkey = keys[a];
631
+ float tvalue = values[a];
632
+ keys[a] = keys[b];
633
+ values[a] = values[b];
634
+ keys[b] = tkey;
635
+ values[b] = tvalue;
801
636
 
802
- public void print() {
803
- for (int i = 0; i < size(); i++) {
804
- System.out.println(keys[i] + " = " + values[i]);
637
+ // indices.put(keys[a], Integer.valueOf(a));
638
+ // indices.put(keys[b], Integer.valueOf(b));
805
639
  }
806
- }
807
640
 
641
+ /**
642
+ * Sort the keys alphabetically (ignoring case). Uses the value as a
643
+ * tie-breaker (only really possible with a key that has a case change).
644
+ *
645
+ * @webref floatdict:method
646
+ * @brief Sort the keys alphabetically
647
+ */
648
+ public void sortKeys() {
649
+ sortImpl(true, false, true);
650
+ }
651
+
652
+ /**
653
+ * @webref floatdict:method
654
+ * @brief Sort the keys alphabetically in reverse
655
+ */
656
+ public void sortKeysReverse() {
657
+ sortImpl(true, true, true);
658
+ }
659
+
660
+ /**
661
+ * Sort by values in descending order (largest value will be at [0]).
662
+ *
663
+ * @webref floatdict:method
664
+ * @brief Sort by values in ascending order
665
+ */
666
+ public void sortValues() {
667
+ sortValues(true);
668
+ }
669
+
670
+ /**
671
+ * Set true to ensure that the order returned is identical. Slightly slower
672
+ * because the tie-breaker for identical values compares the keys.
673
+ *
674
+ * @param stable
675
+ */
676
+ public void sortValues(boolean stable) {
677
+ sortImpl(false, false, stable);
678
+ }
679
+
680
+ /**
681
+ * @webref floatdict:method
682
+ * @brief Sort by values in descending order
683
+ */
684
+ public void sortValuesReverse() {
685
+ sortValuesReverse(true);
686
+ }
687
+
688
+ public void sortValuesReverse(boolean stable) {
689
+ sortImpl(false, true, stable);
690
+ }
691
+
692
+ protected void sortImpl(final boolean useKeys, final boolean reverse,
693
+ final boolean stable) {
694
+ Sort s = new Sort() {
695
+ @Override
696
+ public int size() {
697
+ if (useKeys) {
698
+ return count; // don't worry about NaN values
699
+
700
+ } else if (count == 0) { // skip the NaN check, it'll AIOOBE
701
+ return 0;
702
+
703
+ } else { // first move NaN values to the end of the list
704
+ int right = count - 1;
705
+ while (values[right] != values[right]) {
706
+ right--;
707
+ if (right == -1) {
708
+ return 0; // all values are NaN
709
+ }
710
+ }
711
+ for (int i = right; i >= 0; --i) {
712
+ if (Float.isNaN(values[i])) {
713
+ swap(i, right);
714
+ --right;
715
+ }
716
+ }
717
+ return right + 1;
718
+ }
719
+ }
808
720
 
809
- /**
810
- * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
811
- */
812
- public void save(File file) {
813
- PrintWriter writer = PApplet.createWriter(file);
814
- write(writer);
815
- writer.close();
816
- }
817
-
721
+ @Override
722
+ public int compare(int a, int b) {
723
+ float diff = 0;
724
+ if (useKeys) {
725
+ diff = keys[a].compareToIgnoreCase(keys[b]);
726
+ if (diff == 0) {
727
+ diff = values[a] - values[b];
728
+ }
729
+ } else { // sort values
730
+ diff = values[a] - values[b];
731
+ if (diff == 0 && stable) {
732
+ diff = keys[a].compareToIgnoreCase(keys[b]);
733
+ }
734
+ }
735
+ if (diff == 0) {
736
+ return 0;
737
+ } else if (reverse) {
738
+ return diff < 0 ? 1 : -1;
739
+ } else {
740
+ return diff < 0 ? -1 : 1;
741
+ }
742
+ }
818
743
 
819
- /**
820
- * Write tab-delimited entries out to
821
- * @param writer
822
- */
823
- public void write(PrintWriter writer) {
824
- for (int i = 0; i < count; i++) {
825
- writer.println(keys[i] + "\t" + values[i]);
744
+ @Override
745
+ public void swap(int a, int b) {
746
+ FloatDict.this.swap(a, b);
747
+ }
748
+ };
749
+ s.run();
750
+
751
+ // Set the indices after sort/swaps (performance fix 160411)
752
+ resetIndices();
753
+ }
754
+
755
+ /**
756
+ * Sum all of the values in this dictionary, then return a new FloatDict of
757
+ * each key, divided by the total sum. The total for all values will be
758
+ * ~1.0.
759
+ *
760
+ * @return a FloatDict with the original keys, mapped to their pct of the
761
+ * total
762
+ */
763
+ public FloatDict getPercent() {
764
+ double sum = sum();
765
+ FloatDict outgoing = new FloatDict();
766
+ for (int i = 0; i < size(); i++) {
767
+ double percent = value(i) / sum;
768
+ outgoing.set(key(i), (float) percent);
769
+ }
770
+ return outgoing;
771
+ }
772
+
773
+ /**
774
+ * Returns a duplicate copy of this object.
775
+ *
776
+ * @return
777
+ */
778
+ public FloatDict copy() {
779
+ FloatDict outgoing = new FloatDict(count);
780
+ System.arraycopy(keys, 0, outgoing.keys, 0, count);
781
+ System.arraycopy(values, 0, outgoing.values, 0, count);
782
+ for (int i = 0; i < count; i++) {
783
+ outgoing.indices.put(keys[i], i);
784
+ }
785
+ outgoing.count = count;
786
+ return outgoing;
826
787
  }
827
- writer.flush();
828
- }
829
788
 
789
+ public void print() {
790
+ for (int i = 0; i < size(); i++) {
791
+ System.out.println(keys[i] + " = " + values[i]);
792
+ }
793
+ }
830
794
 
831
- /**
832
- * Return this dictionary as a String in JSON format.
833
- */
834
- public String toJSON() {
835
- StringList items = new StringList();
836
- for (int i = 0; i < count; i++) {
837
- items.append(JSONObject.quote(keys[i])+ ": " + values[i]);
795
+ /**
796
+ * Save tab-delimited entries to a file (TSV format, UTF-8 encoding)
797
+ *
798
+ * @param file
799
+ */
800
+ public void save(File file) {
801
+ try (PrintWriter writer = PApplet.createWriter(file)) {
802
+ write(writer);
803
+ }
838
804
  }
839
- return "{ " + items.join(", ") + " }";
840
- }
841
805
 
806
+ /**
807
+ * Write tab-delimited entries out to
808
+ *
809
+ * @param writer
810
+ */
811
+ public void write(PrintWriter writer) {
812
+ for (int i = 0; i < count; i++) {
813
+ writer.println(keys[i] + "\t" + values[i]);
814
+ }
815
+ writer.flush();
816
+ }
817
+
818
+ /**
819
+ * Return this dictionary as a String in JSON format.
820
+ *
821
+ * @return
822
+ */
823
+ public String toJSON() {
824
+ StringList items = new StringList();
825
+ for (int i = 0; i < count; i++) {
826
+ items.append(JSONObject.quote(keys[i]) + ": " + values[i]);
827
+ }
828
+ return "{ " + items.join(", ") + " }";
829
+ }
842
830
 
843
- @Override
844
- public String toString() {
845
- return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
846
- }
831
+ @Override
832
+ public String toString() {
833
+ return getClass().getSimpleName() + " size=" + size() + " " + toJSON();
834
+ }
847
835
  }