/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (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.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */

#ifndef _FAST_BITSET_H_
#define _FAST_BITSET_H_

#include "Fundamentals.h"
#include "Pool.h"

#define FBS_ROUNDED_WORDS(x) (((x) + 31) >> 5)

class FastBitSet
{
private:
  Pool*     pool;
  PRUint32* words;
  PRUint32  nWords;
  PRUint32  nBits;
  bool      wordsAreMine;
  
  inline void reserve(PRUint32 n);
  inline void copy(PRUint32* src, PRUint32 size);

public:
  FastBitSet() : pool(NULL), words(0), nWords(0), nBits(0), wordsAreMine(false) {}
  FastBitSet(Pool& p) : pool(&p), words(0), nWords(0), nBits(0), wordsAreMine(false) {}
  FastBitSet(PRUint32 n) : pool(NULL), words(0), nWords(0), nBits(n), wordsAreMine(false) {sizeToAndClear(n);}
  FastBitSet(Pool& p, PRUint32 n) : pool(&p), words(0), nWords(0), nBits(n), wordsAreMine(false) {sizeToAndClear(n);}
  FastBitSet(PRUint32* ptr, PRUint32 n) : pool(NULL), words(ptr), nWords(FBS_ROUNDED_WORDS(n)), nBits(n), wordsAreMine(false) {}
  FastBitSet(const FastBitSet& x) : pool(x.pool), words(0), nWords(x.nWords), nBits(x.nBits), wordsAreMine(false) {sizeTo(x.nBits); copy(x.words, x.nWords);}
  ~FastBitSet() {if (words && wordsAreMine) delete words;}

  inline PRUint32* getWords() {return words;}

  inline void copyFrom(const FastBitSet& x) {copy(x.words, x.nWords);}
  inline void operator = (const FastBitSet& x) {sizeTo(x.nBits); copy(x.words, x.nWords);}
  inline FastBitSet& operator |= (const FastBitSet& x);
  inline FastBitSet& operator &= (const FastBitSet& x);
  inline FastBitSet& operator -= (const FastBitSet& x);
  inline friend bool operator == (const FastBitSet& x, const FastBitSet& y);
  inline friend bool operator != (const FastBitSet& x, const FastBitSet& y);

  inline void sizeTo(PRUint32 n) {PR_ASSERT(n); nBits = n; if (nWords < FBS_ROUNDED_WORDS(n)) reserve(n);}
  inline void sizeTo(Pool& p, PRUint32 n) {PR_ASSERT(n); pool = &p; nBits = n; if (nWords < FBS_ROUNDED_WORDS(n)) reserve(n);}
  inline void sizeToAndClear(PRUint32 n) {PR_ASSERT(n); sizeTo(n); clear();}
  inline void sizeToAndClear(Pool& p, PRUint32 n) {PR_ASSERT(n); sizeTo(p, n); clear();}

  inline bool empty();

  inline void clear();
  inline void clear(PRUint32 n);
  void clear(PRUint32 from, PRUint32 to);

  inline void set();
  inline void set(PRUint32 n);
  void set(PRUint32 from, PRUint32 to);

  bool setAndTest(const FastBitSet& x);

  inline bool test(PRUint32 n) const;

  inline PRInt32 firstOne() const {return nextOne(-1);}
  PRInt32 nextOne(PRInt32 pos) const;
  inline PRInt32 lastOne() const {return previousOne(nBits);}
  PRInt32 previousOne(PRInt32 pos) const;

  inline PRInt32 firstZero() const {return nextZero(-1);}
  PRInt32 nextZero(PRInt32 pos) const;

  PRUint32 countOnes();
  PRUint32 countOnes(PRUint32 from, PRUint32 to);
  inline PRUint32 countZeros() {return nBits - countOnes();}

#ifdef DEBUG_LOG
  void printPretty(FILE *f);
  void printDiff(FILE *f, const FastBitSet& x);
  void printPrettyOnes(FILE *f);
  void printPrettyZeros(FILE *f);
#endif
};

inline bool 
operator == (const FastBitSet& x, const FastBitSet& y)
{
  register PRUint32* xWords = x.words;
  register PRUint32* yWords = y.words;
  register int size = x.nWords-1;

  do if (xWords[size] != yWords[size]) return false; while (--size >= 0);
  return true;
}

inline bool
operator != (const FastBitSet& x, const FastBitSet& y)
{
	return !(x==y);
}

inline FastBitSet& FastBitSet::operator |= (const FastBitSet& x)
{
  register PRUint32* xWords = words;
  register PRUint32* yWords = x.words;
  register int size = nWords - 1;
  do xWords[size] |= yWords[size]; while (--size >= 0);
  return *this;
}

inline FastBitSet& FastBitSet::operator &= (const FastBitSet& x)
{
  register PRUint32* xWords = words;
  register PRUint32* yWords = x.words;
  register int size = nWords - 1;
  do xWords[size] &= yWords[size]; while (--size >= 0);
  return *this;
}

inline FastBitSet& FastBitSet::operator -= (const FastBitSet& x)
{
  register PRUint32* xWords = words;
  register PRUint32* yWords = x.words;
  register int size = nWords - 1;
  do xWords[size] &= ~yWords[size]; while (--size >= 0);
  return *this;
}

inline bool FastBitSet::test(PRUint32 n) const 
{
  register PRUint8 shift = n & 31;
  register PRUint32 mask = (1 << shift);
  if ((words[n >> 5] & mask) > 0)
	return true;
  else
	return false;
}

inline void FastBitSet::set(PRUint32 n)
{
  register PRUint8 shift = n & 31;
  register PRUint32 mask = (1 << shift);
  words[n >> 5] |= mask;
}

inline void FastBitSet::clear(PRUint32 n)
{
  register PRUint8 shift = n & 31;
  register PRUint32 mask = (1 << shift);
  mask = ~mask;
  words[n >> 5] &= mask;
}

inline bool FastBitSet::empty()
{
  register PRUint32* ptr = words;
  register int size = nWords-1;
  do 
	if (ptr[size] != 0)
	  return false;
  while (--size >= 0);

  return true;
}

inline void FastBitSet::set()
{
  register PRUint32* ptr = words;
  register PRUint32 val = ~0;
  register int size = nWords-1;
  do ptr[size] = val; while (--size >= 0);
}

inline void FastBitSet::clear()
{
  register PRUint32* ptr = words;
  register PRUint32 val = 0;
  register int size = nWords-1;
  do ptr[size] = val; while (--size >= 0);
}

inline void FastBitSet::reserve(PRUint32 n)
{
  if (words && wordsAreMine) delete words;
  nWords = FBS_ROUNDED_WORDS(n);
  if (pool != NULL)
	{
	  words = new(*pool) PRUint32[nWords];
	  wordsAreMine = false;
	}
  else
	{
	  words = new PRUint32[nWords];
	  wordsAreMine = true;
	}
}

inline void FastBitSet::
copy(PRUint32* src, PRUint32 size)
{
  PR_ASSERT(size <= nWords);
  register PRUint32* ptr = words;
  register int len = size-1;
  src = &src[len];
  do ptr[len] = *src--; while (--len >= 0);
  if (size < nWords)
	do ptr[size] = 0; while (++size < nWords);
}


#endif /* _FAST_BITSET_H_ */
