You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
479 lines
12 KiB
479 lines
12 KiB
"use strict" |
|
|
|
var makeTree = require("../rbtree.js") |
|
var tape = require("tape") |
|
var util = require("util") |
|
var iota = require("iota-array") |
|
|
|
var COLORS = [ "r", "b", "bb" ] |
|
|
|
function printTree(tree) { |
|
if(!tree) { |
|
return [] |
|
} |
|
return [ COLORS[tree._color], tree.key, printTree(tree.left), printTree(tree.right) ] |
|
} |
|
|
|
function print(t) { |
|
console.log(util.inspect(printTree(t.root), {depth:12})) |
|
} |
|
|
|
//Ensures the red black axioms are satisfied by tree |
|
function checkTree(tree, t) { |
|
if(!tree.root) { |
|
return |
|
} |
|
t.equals(tree.root._color, 1, "root is black") |
|
function checkNode(node) { |
|
if(!node) { |
|
return [1, 0] |
|
} |
|
if(node._color === 0) { |
|
t.assert(!node.left || node.left._color === 1, "children of red node must be black") |
|
t.assert(!node.right || node.right._color === 1, "children of red node must be black") |
|
} else { |
|
t.equals(node._color, 1, "node color must be red or black") |
|
} |
|
if(node.left) { |
|
t.assert(tree._compare(node.left.key, node.key) <= 0, "left tree order invariant") |
|
} |
|
if(node.right) { |
|
t.assert(tree._compare(node.right.key, node.key) >= 0, "right tree order invariant") |
|
} |
|
var cl = checkNode(node.left) |
|
var cr = checkNode(node.right) |
|
t.equals(cl[0], cr[0], "number of black nodes along all paths to root must be constant") |
|
t.equals(cl[1] + cr[1] + 1, node._count, "item count consistency") |
|
return [cl[0] + node._color, cl[1] + cr[1] + 1] |
|
} |
|
var r = checkNode(tree.root) |
|
t.equals(r[1], tree.length, "tree length") |
|
} |
|
|
|
tape("insert()", function(t) { |
|
var t1 = makeTree() |
|
|
|
var u = t1 |
|
var arr = [] |
|
for(var i=20; i>=0; --i) { |
|
var x = i |
|
var next = u.insert(x, true) |
|
checkTree(u, t) |
|
checkTree(next, t) |
|
t.equals(u.length, arr.length) |
|
arr.push(x) |
|
u = next |
|
} |
|
for(var i=-20; i<0; ++i) { |
|
var x = i |
|
var next = u.insert(x, true) |
|
checkTree(u, t) |
|
checkTree(next, t) |
|
arr.sort(function(a,b) { return a-b }) |
|
var ptr = 0 |
|
u.forEach(function(k,v) { |
|
t.equals(k, arr[ptr++]) |
|
}) |
|
t.equals(ptr, arr.length) |
|
arr.push(x) |
|
u = next |
|
} |
|
|
|
var start = u.begin |
|
for(var i=-20, j=0; j<=40; ++i, ++j) { |
|
t.equals(u.at(j).key, i, "checking at()") |
|
t.equals(start.key, i, "checking iter") |
|
t.equals(start.index, j, "checking index") |
|
t.assert(start.valid, "checking valid") |
|
if(j < 40) { |
|
t.assert(start.hasNext, "hasNext()") |
|
} else { |
|
t.assert(!start.hasNext, "eof hasNext()") |
|
} |
|
start.next() |
|
} |
|
t.assert(!start.valid, "invalid eof iterator") |
|
t.assert(!start.hasNext, "hasNext() at eof fail") |
|
t.equals(start.index, 41, "eof index") |
|
|
|
t.end() |
|
}) |
|
|
|
tape("foreach", function(t) { |
|
var u = iota(31).reduce(function(u, k, v) { |
|
return u.insert(k, v) |
|
}, makeTree()) |
|
|
|
//Check basic foreach |
|
var visit_keys = [] |
|
var visit_vals = [] |
|
u.forEach(function(k,v) { |
|
visit_keys.push(k) |
|
visit_vals.push(v) |
|
}) |
|
t.same(visit_keys, u.keys) |
|
t.same(visit_vals, u.values) |
|
|
|
//Check foreach with termination |
|
visit_keys = [] |
|
visit_vals = [] |
|
t.equals(u.forEach(function(k,v) { |
|
if(k === 5) { |
|
return 1000 |
|
} |
|
visit_keys.push(k) |
|
visit_vals.push(v) |
|
}), 1000) |
|
t.same(visit_keys, u.keys.slice(0, 5)) |
|
t.same(visit_vals, u.values.slice(0, 5)) |
|
|
|
//Check half interval foreach |
|
visit_keys = [] |
|
visit_vals = [] |
|
u.forEach(function(k,v) { |
|
visit_keys.push(k) |
|
visit_vals.push(v) |
|
}, 3) |
|
t.same(visit_keys, u.keys.slice(3)) |
|
t.same(visit_vals, u.values.slice(3)) |
|
|
|
//Check half interval foreach with termination |
|
visit_keys = [] |
|
visit_vals = [] |
|
t.equals(u.forEach(function(k,v) { |
|
if(k === 12) { |
|
return 1000 |
|
} |
|
visit_keys.push(k) |
|
visit_vals.push(v) |
|
}, 3), 1000) |
|
t.same(visit_keys, u.keys.slice(3, 12)) |
|
t.same(visit_vals, u.values.slice(3, 12)) |
|
|
|
|
|
//Check interval foreach |
|
visit_keys = [] |
|
visit_vals = [] |
|
u.forEach(function(k,v) { |
|
visit_keys.push(k) |
|
visit_vals.push(v) |
|
}, 3, 15) |
|
t.same(visit_keys, u.keys.slice(3, 15)) |
|
t.same(visit_vals, u.values.slice(3, 15)) |
|
|
|
//Check interval foreach with termination |
|
visit_keys = [] |
|
visit_vals = [] |
|
t.equals(u.forEach(function(k,v) { |
|
if(k === 12) { |
|
return 1000 |
|
} |
|
visit_keys.push(k) |
|
visit_vals.push(v) |
|
}, 3, 15), 1000) |
|
t.same(visit_keys, u.keys.slice(3, 12)) |
|
t.same(visit_vals, u.values.slice(3, 12)) |
|
|
|
t.end() |
|
}) |
|
|
|
function compareIterators(a, b, t) { |
|
t.equals(a.tree, b.tree, "iter trees") |
|
t.equals(a.valid, b.valid, "iter validity") |
|
if(!b.valid) { |
|
return |
|
} |
|
t.equals(a.node, b.node, "iter node") |
|
t.equals(a.key, b.key, "iter key") |
|
t.equals(a.value, b.value, "iter value") |
|
t.equals(a.index, b.index, "iter index") |
|
} |
|
|
|
tape("iterators", function(t) { |
|
var u = iota(20).reduce(function(u, k, v) { |
|
return u.insert(k, v) |
|
}, makeTree()) |
|
|
|
//Try walking forward |
|
var iter = u.begin |
|
var c = iter.clone() |
|
t.ok(iter.hasNext, "must have next at beginneing") |
|
t.ok(!iter.hasPrev, "must not have predecessor") |
|
for(var i=0; i<20; ++i) { |
|
var v = u.at(i) |
|
compareIterators(iter, v, t) |
|
t.equals(iter.index, i) |
|
iter.next() |
|
} |
|
t.ok(!iter.valid, "must be eof iterator") |
|
|
|
//Check if the clone worked |
|
compareIterators(c, u.begin, t) |
|
|
|
//Try walking backward |
|
var iter = u.end |
|
t.ok(!iter.hasNext, "must not have next") |
|
t.ok(iter.hasPrev, "must have predecessor") |
|
for(var i=19; i>=0; --i) { |
|
var v = u.at(i) |
|
compareIterators(iter, v, t) |
|
t.equals(iter.index, i) |
|
iter.prev() |
|
} |
|
t.ok(!iter.valid, "must be eof iterator") |
|
|
|
t.end() |
|
}) |
|
|
|
|
|
tape("remove()", function(t) { |
|
|
|
var sz = [1, 2, 10, 20, 23, 31, 32, 33] |
|
for(var n=0; n<sz.length; ++n) { |
|
var c = sz[n] |
|
var u = iota(c).reduce(function(u, k, v) { |
|
return u.insert(k, v) |
|
}, makeTree()) |
|
for(var i=0; i<c; ++i) { |
|
checkTree(u.remove(i), t) |
|
} |
|
} |
|
|
|
t.end() |
|
}) |
|
|
|
tape("update()", function(t) { |
|
var arr = [0, 1, 2, 3, 4, 5, 6 ] |
|
var u = arr.reduce(function(u, k, v) { |
|
return u.insert(k, v) |
|
}, makeTree()) |
|
for(var iter=u.begin; iter.hasNext; iter.next()) { |
|
var p = iter.value |
|
var updated = iter.update(1000) |
|
t.equals(iter.value, iter.key, "ensure no mutation") |
|
t.equals(updated.find(iter.key).value, 1000, "ensure update applied") |
|
checkTree(updated, t) |
|
checkTree(u, t) |
|
} |
|
t.end() |
|
}) |
|
|
|
|
|
tape("keys and values", function(t) { |
|
|
|
var original_keys = [ "potato", "sock", "foot", "apple", "newspaper", "gameboy" ] |
|
var original_values = [ 42, 10, false, "!!!", {}, null ] |
|
|
|
var u = makeTree() |
|
for(var i=0; i<original_keys.length; ++i) { |
|
u = u.insert(original_keys[i], original_values[i]) |
|
} |
|
|
|
var zipped = iota(6).map(function(i) { |
|
return [ original_keys[i], original_values[i] ] |
|
}) |
|
|
|
zipped.sort(function(a,b) { |
|
if(a[0] < b[0]) { return -1 } |
|
if(a[0] > b[0]) { return 1 } |
|
return 0 |
|
}) |
|
|
|
var keys = zipped.map(function(v) { return v[0] }) |
|
var values = zipped.map(function(v) { return v[1] }) |
|
|
|
t.same(u.keys, keys) |
|
t.same(u.values, values) |
|
|
|
t.end() |
|
}) |
|
|
|
tape("searching", function(t) { |
|
|
|
var arr = [0, 1, 1, 1, 1, 2, 3, 4, 5, 6, 6 ] |
|
var u = arr.reduce(function(u, k, v) { |
|
return u.insert(k, v) |
|
}, makeTree()) |
|
|
|
|
|
for(var i=0; i<arr.length; ++i) { |
|
if(arr[i] !== arr[i-1] && arr[i] !== arr[i+1]) { |
|
t.equals(u.get(arr[i]), i, "get " + arr[i]) |
|
} |
|
} |
|
t.equals(u.get(-1), undefined, "get missing") |
|
|
|
t.equals(u.ge(3).index, 6, "ge simple") |
|
t.equals(u.ge(0.9).index, 1, "ge run start") |
|
t.equals(u.ge(1).index, 1, "ge run mid") |
|
t.equals(u.ge(1.1).index, 5, "ge run end") |
|
t.equals(u.ge(0).index, 0, "ge first") |
|
t.equals(u.ge(6).index, 9, "ge last") |
|
t.equals(u.ge(100).valid, false, "ge big") |
|
t.equals(u.ge(-1).index, 0, "ge small") |
|
|
|
t.equals(u.gt(3).index, 7, "gt simple") |
|
t.equals(u.gt(0.9).index, 1, "gt run start") |
|
t.equals(u.gt(1).index, 5, "gt run mid") |
|
t.equals(u.gt(1.1).index, 5, "gt run end") |
|
t.equals(u.gt(0).index, 1, "gt first") |
|
t.equals(u.gt(6).valid, false, "gt last") |
|
t.equals(u.gt(100).valid, false, "gt big") |
|
t.equals(u.gt(-1).index, 0, "ge small") |
|
|
|
t.equals(u.le(3).index, 6, "le simple") |
|
t.equals(u.le(0.9).index, 0, "le run start") |
|
t.equals(u.le(1).index, 4, "le run mid") |
|
t.equals(u.le(1.1).index, 4, "le run end") |
|
t.equals(u.le(0).index, 0, "le first") |
|
t.equals(u.le(6).index, 10, "le last") |
|
t.equals(u.le(100).index, 10, "le big") |
|
t.equals(u.le(-1).valid, false, "le small") |
|
|
|
t.equals(u.lt(3).index, 5, "lt simple") |
|
t.equals(u.lt(0.9).index, 0, "lt run start") |
|
t.equals(u.lt(1).index, 0, "lt run mid") |
|
t.equals(u.lt(1.1).index, 4, "lt run end") |
|
t.equals(u.lt(0).valid, false, "lt first") |
|
t.equals(u.lt(6).index, 8, "lt last") |
|
t.equals(u.lt(100).index, 10, "lt big") |
|
t.equals(u.lt(-1).valid, false, "lt small") |
|
|
|
t.equals(u.find(-1).valid, false, "find missing small") |
|
t.equals(u.find(10000).valid, false, "find missing big") |
|
t.equals(u.find(3).index, 6, "find simple") |
|
t.ok(u.find(1).index > 0, "find repeat") |
|
t.ok(u.find(1).index < 5, "find repeat") |
|
|
|
for(var i=0; i<arr.length; ++i) { |
|
t.equals(u.find(arr[i]).key, arr[i], "find " + i) |
|
} |
|
|
|
for(var i=0; i<arr.length; ++i) { |
|
t.equals(u.at(i).key, arr[i], "at " + i) |
|
} |
|
t.equals(u.at(-1).valid, false, "at missing small") |
|
t.equals(u.at(1000).valid, false, "at missing big") |
|
|
|
|
|
t.end() |
|
}) |
|
|
|
tape("slab-sequence", function(t) { |
|
|
|
var tree = makeTree() |
|
|
|
tree=tree.insert(0, 0) |
|
checkTree(tree, t) |
|
t.same(tree.values, [0]) |
|
|
|
tree=tree.insert(1, 1) |
|
checkTree(tree, t) |
|
t.same(tree.values, [0,1]) |
|
|
|
tree=tree.insert(0.5, 2) |
|
checkTree(tree, t) |
|
t.same(tree.values, [0,2,1]) |
|
|
|
tree=tree.insert(0.25, 3) |
|
checkTree(tree, t) |
|
t.same(tree.values, [0,3,2,1]) |
|
|
|
tree=tree.remove(0) |
|
checkTree(tree, t) |
|
t.same(tree.values, [3,2,1]) |
|
|
|
tree=tree.insert(0.375, 4) |
|
checkTree(tree, t) |
|
t.same(tree.values, [3, 4, 2, 1]) |
|
|
|
tree=tree.remove(1) |
|
checkTree(tree, t) |
|
t.same(tree.values, [3,4,2]) |
|
|
|
tree=tree.remove(0.5) |
|
checkTree(tree, t) |
|
t.same(tree.values, [3,4]) |
|
|
|
tree=tree.remove(0.375) |
|
checkTree(tree, t) |
|
t.same(tree.values, [3]) |
|
|
|
tree=tree.remove(0.25) |
|
checkTree(tree, t) |
|
t.same(tree.values, []) |
|
|
|
t.end() |
|
}) |
|
|
|
tape("slab-sequence-2", function(t) { |
|
|
|
var u = makeTree() |
|
|
|
u=u.insert( 12 , 22 ) |
|
u=u.insert( 11 , 3 ) |
|
u=u.insert( 10 , 28 ) |
|
u=u.insert( 13 , 16 ) |
|
u=u.insert( 9 , 9 ) |
|
u=u.insert( 14 , 10 ) |
|
u=u.insert( 8 , 15 ) |
|
u=u.insert( 15 , 29 ) |
|
u=u.insert( 16 , 4 ) |
|
u=u.insert( 7 , 21 ) |
|
u=u.insert( 17 , 23 ) |
|
u=u.insert( 6 , 2 ) |
|
u=u.insert( 5 , 27 ) |
|
u=u.insert( 18 , 17 ) |
|
u=u.insert( 4 , 8 ) |
|
u=u.insert( 31 , 11 ) |
|
u=u.insert( 30 , 30 ) |
|
u=u.insert( 29 , 5 ) |
|
u=u.insert( 28 , 24 ) |
|
u=u.insert( 27 , 18 ) |
|
u=u.insert( 26 , 12 ) |
|
u=u.insert( 25 , 31 ) |
|
u=u.insert( 24 , 6 ) |
|
u=u.insert( 23 , 25 ) |
|
u=u.insert( 19 , 7 ) |
|
u=u.insert( 20 , 13 ) |
|
u=u.insert( 1 , 20 ) |
|
u=u.insert( 0 , 14 ) |
|
u=u.insert( 22 , 0 ) |
|
u=u.insert( 2 , 1 ) |
|
u=u.insert( 3 , 26 ) |
|
u=u.insert( 21 , 19 ) |
|
u=u.remove( 18 , 17 ) |
|
u=u.remove( 17 , 23 ) |
|
u=u.remove( 16 , 4 ) |
|
u=u.remove( 15 , 29 ) |
|
u=u.remove( 14 , 10 ) |
|
u=u.remove( 13 , 16 ) |
|
u=u.remove( 12 , 22 ) |
|
u=u.remove( 6 , 2 ) |
|
u=u.remove( 7 , 21 ) |
|
u=u.remove( 8 , 15 ) |
|
u=u.remove( 11 , 3 ) |
|
u=u.remove( 4 , 8 ) |
|
u=u.remove( 9 , 9 ) |
|
u=u.remove( 10 , 28 ) |
|
u=u.remove( 5 , 27 ) |
|
u=u.remove( 31 , 11 ) |
|
u=u.remove( 0 , 14 ) |
|
u=u.remove( 30 , 30 ) |
|
u=u.remove( 29 , 5 ) |
|
u=u.remove( 1 , 20 ) |
|
u=u.remove( 28 , 24 ) |
|
u=u.remove( 2 , 1 ) |
|
u=u.remove( 3 , 26 ) |
|
u=u.remove( 27 , 18 ) |
|
u=u.remove( 19 , 7 ) |
|
u=u.remove( 26 , 12 ) |
|
u=u.remove( 20 , 13 ) |
|
u=u.remove( 25 , 31 ) |
|
u=u.remove( 24 , 6 ) |
|
u=u.remove( 21 , 19 ) |
|
u=u.remove( 23 , 25 ) |
|
u=u.remove( 22 , 0 ) |
|
|
|
t.end() |
|
})
|
|
|