GOPHERSPACE.DE - P H O X Y
gophering on sdf.org
//
//
// JavaScript STL (extension)
//
// Free Software Licence:
// This software is intended for free use for personal and comercial use.
// No Warranty:
// Because this software is licenced free of charge, there is no warranty
// expressed or implied for any code contained within this file.
// Guyon Roche
function STDEXT()
{
// namespace class STDEXT
}

STDEXT.prototype.hash_map = function(hash,less)
{
return new STDEXTHashMap(hash,less);
}
STDEXT.prototype.hash_multimap = function(hash,less)
{
return new STDEXTHashMultiMap(hash,less);
}
STDEXT.prototype.hash_set = function(hash,less)
{
return new STDEXTHashSet(hash,less);
}
STDEXT.prototype.hash_multiset = function(hash,less)
{
return new STDEXTHashMultiSet(hash,less);
}
STDEXT.prototype.hash = 
{
// standard hash behaviour is to assume value is already an integer
fn:function(a)
{
return a;

};

// declare namespace stdext
var stdext = new STDEXT();

//=============================================================================
// STDEXTHash
// basic hash based associative container.
// assumed member list:
// this.createNode() - create a node from a value with the following members
//    node.item - the item
//    node.key - the key used for hashing and comparisons
//    node.next & node.prev - initially set to null
// this.less - comparison functor to determine ordering within a hash bucket
// this.hash - hash functor to determine to which hash bucket a key belongs
// this.type - string identifying the class
function STDEXTHash()
{
}
STDEXTHash.prototype.initialise = function(hash,less)
{
// assign appropriate hash and less functors
this.hash = hash ? hash : STDEXT.prototype.hash;
this.less = less ? less : STD.prototype.less;

// create the hash-indexed collection
this.bucket = new Array();

// add list sentinels
this.head = {item:"head",prev:null,bSentinel:true};
this.tail = {item:"tail",tail:null,bSentinel:true};
this.head.next = this.tail;
this.tail.prev = this.head;

// keep track of count
this.nCount = 0;
}
STDEXTHash.prototype.iterator = function(node,bForward)
{
if ( arguments.length == 1 ) bForward = true;
return new STDLinkIterator(this,node,bForward,this.type);
}
STDEXTHash.prototype.isIterator = STD.prototype.isIterator;
STDEXTHash.prototype.createNode = function(item)
{
// default behaviour; the item is the key
return {item:item,key:item,next:null,prev:null};
}
STDEXTHash.prototype.insertNode = function(node, prev,next)
{
// insert a node into the linked list
node.prev = prev;
prev.next = node;

node.next = next;
next.prev = node;

// increment the count
this.nCount++;
}
STDEXTHash.prototype.removeNode = function(node)
{
// remove the node from the list
node.prev.next = node.next;
node.next.prev = node.prev;

// decrement the count
this.nCount--;
}
STDEXTHash.prototype.begin = function()
{
// return iterator at start of collection
return this.iterator(this.head.next);
}
STDEXTHash.prototype.clear = function()
{
// remove all items from collection
this.bucket = new Array();

// and empty the list
this.head.next = this.tail;
this.tail.prev = this.head;

// reset the count
this.nCount = 0;
}
STDEXTHash.prototype.count = function(key)
{
// count the number of items that match key
var node = this.lower_bound(key).node;
var nCount = 0;
var nHash = this.hash.fn(key);
while ( !node.bSentinel &&
(this.hash.fn(node.key) == nHash) &&
!this.less.fn(key,node.key) )
{
nCount++;
node = node.next;
}
return nCount;
}
STDEXTHash.prototype.empty = function()
{
// return true if no items contained.
return this.nCount == 0;
}
STDEXTHash.prototype.end = function()
{
// return iterator one step beyond the last item
return this.iterator(this.tail);
}
STDEXTHash.prototype.equal_range = function(key)
{
// return lower and upper bounds of key
var lb = this.lower_bound(key);
var ub = this.upper_bound(key);
return {first:lb,second:ub};
}
STDEXTHash.prototype.eraseImpl = function(node)
{
var next = node.next;

// find the array index
var h = this.hash.fn(node.key);

// if node is at head of bucket
if ( this.bucket[h] === node )
{
if ( this.hash.fn(next.key) == h )
{
// put the next one in this bucket
this.bucket[h] = next;
}
else
{
// empty the bucket
delete this.bucket[h];
}
}

// remove node from linked list
this.removeNode(node);

// return the next node in the list
return next;
}
STDEXTHash.prototype.erase = function(a,b)
{
var iNode, nCount;

// erase items from the collection
if ( arguments.length == 0 ) return;

if ( !this.isIterator(a,true,true) )
{
// a is a key
iNode = this.lower_bound(a).node;
nCount = 0;
while ( !this.less.fn(a, iNode.key) )
{
iNode = this.eraseImpl(iNode);
nCount++;
}
return nCount;
}

// if b not specified, erase just a
if ( arguments.length == 1 )
{
if ( !a.node.bSentinel )
{
return this.iterator(this.eraseImpl(a.node));
}
else return this.end();
}
else
{
iNode = a.node;
while ( iNode !== b.node )
{
iNode = this.eraseImpl(iNode);
}
return this.iterator(iNode);
}
}
STDEXTHash.prototype.find = function(key)
{
var nIndex = this.hash.fn(key);
var node = this.bucket[nIndex];
if ( !node ) return this.end();
while ( !node.bSentinel &&
(this.hash.fn(node.key) == nIndex) &&
this.less.fn(node.key,key) )
{
node = node.next;
}
if ( (this.hash.fn(node.key) == nIndex) &&
 !this.less.fn(key, node.key) ) return this.iterator(node);
else return this.end();
}
STDEXTHash.prototype.insertImpl = function(value)
{
var node = this.createNode(value);
var nIndex = this.hash.fn(node.key);
if ( this.bucket[nIndex] )
{
// find the first item in this bin not less than value
var pNode = this.bucket[nIndex];
while ( (this.hash.fn(pNode.key) == nIndex) &&
this.less.fn(pNode.key,node.key) )
{
pNode = pNode.next;
}

// if not multi container and key exists already, return it
if ( !this.bMultiContainer && !this.less.fn(node.key,pNode.key) ) return {first:pNode,second:false};

// if head of bin, adjust bin
if ( pNode === this.bucket[nIndex] ) this.bucket[nIndex] = node;

// insert in list just before pNode
this.insertNode(node, pNode.prev, pNode);
}
else
{
this.bucket[nIndex] = node;

// insert at tail of list
this.insertNode(node, this.tail.prev,this.tail);
}
// return new node.
return {first:node,second:true};
}
STDEXTHash.prototype.insertReturn = function(pair)
{
// pair: first:node, second:bInserted
if ( this.bMultiContainer ) return this.iterator(pair.first);
else return {first:this.iterator(pair.first),second:pair.second};
}
STDEXTHash.prototype.insert = function(a,b)
{
// insert item(s) into collection
if ( arguments.length == 0 ) return;

var pair = null;
var hint;
if ( arguments.length == 1 )
{
// case 1: a is value type
return this.insertReturn(this.insertImpl(a));
}

if ( this.isIterator(a) && this.isIterator(b) )
{
// case 2: a and b are input iterators. Insert [a,b)
node = a.node;
while ( node !== b.node )
{
this.insertImpl(node.item);
node = node.next;
}
return;
}

if ( this.isIterator(a,true,true) )
{
// case 3: a is a hint. Test and insert
if ( !a.node.bSentinel )
{
node = this.createNode(b);
var next = a.node.next;
var nHint = this.hash.fn(a.node.key);
var nIndex = this.hash.fn(node.key);
if ( nHint == nIndex )
{
if ( this.bMultiContainer )
{
if ( !this.less.fn(node.key,a.node.key) &&
(next.bSentinel || 
(this.hash.fn(next.key) != nIndex) ||
!this.less.fn(next.key,node.key)) )
{
// next is end or in a different hash or not less than b
// so insert here
this.insertNode(node,a.node,next);
return this.iterator(node);
}
}
else
{
if ( this.less.fn(a.node.key, node.key) &&
(next.bSentinel || 
(this.hash.fn(next.key) != nIndex) ||
this.less.fn(node.key,next.key)) )
{
// next is end or in a different hash or not less than b
// so insert here
this.insertNode(node,a.node,next);
return this.iterator(node);
}
}
}
}
// if the attempt to use the hint failed, fall back on normal insert
return this.iterator(this.insertImpl(b).first);
}
}
STDEXTHash.prototype.key_comp = function()
{
return this.less;
}
STDEXTHash.prototype.lower_bound = function(key)
{
var nIndex = this.hash.fn(key);
var node = this.bucket[nIndex];
if ( !node ) return this.end();
while ( !node.bSentinel &&
(this.hash.fn(node.key) == nIndex) &&
this.less.fn(node.key,key) )
{
node = node.next;
}
return this.iterator(node);
}
STDEXTHash.prototype.rbegin = function()
{
return this.iterator(this.tail.prev, false);
}
STDEXTHash.prototype.rend = function()
{
return this.iterator(this.head, false);
}
STDEXTHash.prototype.size = function()
{
return this.nCount;
}
STDEXTHash.prototype.swap = function(other)
{
// save the first element for insertion into other.
var begin = this.head.next;

// insert the contents of other into this
this.clear();
this.insert(other.begin(), other.end());

// insert the old contents of this into other. 
// Note: still terminated by this.end()
other.clear();
other.insert(this.iterator(begin), this.end());
}
STDEXTHash.prototype.upper_bound = function(key)
{
// first element greater than key
var nIndex = this.hash.fn(key);
var node = this.bucket[nIndex];
if ( !node ) return this.end();
while ( !node.bSentinel &&
(this.hash.fn(node.key) == nIndex) &&
!this.less.fn(key, node.key) )
{
node = node.next;
}
return this.iterator(node);
}

//=============================================================================
// STDEXTHashMap

function STDEXTHashMap(hash,less)
{
// add special map behaviour
this.createNode = STDEXTHashMap_createNode;

// initialise hash_map settings
this.type = "hash_map";
this.bMultiContainer = false;

// initialise STDEXTHash settings
this.initialise(hash,less);
}

// inherit behaviour from STDEXTHash
STDEXTHashMap.prototype = STDEXTHash.prototype;

STDEXTHashMap_createNode = function(item)
{
// in a map, the key is item.first
return {item:item,key:item.first,next:null,prev:null};
}

//=============================================================================
// STDEXTHashMultiMap

function STDEXTHashMultiMap(hash,less)
{
// add special map behaviour
this.createNode = STDEXTHashMap_createNode;

// initialise hash_multimap settings
this.type = "hash_multimap";
this.bMultiContainer = true;

// initialise STDEXTHash settings
this.initialise(hash,less);
}

// inherit behaviour from STDEXTHash
STDEXTHashMultiMap.prototype = STDEXTHash.prototype;

//=============================================================================
// STDEXTHashSet

function STDEXTHashSet(hash,less)
{
// initialise hash_set settings
this.type = "hash_set";
this.bMultiContainer = false;

// initialise STDEXTHash settings
this.initialise(hash,less);
}

// inherit behaviour from STDEXTHash
STDEXTHashSet.prototype = STDEXTHash.prototype;

//=============================================================================
// STDEXTHashMultiSet

function STDEXTHashMultiSet(hash,less)
{
// initialise map settings
this.type = "hash_multiset";

this.bMultiContainer = true;

// initialise STDEXTHash settings
this.initialise(hash,less);
}

// inherit behaviour from STDEXTHash
STDEXTHashMultiSet.prototype = STDEXTHash.prototype;

//