//
// $Id: BitSet.h,v 1.26 2007/03/08 19:59:21 will_mason Exp $
//
// vi: set ft=objc:

/*
 * ObjectiveLib - a library of containers and algorithms for Objective-C
 *
 * Copyright (c) 2004-2007
 * Will Mason
 *
 * Portions:
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Copyright (c) 1996,1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1997
 * Moscow Center for SPARC Technology
 *
 * Copyright (c) 1999 
 * Boris Fomitchev
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * You may contact the author at will_mason@users.sourceforge.net.
 */

#if !defined(BITSET_OL_GUARD)
#define BITSET_OL_GUARD

#include <ObjectiveLib/ObjectBase.h>
#if defined(OL_HAVE_INTTYPES_H)
#include <inttypes.h>
#else
#include <stdint.h>
#endif

/**
 * @class OLBitSet BitSet.h ObjectiveLib/BitSet.h
 *
 * A set of bits. A bit set is a collection of bits the size of which cannot
 * be changed. Operations typical for bits are provided, which include flipping,
 * testing and performing logical operations on them. A bit set is very similar
 * to a @ref OLBoolVector "boolean vector" except that it does not pretend to
 * be a true @ref Containers "container". It does not provide iterators, and
 * therefore cannot be used with @ref Algorithms "generic algorithms".
 *
 * @sa OLBoolVector
 *
 * @ingroup Containers
 */
@interface OLBitSet :
#if defined(OL_NO_OPENSTEP)
    Object <OLStreamable>
#else
    NSObject <OLStreamable, NSCopying, NSCoding>
#endif
{
@protected
    /**
     * The allocated memory for the bit set
     */
    uint32_t*       words;

    /**
     * The number of bits
     */
    unsigned        numberOfBits;

    /**
     * The number of words in the allocated range
     */
    unsigned        numberOfWords;
}

/**
 * Create and return a new bit set. The new bit set is initialized with the
 * contents of @a bitSet.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param bitSet the bit set with which to initialize the new one
 * @return a new bit set
 */
+ (id) bitSetWithBitSet: (OLBitSet*)bitSet;

/**
 * Create and return a new bit set. The new bit is allocated to contain
 * @a bits bits, and initially all bits are set to zero.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param bits the number of bits that the new set should be able to address
 * @return a new bit set
 */
+ (id) bitSetWithSetSize: (unsigned)bits;

/**
 * Create and return a new bit set. The new bit set is initialized with the
 * contents of @a str. Working from left to right starting at the position
 * @a pos in the string, each character is checked. For every index in the
 * bit set starting at zero, if the string's character is '1', then the bit
 * at that index is set, otherwise it remains unset. For example, the string
 * "0101" will produce a bit set of size four where the second and fourth
 * indexes are set and first and third are unset.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param str the string with which to initialize the new bit set
 * @param pos the position in the string from which to start processing
 * @param count the number of positions in the string that will be considered
 * @return a new bit set
 */
+ (id) bitSetWithString: (const char*)str position: (unsigned)pos count: (unsigned)count;

/**
 * Create and return a new bit set. The new bit set is initialized using
 * a <tt>uint32_t</tt> value. That is, the size of the bit set will be 32 bits
 * and all the bits set in @a val will be set in the bit set.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param val the <tt>uint32_t</tt> with which to initialize the bit set
 * @return a new bit set
 */
+ (id) bitSetWithValue: (uint32_t)val;

/**
 * @name Initializers and Deallocators
 */
/* @{ */
/**
 * Initialize the bit set. All of the bits are copied from @a bitSet.
 *
 * @param bitSet the bit set with which to initialize this one
 * @return a reference to this bit set
 */
- (id) initWithBitSet: (OLBitSet*)bitSet;

#if !defined(OL_NO_OPENSTEP)
/**
 * Initialize the bit set. Creates a new bit set from an archive and returns it.
 *
 * @post The bit set returned will be identical to the bit set saved to the archive
 * using the #encodeWithCoder: message.
 *
 * @param decoder the coder which will decode the archived bit set
 * @return a reference to this bit set
 */
- (id) initWithCoder: (NSCoder*)decoder;
#endif

- (id) initWithObjectInStream: (OLObjectInStream*)stream;

