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

package hlt.language.design.backend;

/**
 * @version     Last modified on Wed Jun 20 22:36:12 2012 by hak
 * @author      <a href="mailto:hak@acm.org">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; <a href="http://www.hassan-ait-kaci.net/">by the author</a>
 */

import hlt.language.design.types.Type;
import hlt.language.util.ToIntMap;
import hlt.language.tools.Misc;
import java.util.Iterator;

/**
 * This is the mother of all runtime collection representations. There
 * are three abstract subclasses:
 * <ul>
 * <li><a href="RuntimeSet.html"><tt>RuntimeSet</tt></a>
 * <li><a href="RuntimeBag.html"><tt>RuntimeBag</tt></a>
 * <li><a href="RuntimeList.html"><tt>RuntimeList</tt></a>
 * </ul>
 * which only differ in their respective algebraic properties. Namely,
 * <ul>
 * <li>associative, commutative, idempotent (<i>i.e.</i>, a set has
 * no order nor any repeated element)
 * <li>associative, commutative (<i>i.e.</i>, a bag has
 * no order, but may have repeated elements)
 * <li>associative (<i>i.e.</i>, a list has
 * order and may have repeated elements)
 * </ul>
 * 
 */
abstract public class RuntimeCollection implements RuntimeObject, IndexableContainer
{
  /**
   * This indicates what kind of collection this is. By default it is taken to be a set.
   */
  protected byte _kind = Type.SET;

  /**
   * This flag is collection whenever this collection has "holes".  A hole is a gap
   * in the indexing sequence.
   */
  protected boolean _hasHoles = false;

  /**
   * This is the maximum index. When the collection has no holes, it is equal to
   * the size of the collection. Otherwise, it is equal to the size of the collection
   * 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 collection.
   */
  abstract ToIntMap map ();    

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

  /**
   * Returns the first element of this collection 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 collection 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 collection 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 collection 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 collection 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 collection 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 collection.
   * 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 collection.
   * 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 collection.
   * 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 collection, 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 collection, 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 collection, 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 collection, 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 collection, 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 collection, 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 collection, 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 collection, 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 collection, 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 collection, 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 collection, 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 collection, 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 this collection modified to contain the union of this and the specified collection.
   * If this collection is locked, a <tt>LockViolationException</tt> is thrown.
   */
  public final RuntimeCollection union (RuntimeCollection collection) throws LockViolationException
    {
      if (_isLocked)
        throw new LockViolationException();

      return _union(collection);
    }

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

      return _intersection(collection);
    }

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

      return _minus(collection);
    }

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

      return _exclusion(collection);
    }

  /**
   * Returns a new collection equal to the union of the two specified collections.
   */
  public static final RuntimeCollection union (RuntimeCollection collection1, RuntimeCollection collection2)
    {
      return collection1.copy()._union(collection2);
    }

  /**
   * Returns a new collection equal to the intersection of the two specified collections.
   */
  public static final RuntimeCollection intersection (RuntimeCollection collection1, RuntimeCollection collection2)
    {
      return collection1.copy()._intersection(collection2);
    }

  /**
   * Returns a new collection equal to the collection difference of the two specified collections.
   */
  public static final RuntimeCollection minus (RuntimeCollection collection1, RuntimeCollection collection2)
    {
      return collection1.copy()._minus(collection2);
    }

  /**
   * Returns a new collection equal to the symmetric difference of the two specified collections.
   */
  public static final RuntimeCollection exclusion (RuntimeCollection collection1, RuntimeCollection collection2)
    {
      return collection1.copy()._exclusion(collection2);
    }
}
