// $Revision: 1.21 $
var ibihash_js_Revision = "$Revision: 1.21 $";

ibihash.prototype.findKeyIndexInBin = function ibihash_findKeyIndexInBin(bin,key)
{
if(bin == null) return -1;
var bin_length=bin.length;
for(var i = 0 ; i < bin_length ; ++i)
  {
  ++this.testLoopCount;
  if(bin[i].key == key) return i;
  }
return -1;
}
ibihash.prototype.hashKey = function ibihash_hashKey(key)
{
var code = 0;
if(key != null)
  {
  code = key.length;
  var end = Math.min(code,16);
  for(var i = 0 ; i < end ; ++i)
    code = (code + (key.charCodeAt(i) * i)) % this.size;
  }
return code;
}
ibihash.prototype.findBin = function ibihash_findBin(key)
{
var code = this.hashKey(key);
if(this.table[code] == null)
  this.table[code] = new Array();
return this.table[code];
}

ibihash.prototype.remove = function ibihash_remove(key)
{
var bin = this.findBin(key);
var index = this.findKeyIndexInBin(bin,key)
if(index == -1) return null;
//bin[index].splice(index,1);
bin.splice(index,1);
return this;
}
ibihash.prototype.get = function ibihash_get(key)
{
++this.lookupCount;
var bin = this.findBin(key);
var index = this.findKeyIndexInBin(bin,key)
if(this.debug)
  debugWindowLine(this.testLoopCount,this.keyCount,this.size,this.lookupCount,this.testLoopCount/this.lookupCount);
if(index != -1)
  return bin[index].value;
if(this.otherTables != null) {
  var this_otherTables_length=this.otherTables.length;
  for(var i = 0 ; i < this_otherTables_length ; ++i)
    {
    var s = this.otherTables[i].get(key);
    if(s != null)
      return s;
    }
}
return null;
}
ibihash.prototype.eval = function ibihash_eval(key,p0,p1,p2,p3,p4,p5,p6,p7,p8,p9)
{
var s = this.getStr(key);
if(s != null)
  return eval(s);
return null;
}
ibihash.prototype.getStr = function ibihash_getStr(key)
{
var str = this.get(key);
if(str == null)
  {
  str = "ERROR--- can't translate string '" + key + "' ---ERROR";
  if(debuggerIsIncluded())
    {
    debugWindow(str);
//    debugWindowStack();
    }
  }
return str;
}
ibihash.prototype.nextTableSize = function ibihash_nextTableSize(size)
{
// primes they work well for hash table sizes
var t=this.primeNumbersTable;
// var t = new Array( 31, 127, 509, 2039, 8161);
var t_length= t.length;
for(var i = 0 ; i < t_length-1 ; ++i)
  if(size <= t[i])
    return t[i+1];
return size * 4;
}
ibihash.prototype.findBestFitTableSize = function ibihash_findBestFitTableSize(size)
{
var t=this.primeNumbersTable;
var t_length= t.length;
for(var i = 0 ; i < t_length ; ++i)
  if(t[i] >= size)
	  return t[i];
return size * 4;
}
ibihash.prototype.rehash = function ibihash_rehash(newSize)
{
if(newSize == null)
  newSize = this.nextTableSize(this.size);

var table = this.table;   // save old table
this.size = newSize;    // set new size
this.table = new Array(this.size); // create new table

var length = table.length
for(var i = 0 ; i < length ; ++i)
  {
  var bin = table[i];
  if(bin == null)
    continue;
  for(var j = 0 ; j < bin.length ; ++j)
    {
    ++this.testLoopCount;
    this.put(bin[j].key,bin[j].value);
    }
  }
if(this.debug)
  debugWindow(this.toString());
return this.size;
}

ibihash.prototype.put = function ibihash_put(key,value)
{
++this.keyCount;
if(this.allowAutoResize && this.keyCount > this.size)
  this.rehash(null);
var bin = this.findBin(key);
var index = this.findKeyIndexInBin(bin,key)
if(index != -1)
  bin[index].value = value;
else
  {
  var o = new Object();
  o.key =  key;
  o.value = value;
  bin[bin.length] = o;
  }
return true;
}

ibihash.prototype.keys = function ibihash_keys()
{
var rtn = new Array();
for(var i = 0 ; i < this.size ; ++i)
  {
  if( this.table[i] == null)
    continue;
  for(var j = 0 ; j < this.table[i].length ; ++j)
    rtn.addElement(this.table[i][j].key);
  }
return rtn;
}
ibihash.prototype.toString = function ibihash_toString()
{
var s = "\n";
for(var i = 0 ; i < this.size ; ++i)
  {
  if( this.table[i] == null)
    continue;
  var this_table_i__length=this.table[i].length;
  if(this_table_i__length  == 0)
    continue;
  s += "Bin (" + i + ") (" + this_table_i__length + ") ";
  s += "\n";
  for(var j = 0 ; j < this_table_i__length ; ++j)
    {
    s += "\t";
    s += "'" + this.table[i][j].key + "'";
    s += " : "
    s += "'" + this.table[i][j].value+ "'";
    s += "\n";
    }
  }
s += "size = " + this.size + "\n";
s += "keyCount = " + this.keyCount + "\n";
s += "testLoopCount = " + this.testLoopCount + "\n";
s += "lookupCount = " + this.lookupCount + "\n";
s += "allowAutoResize = " + this.allowAutoResize + "\n";
return s;
}
ibihash.prototype.putTranslate = function ibihash_putTranslate(key,value)
{
if(this.translateFunc == null)
  {
  debugWindow("putTranslate called NO translate method set");
  return;
  }
this.put(key,this.translateFunc(value));
}
ibihash.prototype.addTable = function ibihash_addTable(table)
{
if(this.otherTables == null)
  this.otherTables = new Array();
this.otherTables.addElement(table);
}

ibihash.prototype.getSize = function ibihash_getSize() {

  return this.keys().length;

}

ibihash.prototype.clear = function ibihash_clear() {
  for(var i = 0; i < this.size; i++)
    this.table[i] = new Array();
}

ibihash.prototype.containsKey = function ibihash_containsKey(key) {
  var keys = this.keys();
  var  keys_length= keys.length;
  for(var i=0; i < keys_length; i++)
    if(keys[i] == key)
      return true;
  return false
}

function ibihash(translateFunc,suggestedSize)
{
this.otherTables = null;
this.primeNumbersTable = new Array( 31, 127, 509, 2039, 8161);
this.translateFunc = translateFunc;
this.size = 127;
if (suggestedSize != null) 
   this.size=this.findBestFitTableSize(suggestedSize);

this.table = new Array(this.size);
this.keyCount = 0;
this.testLoopCount = 0;
this.lookupCount = 0;
this.allowAutoResize = true;
this.debug = false;
}
