// Binary digit (bit) vector -*- c++ -*-

#ifndef BITVECTOR_H_
# define BITVECTOR_H_
# ifdef __GNUC__
#  pragma interface
# endif // __GNUC__

# include <limits.h>
# include <string.h>
# include <assert.h>

/** @file BitVector.h
 * Bit vector
 */

/* Copyright © 2000-2002 Marko Mäkelä (msmakela@tcs.hut.fi).

   This file is part of MARIA, a reachability analyzer and model checker
   for high-level Petri nets.

   MARIA is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   MARIA 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
   General Public License for more details.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/** Binary digit (bit) vector */
class BitVector
{
public:
  /** machine word */
  typedef unsigned word_t;

  /** Constructor
   * @param size	number of elements in the vector
   */
  explicit BitVector (unsigned size = 0) :
    mySize (0), myAllocated (1), myBits (new word_t[1]) {
    *myBits = 0;
    setSize (size);
  }
  /** Copy constructor */
  explicit BitVector (const class BitVector& old) :
    mySize (old.mySize), myAllocated (getNumWords (old.mySize)), myBits (0) {
    memcpy (myBits = new word_t[myAllocated], old.myBits,
	    myAllocated * sizeof (word_t));
  }
private:
  /** Assignment operator */
  class BitVector& operator= (const class BitVector& old);
public:
  /** Destructor */
  ~BitVector () { delete[] myBits; }

  /** Determine the number of words the specified amount of bits occupy */
  static unsigned getNumWords (unsigned bits) {
    const unsigned bitsPerWord = CHAR_BIT * sizeof (word_t);
    return (bits + bitsPerWord - 1) / bitsPerWord;
  }

  /** Clear the vector */
  void clear () {
    memset (myBits, 0, myAllocated * sizeof *myBits);
  }

  /** Truncate the vector
   * @param size	number of elements to leave in the vector
   */
  void truncate (unsigned size = 0) {
    if (mySize <= size) return;
    mySize = size;
    const unsigned init = getNumWords (size);
    if (const unsigned rest = size % (CHAR_BIT * sizeof (word_t)))
      assert (init >= 1), myBits[init - 1] &= (1u << rest) - 1;
    memset (myBits, 0, (myAllocated - init) * sizeof *myBits);
  }

  /** Set the size of the vector */
  void setSize (unsigned size) {
    const unsigned numWords = getNumWords (size);
    if (myAllocated < numWords) {
      while (myAllocated < numWords)
	myAllocated <<= 1;
      word_t* bits = new word_t[myAllocated];
      const unsigned init = getNumWords (mySize);
      memcpy (bits, myBits, init * sizeof *myBits);
      memset (bits + init, 0, (myAllocated - init) * sizeof *myBits);
      delete[] myBits;
      myBits = bits;
    }
    mySize = size;
  }
  /** Determine the size of the vector */
  unsigned getSize () const { return mySize; }

  /** Read a binary digit
   * @param i	zero-based index of the element
   * @return	value of the ternary digit
   */
  bool operator[] (unsigned i) const {
    assert (i < mySize);
    return myBits[i / (CHAR_BIT * sizeof (word_t))] &
      (1u << (i % (CHAR_BIT * sizeof (word_t))));
  }

  /** Assign a binary digit
   * @param i	zero-based index of the element
   * @param b	new value of the digit
   */
  void assign (unsigned i, bool b) {
    assert (i < mySize);
    word_t& word = myBits[i / (CHAR_BIT * sizeof (word_t))];
    word_t bit = 1u << (i % (CHAR_BIT * sizeof (word_t)));
    if (b)
      word |= bit;
    else
      word &= ~bit;
  }

  /** Test and set: read and set a binary digit
   * @param i	zero-based index of the element
   * @return	whether the element already was set
   */
  bool tset (unsigned i) {
    assert (i < mySize);
    word_t& word = myBits[i / (CHAR_BIT * sizeof (word_t))];
    word_t bit = 1u << (i % (CHAR_BIT * sizeof (word_t)));
    if (word & bit)
      return true;
    word |= bit;
    return false;
  }

  /** Test and reset: read and reset a binary digit
   * @param i	zero-based index of the element
   * @return	whether the element already was set
   */
  bool treset (unsigned i) {
    assert (i < mySize);
    word_t& word = myBits[i / (CHAR_BIT * sizeof (word_t))];
    word_t bit = 1u << (i % (CHAR_BIT * sizeof (word_t)));
    if (!(word & bit))
      return false;
    word &= ~bit;
    return true;
  }

  /** Assign a binary digit, extending the vector with zeros if required
   * @param i	zero-based index of the element
   * @param b	new value of the digit
   */
  void extend (unsigned i, bool b) {
    if (i >= mySize) setSize (i + 1);
    assign (i, b);
  }

  /** Determine whether the whole vector is filled with zero bits */
  bool allClear () const {
    unsigned i = mySize / (CHAR_BIT * sizeof (word_t));
    if (mySize && (myBits[i] &
		   ((1u << (mySize % (CHAR_BIT * sizeof (word_t)))) - 1)))
      return false;
    while (i--) if (myBits[i]) return false;
    return true;
  }

  /** Determine whether the whole vector is filled with zero bits */
  bool allSet () const {
    unsigned i = mySize / (CHAR_BIT * sizeof (word_t));
    if (mySize && ~(myBits[i] |
		    ~((1u << (mySize % (CHAR_BIT * sizeof (word_t)))) - 1)))
      return false;
    while (i--) if (~myBits[i]) return false;
    return true;
  }

  /** Compute bit wise disjunction with the complement of a bit vector
   * @param other	bit vector whose complement is to be ORed with this
   */
  void orNot (const class BitVector& other) {
    assert (mySize == other.mySize);
    for (unsigned i = getNumWords (mySize); i--; )
      myBits[i] |= ~other.myBits[i];
  }

  /** Compute bit wise conjunction with the complement of a bit vector
   * @param other	bit vector whose complement is to be ANDed with this
   * @return		true if any bits were left '1' after the operation
   */
  bool andNot (const class BitVector& other) {
    assert (mySize == other.mySize);
    bool someset = false;
    for (unsigned i = getNumWords (mySize); i--; )
      if (myBits[i] &= ~other.myBits[i])
	someset = true;
    return someset;
  }

private:
  /** Number of elements in the vector */
  unsigned mySize;
  /** Size of the allocated vector in words */
  unsigned myAllocated;
  /** The vector */
  word_t* myBits;
};

#endif // BITVECTOR_H_
