mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
245 lines
9.4 KiB
JavaScript
245 lines
9.4 KiB
JavaScript
|
/* global describe, before, it */
|
||
|
|
||
|
var assert = require('assert');
|
||
|
var BinaryIndexedTree = require('app/common/BinaryIndexedTree');
|
||
|
|
||
|
describe("BinaryIndexedTree", function() {
|
||
|
describe('#leastSignificantOne', function() {
|
||
|
it("should only keep the least significant one", function() {
|
||
|
assert.equal(BinaryIndexedTree.leastSignificantOne(1), 1);
|
||
|
assert.equal(BinaryIndexedTree.leastSignificantOne(6), 2);
|
||
|
assert.equal(BinaryIndexedTree.leastSignificantOne(15), 1);
|
||
|
assert.equal(BinaryIndexedTree.leastSignificantOne(16), 16);
|
||
|
assert.equal(BinaryIndexedTree.leastSignificantOne(0), 0);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('#stripLeastSignificantOne', function() {
|
||
|
it("should strip the least significant one", function() {
|
||
|
assert.equal(BinaryIndexedTree.stripLeastSignificantOne(1), 0);
|
||
|
assert.equal(BinaryIndexedTree.stripLeastSignificantOne(6), 4);
|
||
|
assert.equal(BinaryIndexedTree.stripLeastSignificantOne(15), 14);
|
||
|
assert.equal(BinaryIndexedTree.stripLeastSignificantOne(16), 0);
|
||
|
assert.equal(BinaryIndexedTree.stripLeastSignificantOne(0), 0);
|
||
|
assert.equal(BinaryIndexedTree.stripLeastSignificantOne(24), 16);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('#mostSignificantOne', function() {
|
||
|
it("should keep the most significant one", function() {
|
||
|
assert.equal(BinaryIndexedTree.mostSignificantOne(1), 1);
|
||
|
assert.equal(BinaryIndexedTree.mostSignificantOne(6), 4);
|
||
|
assert.equal(BinaryIndexedTree.mostSignificantOne(15), 8);
|
||
|
assert.equal(BinaryIndexedTree.mostSignificantOne(16), 16);
|
||
|
assert.equal(BinaryIndexedTree.mostSignificantOne(24), 16);
|
||
|
assert.equal(BinaryIndexedTree.mostSignificantOne(0), 0);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('#cumulToValues', function() {
|
||
|
it("should convert cumulative array to regular values", function() {
|
||
|
assert.deepEqual(BinaryIndexedTree.cumulToValues([1, 3, 6, 10]), [1, 2, 3, 4]);
|
||
|
assert.deepEqual(BinaryIndexedTree.cumulToValues([1, 3, 6, 10, 15, 21]), [1, 2, 3, 4, 5, 6]);
|
||
|
assert.deepEqual(BinaryIndexedTree.cumulToValues([]), []);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('#valuesToCumul', function() {
|
||
|
it("should convert value array to cumulative array", function() {
|
||
|
assert.deepEqual(BinaryIndexedTree.valuesToCumul([1, 2, 3, 4]), [1, 3, 6, 10]);
|
||
|
assert.deepEqual(BinaryIndexedTree.valuesToCumul([1, 2, 3, 4, 5, 6]), [1, 3, 6, 10, 15, 21]);
|
||
|
assert.deepEqual(BinaryIndexedTree.valuesToCumul([]), []);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
//----------------------------------------------------------------------
|
||
|
|
||
|
// Test array of length 25.
|
||
|
var data1 = [47, 17, 28, 96, 10, 2, 11, 43, 7, 94, 37, 81, 75, 2, 33, 57, 68, 71, 68, 86, 27, 44, 64, 41, 23];
|
||
|
|
||
|
// Test array of length 64.
|
||
|
var data2 = [722, 106, 637, 881, 752, 940, 989, 295, 344, 716, 283, 609, 482, 268, 884, 782, 628, 778, 442, 456, 171, 821, 346, 367, 12, 46, 582, 164, 876, 421, 749, 357, 586, 319, 847, 79, 649, 353, 545, 353, 609, 865, 229, 476, 697, 579, 109, 935, 412, 286, 701, 712, 288, 45, 990, 176, 775, 143, 187, 241, 721, 691, 162, 460];
|
||
|
var cdata1, cdata2; // Cumulative versions.
|
||
|
|
||
|
function dumbGetCumulativeValue(array, index) {
|
||
|
for (var i = 0, x = 0; i <= index; i++) {
|
||
|
x += array[i];
|
||
|
}
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
function dumbGetIndex(array, cumulValue) {
|
||
|
for (var i = 0, x = 0; i <= array.length && x <= cumulValue; i++) {
|
||
|
x += array[i];
|
||
|
}
|
||
|
return i;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
before(function() {
|
||
|
cdata1 = data1.map(function(value, i) { return dumbGetCumulativeValue(data1, i); });
|
||
|
cdata2 = data2.map(function(value, i) { return dumbGetCumulativeValue(data2, i); });
|
||
|
});
|
||
|
|
||
|
describe('BinaryIndexedTree class', function() {
|
||
|
it("should construct trees with zeroes", function() {
|
||
|
var bit = new BinaryIndexedTree();
|
||
|
assert.equal(bit.size(), 0);
|
||
|
bit.fillFromValues([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||
|
var bit2 = new BinaryIndexedTree(10);
|
||
|
assert.deepEqual(bit, bit2);
|
||
|
});
|
||
|
|
||
|
it("should convert from cumulative array and back", function() {
|
||
|
var bit = new BinaryIndexedTree();
|
||
|
bit.fillFromCumulative(cdata1);
|
||
|
assert.equal(bit.size(), 25);
|
||
|
assert.deepEqual(bit.toCumulativeArray(), cdata1);
|
||
|
assert.deepEqual(bit.toValueArray(), data1);
|
||
|
|
||
|
bit.fillFromCumulative([]);
|
||
|
assert.equal(bit.size(), 0);
|
||
|
assert.deepEqual(bit.toCumulativeArray(), []);
|
||
|
assert.deepEqual(bit.toValueArray(), []);
|
||
|
|
||
|
bit.fillFromCumulative(cdata2);
|
||
|
assert.equal(bit.size(), 64);
|
||
|
assert.deepEqual(bit.toCumulativeArray(), cdata2);
|
||
|
assert.deepEqual(bit.toValueArray(), data2);
|
||
|
});
|
||
|
|
||
|
it("should convert from value array and back", function() {
|
||
|
var bit = new BinaryIndexedTree();
|
||
|
bit.fillFromValues(data1);
|
||
|
assert.equal(bit.size(), 25);
|
||
|
assert.deepEqual(bit.toCumulativeArray(), cdata1);
|
||
|
assert.deepEqual(bit.toValueArray(), data1);
|
||
|
|
||
|
bit.fillFromValues([]);
|
||
|
assert.equal(bit.size(), 0);
|
||
|
assert.deepEqual(bit.toCumulativeArray(), []);
|
||
|
assert.deepEqual(bit.toValueArray(), []);
|
||
|
|
||
|
bit.fillFromValues(data2);
|
||
|
assert.equal(bit.size(), 64);
|
||
|
assert.deepEqual(bit.toCumulativeArray(), cdata2);
|
||
|
assert.deepEqual(bit.toValueArray(), data2);
|
||
|
|
||
|
bit.fillFromValues([1, 2, 3, 4, 5]);
|
||
|
assert.equal(bit.size(), 5);
|
||
|
assert.deepEqual(bit.toCumulativeArray(), [1, 3, 6, 10, 15]);
|
||
|
assert.deepEqual(bit.toValueArray(), [1, 2, 3, 4, 5]);
|
||
|
});
|
||
|
|
||
|
it("should compute individual and cumulative values", function() {
|
||
|
var i, bit = new BinaryIndexedTree();
|
||
|
bit.fillFromValues(data1);
|
||
|
assert.equal(bit.size(), 25);
|
||
|
for (i = 0; i < 25; i++) {
|
||
|
assert.equal(bit.getValue(i), data1[i]);
|
||
|
assert.equal(bit.getCumulativeValue(i), cdata1[i]);
|
||
|
assert.equal(bit.getSumTo(i), cdata1[i] - data1[i]);
|
||
|
}
|
||
|
assert.equal(bit.getTotal(), data1.reduce(function(a, b) { return a + b; }));
|
||
|
|
||
|
bit.fillFromValues(data2);
|
||
|
assert.equal(bit.size(), 64);
|
||
|
for (i = 0; i < 64; i++) {
|
||
|
assert.equal(bit.getValue(i), data2[i]);
|
||
|
assert.equal(bit.getCumulativeValue(i), cdata2[i]);
|
||
|
assert.equal(bit.getSumTo(i), cdata2[i] - data2[i]);
|
||
|
}
|
||
|
assert.equal(bit.getTotal(), data2.reduce(function(a, b) { return a + b; }));
|
||
|
});
|
||
|
|
||
|
it("should compute cumulative range values", function() {
|
||
|
var i, bit = new BinaryIndexedTree();
|
||
|
bit.fillFromValues(data1);
|
||
|
|
||
|
assert.equal(bit.getCumulativeValueRange(0, data1.length),
|
||
|
bit.getCumulativeValue(data1.length-1));
|
||
|
for(i = 1; i < 25; i++) {
|
||
|
assert.equal(bit.getCumulativeValueRange(i, 25),
|
||
|
cdata1[24] - cdata1[i-1]);
|
||
|
}
|
||
|
for(i = 24; i >= 0; i-- ){
|
||
|
assert.equal(bit.getCumulativeValueRange(0, i+1), cdata1[i]);
|
||
|
}
|
||
|
|
||
|
bit.fillFromValues(data2);
|
||
|
assert.equal(bit.getCumulativeValueRange(0, 64),
|
||
|
bit.getCumulativeValue(63));
|
||
|
for(i = 1; i < 64; i++) {
|
||
|
assert.equal(bit.getCumulativeValueRange(i, 64),
|
||
|
cdata2[63] - cdata2[i-1]);
|
||
|
}
|
||
|
for(i = 63; i >= 0; i-- ){
|
||
|
assert.equal(bit.getCumulativeValueRange(0, i+1), cdata2[i]);
|
||
|
}
|
||
|
|
||
|
|
||
|
});
|
||
|
|
||
|
it("should search by cumulative value", function() {
|
||
|
var bit = new BinaryIndexedTree();
|
||
|
bit.fillFromValues([1, 2, 3, 4]);
|
||
|
assert.equal(bit.getIndex(-1), 0);
|
||
|
assert.equal(bit.getIndex(0), 0);
|
||
|
assert.equal(bit.getIndex(1), 0);
|
||
|
assert.equal(bit.getIndex(2), 1);
|
||
|
assert.equal(bit.getIndex(3), 1);
|
||
|
assert.equal(bit.getIndex(4), 2);
|
||
|
assert.equal(bit.getIndex(5), 2);
|
||
|
assert.equal(bit.getIndex(6), 2);
|
||
|
assert.equal(bit.getIndex(7), 3);
|
||
|
assert.equal(bit.getIndex(8), 3);
|
||
|
assert.equal(bit.getIndex(9), 3);
|
||
|
assert.equal(bit.getIndex(10), 3);
|
||
|
assert.equal(bit.getIndex(11), 4);
|
||
|
|
||
|
bit.fillFromValues(data1);
|
||
|
// data1 is [47,17,28,96,10,2,11,43,7,94,37,81,75,2,33,57,68,71,68,86,27,44,64,41,23];
|
||
|
assert.equal(bit.getIndex(0), 0);
|
||
|
assert.equal(bit.getIndex(1), 0);
|
||
|
assert.equal(bit.getIndex(46.9), 0);
|
||
|
assert.equal(bit.getIndex(47), 0);
|
||
|
assert.equal(bit.getIndex(63), 1);
|
||
|
assert.equal(bit.getIndex(64), 1);
|
||
|
assert.equal(bit.getIndex(64.1), 2);
|
||
|
assert.equal(bit.getIndex(bit.getCumulativeValue(5)), 5);
|
||
|
assert.equal(bit.getIndex(bit.getCumulativeValue(20)), 20);
|
||
|
assert.equal(bit.getIndex(bit.getCumulativeValue(24)), 24);
|
||
|
assert.equal(bit.getIndex(1000000), 25);
|
||
|
});
|
||
|
|
||
|
it("should support add and set", function() {
|
||
|
var i, bit = new BinaryIndexedTree(4);
|
||
|
bit.setValue(1, 2);
|
||
|
assert.deepEqual(bit.toValueArray(), [0, 2, 0, 0]);
|
||
|
bit.setValue(3, 4);
|
||
|
assert.deepEqual(bit.toValueArray(), [0, 2, 0, 4]);
|
||
|
bit.setValue(0, 1);
|
||
|
assert.deepEqual(bit.toValueArray(), [1, 2, 0, 4]);
|
||
|
bit.addValue(2, 1);
|
||
|
assert.deepEqual(bit.toValueArray(), [1, 2, 1, 4]);
|
||
|
bit.addValue(2, 1);
|
||
|
assert.deepEqual(bit.toValueArray(), [1, 2, 2, 4]);
|
||
|
bit.addValue(2, 1);
|
||
|
assert.deepEqual(bit.toValueArray(), [1, 2, 3, 4]);
|
||
|
|
||
|
bit.fillFromValues(data1);
|
||
|
for (i = 0; i < data1.length; i++) {
|
||
|
bit.addValue(i, -data1[i]);
|
||
|
}
|
||
|
assert.deepEqual(bit.toValueArray(), data1.map(function() { return 0; }));
|
||
|
|
||
|
bit.fillFromValues(data1);
|
||
|
for (i = data1.length - 1; i >= 0; i--) {
|
||
|
bit.addValue(i, data1[i]);
|
||
|
}
|
||
|
assert.deepEqual(bit.toValueArray(), data1.map(function(x) { return 2*x; }));
|
||
|
});
|
||
|
});
|
||
|
});
|