/*
 *
 * Copyright (c) 2007
 * Adrian Michel
 * http://www.tradery.com
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Adrian Michel makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
*/


package  com.tradery.collections;

import com.tradery.contract.Contract;

import java.util.Hashtable;
import java.util.Enumeration;

import java.io.IOException;
import java.io.OutputStream;


/**
 * IntegerSet class - models a set of unique integer values.
 * 
 * The hash value is the sum of all elements, which allows for fast comparison
 * in case the two hash values are different, the sets are different.
 * 
 * Note for syntax tree class: when the tree is brought to the Star Normal Form, the integer set used by model
 * will be implemented as lists, because all sets manipulated are disjoint in this case and so
 * the reunion operation can be modeled as a simple concatenation of two lists (this is to optimize
 * the DFA generation algorithm)
 */
public class IntegerSet extends SetAsHashtable
{
  private int hashCode;
  
  /**
   * Default constructor
   */
  public IntegerSet()
  {
    hashCode = 0;
  }

  /**
   * Constructor that sets the first integer value in the set
   * 
   * @param n      The integer value to be added to the set
   */
  public IntegerSet( int n )
  {
    this();
    put( new Integer( n ) );
    hashCode = n;
  }
  
  /**
   * Does the reunion of an integer set with the current integer sets.
   * All the new values are added to the current integer set, which holds
   * the reunion.
   * In case of duplicate values, only one of them is added to the new set.
   * 
   * @param set    The set to do the reunion with
   * @return A reference to the current integer set
   */
  public IntegerSet reunion( IntegerSet set )
  {
    if( Contract.REQUIRE )
      Contract.require( set != null );
    
    for( Enumeration e = set.elements() ; e.hasMoreElements() ;) 
    {
      Integer integer = (Integer)e.nextElement();
      
      // only add to the hashcode the new values
      if( put( integer ) == null )
        hashCode += integer.intValue();
    }
    return this;
  }

  /**
   * Indicates whether two sets have any common values.
   * 
   * @param set    The set to compare with
   * @return true if the two sets have common values, false otherwise
   */
  public boolean intersection( SetAsHashtable set )
  {
    if( Contract.REQUIRE )
      Contract.require( set != null );
    
    //          Set intersection = new Set();
    for( Enumeration e = set.elements(); e.hasMoreElements(); )
    {
      if( contains( e.nextElement() ) )
        return true;
    }
    return false;
  }

  /**
   * Adds a new value to the set. If it is a duplicate of an existing value,
   * it does not do anything
   * 
   * @param n      The value to be added to the set
   */
  public void add( int n )
  {
    // add to hashcode only if it is a new element
    if( put( new Integer( n ) ) == null )
      hashCode += n;
  }
  
  /**
   * Tests two sets for equality
   * 
   * @param set    The set to be tested for equality relative to the current one
   * @return true if the two sets are equal, false otherwise
   */
  public boolean equals( Object set )
  {
    if( set == null )
      return false;
    
    IntegerSet h = (IntegerSet)set;
    // if the hashcodes or sizes are not equal, then the objects are not equal
    if( hashCode() != set.hashCode() || h.size() != size() )
      return false;
    else
    {
      // compare all elements 
      for( Enumeration e = elements(); e.hasMoreElements() ; )
      {
        // TODO: this is not efficient, because contains does a search for each element
        if( !h.contains( e.nextElement() ) )
          return false;
      }
      return true;
    }
  }
  /**
   * Returns a hascode value. Used by collections
   * 
   * @return The hashcode value
   */
  public int hashCode() { return hashCode; }
  
  /**
   * Indicates if the current set contains a value
   * 
   * @param n      The value to be tested
   * @return true if the set contains the value, false otherwise
   */
  public boolean contains( int n ) { return contains( new Integer( n ) ); }

  /**
   * Dumps the contents of the set to an output stream
   * 
   * @param os     the output stream
   * @exception IOException
   */
  public void dump( OutputStream os )
    throws IOException
  {
    //TODO: replace all ModelWriter... with C1 statements
    os.write( toString().getBytes() );
  }

  /**
   * Creates a string representation of the IntegerSet object and it has the
   * following format: "{1,2,3}".
   * 
   * @return The string representation of the IntegerSet object
   */
  public String toString()
  {
    String str = new String();

    str += "{";

    boolean first = true;

    for( Enumeration e = elements(); e.hasMoreElements() ; )
    {
      if( !first )
        str += ",";
      else
        first = false;
      str += e.nextElement().toString();
    }
    str += "}";

    return str;
  }
}