crossfilter_rails 1.0.3.rc2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1180 @@
1
+ (function(exports){
2
+ crossfilter.version = "1.0.3";
3
+ function crossfilter_identity(d) {
4
+ return d;
5
+ }
6
+ crossfilter.permute = permute;
7
+
8
+ function permute(array, index) {
9
+ for (var i = 0, n = index.length, copy = new Array(n); i < n; ++i) {
10
+ copy[i] = array[index[i]];
11
+ }
12
+ return copy;
13
+ }
14
+ var bisect = crossfilter.bisect = bisect_by(crossfilter_identity);
15
+
16
+ bisect.by = bisect_by;
17
+
18
+ function bisect_by(f) {
19
+
20
+ // Locate the insertion point for x in a to maintain sorted order. The
21
+ // arguments lo and hi may be used to specify a subset of the array which
22
+ // should be considered; by default the entire array is used. If x is already
23
+ // present in a, the insertion point will be before (to the left of) any
24
+ // existing entries. The return value is suitable for use as the first
25
+ // argument to `array.splice` assuming that a is already sorted.
26
+ //
27
+ // The returned insertion point i partitions the array a into two halves so
28
+ // that all v < x for v in a[lo:i] for the left side and all v >= x for v in
29
+ // a[i:hi] for the right side.
30
+ function bisectLeft(a, x, lo, hi) {
31
+ while (lo < hi) {
32
+ var mid = lo + hi >> 1;
33
+ if (f(a[mid]) < x) lo = mid + 1;
34
+ else hi = mid;
35
+ }
36
+ return lo;
37
+ }
38
+
39
+ // Similar to bisectLeft, but returns an insertion point which comes after (to
40
+ // the right of) any existing entries of x in a.
41
+ //
42
+ // The returned insertion point i partitions the array into two halves so that
43
+ // all v <= x for v in a[lo:i] for the left side and all v > x for v in
44
+ // a[i:hi] for the right side.
45
+ function bisectRight(a, x, lo, hi) {
46
+ while (lo < hi) {
47
+ var mid = lo + hi >> 1;
48
+ if (x < f(a[mid])) hi = mid;
49
+ else lo = mid + 1;
50
+ }
51
+ return lo;
52
+ }
53
+
54
+ bisectRight.right = bisectRight;
55
+ bisectRight.left = bisectLeft;
56
+ return bisectRight;
57
+ }
58
+ var heap = crossfilter.heap = heap_by(crossfilter_identity);
59
+
60
+ heap.by = heap_by;
61
+
62
+ function heap_by(f) {
63
+
64
+ // Builds a binary heap within the specified array a[lo:hi]. The heap has the
65
+ // property such that the parent a[lo+i] is always less than or equal to its
66
+ // two children: a[lo+2*i+1] and a[lo+2*i+2].
67
+ function heap(a, lo, hi) {
68
+ var n = hi - lo,
69
+ i = (n >>> 1) + 1;
70
+ while (--i > 0) sift(a, i, n, lo);
71
+ return a;
72
+ }
73
+
74
+ // Sorts the specified array a[lo:hi] in descending order, assuming it is
75
+ // already a heap.
76
+ function sort(a, lo, hi) {
77
+ var n = hi - lo,
78
+ t;
79
+ while (--n > 0) t = a[lo], a[lo] = a[lo + n], a[lo + n] = t, sift(a, 1, n, lo);
80
+ return a;
81
+ }
82
+
83
+ // Sifts the element a[lo+i-1] down the heap, where the heap is the contiguous
84
+ // slice of array a[lo:lo+n]. This method can also be used to update the heap
85
+ // incrementally, without incurring the full cost of reconstructing the heap.
86
+ function sift(a, i, n, lo) {
87
+ var d = a[--lo + i],
88
+ x = f(d),
89
+ child;
90
+ while ((child = i << 1) <= n) {
91
+ if (child < n && f(a[lo + child]) > f(a[lo + child + 1])) child++;
92
+ if (x <= f(a[lo + child])) break;
93
+ a[lo + i] = a[lo + child];
94
+ i = child;
95
+ }
96
+ a[lo + i] = d;
97
+ }
98
+
99
+ heap.sort = sort;
100
+ return heap;
101
+ }
102
+ var heapselect = crossfilter.heapselect = heapselect_by(crossfilter_identity);
103
+
104
+ heapselect.by = heapselect_by;
105
+
106
+ function heapselect_by(f) {
107
+ var heap = heap_by(f);
108
+
109
+ // Returns a new array containing the top k elements in the array a[lo:hi].
110
+ // The returned array is not sorted, but maintains the heap property. If k is
111
+ // greater than hi - lo, then fewer than k elements will be returned. The
112
+ // order of elements in a is unchanged by this operation.
113
+ function heapselect(a, lo, hi, k) {
114
+ var queue = new Array(k = Math.min(hi - lo, k)),
115
+ min,
116
+ i,
117
+ x,
118
+ d;
119
+
120
+ for (i = 0; i < k; ++i) queue[i] = a[lo++];
121
+ heap(queue, 0, k);
122
+
123
+ if (lo < hi) {
124
+ min = f(queue[0]);
125
+ do {
126
+ if (x = f(d = a[lo]) > min) {
127
+ queue[0] = d;
128
+ min = f(heap(queue, 0, k)[0]);
129
+ }
130
+ } while (++lo < hi);
131
+ }
132
+
133
+ return queue;
134
+ }
135
+
136
+ return heapselect;
137
+ }
138
+ var insertionsort = crossfilter.insertionsort = insertionsort_by(crossfilter_identity);
139
+
140
+ insertionsort.by = insertionsort_by;
141
+
142
+ function insertionsort_by(f) {
143
+
144
+ function insertionsort(a, lo, hi) {
145
+ for (var i = lo + 1; i < hi; ++i) {
146
+ for (var j = i, t = a[i], x = f(t); j > lo && f(a[j - 1]) > x; --j) {
147
+ a[j] = a[j - 1];
148
+ }
149
+ a[j] = t;
150
+ }
151
+ return a;
152
+ }
153
+
154
+ return insertionsort;
155
+ }
156
+ // Algorithm designed by Vladimir Yaroslavskiy.
157
+ // Implementation based on the Dart project; see lib/dart/LICENSE for details.
158
+
159
+ var quicksort = crossfilter.quicksort = quicksort_by(crossfilter_identity);
160
+
161
+ quicksort.by = quicksort_by;
162
+
163
+ function quicksort_by(f) {
164
+ var insertionsort = insertionsort_by(f);
165
+
166
+ function sort(a, lo, hi) {
167
+ return (hi - lo < quicksort_sizeThreshold
168
+ ? insertionsort
169
+ : quicksort)(a, lo, hi);
170
+ }
171
+
172
+ function quicksort(a, lo, hi) {
173
+
174
+ // Compute the two pivots by looking at 5 elements.
175
+ var sixth = (hi - lo) / 6 | 0,
176
+ i1 = lo + sixth,
177
+ i5 = hi - 1 - sixth,
178
+ i3 = lo + hi - 1 >> 1, // The midpoint.
179
+ i2 = i3 - sixth,
180
+ i4 = i3 + sixth;
181
+
182
+ var e1 = a[i1], x1 = f(e1),
183
+ e2 = a[i2], x2 = f(e2),
184
+ e3 = a[i3], x3 = f(e3),
185
+ e4 = a[i4], x4 = f(e4),
186
+ e5 = a[i5], x5 = f(e5);
187
+
188
+ var t;
189
+
190
+ // Sort the selected 5 elements using a sorting network.
191
+ if (x1 > x2) t = e1, e1 = e2, e2 = t, t = x1, x1 = x2, x2 = t;
192
+ if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t;
193
+ if (x1 > x3) t = e1, e1 = e3, e3 = t, t = x1, x1 = x3, x3 = t;
194
+ if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t;
195
+ if (x1 > x4) t = e1, e1 = e4, e4 = t, t = x1, x1 = x4, x4 = t;
196
+ if (x3 > x4) t = e3, e3 = e4, e4 = t, t = x3, x3 = x4, x4 = t;
197
+ if (x2 > x5) t = e2, e2 = e5, e5 = t, t = x2, x2 = x5, x5 = t;
198
+ if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t;
199
+ if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t;
200
+
201
+ var pivot1 = e2, pivotValue1 = x2,
202
+ pivot2 = e4, pivotValue2 = x4;
203
+
204
+ // e2 and e4 have been saved in the pivot variables. They will be written
205
+ // back, once the partitioning is finished.
206
+ a[i1] = e1;
207
+ a[i2] = a[lo];
208
+ a[i3] = e3;
209
+ a[i4] = a[hi - 1];
210
+ a[i5] = e5;
211
+
212
+ var less = lo + 1, // First element in the middle partition.
213
+ great = hi - 2; // Last element in the middle partition.
214
+
215
+ // Note that for value comparison, <, <=, >= and > coerce to a primitive via
216
+ // Object.prototype.valueOf; == and === do not, so in order to be consistent
217
+ // with natural order (such as for Date objects), we must do two compares.
218
+ var pivotsEqual = pivotValue1 <= pivotValue2 && pivotValue1 >= pivotValue2;
219
+ if (pivotsEqual) {
220
+
221
+ // Degenerated case where the partitioning becomes a dutch national flag
222
+ // problem.
223
+ //
224
+ // [ | < pivot | == pivot | unpartitioned | > pivot | ]
225
+ // ^ ^ ^ ^ ^
226
+ // left less k great right
227
+ //
228
+ // a[left] and a[right] are undefined and are filled after the
229
+ // partitioning.
230
+ //
231
+ // Invariants:
232
+ // 1) for x in ]left, less[ : x < pivot.
233
+ // 2) for x in [less, k[ : x == pivot.
234
+ // 3) for x in ]great, right[ : x > pivot.
235
+ for (var k = less; k <= great; ++k) {
236
+ var ek = a[k], xk = f(ek);
237
+ if (xk < pivotValue1) {
238
+ if (k !== less) {
239
+ a[k] = a[less];
240
+ a[less] = ek;
241
+ }
242
+ ++less;
243
+ } else if (xk > pivotValue1) {
244
+
245
+ // Find the first element <= pivot in the range [k - 1, great] and
246
+ // put [:ek:] there. We know that such an element must exist:
247
+ // When k == less, then el3 (which is equal to pivot) lies in the
248
+ // interval. Otherwise a[k - 1] == pivot and the search stops at k-1.
249
+ // Note that in the latter case invariant 2 will be violated for a
250
+ // short amount of time. The invariant will be restored when the
251
+ // pivots are put into their final positions.
252
+ while (true) {
253
+ var greatValue = f(a[great]);
254
+ if (greatValue > pivotValue1) {
255
+ great--;
256
+ // This is the only location in the while-loop where a new
257
+ // iteration is started.
258
+ continue;
259
+ } else if (greatValue < pivotValue1) {
260
+ // Triple exchange.
261
+ a[k] = a[less];
262
+ a[less++] = a[great];
263
+ a[great--] = ek;
264
+ break;
265
+ } else {
266
+ a[k] = a[great];
267
+ a[great--] = ek;
268
+ // Note: if great < k then we will exit the outer loop and fix
269
+ // invariant 2 (which we just violated).
270
+ break;
271
+ }
272
+ }
273
+ }
274
+ }
275
+ } else {
276
+
277
+ // We partition the list into three parts:
278
+ // 1. < pivot1
279
+ // 2. >= pivot1 && <= pivot2
280
+ // 3. > pivot2
281
+ //
282
+ // During the loop we have:
283
+ // [ | < pivot1 | >= pivot1 && <= pivot2 | unpartitioned | > pivot2 | ]
284
+ // ^ ^ ^ ^ ^
285
+ // left less k great right
286
+ //
287
+ // a[left] and a[right] are undefined and are filled after the
288
+ // partitioning.
289
+ //
290
+ // Invariants:
291
+ // 1. for x in ]left, less[ : x < pivot1
292
+ // 2. for x in [less, k[ : pivot1 <= x && x <= pivot2
293
+ // 3. for x in ]great, right[ : x > pivot2
294
+ for (var k = less; k <= great; k++) {
295
+ var ek = a[k], xk = f(ek);
296
+ if (xk < pivotValue1) {
297
+ if (k !== less) {
298
+ a[k] = a[less];
299
+ a[less] = ek;
300
+ }
301
+ ++less;
302
+ } else {
303
+ if (xk > pivotValue2) {
304
+ while (true) {
305
+ var greatValue = f(a[great]);
306
+ if (greatValue > pivotValue2) {
307
+ great--;
308
+ if (great < k) break;
309
+ // This is the only location inside the loop where a new
310
+ // iteration is started.
311
+ continue;
312
+ } else {
313
+ // a[great] <= pivot2.
314
+ if (greatValue < pivotValue1) {
315
+ // Triple exchange.
316
+ a[k] = a[less];
317
+ a[less++] = a[great];
318
+ a[great--] = ek;
319
+ } else {
320
+ // a[great] >= pivot1.
321
+ a[k] = a[great];
322
+ a[great--] = ek;
323
+ }
324
+ break;
325
+ }
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+
332
+ // Move pivots into their final positions.
333
+ // We shrunk the list from both sides (a[left] and a[right] have
334
+ // meaningless values in them) and now we move elements from the first
335
+ // and third partition into these locations so that we can store the
336
+ // pivots.
337
+ a[lo] = a[less - 1];
338
+ a[less - 1] = pivot1;
339
+ a[hi - 1] = a[great + 1];
340
+ a[great + 1] = pivot2;
341
+
342
+ // The list is now partitioned into three partitions:
343
+ // [ < pivot1 | >= pivot1 && <= pivot2 | > pivot2 ]
344
+ // ^ ^ ^ ^
345
+ // left less great right
346
+
347
+ // Recursive descent. (Don't include the pivot values.)
348
+ sort(a, lo, less - 1);
349
+ sort(a, great + 2, hi);
350
+
351
+ if (pivotsEqual) {
352
+ // All elements in the second partition are equal to the pivot. No
353
+ // need to sort them.
354
+ return a;
355
+ }
356
+
357
+ // In theory it should be enough to call _doSort recursively on the second
358
+ // partition.
359
+ // The Android source however removes the pivot elements from the recursive
360
+ // call if the second partition is too large (more than 2/3 of the list).
361
+ if (less < i1 && great > i5) {
362
+ var lessValue, greatValue;
363
+ while ((lessValue = f(a[less])) <= pivotValue1 && lessValue >= pivotValue1) ++less;
364
+ while ((greatValue = f(a[great])) <= pivotValue2 && greatValue >= pivotValue2) --great;
365
+
366
+ // Copy paste of the previous 3-way partitioning with adaptions.
367
+ //
368
+ // We partition the list into three parts:
369
+ // 1. == pivot1
370
+ // 2. > pivot1 && < pivot2
371
+ // 3. == pivot2
372
+ //
373
+ // During the loop we have:
374
+ // [ == pivot1 | > pivot1 && < pivot2 | unpartitioned | == pivot2 ]
375
+ // ^ ^ ^
376
+ // less k great
377
+ //
378
+ // Invariants:
379
+ // 1. for x in [ *, less[ : x == pivot1
380
+ // 2. for x in [less, k[ : pivot1 < x && x < pivot2
381
+ // 3. for x in ]great, * ] : x == pivot2
382
+ for (var k = less; k <= great; k++) {
383
+ var ek = a[k], xk = f(ek);
384
+ if (xk <= pivotValue1 && xk >= pivotValue1) {
385
+ if (k !== less) {
386
+ a[k] = a[less];
387
+ a[less] = ek;
388
+ }
389
+ less++;
390
+ } else {
391
+ if (xk <= pivotValue2 && xk >= pivotValue2) {
392
+ while (true) {
393
+ var greatValue = f(a[great]);
394
+ if (greatValue <= pivotValue2 && greatValue >= pivotValue2) {
395
+ great--;
396
+ if (great < k) break;
397
+ // This is the only location inside the loop where a new
398
+ // iteration is started.
399
+ continue;
400
+ } else {
401
+ // a[great] < pivot2.
402
+ if (greatValue < pivotValue1) {
403
+ // Triple exchange.
404
+ a[k] = a[less];
405
+ a[less++] = a[great];
406
+ a[great--] = ek;
407
+ } else {
408
+ // a[great] == pivot1.
409
+ a[k] = a[great];
410
+ a[great--] = ek;
411
+ }
412
+ break;
413
+ }
414
+ }
415
+ }
416
+ }
417
+ }
418
+ }
419
+
420
+ // The second partition has now been cleared of pivot elements and looks
421
+ // as follows:
422
+ // [ * | > pivot1 && < pivot2 | * ]
423
+ // ^ ^
424
+ // less great
425
+ // Sort the second partition using recursive descent.
426
+
427
+ // The second partition looks as follows:
428
+ // [ * | >= pivot1 && <= pivot2 | * ]
429
+ // ^ ^
430
+ // less great
431
+ // Simply sort it by recursive descent.
432
+
433
+ return sort(a, less, great + 1);
434
+ }
435
+
436
+ return sort;
437
+ }
438
+
439
+ var quicksort_sizeThreshold = 32;
440
+ var crossfilter_array8 = crossfilter_arrayUntyped,
441
+ crossfilter_array16 = crossfilter_arrayUntyped,
442
+ crossfilter_array32 = crossfilter_arrayUntyped,
443
+ crossfilter_arrayLengthen = crossfilter_identity,
444
+ crossfilter_arrayWiden = crossfilter_identity;
445
+
446
+ if (typeof Uint8Array !== "undefined") {
447
+ crossfilter_array8 = function(n) { return new Uint8Array(n); };
448
+ crossfilter_array16 = function(n) { return new Uint16Array(n); };
449
+ crossfilter_array32 = function(n) { return new Uint32Array(n); };
450
+
451
+ crossfilter_arrayLengthen = function(array, length) {
452
+ var copy = new array.constructor(length);
453
+ copy.set(array);
454
+ return copy;
455
+ };
456
+
457
+ crossfilter_arrayWiden = function(array, width) {
458
+ var copy;
459
+ switch (width) {
460
+ case 16: copy = crossfilter_array16(array.length); break;
461
+ case 32: copy = crossfilter_array32(array.length); break;
462
+ default: throw new Error("invalid array width!");
463
+ }
464
+ copy.set(array);
465
+ return copy;
466
+ };
467
+ }
468
+
469
+ function crossfilter_arrayUntyped(n) {
470
+ return new Array(n);
471
+ }
472
+ function crossfilter_filterExact(bisect, value) {
473
+ return function(values) {
474
+ var n = values.length;
475
+ return [bisect.left(values, value, 0, n), bisect.right(values, value, 0, n)];
476
+ };
477
+ }
478
+
479
+ function crossfilter_filterRange(bisect, range) {
480
+ var min = range[0],
481
+ max = range[1];
482
+ return function(values) {
483
+ var n = values.length;
484
+ return [bisect.left(values, min, 0, n), bisect.left(values, max, 0, n)];
485
+ };
486
+ }
487
+
488
+ function crossfilter_filterAll(values) {
489
+ return [0, values.length];
490
+ }
491
+ function crossfilter_null() {
492
+ return null;
493
+ }
494
+ function crossfilter_zero() {
495
+ return 0;
496
+ }
497
+ function crossfilter_reduceIncrement(p) {
498
+ return p + 1;
499
+ }
500
+
501
+ function crossfilter_reduceDecrement(p) {
502
+ return p - 1;
503
+ }
504
+
505
+ function crossfilter_reduceAdd(f) {
506
+ return function(p, v) {
507
+ return p + +f(v);
508
+ };
509
+ }
510
+
511
+ function crossfilter_reduceSubtract(f) {
512
+ return function(p, v) {
513
+ return p - f(v);
514
+ };
515
+ }
516
+ exports.crossfilter = crossfilter;
517
+
518
+ function crossfilter() {
519
+ var crossfilter = {
520
+ add: add,
521
+ dimension: dimension,
522
+ groupAll: groupAll,
523
+ size: size
524
+ };
525
+
526
+ var data = [], // the records
527
+ n = 0, // the number of records; data.length
528
+ m = 0, // number of dimensions in use
529
+ M = 8, // number of dimensions that can fit in `filters`
530
+ filters = crossfilter_array8(0), // M bits per record; 1 is filtered out
531
+ filterListeners = [], // when the filters change
532
+ dataListeners = []; // when data is added
533
+
534
+ // Adds the specified new records to this crossfilter.
535
+ function add(newData) {
536
+ var n0 = n,
537
+ n1 = newData.length;
538
+
539
+ // If there's actually new data to add…
540
+ // Merge the new data into the existing data.
541
+ // Lengthen the filter bitset to handle the new records.
542
+ // Notify listeners (dimensions and groups) that new data is available.
543
+ if (n1) {
544
+ data = data.concat(newData);
545
+ filters = crossfilter_arrayLengthen(filters, n += n1);
546
+ dataListeners.forEach(function(l) { l(newData, n0, n1); });
547
+ }
548
+
549
+ return crossfilter;
550
+ }
551
+
552
+ // Adds a new dimension with the specified value accessor function.
553
+ function dimension(value) {
554
+ var dimension = {
555
+ filter: filter,
556
+ filterExact: filterExact,
557
+ filterRange: filterRange,
558
+ filterAll: filterAll,
559
+ top: top,
560
+ group: group,
561
+ groupAll: groupAll
562
+ };
563
+
564
+ var one = 1 << m++, // bit mask, e.g., 00001000
565
+ zero = ~one, // inverted one, e.g., 11110111
566
+ values, // sorted, cached array
567
+ index, // value rank ↦ object id
568
+ newValues, // temporary array storing newly-added values
569
+ newIndex, // temporary array storing newly-added index
570
+ sort = quicksort_by(function(i) { return newValues[i]; }),
571
+ refilter = crossfilter_filterAll, // for recomputing filter
572
+ indexListeners = [], // when data is added
573
+ lo0 = 0,
574
+ hi0 = 0;
575
+
576
+ // Updating a dimension is a two-stage process. First, we must update the
577
+ // associated filters for the newly-added records. Once all dimensions have
578
+ // updated their filters, the groups are notified to update.
579
+ dataListeners.unshift(preAdd);
580
+ dataListeners.push(postAdd);
581
+
582
+ // Incorporate any existing data into this dimension, and make sure that the
583
+ // filter bitset is wide enough to handle the new dimension.
584
+ if (m > M) filters = crossfilter_arrayWiden(filters, M <<= 1);
585
+ preAdd(data, 0, n);
586
+ postAdd(data, 0, n);
587
+
588
+ // Incorporates the specified new records into this dimension.
589
+ // This function is responsible for updating filters, values, and index.
590
+ function preAdd(newData, n0, n1) {
591
+
592
+ // Permute new values into natural order using a sorted index.
593
+ newValues = newData.map(value);
594
+ newIndex = sort(crossfilter_range(n1), 0, n1);
595
+ newValues = permute(newValues, newIndex);
596
+
597
+ // Bisect newValues to determine which new records are selected.
598
+ var bounds = refilter(newValues), lo1 = bounds[0], hi1 = bounds[1], i;
599
+ for (i = 0; i < lo1; ++i) filters[newIndex[i] + n0] |= one;
600
+ for (i = hi1; i < n1; ++i) filters[newIndex[i] + n0] |= one;
601
+
602
+ // If this dimension previously had no data, then we don't need to do the
603
+ // more expensive merge operation; use the new values and index as-is.
604
+ if (!n0) {
605
+ values = newValues;
606
+ index = newIndex;
607
+ lo0 = lo1;
608
+ hi0 = hi1;
609
+ return;
610
+ }
611
+
612
+ var oldValues = values,
613
+ oldIndex = index,
614
+ i0 = 0,
615
+ i1 = 0;
616
+
617
+ // Otherwise, create new arrays into which to merge new and old.
618
+ values = new Array(n);
619
+ index = crossfilter_index(n, n);
620
+
621
+ // Merge the old and new sorted values, and old and new index.
622
+ for (i = 0; i0 < n0 && i1 < n1; ++i) {
623
+ if (oldValues[i0] < newValues[i1]) {
624
+ values[i] = oldValues[i0];
625
+ index[i] = oldIndex[i0++];
626
+ } else {
627
+ values[i] = newValues[i1];
628
+ index[i] = newIndex[i1++] + n0;
629
+ }
630
+ }
631
+
632
+ // Add any remaining old values.
633
+ for (; i0 < n0; ++i0, ++i) {
634
+ values[i] = oldValues[i0];
635
+ index[i] = oldIndex[i0];
636
+ }
637
+
638
+ // Add any remaining new values.
639
+ for (; i1 < n1; ++i1, ++i) {
640
+ values[i] = newValues[i1];
641
+ index[i] = newIndex[i1] + n0;
642
+ }
643
+
644
+ // Bisect again to recompute lo0 and hi0.
645
+ bounds = refilter(values), lo0 = bounds[0], hi0 = bounds[1];
646
+ }
647
+
648
+ // When all filters have updated, notify index listeners of the new values.
649
+ function postAdd(newData, n0, n1) {
650
+ indexListeners.forEach(function(l) { l(newValues, newIndex, n0, n1); });
651
+ newValues = newIndex = null;
652
+ }
653
+
654
+ // Updates the selected values based on the specified bounds [lo, hi].
655
+ // This implementation is used by all the public filter methods.
656
+ function filterIndex(bounds) {
657
+ var i,
658
+ j,
659
+ k,
660
+ lo1 = bounds[0],
661
+ hi1 = bounds[1],
662
+ added = [],
663
+ removed = [];
664
+
665
+ // Fast incremental update based on previous lo index.
666
+ if (lo1 < lo0) {
667
+ for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) {
668
+ filters[k = index[i]] ^= one;
669
+ added.push(k);
670
+ }
671
+ } else if (lo1 > lo0) {
672
+ for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) {
673
+ filters[k = index[i]] ^= one;
674
+ removed.push(k);
675
+ }
676
+ }
677
+
678
+ // Fast incremental update based on previous hi index.
679
+ if (hi1 > hi0) {
680
+ for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) {
681
+ filters[k = index[i]] ^= one;
682
+ added.push(k);
683
+ }
684
+ } else if (hi1 < hi0) {
685
+ for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) {
686
+ filters[k = index[i]] ^= one;
687
+ removed.push(k);
688
+ }
689
+ }
690
+
691
+ lo0 = lo1;
692
+ hi0 = hi1;
693
+ filterListeners.forEach(function(l) { l(one, added, removed); });
694
+ return dimension;
695
+ }
696
+
697
+ // Filters this dimension using the specified range, value, or null.
698
+ // If the range is null, this is equivalent to filterAll.
699
+ // If the range is an array, this is equivalent to filterRange.
700
+ // Otherwise, this is equivalent to filterExact.
701
+ function filter(range) {
702
+ return range == null
703
+ ? filterAll() : Array.isArray(range)
704
+ ? filterRange(range)
705
+ : filterExact(range);
706
+ }
707
+
708
+ // Filters this dimension to select the exact value.
709
+ function filterExact(value) {
710
+ return filterIndex((refilter = crossfilter_filterExact(bisect, value))(values));
711
+ }
712
+
713
+ // Filters this dimension to select the specified range [lo, hi].
714
+ // The lower bound is inclusive, and the upper bound is exclusive.
715
+ function filterRange(range) {
716
+ return filterIndex((refilter = crossfilter_filterRange(bisect, range))(values));
717
+ }
718
+
719
+ // Clears any filters on this dimension.
720
+ function filterAll() {
721
+ return filterIndex((refilter = crossfilter_filterAll)(values));
722
+ }
723
+
724
+ // Returns the top K selected records, based on this dimension's order.
725
+ // Note: observes this dimension's filter, unlike group and groupAll.
726
+ function top(k) {
727
+ var array = [],
728
+ i = hi0,
729
+ j;
730
+
731
+ while (--i >= lo0 && k > 0) {
732
+ if (!filters[j = index[i]]) {
733
+ array.push(data[j]);
734
+ --k;
735
+ }
736
+ }
737
+
738
+ return array;
739
+ }
740
+
741
+ // Adds a new group to this dimension, using the specified key function.
742
+ function group(key) {
743
+ var group = {
744
+ top: top,
745
+ all: all,
746
+ reduce: reduce,
747
+ reduceCount: reduceCount,
748
+ reduceSum: reduceSum,
749
+ order: order,
750
+ orderNatural: orderNatural,
751
+ size: size
752
+ };
753
+
754
+ var groups, // array of {key, value}
755
+ groupIndex, // object id ↦ group id
756
+ groupWidth = 8,
757
+ groupCapacity = crossfilter_capacity(groupWidth),
758
+ k = 0, // cardinality
759
+ select,
760
+ heap,
761
+ reduceAdd,
762
+ reduceRemove,
763
+ reduceInitial,
764
+ update = crossfilter_null,
765
+ reset = crossfilter_null,
766
+ resetNeeded = true;
767
+
768
+ if (arguments.length < 1) key = crossfilter_identity;
769
+
770
+ // The group listens to the crossfilter for when any dimension changes, so
771
+ // that it can update the associated reduce values. It must also listen to
772
+ // the parent dimension for when data is added, and compute new keys.
773
+ filterListeners.push(update);
774
+ indexListeners.push(add);
775
+
776
+ // Incorporate any existing data into the grouping.
777
+ add(values, index, 0, n);
778
+
779
+ // Incorporates the specified new values into this group.
780
+ // This function is responsible for updating groups and groupIndex.
781
+ function add(newValues, newIndex, n0, n1) {
782
+ var oldGroups = groups,
783
+ reIndex = crossfilter_index(k, groupCapacity),
784
+ add = reduceAdd,
785
+ initial = reduceInitial,
786
+ k0 = k, // old cardinality
787
+ i0 = 0, // index of old group
788
+ i1 = 0, // index of new record
789
+ j, // object id
790
+ g0, // old group
791
+ x0, // old key
792
+ x1, // new key
793
+ g, // group to add
794
+ x; // key of group to add
795
+
796
+ // If a reset is needed, we don't need to update the reduce values.
797
+ if (resetNeeded) add = initial = crossfilter_null;
798
+
799
+ // Reset the new groups (k is a lower bound).
800
+ // Also, make sure that groupIndex exists and is long enough.
801
+ groups = new Array(k), k = 0;
802
+ groupIndex = k0 > 1 ? crossfilter_arrayLengthen(groupIndex, n) : crossfilter_index(n, groupCapacity);
803
+
804
+ // Get the first old key (x0 of g0), if it exists.
805
+ if (k0) x0 = (g0 = oldGroups[0]).key;
806
+
807
+ // Find the first new key (x1), skipping NaN keys.
808
+ while (i1 < n1 && !((x1 = key(newValues[i1])) >= x1)) ++i1;
809
+
810
+ // While new keys remain…
811
+ while (i1 < n1) {
812
+
813
+ // Determine the lesser of the two current keys; new and old.
814
+ // If there are no old keys remaining, then always add the new key.
815
+ if (g0 && x0 <= x1) {
816
+ g = g0, x = x0;
817
+
818
+ // Record the new index of the old group.
819
+ reIndex[i0] = k;
820
+
821
+ // Retrieve the next old key.
822
+ if (g0 = oldGroups[++i0]) x0 = g0.key;
823
+ } else {
824
+ g = {key: x1, value: initial()}, x = x1;
825
+ }
826
+
827
+ // Add the lesser group.
828
+ groups[k] = g;
829
+
830
+ // Add any selected records belonging to the added group, while
831
+ // advancing the new key and populating the associated group index.
832
+ while (!(x1 > x)) {
833
+ groupIndex[j = newIndex[i1] + n0] = k;
834
+ if (!(filters[j] & zero)) g.value = add(g.value, data[j]);
835
+ if (++i1 >= n1) break;
836
+ x1 = key(newValues[i1]);
837
+ }
838
+
839
+ groupIncrement();
840
+ }
841
+
842
+ // Add any remaining old groups that were greater than all new keys.
843
+ // No incremental reduce is needed; these groups have no new records.
844
+ // Also record the new index of the old group.
845
+ while (i0 < k0) {
846
+ groups[reIndex[i0] = k] = oldGroups[i0++];
847
+ groupIncrement();
848
+ }
849
+
850
+ // If we added any new groups before any old groups,
851
+ // update the group index of all the old records.
852
+ if (k > i0) for (i0 = 0; i0 < n0; ++i0) {
853
+ groupIndex[i0] = reIndex[groupIndex[i0]];
854
+ }
855
+
856
+ // Modify the update and reset behavior based on the cardinality.
857
+ // If the cardinality is less than or equal to one, then the groupIndex
858
+ // is not needed. If the cardinality is zero, then there are no records
859
+ // and therefore no groups to update or reset. Note that we also must
860
+ // change the registered listener to point to the new method.
861
+ j = filterListeners.indexOf(update);
862
+ if (k > 1) {
863
+ update = updateMany;
864
+ reset = resetMany;
865
+ } else {
866
+ if (k === 1) {
867
+ update = updateOne;
868
+ reset = resetOne;
869
+ } else {
870
+ update = crossfilter_null;
871
+ reset = crossfilter_null;
872
+ }
873
+ groupIndex = null;
874
+ }
875
+ filterListeners[j] = update;
876
+
877
+ // Count the number of added groups,
878
+ // and widen the group index as needed.
879
+ function groupIncrement() {
880
+ if (++k === groupCapacity) {
881
+ reIndex = crossfilter_arrayWiden(reIndex, groupWidth <<= 1);
882
+ groupIndex = crossfilter_arrayWiden(groupIndex, groupWidth);
883
+ groupCapacity = crossfilter_capacity(groupWidth);
884
+ }
885
+ }
886
+ }
887
+
888
+ // Reduces the specified selected or deselected records.
889
+ // This function is only used when the cardinality is greater than 1.
890
+ function updateMany(filterOne, added, removed) {
891
+ if (filterOne === one || resetNeeded) return;
892
+
893
+ var i,
894
+ k,
895
+ n,
896
+ g;
897
+
898
+ // Add the added values.
899
+ for (i = 0, n = added.length; i < n; ++i) {
900
+ if (!(filters[k = added[i]] & zero)) {
901
+ g = groups[groupIndex[k]];
902
+ g.value = reduceAdd(g.value, data[k]);
903
+ }
904
+ }
905
+
906
+ // Remove the removed values.
907
+ for (i = 0, n = removed.length; i < n; ++i) {
908
+ if ((filters[k = removed[i]] & zero) === filterOne) {
909
+ g = groups[groupIndex[k]];
910
+ g.value = reduceRemove(g.value, data[k]);
911
+ }
912
+ }
913
+ }
914
+
915
+ // Reduces the specified selected or deselected records.
916
+ // This function is only used when the cardinality is 1.
917
+ function updateOne(filterOne, added, removed) {
918
+ if (filterOne === one || resetNeeded) return;
919
+
920
+ var i,
921
+ k,
922
+ n,
923
+ g = groups[0];
924
+
925
+ // Add the added values.
926
+ for (i = 0, n = added.length; i < n; ++i) {
927
+ if (!(filters[k = added[i]] & zero)) {
928
+ g.value = reduceAdd(g.value, data[k]);
929
+ }
930
+ }
931
+
932
+ // Remove the removed values.
933
+ for (i = 0, n = removed.length; i < n; ++i) {
934
+ if ((filters[k = removed[i]] & zero) === filterOne) {
935
+ g.value = reduceRemove(g.value, data[k]);
936
+ }
937
+ }
938
+ }
939
+
940
+ // Recomputes the group reduce values from scratch.
941
+ // This function is only used when the cardinality is greater than 1.
942
+ function resetMany() {
943
+ var i,
944
+ g;
945
+
946
+ // Reset all group values.
947
+ for (i = 0; i < k; ++i) {
948
+ groups[i].value = reduceInitial();
949
+ }
950
+
951
+ // Add any selected records.
952
+ for (i = 0; i < n; ++i) {
953
+ if (!(filters[i] & zero)) {
954
+ g = groups[groupIndex[i]];
955
+ g.value = reduceAdd(g.value, data[i]);
956
+ }
957
+ }
958
+ }
959
+
960
+ // Recomputes the group reduce values from scratch.
961
+ // This function is only used when the cardinality is 1.
962
+ function resetOne() {
963
+ var i,
964
+ g = groups[0];
965
+
966
+ // Reset the singleton group values.
967
+ g.value = reduceInitial();
968
+
969
+ // Add any selected records.
970
+ for (i = 0; i < n; ++i) {
971
+ if (!(filters[i] & zero)) {
972
+ g.value = reduceAdd(g.value, data[i]);
973
+ }
974
+ }
975
+ }
976
+
977
+ // Returns the array of group values, in the dimension's natural order.
978
+ function all() {
979
+ if (resetNeeded) reset(), resetNeeded = false;
980
+ return groups;
981
+ }
982
+
983
+ // Returns a new array containing the top K group values, in reduce order.
984
+ function top(k) {
985
+ var top = select(all(), 0, groups.length, k);
986
+ return heap.sort(top, 0, top.length);
987
+ }
988
+
989
+ // Sets the reduce behavior for this group to use the specified functions.
990
+ // This method lazily recomputes the reduce values, waiting until needed.
991
+ function reduce(add, remove, initial) {
992
+ reduceAdd = add;
993
+ reduceRemove = remove;
994
+ reduceInitial = initial;
995
+ resetNeeded = true;
996
+ return group;
997
+ }
998
+
999
+ // A convenience method for reducing by count.
1000
+ function reduceCount() {
1001
+ return reduce(crossfilter_reduceIncrement, crossfilter_reduceDecrement, crossfilter_zero);
1002
+ }
1003
+
1004
+ // A convenience method for reducing by sum(value).
1005
+ function reduceSum(value) {
1006
+ return reduce(crossfilter_reduceAdd(value), crossfilter_reduceSubtract(value), crossfilter_zero);
1007
+ }
1008
+
1009
+ // Sets the reduce order, using the specified accessor.
1010
+ function order(value) {
1011
+ select = heapselect_by(valueOf);
1012
+ heap = heap_by(valueOf);
1013
+ function valueOf(d) { return value(d.value); }
1014
+ return group;
1015
+ }
1016
+
1017
+ // A convenience method for natural ordering by reduce value.
1018
+ function orderNatural() {
1019
+ return order(crossfilter_identity);
1020
+ }
1021
+
1022
+ // Returns the cardinality of this group, irrespective of any filters.
1023
+ function size() {
1024
+ return k;
1025
+ }
1026
+
1027
+ return reduceCount().orderNatural();
1028
+ }
1029
+
1030
+ // A convenience function for generating a singleton group.
1031
+ function groupAll() {
1032
+ var g = group(crossfilter_null), all = g.all;
1033
+ delete g.all;
1034
+ delete g.top;
1035
+ delete g.order;
1036
+ delete g.orderNatural;
1037
+ delete g.size;
1038
+ g.value = function() { return all()[0].value; };
1039
+ return g;
1040
+ }
1041
+
1042
+ return dimension;
1043
+ }
1044
+
1045
+ // A convenience method for groupAll on a dummy dimension.
1046
+ // This implementation can be optimized since it is always cardinality 1.
1047
+ function groupAll() {
1048
+ var group = {
1049
+ reduce: reduce,
1050
+ reduceCount: reduceCount,
1051
+ reduceSum: reduceSum,
1052
+ value: value
1053
+ };
1054
+
1055
+ var reduceValue,
1056
+ reduceAdd,
1057
+ reduceRemove,
1058
+ reduceInitial,
1059
+ resetNeeded = true;
1060
+
1061
+ // The group listens to the crossfilter for when any dimension changes, so
1062
+ // that it can update the reduce value. It must also listen to the parent
1063
+ // dimension for when data is added.
1064
+ filterListeners.push(update);
1065
+ dataListeners.push(add);
1066
+
1067
+ // For consistency; actually a no-op since resetNeeded is true.
1068
+ add(data, 0, n);
1069
+
1070
+ // Incorporates the specified new values into this group.
1071
+ function add(newData, n0, n1) {
1072
+ var i;
1073
+
1074
+ if (resetNeeded) return;
1075
+
1076
+ // Add the added values.
1077
+ for (i = n0; i < n; ++i) {
1078
+ if (!filters[i]) {
1079
+ reduceValue = reduceAdd(reduceValue, data[i]);
1080
+ }
1081
+ }
1082
+ }
1083
+
1084
+ // Reduces the specified selected or deselected records.
1085
+ function update(filterOne, added, removed) {
1086
+ var i,
1087
+ k,
1088
+ n;
1089
+
1090
+ if (resetNeeded) return;
1091
+
1092
+ // Add the added values.
1093
+ for (i = 0, n = added.length; i < n; ++i) {
1094
+ if (!filters[k = added[i]]) {
1095
+ reduceValue = reduceAdd(reduceValue, data[k]);
1096
+ }
1097
+ }
1098
+
1099
+ // Remove the removed values.
1100
+ for (i = 0, n = removed.length; i < n; ++i) {
1101
+ if (filters[k = removed[i]] === filterOne) {
1102
+ reduceValue = reduceRemove(reduceValue, data[k]);
1103
+ }
1104
+ }
1105
+ }
1106
+
1107
+ // Recomputes the group reduce value from scratch.
1108
+ function reset() {
1109
+ var i;
1110
+
1111
+ reduceValue = reduceInitial();
1112
+
1113
+ for (i = 0; i < n; ++i) {
1114
+ if (!filters[i]) {
1115
+ reduceValue = reduceAdd(reduceValue, data[i]);
1116
+ }
1117
+ }
1118
+ }
1119
+
1120
+ // Sets the reduce behavior for this group to use the specified functions.
1121
+ // This method lazily recomputes the reduce value, waiting until needed.
1122
+ function reduce(add, remove, initial) {
1123
+ reduceAdd = add;
1124
+ reduceRemove = remove;
1125
+ reduceInitial = initial;
1126
+ resetNeeded = true;
1127
+ return group;
1128
+ }
1129
+
1130
+ // A convenience method for reducing by count.
1131
+ function reduceCount() {
1132
+ return reduce(crossfilter_reduceIncrement, crossfilter_reduceDecrement, crossfilter_zero);
1133
+ }
1134
+
1135
+ // A convenience method for reducing by sum(value).
1136
+ function reduceSum(value) {
1137
+ return reduce(crossfilter_reduceAdd(value), crossfilter_reduceSubtract(value), crossfilter_zero);
1138
+ }
1139
+
1140
+ // Returns the computed reduce value.
1141
+ function value() {
1142
+ if (resetNeeded) reset(), resetNeeded = false;
1143
+ return reduceValue;
1144
+ }
1145
+
1146
+ return reduceCount();
1147
+ }
1148
+
1149
+ // Returns the number of records in this crossfilter, irrespective of any filters.
1150
+ function size() {
1151
+ return n;
1152
+ }
1153
+
1154
+ return arguments.length
1155
+ ? add(arguments[0])
1156
+ : crossfilter;
1157
+ }
1158
+
1159
+ // Returns an array of size n, big enough to store ids up to m.
1160
+ function crossfilter_index(n, m) {
1161
+ return (m < 0x101
1162
+ ? crossfilter_array8 : m < 0x10001
1163
+ ? crossfilter_array16
1164
+ : crossfilter_array32)(n);
1165
+ }
1166
+
1167
+ // Constructs a new array of size n, with sequential values from 0 to n - 1.
1168
+ function crossfilter_range(n) {
1169
+ var range = crossfilter_index(n, n);
1170
+ for (var i = -1; ++i < n;) range[i] = i;
1171
+ return range;
1172
+ }
1173
+
1174
+ function crossfilter_capacity(w) {
1175
+ return w === 8
1176
+ ? 0x100 : w === 16
1177
+ ? 0x10000
1178
+ : 0x100000000;
1179
+ }
1180
+ })(this);