/***************************************************************************
 *
 * implement hash table of iterators into an slist
 *
 * $Id: lhash.cc,v 7.5 1996/02/19 12:34:12 jims Exp $
 *
 * (c) Copyright 1989 - 1996 Rogue Wave Software, Inc.
 * ALL RIGHTS RESERVED
 *
 * The software and information contained herein are proprietary to, and
 * comprise valuable trade secrets of, Rogue Wave Software, Inc., which
 * intends to preserve as trade secrets such software and information.
 * This software is furnished pursuant to a written license agreement and
 * may be used, copied, transmitted, and stored only in accordance with
 * the terms of such license and with the inclusion of the above copyright
 * notice.  This software and information or any other copies thereof may
 * not be provided or otherwise made available to any other person.
 *
 * Notwithstanding any other lease or license that may pertain to, or
 * accompany the delivery of, this computer software and information, the
 * rights of the Government regarding its use, reproduction and disclosure
 * are as set forth in Section 52.227-19 of the FARS Computer
 * Software-Restricted Rights clause.
 * 
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in
 * Technical Data and Computer Software clause at DFARS 252.227-7013.
 * Contractor/Manufacturer is Rogue Wave Software, Inc.,
 * P.O. Box 2328, Corvallis, Oregon 97339.
 *
 * This computer software and information is distributed with "restricted
 * rights."  Use, duplication or disclosure is subject to restrictions as
 * set forth in NASA FAR SUP 18-52.227-79 (April 1985) "Commercial
 * Computer Software-Restricted Rights (April 1985)."  If the Clause at
 * 18-52.227-74 "Rights in Data General" is specified in the contract,
 * then the "Alternate III" clause applies.
 *
 ***************************************************************************
 *
 * $Log: lhash.cc,v $
 * Revision 7.5  1996/02/19 12:34:12  jims
 * Port to Stdlib 1.2
 *
 * Revision 7.4  1995/10/27 16:09:25  jims
 * Change  const EQ equals  to  const EQ& equals
 *
 * Revision 7.3  1995/09/05  19:35:03  jims
 * Update header section
 *
 * Revision 7.2  1995/08/31  12:03:51  jims
 * next rev
 *
 * Revision 7.1  1995/08/29  09:16:50  jims
 * Port to sgi and Borland
 *
 * Revision 1.2  1995/08/28  22:31:00  griswolf
 * miscellaneous changes to get past the compiler.
 *
 * Revision 1.1  1995/08/25  20:29:30  griswolf
 * Initial revision
 *
 ***************************************************************************
 */

#undef VECSIZE
#define VECSIZE sizeof(vector<bucket_t,allocator>)
#undef LISTSIZE
#define LISTSIZE sizeof(rw_slist<node_t>)

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::LHashTable(
				LHashTable<T,Hash,EQ>::size_type sz,
				const Hash& h, const EQ& equals
			       )
: table_(0), list_(0), hash_(h), eq_(equals)
{
  list_ =
    new (listAllocator.allocate(LISTSIZE)) rw_slist<node_t>;
  table_ =
    new (vectorAllocator.allocate(VECSIZE)) vector<bucket_t,allocator> (sz); 
}


template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::LHashTable(
				ConstIterator first, ConstIterator bound,
				size_type sz, const Hash& h, const EQ& equals
			       )
