/* -*- 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): 
 */

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

//
// Resize the bitset to sizeInBits. All the bits present in the bitset will be lost.
// The bitset will be unitialized. A call to clear() or set() is needed after sizeTo() !!!
//
void FastBitSet::sizeTo(Pool& pool, Uint32 sizeInBits)
{
	PR_ASSERT(sizeInBits);

	Uint32 newSizeInWords = getNumberOfWords(sizeInBits);
	if (newSizeInWords > sizeInWords) {
		sizeInWords = newSizeInWords;
		bits = new(pool) Uint32[newSizeInWords];
	}
#ifdef DEBUG_LOG
	this->sizeInBits = sizeInBits;
#endif /* DEBUG_LOG */
}


//
// Set the bits (in this bitset) that are defined in x and return true if this bitset changed.
//
bool FastBitSet::set(const FastBitSet& x)
{
	PR_ASSERT(sizeInWords == x.sizeInWords);

	bool differ = false;
	Uint32* dst = bits;
	Uint32* src = x.bits;
	Int32 count = sizeInWords - 1;

	do {
		Uint32 oldWord = dst[count];
		Uint32 newWord = oldWord | src[count];
		differ |= (newWord != oldWord);
		dst[count] = newWord;
	} while(--count >= 0);

	return differ;
}

//
// Arithmetic operators.
//

#define FastBitSetOperation(x, op)														\
	PR_ASSERT(sizeInWords && sizeInWords == x.sizeInWords);								\
	Uint32* dst = bits;																	\
	Uint32* src = x.bits;																\
	Int32 count = sizeInWords < x.sizeInWords ? sizeInWords - 1 : x.sizeInWords - 1;	\
	do {																				\
		dst[count] op src[count];														\
	} while(--count >= 0);


FastBitSet& FastBitSet::operator |= (const FastBitSet& x)
{
	FastBitSetOperation(x, |=);
	return *this;
}

FastBitSet& FastBitSet::operator &= (const FastBitSet& x)
{
	FastBitSetOperation(x, &=);
	return *this;
}
 
FastBitSet& FastBitSet::operator ^= (const FastBitSet& x)
{
	FastBitSetOperation(x, ^=);
	return *this;
}

FastBitSet& FastBitSet::operator -= (const FastBitSet& x)
{
	FastBitSetOperation(x, &= ~);
	return *this;
}

//
// Copy operator.
//
FastBitSet& FastBitSet::operator = (const FastBitSet& x)
{
	FastBitSetOperation(x, = );
	return *this;
}

#undef FastBitSetOperation

//
// Comparison operator.
//
bool operator == (const FastBitSet& x, const FastBitSet& y)
{
	PR_ASSERT(x.sizeInWords == y.sizeInWords);

	Uint32* srcX = x.bits;
	Uint32* srcY = y.bits;
	Uint32* limitX = &srcX[x.sizeInWords];

	while (srcX < limitX)
		if (*srcX++ != *srcY++)
			return false;

	return true;
}

//
// Return the next '1' bit just after pos or -1 if not found.
//
Int32 FastBitSet::nextOne(Int32 pos) const
{
	PR_ASSERT(sizeInWords);

	++pos; // Skip this bit.

	Uint32 wordOffset = getWordOffset(pos);
	if (wordOffset < sizeInWords) {
		Uint32* ptr = &bits[wordOffset];
		Uint8 bitOffset = getBitOffset(pos);
		Uint32 word = *ptr++ >> bitOffset;
		if (word != 0) {
			while ((word & 3) == 0) {
				bitOffset+=2;
				word >>= 2;
			}
			if ((word & 1) == 0)
				bitOffset++;

			return (wordOffset << 5) + bitOffset;
		}

		Uint32* limit = &bits[sizeInWords];
		for (++wordOffset; ptr < limit; ++wordOffset) {
			word = *ptr++;
			if (word != 0) {
				bitOffset = 0;
				while ((word & 3) == 0) {
					bitOffset+=2;
					word >>= 2;
				}
				if ((word & 1) == 0)
					bitOffset++;
			  
				return (wordOffset << 5) + bitOffset;
			}
		}
	}
	return -1;
}

//
// Return the '1' bit just before pos or -1 if not found.
//
Int32 FastBitSet::previousOne(Int32 pos) const
{
	PR_ASSERT(sizeInWords);

	--pos; // Skip this bit.

	Uint32 wordOffset = getWordOffset(pos);
	if (wordOffset < sizeInWords) {
		Uint32* ptr = &bits[wordOffset];
		Uint8 bitOffset = getBitOffset(pos);
		Uint32 word = *ptr-- << (31 - bitOffset);
		if (word != 0) {
			while ((word & 0x80000000) == 0) {
				bitOffset--;
				word <<= 1;
			}
			return (wordOffset << 5) + bitOffset;
		}

		Uint32* limit = bits;
		for (--wordOffset; ptr >= limit; --wordOffset) {
			word = *ptr--;
			if (word != 0) {
				bitOffset = 31;
				while ((word & 0x80000000) == 0) {
					bitOffset--;
					word <<= 1;
				}
			  
				return (wordOffset << 5) + bitOffset;
			}
		}
	}
	return -1;
}

