gristlabs_grist-core/test/common/BinaryIndexedTree.js

245 lines
9.4 KiB
JavaScript
Raw Normal View History

/* 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; }));
});
});
});