aboutsummaryrefslogblamecommitdiffstats
path: root/trunk/infrastructure/ace/www/bbtree.js
blob: 70cb8c040c87b3b468762754ba964e7fad91960b (plain) (tree)



















































































































































































































































































































































































                                                                                         
/**
 * Copyright 2009 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


function makeBBTree(transferContents, augment) {

  var nil = [];
  nil.push(nil,nil);
  nil.level = 0;
  var root = nil;
  augment = (augment || (function(n) {}));
  
  function skew(t) {
    if (t != nil && t[0].level == t.level) {
      // rotate right
      var tmp = t;
      t = t[0];
      tmp[0] = t[1];
      t[1] = tmp;
      reaugment(tmp);
      reaugment(t);
    }
    return t;
  }

  function split(t) {
    if (t != nil && t[1][1].level == t.level) {
      // rotate left
      var tmp = t;
      t = t[1];
      tmp[1] = t[0];
      t[0] = tmp;
      t.level++;
      reaugment(tmp);
      reaugment(t);
    }
    return t;
  }

  function reaugment(n) {
    if (n != nil) augment(n);
  }
  
  var self = {};
  
  self.insert = function(compare) {
    var n;
    function recurse(t) {
      if (t == nil) {
	t = [nil,nil];
	t.level = 1;
	n = t;
      }
      else {
	var cmp = compare(t);
	if (cmp < 0) {
	  t[0] = recurse(t[0]);
	}
	else if (cmp > 0) {
	  t[1] = recurse(t[1]);
	}
	t = skew(t);
	t = split(t);
      }
      reaugment(t);
      return t;
    }
    root = recurse(root);
    return n;
  }
  
  self.remove = function(compare) {
    var deleted = nil;
    var last;
    var deletedEqual = false;
    function recurse(t) {
      if (t != nil) {
	last = t;
	var cmp = compare(t);
	if (cmp < 0) {
	  t[0] = recurse(t[0]);
	}
	else {
	  deleted = t;
	  // whether the node called "deleted" is actually a
	  // match for deletion
	  deletedEqual = (cmp == 0);
	  t[1] = recurse(t[1]);
	}
	if (t == last && deleted != nil && deletedEqual) {
	  // t may be same node as deleted
	  transferContents(t, deleted);
	  t = t[1];
	  reaugment(deleted);
	  deleted = nil;
	}
	else {
	  reaugment(t);
	  if (t[0].level < t.level-1 ||
	      t[1].level < t.level-1) {
	    t.level--;
	    if (t[1].level > t.level)
	      t[1].level = t.level;
	    t = skew(t);
	    t[1] = skew(t[1]);
	    t[1][1] = skew(t[1][1]);
	    t = split(t);
	    t[1] = split(t[1]);
	  }
	}
      }
      return t;
    }
    root = recurse(root);
  }

  self.find = function(compare) {
    function recurse(t) {
      if (t == nil) return t;
      var cmp = compare(t);
      if (cmp < 0) {
	return recurse(t[0]);
      }
      else if (cmp > 0) {
	return recurse(t[1]);
      }
      else {
	return t;
      }
    }
    var result = recurse(root);
    return (result != nil && result) || null;
  }

  self.root = function() { return root; }

  self.forEach = function(func) {
    function recurse(t) {
      if (t != nil) {
	recurse(t[0]);
	func(t);
	recurse(t[1]);
      }
    }
    recurse(root);
  }
  
  return self;
}

function makeBBList() {
  var length = 0;
  var totalWidth = 0;

  function _treeSize(n) { return n.size || 0; }
  function _treeWidth(n) { return n.width || 0; }
  function _width(n) { return (n && n.entry && n.entry.width) || 0; }
  
  function _transferContents(a, b) {
    b.key = a.key;
    b.entry = a.entry;
  }
  function _augment(n) {
    n.size = _treeSize(n[0]) + _treeSize(n[1]) + 1;
    n.width = _treeWidth(n[0]) + _treeWidth(n[1]) + _width(n);
  }

  var keyToEntry = {};
  
  var bb = makeBBTree(transferContents, augment);

  function makeIndexComparator(indexFunc) {
    var curIndex = _treeSize(bb.root()[0]);
    return function (n) {
      var dir = indexFunc(curIndex, n);
      if (dir < 0) {
	curIndex -= _treeSize(n[0][1]) + 1;
      }
      else if (dir >= 0) {
	curIndex += _treeSize(n[1][0]) + 1;      
      }
      return dir;
    }
  }

  function makeWidthComparator(widthFunc) {
    var curIndex = _treeWidth(bb.root()[0]);
    return function (n) {
      var dir = indexFunc(curIndex, n);
      if (dir < 0) {
	curIndex -= _treeWidth(n[0][1]) + _width(n[0]);
      }
      else if (dir >= 0) {
	curIndex += _treeWidth(n[1][0]) + _width(n);
      }
      return dir;
    }
  }

  function numcomp(a,b) { if (a < b) return -1; if (a > b) return 1; return 0; }
  
  function removeByIndex(idx) {
    var entry;
    bb.remove(makeComparator(function (curIndex, n) {
      var cmp = numcomp(idx, curIndex);
      if (cmp == 0) entry = n.entry;
      return cmp;
    }));
    return entry;
  }

  function insertAtIndex(idx, entry) {
    var newNode = bb.insert(makeComparator(function (curIndex) {
      if (idx <= curIndex) return -1;
      return 1;
    }));
    newNode.entry = entry;
    return newNode;
  }

  var entriesByKey = {};
  
  var self = {
    splice: function (start, deleteCount, newEntryArray) {
      for(var i=0;i<deleteCount;i++) {
	var oldEntry = removeByIndex(start);
	length--;
	totalWidth -= (entry.width || 0);
	delete entriesByKey[oldEntry.key];
      }
      for(var i=0;i<newEntryArray.length;i++) {
	var entry = newEntryArray[i];
	var newNode = insertAtIndex(start+i, entry);
	length++;
	totalWidth += (entry.width || 0);
	entriesByKey[entry.key] = entry;
      }
    },
    next: function (entry) {
      
    }
  };

  return self;
}

/*function size(n) {
  return n.size || 0;
}

var a = makeBBTree(function (a,b) {
  b.data = a.data;
},
		   function (n) {
		     n.size = size(n[0]) + size(n[1]) + 1;
		   });

var arrayRep = [];

function makeComparator(indexFunc) {
  var curIndex = size(a.root()[0]);
  return function (n) {
    var dir = indexFunc(curIndex);
    if (dir < 0) {
      curIndex -= size(n[0][1]) + 1;
    }
    else if (dir >= 0) {
      curIndex += size(n[1][0]) + 1;      
    }
    return dir;
  }
}

function insert(idx, data) {
  arrayRep.splice(idx, 0, data);
  var newNode = a.insert(makeComparator(function (curIndex) {
    if (idx <= curIndex) return -1;
    return 1;
  }));
  newNode.data = data;
  checkRep();
}

function remove(idx) {
  arrayRep.splice(idx, 1);
  a.remove(makeComparator(function (curIndex) {
    return numcomp(idx, curIndex);
  }));
  checkRep();
}

function genArray() {
  var array = [];
  a.forEach(function (n) { array.push(n.data); });
  return array;
}

function checkRep() {
  var array2 = genArray();
  var str1 = array2.join(',');
  var str2 = arrayRep.join(',');
  if (str1 != str2) console.error(str1+" != "+str2);

  a.forEach(function(n) {
    if (size(n) != size(n[0]) + size(n[1]) + 1) {
      console.error("size of "+n.data+" is wrong");
    }
  });
}

function print() {
  console.log(genArray().join(','));
}

insert(0,1);
insert(0,2);
insert(0,3);
insert(1,4);
insert(4,5);
insert(0,6);
print();

function numcomp(a,b) { if (a < b) return -1; if (a > b) return 1; return 0; }
*/
/*var tree = makeBBTree(function(a, b) { b.key = a.key; });
function insert(x) { tree.insert(function(n) { return numcomp(x, n.key) }).key = x; }
function remove(x) { tree.remove(function (n) { return numcomp(x, n.key); }); }
function contains(x) { return !! tree.find(function (n) { return numcomp(x, n.key); }); }

function print() {
  function recurse(t) {
    if (! ('key' in t)) return '';
    return '('+recurse(t[0])+','+t.key+','+recurse(t[1])+')';
  }
  return recurse(tree.root());
}


insert(2);
insert(1);
insert(8);
insert(7);
console.log(print());
insert(6);
insert(3);
insert(5);
insert(4);
console.log(print());
remove(2);
remove(1);
remove(8);
remove(7);
console.log(print());
//remove(6);
//remove(3);
//remove(5);
//remove(4);
//console.log(print());
*/