Skip to content
This repository has been archived by the owner on Feb 19, 2023. It is now read-only.

Commit

Permalink
Add BTree
Browse files Browse the repository at this point in the history
- BTree has get and insert
- Remove not yet implemented
  • Loading branch information
RobertWHurst committed Jul 7, 2013
1 parent 3dae37f commit 0b8022d
Show file tree
Hide file tree
Showing 3 changed files with 350 additions and 1 deletion.
198 changes: 198 additions & 0 deletions lib/b-tree.js
@@ -0,0 +1,198 @@


function Leaf(key, data) {
this.key = key;
this.data = data;
}


function Node(parent, size) {
this.leafs = [];
this.nodes = [];
this.parent = parent;
this.size = size;
}

Node.prototype.getLeaf = function(key) {

var ctLeaf, ltNode, rtNode, result;
for(var i = 0; i < this.size; i += 1) {

ctLeaf = this.leafs[i];
ltNode = this.nodes[i];
rtNode = this.nodes[i + 1];

// if a leaf exists in the current slot
if(ctLeaf) {

// if a direct key match return the
// leaf
if(ctLeaf.key == key) {
return ctLeaf;
}

// if the key is lesser and a left
// sub node exists
else if(ctLeaf.key > key && ltNode) {
result = ltNode.getLeaf(key);
if(result) { return result; }
}

// if the key is greater and a right
// sub node exists
else if(ctLeaf.key < key && rtNode) {
result = rtNode.getLeaf(key);
if(result) { return result; }
}
}

// return an empty result
return;
}
}

Node.prototype.insertLeaf = function(leaf, upTree) {

// loop through all slots within the node
var ctLeaf, ntLeaf, ltNode, rtNode;
for(var i = 0; i < this.size + 1; i += 1) {

ctLeaf = this.leafs[i];
ntLeaf = this.leafs[i + 1];
ltNode = this.nodes[i];
rtNode = this.nodes[i + 1];

// if there is a key
if(ctLeaf) {

// if the keys match
if(ctLeaf.key == leaf.key) {
// replace the data
ctLeaf.data = leaf.data;
break;
}

// if the key is greater and a node
// exists to the right but there
// is no leaf to following the current
else if(ctLeaf.key < leaf.key && !ntLeaf && rtNode && !upTree) {
// insert into the right node
rtNode.insertLeaf(leaf);
break;
}

// if the key is lesser
else if(ctLeaf.key > leaf.key) {

// if a left node exists to the
// right
if(ltNode && !upTree) {
// insert into the left node
ltNode.insertLeaf(leaf);
break;
}

// if there isn't a left node
else {
// insert before the current
// leaf
this.leafs.splice(i, 0, leaf);
break;
}
}
}

// if the slot is empty
else {

// insert the leaf
this.leafs[i] = leaf;
break;
}
}

// if the leaf overflows
if(this.leafs.length > this.size) {
this._split();
}

};

Node.prototype.removeLeaf = function(key) {

};

Node.prototype._split = function() {

// seperate the middle and right leafs
var groupSize = Math.floor(this.size / 2);
var middleLeaf = this.leafs.splice(groupSize, 1)[0];
var rightLeafs = this.leafs.splice(groupSize);

// collect the right nodes
var rightNodes = this.nodes.splice(groupSize + 1);

// if this is the root node
var qTree;
if(this.parent.root) {
qTree = this.parent;
this.parent = new Node(qTree, this.size);
this.parent.nodes.push(this);
qTree.root = this.parent;
}

// insert the middle leaf into the parent node
this.parent.insertLeaf(middleLeaf, true);


// create the right node
var rightNode = new Node(this.parent, this.size);
this.parent.nodes.push(rightNode);
while(rightLeafs[0]) { rightNode.insertLeaf(rightLeafs.shift(), true); }
var subNode;
while(rightNodes[0]) {
var subNode = rightNodes.shift();
subNode.parent = rightNode;
rightNode.nodes.push(subNode);
}
};


function BTree(nodeSize) {

nodeSize = nodeSize || 2;

if(typeof nodeSize != 'number' || nodeSize < 1) { throw new Error('nodeSize must be a number greater than zero'); }
if(typeof maxDepth != 'number' || maxDepth < 1) { throw new Error('maxDepth must be greater than zero'); }

this.root = new Node(this, size);
}

BTree.Node = Node;
BTree.Leaf = Leaf;

BTree.prototype.insert = function(key, data) {
var leaf = new Leaf(key, data);
this.root.insertLeaf(leaf);
};

BTree.prototype.get = function(key) {
var leaf = this.root.getLeaf(key);
return leaf.data;
};

BTree.prototype.remove = function(key) {
var leaf = this.root.removeLeaf(key);
return leaf;
};

BTree.prototype.truncate = function() {

// replace the entire root node; deleting all
// data within the previous root node and its
// tree or sub nodes.
this.root = new Node(this, this.root.size);

};

module.exports = BTree;
2 changes: 1 addition & 1 deletion main.js
@@ -1,5 +1,5 @@