/**
 * Initialize the bit set. Enough memory is allocated to account for @a bits bits,
 * and initially all bits are set to zero.
 *
 * @param bits the number of bits that this set should be able to address
 * @return a reference to this bit set
 */
- (id) initWithSetSize: (unsigned)bits;

/**
 * Initialize the bit set. A special string is used to initialize the bit set.
 * Working from left to right starting at the position @a pos in the string,
 * each character is checked. For every index in the bit set starting at zero,
 * if the string's character is '1', then the bit at that index is set, otherwise
 * it remains unset. For example, the string "0101" will produce a bit set of size four
 * where the second and fourth indexes are set and first and third are unset.
 *
 * @param str the string with which to initialize the bit set
 * @param pos the position in the string from which to start processing
 * @param count the number of positions in the string that will be considered
 * @return a reference to this bit set
 */
- (id) initWithString: (const char*)str position: (unsigned)pos count: (unsigned)count;

/**
 * Initialize the bit set. The bit set is initialized using a <tt>uint32_t</tt>
 * value. That is, the size of the bit set will be the size of a <tt>uint32_t</tt>
 * (that's 32 bits, if you're a little slow), and all the bits set in @a val
 * will be set in the bit set.
 *
 * @param val the <tt>uint32_t</tt> with which to initialize the bit set
 * @return a reference to this bit set
 */
- (id) initWithValue: (uint32_t)val;

/**
 * Finalize the bit set and deallocate any allocated memory.
 */
#if defined(OL_NO_OPENSTEP)
- (id) free;
#else
- (void) dealloc;
#endif
/* @} */

/**
 * Test whether any bits are set.
 *
 * @return YES if any bits are set, NO otherwise
 */
- (BOOL) any;

/**
 * Return a flipped copy of this bit set. A bit set is created with the same size
 * as this one, and all bits are the opposite of the corresponding bits in this
 * bit set.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @return a bit set with all bits flipped relative to this one
 */
- (OLBitSet*) bitSetFlipped;

/**
 * Return a left-shifted copy of this bit set. A bit set is created with the
 * same size as this one, and the bits are set as if all the bits in this set
 * had been shifted to the left by @a count. The @a count most significant
 * bits in the returned set will be set to zero.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param count the number of bits to shift to the left
 * @return a copy of this bit set that is shifted to the left by @a count
 * relative to this one
 */
- (OLBitSet*) bitSetShiftedLeft: (unsigned)count;

/**
 * Return a right-shifted copy of this bit set. A bit set is created with the
 * same size as this one, and the bits are set as if all the bits in this set
 * had been shifted to the right by @a count. The @a count least significant
 * bits in the returned set will be set to zero.
 *
 * @note If OpenStep is present the returned object will be autoreleased
 * before being returned.
 *
 * @param count the number of bits to shift to the right
 * @return a copy of this bit set that is shifted to the right by @a count
 * relative to this one
 */
- (OLBitSet*) bitSetShiftedRight: (unsigned)count;

#if defined(OL_NO_OPENSTEP)
/**
 * Make a copy of this bit set.
 *
 * @return the copy
 */
- (id) copy;
#else
/**
 * Make a copy of this bit set allocating memory from the given zone.
 *
 * @param zone the zone from which to allocate memory
 * @return the copy
 */
- (id) copyWithZone: (NSZone*)zone;
#endif

/**
 * Return the number of bits set. Any bit in the bit set is that is not zero
 * is added to the total count which is returned.
 *
 * @return the number of bits set
 */
- (unsigned) count;

#if !defined(OL_NO_OPENSTEP)
/**
 * Encode the bit set. The bit set is saved to an archive using @a encoder. The bit
 * set will be retrieved from the archive using the initializer #initWithCoder:.
 *
 * @param encoder the coder which will save the bit set to the archive
 */
- (void) encodeWithCoder: (NSCoder*)encoder;
#endif

/**
 * Flip all the bits. The bit set is tranformed into a bit set that is the logical
 * opposite of this one. All bits that were zero will be one and vice versa.
 */
- (void) flip;