//
// Return the next '0' bit just after pos or -1 if not found.
//
Int32 FastBitSet::nextZero(Int32 pos) const
{
	PR_ASSERT(sizeInWords);

	++pos; // Skip this bit.

	Uint32 wordOffset = getWordOffset(pos);
	if (wordOffset < sizeInWords) {
		Uint32* ptr = &bits[wordOffset];
		Uint8 bitOffset = getBitOffset(pos);
		Uint32 word = *ptr++ >> bitOffset;
		if (word != Uint32(~0)) {
			while (word & 1) {
				bitOffset++;
				word >>= 1;
			}
			return (wordOffset << 5) + bitOffset;
		}

		Uint32* limit = &bits[sizeInWords];
		for (++wordOffset; ptr < limit; ++wordOffset) {
			word = *ptr++;
			if (word != Uint32(~0)) {
				bitOffset = 0;
				while (word & 1) {
					bitOffset++;
					word >>= 1;
				}
				return (wordOffset << 5) + bitOffset;
			}
		}
	}
	return -1;
}

//
// Return the '0' bit just before pos or -1 if not found.
//
Int32 FastBitSet::previousZero(Int32 pos) const
{
	PR_ASSERT(sizeInWords);

	--pos; // Skip this bit.

	Uint32 wordOffset = getWordOffset(pos);
	if (wordOffset < sizeInWords) {
		Uint32* ptr = &bits[wordOffset];
		Uint8 bitOffset = getBitOffset(pos);
		Uint8 shift = (31 - bitOffset);
		Uint32 word = *ptr-- << shift;
		if (word != (Uint32(~0) << shift)) {
			while (word & 0x80000000) {
				bitOffset--;
				word <<= 1;
			}
			return (wordOffset << 5) + bitOffset;
		}

		Uint32* limit = bits;
		for (--wordOffset; ptr >= limit; --wordOffset) {
			word = *ptr--;
			if (word != Uint32(~0)) {
				bitOffset = 31;
				while (word & 0x80000000) {
					bitOffset--;
					word <<= 1;
				}
			  
				return (wordOffset << 5) + bitOffset;
			}
		}
	}
	return -1;
}

//
// Set the bits from position 'from' to position 'to'.
//
void FastBitSet::set(Uint32 from, Uint32 to)
{
	Uint32* ptr = &bits[getWordOffset(from)];
	Uint32* limit = &bits[getWordOffset(to)];
	Uint32 beginMask = ~((1 << getBitOffset(from)) - 1); // 11..(32 - from) times..1100..from times..00
	Uint32 endMask = (2 << getBitOffset(to)) - 1; // 00..(32 - to) times..0011..to times..11

	if (ptr == limit) {
		*ptr |= (beginMask & endMask);
	} else {
		register Uint32 val = ~0;
		*ptr++ |= beginMask;
		while (ptr < limit)
			*ptr++ = val;
		*limit |= endMask;
	}
}

#ifdef DEBUG_LOG
//
// Print the bitset.
//
void FastBitSet::printPretty(FILE* f) const
{
	PR_ASSERT(sizeInWords);

	fprintf(f, "[");
	for (PRUint32 bit = 0; bit < sizeInBits; bit++)
		fprintf(f, test(bit) ? "1" : "0");
	fprintf(f, "]\n");
}

//
// Print the difference between this bitset and the given biset.
//
void FastBitSet::printDiff(FILE *f, const FastBitSet& x) const
{
	fprintf(f, "[");
	for (Uint32 i = 0; i < sizeInBits; i++)
		if (test(i) != x.test(i))
			fprintf(f, test(i) ? "-" : "+");
		else
			fprintf(f, test(i) ? "1" : "0");
	fprintf(f, "]\n");
}

//
// Print all the '1' bits.
//
void FastBitSet::printPrettyOnes(FILE* f) const
{
	PR_ASSERT(sizeInWords);

	fprintf(f, "[ ");
	for (Int32 bit = firstOne(); !done(bit); bit = nextOne(bit))
		fprintf(f, "%d ", bit);
	fprintf(f, "]\n");
}

//
// Print all the '0' bits.
//
void FastBitSet::printPrettyZeros(FILE* f) const
{
	PR_ASSERT(sizeInWords);

	fprintf(f, "[ ");
	for (Int32 bit = firstZero(); !done(bit); bit = nextZero(bit))
		fprintf(f, "%d ", bit);
	fprintf(f, "]\n");
}
#endif /* DEBUG_LOG */