// as trees are added to the library they should
// be requred and exported below
exports.Rect = require('./lib/rect');
exports.BTree = require('./lib/b-tree');
exports.QuadTree = require('./lib/quad-tree');
151 changes: 151 additions & 0 deletions test/b-tree.js
@@ -0,0 +1,151 @@
var BTree = require('../').BTree;
var should = require('should');


describe('Leaf', function() {

var leaf;
beforeEach(function() {
leaf = new BTree.Leaf('key', 'leaf');
});

describe('key', function() {
it('should be equal to the key given to the leaf constructor', function() {
leaf.key.should.be.equal('key');
});
});

describe('data', function() {
it('should be equal to the data given to the leaf constructor', function() {
leaf.data.should.be.equal('leaf');
});
});
});


describe('Node', function() {

var node;
beforeEach(function() {
var fakeBTree = {};
node = new BTree.Node(fakeBTree, 2);
fakeBTree.root = node;
});

describe('getLeaf', function() {

it('should return a leaf with a matching key', function() {
node.insertLeaf(new BTree.Leaf(3, 'three'));
node.insertLeaf(new BTree.Leaf(1, 'one'));
node.insertLeaf(new BTree.Leaf(2, 'two'));
node = node.parent;

var leaf = node.getLeaf(3);
leaf.key.should.equal(3);
leaf.data.should.equal('three');

leaf = node.getLeaf(2);
leaf.key.should.equal(2);
leaf.data.should.equal('two');

leaf = node.getLeaf(1);
leaf.key.should.equal(1);
leaf.data.should.equal('one');
});

it('should return undefined if there is no leaf with the given key', function() {
node.insertLeaf(new BTree.Leaf(3, 'three'));
node.insertLeaf(new BTree.Leaf(1, 'one'));
node.insertLeaf(new BTree.Leaf(2, 'two'));
node = node.parent;

var leaf = node.getLeaf(5);
should.not.exist(leaf);
});

});

describe('insertLeaf', function() {

it('should accept inserted data', function() {
node.insertLeaf(new BTree.Leaf(1, 'one'));
node.leafs[0].key.should.equal(1);
node.leafs[0].data.should.equal('one');
});

it('should split after the maximum leaf count is reached', function() {
node.insertLeaf(new BTree.Leaf(3, 'three'));
node.insertLeaf(new BTree.Leaf(1, 'one'));
node.insertLeaf(new BTree.Leaf(2, 'two'));
node = node.parent;

node.leafs[0].key.should.equal(2);
node.leafs[0].data.should.equal('two');

node.nodes[0].leafs[0].key.should.equal(1);
node.nodes[0].leafs[0].data.should.equal('one');

node.nodes[1].leafs[0].key.should.equal(3);
node.nodes[1].leafs[0].data.should.equal('three');

});

it('should insert into sub nodes when possible', function() {
node.insertLeaf(new BTree.Leaf(1, 'one'));
node.insertLeaf(new BTree.Leaf(6, 'six'));
node.insertLeaf(new BTree.Leaf(3, 'three'));
node = node.parent;
node.insertLeaf(new BTree.Leaf(4, 'four'));
node.insertLeaf(new BTree.Leaf(8, 'eight'));
node.insertLeaf(new BTree.Leaf(5, 'five'));
node.insertLeaf(new BTree.Leaf(7, 'seven'));
node.insertLeaf(new BTree.Leaf(9, 'nine'));
node = node.parent;
node.insertLeaf(new BTree.Leaf(2, 'two'));

// console.log(require('util').inspect(node.parent.root, { depth: null }));

node.nodes[0].nodes[0].leafs[0].key.should.equal(1);
node.nodes[0].nodes[0].leafs[0].data.should.equal('one');

node.nodes[0].nodes[0].leafs[1].key.should.equal(2);
node.nodes[0].nodes[0].leafs[1].data.should.equal('two');

node.nodes[0].leafs[0].key.should.equal(3);
node.nodes[0].leafs[0].data.should.equal('three');

node.nodes[0].nodes[1].leafs[0].key.should.equal(4);
node.nodes[0].nodes[1].leafs[0].data.should.equal('four');

node.nodes[0].nodes[1].leafs[1].key.should.equal(5);
node.nodes[0].nodes[1].leafs[1].data.should.equal('five');

node.leafs[0].key.should.equal(6);
node.leafs[0].data.should.equal('six');

node.nodes[1].nodes[0].leafs[0].key.should.equal(7);
node.nodes[1].nodes[0].leafs[0].data.should.equal('seven');

node.nodes[1].leafs[0].key.should.equal(8);
node.nodes[1].leafs[0].data.should.equal('eight');

node.nodes[1].nodes[1].leafs[0].key.should.equal(9);
node.nodes[1].nodes[1].leafs[0].data.should.equal('nine');
});

it('should allow overwriting existing data', function() {
node.insertLeaf(new BTree.Leaf(1, 'one'));
node.insertLeaf(new BTree.Leaf(2, 'two'));
node.insertLeaf(new BTree.Leaf(3, 'three'));
node = node.parent;
node.insertLeaf(new BTree.Leaf(2, '_two'));

node.leafs[0].key.should.equal(2);
node.leafs[0].data.should.equal('_two');
});

});
});

describe('BTree', function() {
});

0 comments on commit 0b8022d

Please sign in to comment.