//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// PLEASE DO NOT EDIT WITHOUT THE EXPLICIT CONSENT OF THE AUTHOR! \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\

package ilog.language.design.backend;

/**
 * @version     Last modified on Tue Nov 19 21:13:37 2002 by hak
 * @author      <a href="mailto:hak@ilog.fr">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; 2000-2002 <a href="http://www.ilog.fr/">ILOG, S.A.</a>
 */

import ilog.language.util.ToIntMap;
import ilog.language.tools.Misc;
import java.util.Iterator;

/**
 * This is the mother of all runtime representations for sets. There
 * are three subclasses:
 * <ul>
 * <li><a href="IntSet.html"><tt>IntSet</tt></a>
 * <li><a href="RealSet.html"><tt>RealSet</tt></a>
 * <li><a href="ObjectSet.html"><tt>ObjectSet</tt></a>
 * </ul>
 * which only differ in the type of their representation structures.
 */
abstract public class RuntimeSet implements RuntimeObject, IndexableContainer
{
  /**
   * This flag is set whenever this set has "holes".  A hole is a gap
   * in the indexing sequence.
   */
  protected boolean _hasHoles = false;

  /**
   * This is the maximum index. When the set has no holes, it is equal to
   * the size of the set. Otherwise, it is equal to the size of the set
   * at the time the first hole appeared + the number of insertions that
   * happened after that.
   */
  protected int _maxIndex = 0;

  /**
   * This flag is set whenever at least one hole has appeared in the indices.
   */
  protected boolean _isLocked = false;

  /**
   * Returns the underlying index map representing the set.
   */
  abstract ToIntMap map ();    

  /**
   * Returns <tt>true</tt> iff there are holes in the indexing of this set.
   */
  protected final boolean _hasHoles ()
    {
      return _hasHoles;
    }    

  /**
   * Sets the flag indicating that there are holes in the indexing to the
   * specified boolean.
   */
  protected final RuntimeSet _setHasHoles (boolean flag)
    {
      _hasHoles = flag;
      return this;
    }

  /**
   * Sets the max index to the specified int.
   */
  protected final RuntimeSet _setMaxIndex (int index)
    {
      _maxIndex = index;
      return this;
    }

  /**
   * Locks this set to prevent any further modification.
   */
  public final void lock ()
    {
      _isLocked = true;
    }

  /**
   * Unlocks this set to enable further modification.
   */
  public final void unlock ()
    {
      _isLocked = false;
    }

  /**
   * Returns <tt>true</tt> iff this set is locked.
   */
  public final boolean isLocked ()
    {
      return _isLocked;
    }

  /**
   * Returns the number of elements in this set.
   */
  public final int size ()
    {
      return map().size();
    }

  /**
   * Returns <tt>true</tt> iff this set in empty.
   */
  public final boolean isEmpty ()
    {
      return map().isEmpty();
    }

  /**
   * Returns a hash code for this set.
   */
  public final int hashCode ()
    {
      return size();
    }

  /**
   * Reassigns the indices of the elements of the set to eliminate holes in such a
   * way as to preserve the order of the original indices.
   *
   * @returns an array of int map entries in insertion order
   */
  protected final ToIntMap.Entry[] _resetIndices ()
    {
      ToIntMap.Entry[] entries = new ToIntMap.Entry[size()];

      int index = 0;
      for (Iterator i = map().iterator(); i.hasNext();)
        entries[index++] = (ToIntMap.Entry)i.next();

      //System.err.print("Sorting "+index+" elements...");
      //long time = System.currentTimeMillis();
      Misc.sort(entries);
      //System.err.println(" in "+(System.currentTimeMillis()-time)+" ms");

      for (; index-->0;)
        entries[index].setValue(index);

      _hasHoles = false;
      _maxIndex = size();
      return entries;
    }

  /**
   * Returns <tt>true</tt> when this set is equal (as a set) to the specified one,
   * with a side-effect on the specified array of ints that will contain the index
   * permutation when the sets are found to be equal.
   */
  abstract public boolean equals (Object object, int[] permutation);