/**
 * Flip a bit. The bit at index @a pos is flipped. That is, if it was one it will
 * be zero and vice versa.
 *
 * @note The range of the bit set is not checked prior to performing this operation.
 * If an index is given that is greater than or equal to the size of this bit
 * set, then the results are undefined.
 *
 * @param pos the bit position to flip
 */
- (void) flip: (unsigned)pos;

/**
 * Return whether this bit set is equal to another one. The bit sets are considered
 * equal if they have the same size and if they share the same set bits.
 *
 * @param object the object to test
 * @return YES if @a object is equal to this bit set, NO otherwise
 */
- (BOOL) isEqual: (id)object;

/**
 * Perform a logical "and" operation. If bits in both sets are set at the same
 * position, then the bit in this set will remain set, otherwise it will be unset.
 * The two bit sets do not have to be the same size. The number of bits used in
 * the operation is the smaller of the sizes of the two bit sets. All other bits
 * are ignored.
 *
 * @param right the bit set with which to "and" this one
 */
- (void) logicalAnd: (OLBitSet*)right;

/**
 * Perform a logical "or" operation. If either bit in both sets is set at the same
 * position, then the bit in this set will be set, otherwise it will be unset.
 * The two bit sets do not have to be the same size. The number of bits used in
 * the operation is the smaller of the sizes of the two bit sets. All other bits
 * are ignored.
 *
 * @param right the bit set with which to "and" this one
 */
- (void) logicalOr: (OLBitSet*)right;

/**
 * Perform a logical "xor" operation. If either bit in both sets is set at the same
 * position but not both bits, then the bit in this set will be set, otherwise it will be unset.
 * The two bit sets do not have to be the same size. The number of bits used in
 * the operation is the smaller of the sizes of the two bit sets. All other bits
 * are ignored.
 *
 * @param right the bit set with which to "and" this one
 */
- (void) logicalXor: (OLBitSet*)right;

/**
 * Test whether no bit is set.
 *
 * @return YES if no bit is set, NO otherwise
 */
- (BOOL) none;

/**
 * Set every bit to zero.
 */
- (void) reset;

/**
 * Set a bit to zero. The bit at index @a pos will be set to zero.
 *
 * @note The range of the bit set is not checked prior to performing this operation.
 * If an index is given that is greater than or equal to the size of this bit
 * set, then the results are undefined.
 *
 * @param pos the bit position to reset
 */
- (void) reset: (unsigned)pos;

/**
 * Set every bit to one.
 */
- (void) set;

/**
 * Set a bit to one. The bit at index @a pos will be set to one.
 *
 * @note The range of the bit set is not checked prior to performing this operation.
 * If an index is given that is greater than or equal to the size of this bit
 * set, then the results are undefined.
 *
 * @param pos the bit position to set
 */
- (void) set: (unsigned)pos;

/**
 * Shift the bits to the left. The bits will be shifted to the left by @a count
 * positions. The most significant @a count bits will be set to zero.
 *
 * @param count the number of positions by which to shift the bits
 */
- (void) shiftLeft: (unsigned)count;

/**
 * Shift the bits to the right. The bits will be shifted to the right by @a count
 * positions. The least significant @a count bits will be set to zero.
 *
 * @param count the number of positions by which to shift the bits
 */
- (void) shiftRight: (unsigned)count;

/**
 * Return the number of bits in this set.
 *
 * @return the number of bits
 */
- (unsigned) size;

/**
 * Test a bit. The bit at index @a pos is tested.
 *
 * @note The range of the bit set is not checked prior to performing this operation.
 * If an index is given that is greater than or equal to the size of this bit
 * set, then the results are undefined.
 *
 * @param pos the bit position to reset
 */
- (BOOL) test: (unsigned)pos;

/**
 * Return a string describing this bit set. A null-terminated string is returned
 * in which each character is set according to the corresponding bit in this
 * bit set. If a bit in the bit set is one, then the string has the character
 * '1', otherwise the string's character is '0'. The string returned can be used
 * as an argument to the message #initWithString:position:count: to create
 * a copy of this bit set.
 *
 * @sa #initWithString:position:count:
 *
 * @return a string describing this bit set
 */
- (char*) toString;

- (void) writeSelfToStream: (OLObjectOutStream*)stream;

@end

#endif