: table_(0), list_(0), hash_(h), eq_(equals)
{
  list_ =
    new (listAllocator.allocate(LISTSIZE)) rw_slist<node_t>;
  table_ =
    new (vectorAllocator.allocate(VECSIZE)) vector<bucket_t,allocator>(sz);
  Iterator dum;
  insertMaybe(first,bound,dum,false); // assume donor was multi
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::LHashTable(
	const T* first, const T* bound,
	size_type sz, const Hash& h, const EQ& equals
       )
: table_(0), list_(0), hash_(h), eq_(equals)
{
  list_ =
    new (listAllocator.allocate(LISTSIZE)) rw_slist<node_t>;
  table_ =
    new (vectorAllocator.allocate(VECSIZE)) vector<bucket_t,allocator>(sz);
  Iterator dum;
  insertMaybe(first,bound,dum,false); // assume donor was multi
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::LHashTable(const LHashTable<T,Hash,EQ>& t)
: table_(0), list_(0), hash_(t.hash_), eq_(t.eq_)
{
  list_ =
    new (listAllocator.allocate(LISTSIZE)) rw_slist<node_t>;
  table_ =
    new (vectorAllocator.allocate(VECSIZE))
      vector<bucket_t,allocator>(t.capacity());
  
  Iterator dum;
  insertMaybe(t.begin(),t.end(),dum,false);
 }

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>&
LHashTable<T,Hash,EQ>::operator=(const LHashTable<T,Hash,EQ>& t)
{
  if(this != &t) {
    clear();
    if(capacity() > t.capacity()) {
      /* destroy(table_); */ table_->vector<bucket_t,allocator>::~vector();
      vectorAllocator.deallocate(table_);
      table_ = new (vectorAllocator.allocate(VECSIZE))
	vector<bucket_t,allocator>(t.capacity());
    }
    else {
      table_->reserve(t.capacity());
      bucket_t dum;
      while(table_->size() < t.capacity())
	table_->push_back(dum);
    }
    Iterator dum;
    insertMaybe(t.begin(),t.end(),dum,false);
  }
  return *this;
}

#if 0
template <class T, class Hash, class EQ>
rw_slist<LHashTable<T,Hash,EQ>::node_t>::iterator
LHashTable<T,Hash,EQ>::chunkyLowerBound(
		 list_t::iterator start,
		 list_t::iterator bound,
		 const_reference key,
		 bool& hit
		) const
{
  hit = false;
  size_type slot = ((*start).second) % capacity();
  while(start != bound && ((*start).second)%capacity() == slot) {
    if(eq_((*start).first,key)) {
      hit = true;
      break;
    }
    ++start;
  }
  return start;
}

template <class T, class Hash, class EQ>
rw_slist<LHashTable<T,Hash,EQ>::node_t>::iterator
LHashTable<T,Hash,EQ>::chunkyUpperBound(
		 list_t::iterator start,
		 list_t::iterator bound,
		 const_reference key
		) const
{
  size_type slot = ((*start).second) % capacity();
  bool dum;
  start = chunkyLowerBound(start, bound, key, dum);

  while(start != bound && ((*start).second)%capacity() == slot) {
    if(! eq_((*start).first,key))
      break;
    ++start;
  }
  return start;
}
#endif

template <class T, class Hash, class EQ> void
LHashTable<T,Hash,EQ>::clear()
{
  list_->erase(list_->begin(),list_->end());
  table_t::iterator kill = table_->begin();
  while(kill != table_->end()) {
    (*kill).valid() = false;
    ++kill;
  }
}

template <class T, class Hash, class EQ>
bool
LHashTable<T,Hash,EQ>::testEqual(const LHashTable<T,Hash,EQ>& t) const
{
/** precondition(size() == t.size()) **/
  ConstIterator i = begin();
  
  while(i != end()) {
    size_type num = t.count(*i);
    if(num != count(*i)) {
      return false;
    }
    else {
      while(num-- ) ++i;
    }
  }
  return true;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::size_type
LHashTable<T,Hash,EQ>::erase(LHashTable<T,Hash,EQ>::const_reference key)
{
  size_type ret = size();
  erase(equal_range(key));
  return ret - size();
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::Iterator
LHashTable<T,Hash,EQ>::erase(LHashTable<T,Hash,EQ>::Iterator iter)
{
  list_t::iterator kill = iter.iter_;
  list_t::iterator killnext = kill; ++killnext;
  table_t::iterator ti = index((*kill).second);
  list_t::iterator slot = (*ti).chain();
  size_type kbucket = (*kill).second%capacity();
  if(slot == kill) // erasing first item in slot
  {
    // does slot have another item after us?
    if(killnext != list_->end() && (*killnext).second%capacity() == kbucket)
    {
      (*ti).chain() = killnext = list_->erase(kill);
    }
    else // slot had only one item. invalidate slot, fixup next in list.
    {
      (*ti).valid() = false;
      killnext = list_->erase(kill);
      if(killnext != list_->end()) {
	(*index((*killnext).second)).chain() = killnext;
      }
    }
  }
  else // erasing item not first in slot
  {
    // does slot have another item after us?
    if(killnext != list_->end() && (*killnext).second%capacity() == kbucket) 
    {
      killnext = list_->erase(kill);
    }
    else // erasing last item in slot. fixup next
    {
      killnext = list_->erase(kill);
      if(killnext != list_->end()) {
	(*index((*killnext).second)).chain() = killnext;
      }
    }
  }
  return killnext;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::ConstIterator
LHashTable<T,Hash,EQ>::erase(LHashTable<T,Hash,EQ>::ConstIterator iter)
{
  list_t::iterator kill = iter.iter_;
  list_t::iterator killnext = kill; ++killnext;
  table_t::iterator ti = index((*kill).second);
  list_t::iterator slot = (*ti).chain();
  size_type kbucket = (*kill).second%capacity();
  if(slot == kill) // erasing first item in slot
  {
    // does slot have another item after us?
    if(killnext != list_->end() && (*killnext).second%capacity() == kbucket)
    {
      (*ti).chain() = killnext = list_->erase(kill);
    }
    else // slot had only one item. invalidate slot, fixup next in list.
    {
      (*ti).valid() = false;
      killnext = list_->erase(kill);
      if(killnext != list_->end()) {
	(*index((*killnext).second)).chain() = killnext;
      }
    }
  }
  else // erasing item not first in slot
  {
    // does slot have another item after us?
    if(killnext != list_->end() && (*killnext).second%capacity() == kbucket) 
    {
      killnext = list_->erase(kill);
    }
    else // erasing last item in slot. fixup next
    {
      killnext = list_->erase(kill);
      if(killnext != list_->end()) {
	(*index((*killnext).second)).chain() = killnext;
      }
    }
  }
  return killnext;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::Iterator
LHashTable<T,Hash,EQ>::erase(
			    LHashTable<T,Hash,EQ>::Iterator first,
			    LHashTable<T,Hash,EQ>::Iterator bound
			   )
{
  Iterator iter;
  
  if (first != bound) {
    if(bound == end()) {
      while(first != end()) {
	first = erase(first);
      }
    }
    else {
      /*
       * erasing the element to the left of bound invalidates bound,
       * so we have to look ahead and do one last erase after the while loop.
       */
      iter = first;
      ++iter;
      while(iter != bound) {
	first = erase(first);
	iter = first;
	++iter;
      }
      first = erase(first);
    }
  }
  return first;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::ConstIterator
LHashTable<T,Hash,EQ>::erase(
			    LHashTable<T,Hash,EQ>::ConstIterator first,
			    LHashTable<T,Hash,EQ>::ConstIterator bound
			   )
{
  ConstIterator iter;
  
  if (first != bound) {
    if(bound == end()) {
      while(first != end()) {
	first = erase(first);
      }
    }
    else {
      /*
       * erasing the element to the left of bound invalidates bound,
       * so we have to look ahead and do one last erase after the while loop.
       */
      iter = first;
      ++iter;
      while(iter != bound) {
	first = erase(first);
	iter = first;
	++iter;
      }
      first = erase(first);
    }
  }
  return first;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::size_type
LHashTable<T,Hash,EQ>::count(LHashTable<T,Hash,EQ>::const_reference key) const
{
  size_type ret = 0;
  table_t::const_iterator slot = index(key);
  if(*slot) {
    bool found;
    list_t::iterator it =
      chunkyLowerBound((*slot).chain(), list_->end(), key, found);
    if(found) {
      while((it != list_->end() && eq_((*it).first,key))) {
	++ret;
	++it;
      }
    }
  }
  return ret;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::Iterator
LHashTable<T,Hash,EQ>::find(LHashTable<T,Hash,EQ>::const_reference key)
{
  Iterator ret = end();
  table_t::iterator slot = index(key);
  list_t::iterator i;
  if(*slot) {
    bool found;
    i =  chunkyLowerBound((*slot).chain(), list_->end(), key, found);
    if(found)
      ret = i;
  }
  return ret;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::ConstIterator
LHashTable<T,Hash,EQ>::find(LHashTable<T,Hash,EQ>::const_reference key) const
{
  ConstIterator ret = end();
  table_t::const_iterator slot = index(key);
  list_t::iterator i;
  if(*slot) {
    bool found;
    i = chunkyLowerBound((*slot).chain(), list_->end(), key, found);
    if(found)
      ret = ConstIterator(i);
  }
  return ret;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::Iterator
LHashTable<T,Hash,EQ>::lower_bound(
				  LHashTable<T,Hash,EQ>::const_reference key
				 )
{
  Iterator ret = end();
  table_t::iterator slot = index(key);
  if(*slot) {
    bool dum;
    ret = chunkyLowerBound((*slot).chain(), list_->end(), key, dum);
  }
  return ret;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::ConstIterator
LHashTable<T,Hash,EQ>::lower_bound(
				  LHashTable<T,Hash,EQ>::const_reference key
				 ) const
{
  ConstIterator ret = end();
  table_t::iterator slot = index(key);
  if(*slot) {
    bool dum;
    ret =
      ConstIterator(chunkyLowerBound((*slot).chain(), list_->end(), key, dum));
  }
  return ret;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::Iterator
LHashTable<T,Hash,EQ>::upper_bound(
			    LHashTable<T,Hash,EQ>::const_reference key
				 )
{
  Iterator ret = end();
  table_t::iterator slot = index(key);
  if(*slot) {
    ret = chunkyUpperBound((*slot).chain(), list_->end(), key);
  }
  return ret;
}

template <class T, class Hash, class EQ>
LHashTable<T,Hash,EQ>::ConstIterator
LHashTable<T,Hash,EQ>::upper_bound(
			    LHashTable<T,Hash,EQ>::const_reference key
				 ) const
{
  ConstIterator ret = end();
  table_t::iterator slot = index(key);
  if(*slot) {
    ret =
      ConstIterator(chunkyUpperBound((*slot).chain(), list_->end(), key));
  }
  return ret;
}

#if 0
template <class T, class Hash, class EQ>
pair<LHashTable<T,Hash,EQ>::ConstIterator,
     LHashTable<T,Hash,EQ>::ConstIterator> 
LHashTable<T,Hash,EQ>::equal_range(
				  LHashTable<T,Hash,EQ>::const_reference key
				 ) const
{
  table_t::const_iterator slot = index(key);
  list_t::iterator lower = list_->end();
  list_t::iterator upper = list_->end();
  bool hit;
  if(*slot) {
    lower = chunkyLowerBound((*slot).chain(), list_->end(), key, hit);
    upper = lower;
    if(hit)
      upper = chunkyUpperBound(lower,list_->end(), key);
  }
  return pair<ConstIterator,ConstIterator>(lower,upper);
}

template <class T, class Hash, class EQ>
pair<LHashTable<T,Hash,EQ>::Iterator,
     LHashTable<T,Hash,EQ>::Iterator> 
LHashTable<T,Hash,EQ>::equal_range(
				  LHashTable<T,Hash,EQ>::const_reference key
				 )
{
  table_t::iterator slot = index(key);
  list_t::iterator lower = list_->end();
  list_t::iterator upper = list_->end();
  bool hit;
  if(*slot) {
    lower = chunkyLowerBound((*slot).chain(), list_->end(), key, hit);
    upper = lower;
    if(hit)
      upper = chunkyUpperBound(lower,list_->end(), key);
  }
  return pair<Iterator,Iterator>(lower,upper);
}
#endif

template <class T, class Hash, class EQ>
bool
LHashTable<T,Hash,EQ>::insertMaybe(
		     LHashTable<T,Hash,EQ>::const_reference key,
		     Iterator& ret,
		     bool single /* only one == key, or multiple? */
				 )
{
  unsigned long hv = hash_(key);
  table_t::iterator idx = index(hv);
  if(! *idx) // This bucket has never been used. Do it easy.
  {
    (*idx).valid() = true;
    (*idx).chain() = list_->insert(list_->end(),node_t(key,hv));
    ret = Iterator((*idx).chain());
  }
  else // This bucket is in use. Be thoughtful.
  {
    bool hit;
    list_t::iterator it;
    it = chunkyLowerBound((*idx).chain(), list_->end(), key, hit);
    ret = Iterator(it);
    if(single && hit) // then we won't insert
    {
      return false;
    }
    else // either not found or multiple inserts okay
    {
      // tricky: inserting at lower bound, if we had hit, is okay because
      // even if the slot iterator points at this lower bound, we are
      // inserting an eq_ key. If we had no hit, then lower bound ==
      // upper bound, and it references the first node in another slot
      // (or end()). In either of those cases, we will have to adjust 
      // the iterator in that new slot after the insert, because a
      // property of slist iterators is that inserting just prior to 
      // the node they reference invalidates them.

      it = list_->insert(it,node_t(key,hv));
      ret = Iterator(it);

      // now do iterator fixup, if needed
      if(++it != list_->end()) {
	unsigned long nexthv = (*it).second;
	if(nexthv%capacity() != hv%capacity()) {
	  (*index(nexthv)).chain() = it;
	}			// if fixup needed
      }				// if not at end during fixup
    }				// if single && hit (else)
  }				// if bucket empty (else)
  return true;
}

template <class T, class Hash, class EQ>
void
LHashTable<T,Hash,EQ>::resize(LHashTable<T,Hash,EQ>::size_type n)
{
  if(n == 0 || n == capacity()) return; 	// do only if changed
  LHashTable<T,Hash,EQ> tmp(this->begin(),this->end(),
			    n,			// new size
			    hash_,eq_);
  swap(tmp);
  /** postconditions: new capacity() is n;
   ** every element is still here
   ** this == old_this
   **/
}

template <class T, class Hash, class EQ>
void
LHashTable<T,Hash,EQ>::swap(LHashTable<T,Hash,EQ>& t)
{
  ::swap(list_,t.list_);
  ::swap(table_,t.table_);
  ::swap(eq_,t.eq_);
  ::swap(hash_,t.hash_);
}

#if 0 /** used only during debugging: remove before release **/
template <class T, class Hash, class EQ> void
LHashTable<T,Hash,EQ>::idump() const {
  cout << "idump (" << hex << (void*)this << dec << ") " << size() << " item";
  if(size() != 1) cout << "s";
  cout << ". Capacity(): " << capacity();
  cout << ":" << endl;
  static char *sep = "\0\0";
  sep[0] = '\0';
  for(list_t::const_iterator bi = list_->begin(); bi != list_->end(); ++bi) {
    cout << sep << "[" << (*bi).first << "," << (((*bi).second)%capacity())
      << "]" << flush;
    sep[0] = ',';
    sep[1] = ' ';
  }
  cout << endl;
  table_t::const_iterator ti = table_->begin();
  size_t i = 0;
  for(ti = table_->begin();ti != table_->end();++ti)
  {
    if(*ti)
    {
      if(i < 10)
	cout << " ";
      if(i < 100)
	cout << " ";
      if(i < 1000)
	cout << " ";
      cout << i << ": ";
      list_t::const_iterator li = (*ti).chain();
      size_type hs = ((*li).second)%capacity();
      sep[0] = '\0';
      while(li != list_->end())// && ((*li).second)%capacity() == hs)
      {
	cout << sep;
	if(((*li).second)%capacity() != i)
	  cout << ">" << flush;
	cout << (*li).first;
	sep[0]= ';';
	sep[1]= ' ';
	if(((*li).second)%capacity() != i)
	  cout << "<" << flush;
	++li;
      }
      cout << endl;
    }
    ++i;
  }
}
#endif


#undef LISTSIZE
#undef VECSIZE

