// Collection of states -*- c++ -*-

/** @file States.C
 * Collection of states, represented with bit vectors
 */

/* Copyright © 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. */

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

# include "BitBuffer.h"

/** Collection of states */
class States
{
public:
  /** An encoded state */
  struct state {
    /** length of the encoded state, in bytes */
    size_t size;
    /** the deflated encoded state */
    word_t* buf;
  };
  /** Constructor */
  States () : myStates (0), myNumber (0), myAllocated (0) {}
private:
  /** Copy constructor */
  States (const class States& old);
  /** Assignemnt operator */
  class States& operator= (const class States& other);
public:
  /** Destructor */
  ~States () {
    for (unsigned i = myNumber; i--; )
      delete[] myStates[i].buf;
    operator delete (myStates);
  }

  /** Get the number of states in the set */
  size_t size () const { return myNumber; }

  /** Index the state array */
  const struct state& operator[] (unsigned i) {
    return myStates[i];
  }

  /** Clear the set */
  void clear () {
    for (unsigned i = myNumber; i--; )
      delete[] myStates[i].buf;
    memset (myStates, 0, myNumber * sizeof *myStates);
    myNumber = 0;
  }

  /** Search for a state in the set
   * @param buf	the deflated encoded state
   * @param len	length of the state in bytes
   * @return	the index of the state + 1, or 0 if not found
   */
  unsigned search (const void* buf,
		   size_t len) const {
    unsigned i = myNumber;
    while (i--)
      if (len == myStates[i].size &&
	  !memcmp (myStates[i].buf, buf, len))
	break;
    return i + 1;
  }

  /** Add a state to the set
   * @param buf	the deflated encoded state
   * @param len	length of the state in bytes
   * @return	true if the state was new
   */
  bool add (const void* buf,
	    size_t len) {
    if (search (buf, len))
      return false;
    if (!myAllocated) {
      assert (!myNumber && !myStates);
      myAllocated = 1;
      myStates = reinterpret_cast<struct state*>
	(operator new (sizeof *myStates));
    }
    else if (myNumber >= myAllocated) {
      assert (myNumber && myStates);
      struct state* states = reinterpret_cast<struct state*>
	(memcpy (operator new ((myAllocated << 1) * sizeof *states),
		 myStates, myAllocated * sizeof *states));
      delete[] myStates;
      myStates = states;
      myAllocated <<= 1;
    }

    struct state& s = myStates[myNumber++];
    s.size = len;
    memcpy (s.buf = new word_t[1 + (len - 1) / sizeof (word_t)], buf, len);
    return true;
  }

# if (!defined __sgi && !defined __DECCXX) || defined __GNUC__
 private:
# endif // MIPS CC and DEC cxx bug work-around
  struct state* myStates;
 private:
  /** Number of used entries in myStates */
  unsigned myNumber;
  /** Number of entries allocated for myStates */
  unsigned myAllocated;
};

#endif // STATES_H_