  /**
   * Returns the first element of this set as an int. If there is no
   * such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public int firstInt () throws NoSuchElementException;
  /**
   * Returns the first element of this set as a double. If there is no
   * such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public double firstReal () throws NoSuchElementException;
  /**
   * Returns the first element of this set as an object. If there is no
   * such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public Object firstObject () throws NoSuchElementException;

  /**
   * Returns the last element of this set as an int. If there is no
   * such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public int lastInt () throws NoSuchElementException;
  /**
   * Returns the last element of this set as a double. If there is no
   * such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public double lastReal () throws NoSuchElementException;
  /**
   * Returns the last element of this set as an object. If there is no
   * such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public Object lastObject () throws NoSuchElementException;

  /**
   * Returns the position of given int if it is an element of this set.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public int ord (int element) throws NoSuchElementException;
  /**
   * Returns the position of given double if it is an element of this set.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public int ord (double element) throws NoSuchElementException;
  /**
   * Returns the position of given object if it is an element of this set.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public int ord (Object element) throws NoSuchElementException;

  /**
   * Returns the element following the given one in this set, as an int.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public int next (int element) throws NoSuchElementException;
  /**
   * Returns the element following the given one in this set, as a double.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public double next (double element) throws NoSuchElementException;
  /**
   * Returns the element following the given one in this set, as an object.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public Object next (Object element) throws NoSuchElementException;

  /**
   * Returns the element preceding the given one in this set, as an int.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public int prev (int element) throws NoSuchElementException;
  /**
   * Returns the element preceding the given one in this set, as a double.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public double prev (double element) throws NoSuchElementException;
  /**
   * Returns the element preceding the given one in this set, as an object.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public Object prev (Object element) throws NoSuchElementException;

  /**
   * Returns the element following the given one in this set, as an int,
   * wrapping back to the beginning if necessary.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public int nextc (int element) throws NoSuchElementException;
  /**
   * Returns the element following the given one in this set, as a double,
   * wrapping back to the beginning if necessary.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public double nextc (double element) throws NoSuchElementException;
  /**
   * Returns the element following the given one in this set, as an object,
   * wrapping back to the beginning if necessary.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public Object nextc (Object element) throws NoSuchElementException;

  /**
   * Returns the element preceding the given one in this set, as an int,
   * wrapping to last element if necessary.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public int prevc (int element) throws NoSuchElementException;
  /**
   * Returns the element preceding the given one in this set, as a double,
   * wrapping to last element if necessary.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public double prevc (double element) throws NoSuchElementException;
  /**
   * Returns the element preceding the given one in this set, as an object,
   * wrapping to last element if necessary.
   * If there is no such element, throws a <tt>NoSuchElementException</tt>.
   */
  abstract public Object prevc (Object element) throws NoSuchElementException;

  /**
   * Returns a copy of this set.
   */
  abstract public RuntimeSet copy ();

  /**
   * Returns <tt>true</tt> iff the specified set is a subset of this set.
   */
  abstract public boolean contains (RuntimeSet set);

  abstract protected RuntimeSet _add (int element);
  abstract protected RuntimeSet _add (double element);
  abstract protected RuntimeSet _add (Object element);

  abstract protected RuntimeSet _remove (int element);
  abstract protected RuntimeSet _remove (double element);
  abstract protected RuntimeSet _remove (Object element);

  /**
   * If this set is not locked, adds the specified element to this set if it does not
   * already belong to this set, and returns this set. If this set is locked, a
   * <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeSet add (int element) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _add(element);
    }

  /**
   * If this set is not locked, adds the specified element to this set if it does not
   * already belong to this set, and returns this set. If this set is locked, a
   * <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeSet add (double element) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _add(element);
    }

  /**
   * If this set is not locked, adds the specified element to this set if it does not
   * already belong to this set, and returns this set. If this set is locked, a
   * <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeSet add (Object element) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _add(element);
    }

  /**
   * If this set is not locked, removes the specified element from this set and returns
   * this set. If this set is locked, a <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeSet remove (int element) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _remove(element);
    }

  /**
   * If this set is not locked, removes the specified element from this set and returns
   * this set. If this set is locked, a <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeSet remove (double element) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _remove(element);
    }

  /**
   * If this set is not locked, removes the specified element from this set and returns
   * this set. If this set is locked, a <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeSet remove (Object element) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _remove(element);
    }

  abstract protected RuntimeSet _union (RuntimeSet set);
  abstract protected RuntimeSet _intersection (RuntimeSet set);
  abstract protected RuntimeSet _minus (RuntimeSet set);
  abstract protected RuntimeSet _exclusion (RuntimeSet set);

  /**
   * Returns this set modified to contain the union of this and the specified set.
   * If this set is locked, a <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeSet union (RuntimeSet set) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _union(set);
    }

  /**
   * Returns this set modified to contain the intersection of this and the specified
   * set. If this set is locked, a <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeSet intersection (RuntimeSet set) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _intersection(set);
    }

  /**
   * Returns this set modified to contain the set difference of this and the specified
   * set. If this set is locked, a <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeSet minus (RuntimeSet set) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _minus(set);
    }

  /**
   * Returns this set modified to contain the symmetric difference of this and the
   * specified set. If this set is locked, a <tt>LockViolationException</tt> is
   * thrown.
   */
  public final RuntimeSet exclusion (RuntimeSet set) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _exclusion(set);
    }

  /**
   * Returns a new set equal to the union of the two specified sets.
   */
  public static final RuntimeSet union (RuntimeSet set1, RuntimeSet set2)
    {
      return set1.copy()._union(set2);
    }

  /**
   * Returns a new set equal to the intersection of the two specified sets.
   */
  public static final RuntimeSet intersection (RuntimeSet set1, RuntimeSet set2)
    {
      return set1.copy()._intersection(set2);
    }

  /**
   * Returns a new set equal to the set difference of the two specified sets.
   */
  public static final RuntimeSet minus (RuntimeSet set1, RuntimeSet set2)
    {
      return set1.copy()._minus(set2);
    }

  /**
   * Returns a new set equal to the symmetric difference of the two specified sets.
   */
  public static final RuntimeSet exclusion (RuntimeSet set1, RuntimeSet set2)
    {
      return set1.copy()._exclusion(set2);
    }
}
