import java.io.*;
import java.util.Vector;
import java.util.Enumeration;

class TableEntry {
    /*@non_null@*/ public String name;
    public double value;
    
    public TableEntry (/*@non_null@*/ String n, double r) {
	name = n;
	value = r;
    }
    
    public String toString () {
	return "<" + name + ": " + value + ">";
    }
}

public class StringTable 
{
    // overview: StringTable is a set of <String, double> entries,
    //    where the String values are unique keys.  A typical StringTable
    //    is {<s0: d0>, <s1: d1>, ... }.
    //

    //@ghost public int numEntries ; // The number of entries in the table

    // Rep:
    private Vector entries ;

    // Rep Invariant:

    //@invariant entries.elementType == \type(TableEntry) 
    //@invariant entries.containsNull == false 
    //@invariant entries.elementCount == numEntries 
    //@invariant entries != null;

    // invariant the entries are sorted in order by entries[i].value
    //    for all 0 <= i < j < entries.size, entries[i].value <= entries[j].value
    // invariant the entries do not contain duplicate keys 
    //    for all 0 <= i < entries.size, 0 <= j < entries.size,
    //        entries[i].key.equals (entries[j].key) ==> i = j
    
    // Abstraction Function:
    //
    // AF(c) = { <c.entries[i].key: c.entries[i].value> | 0 <= i < entries.size }
    

    public StringTable () 
    //@ensures numEntries == 0;
    {
	entries = new Vector ();
	//@set entries.elementType = \type(TableEntry)
	//@set entries.containsNull = false
    } /* ESC/Java cannot establish invariant for empty Vector */ 
    
    /* 
    ** This method was used in PS2, but you do not need to implement it for PS3.
    */

    // Read a StringTable structure from a file formated as the output of toString
    public StringTable (InputStream instream) 
       // requires: The stream instream is a names file containing lines of the form
       //                   <name>: <rate>
       //           where the name is a string of non-space characters and the rate is
       //           a floating point number.
       // modifies: instream
       // effects:  Initializes this as a names table using the data from instream.
    {
	this ();
	
	try {
	    StructuredReader reader = new StructuredReader (instream);
	    while (true) {
		try {
		    String name = reader.readThroughAny (":");
		    reader.skipWhitespace ();
		    double value = reader.readDouble ();
		    reader.skipWhitespace ();

		    if (lookupEntry (name) != null) {
			System.err.println ("Names file contains duplicate entry: " + name);
		    } else {
			addName (name, value);
		    }
		} catch (EOFException e) {
		    break;
		} 
	    } 
	    
	    reader.close ();
	    // It would be better to propagate these exceptions to the caller,
	    // but for PS2, we avoid the use of exceptions.
	} catch (IOException e) {
	    System.err.println ("Error reading names file: " + e);
	} catch (NoNumberException e) {
	    System.err.println ("Error reading number in names file: " + e);
	} catch (NumberFormatException e) {
	    System.err.println ("Number format error reading names file: " + e);
	}
    }

    // To avoid duplicate code, we implement a helper method for looking 
    // up an entry in the table.  Note that it is declared as "private"
    // so is not visible to callers.  Because it is private, we can
    // return a mutable component of the rep here.  If callers were
    // allowed to call lookupEntry, that would expose the rep.

    private TableEntry lookupEntry (String name)
	// effects: If there is an entry in the table where entry.name.equals (name),
	//    returns that entry.  Otherwise, returns null.

    {
	for (Enumeration e = entries.elements (); e.hasMoreElements (); ) {
	    TableEntry entry = (TableEntry) e.nextElement ();
	    if (entry.name.equals (name)) {
		return entry;
	    }
	}
	
	return null;
    }

    public void addName (/*@non_null@*/ String name, double value)
       // requires: The parameter name is not null.  (This is what the
       //    ESC/Java /*@non_null@*/ annotation means.)
       // modifies: this   
       // effects:  If key matches the value of String in this, replaces the value associated
       //    with that key with value.  Otherwise, inserts <key, value> into this.
       //           e.g., if this_pre = {<s0, d0>, <s1, d1>, <s2, d2>}
       //                     and s0, s1 and s2 are all different from key
       //                 then this_post = {<s0, d0>, <s1, d1>, <s2, d2>, <key: double>}.
       //                 if this_pre = {<s0, d0>, <s1, d1>, <s2, d2>}
       //                     and s1 is the same string as key
       //                 then this_post = {<s0, d0>, <s1, value>, <s2, d2>}
       //                 
       //@modifies numEntries
       //@ensures  numEntries >= \old(numEntries);
    {
	TableEntry oldentry = lookupEntry (name);
	
	if (oldentry != null) {
	    // We can't just replace the value - that would break the rep invariant.
	    // Instead, we remove it and add the entry again.
	    entries.removeElement (oldentry);
	} 
	  
	// Linear search to find the location to insert (before the first
	// element whose value is greater than this one)

	int index;
	
	for (index = 0; index < entries.size (); index++) {
	    TableEntry entry = (TableEntry) entries.elementAt (index);
	    if (entry.value > value) {
		break;
	    }
	}

	entries.insertElementAt (new TableEntry (name, value), index);
    }
    
    public double getValue (String name)
    {
	TableEntry entry = lookupEntry (name);
	
	if (entry != null) {
	    return entry.value;
	} else {
	    return 0;
	}
    }
    
    public /*@non_null@*/ String getNthLowest (int index)
       //@requires index >= 0;
       //@requires index < numEntries;
    {
	TableEntry entry = (TableEntry) entries.elementAt (index);
	return entry.name;
    }
    
    public int size ()
       // EFFECTS: Returns the number of entries in this.
       //@ensures \result == numEntries;
    {
	return entries.size ();
    }
    
    public /*@non_null@*/ StringIterator keys () 
       // EFFECTS: Returns a StringIterator that will iterate through all the keys in this in 
       //    order from lowest to highest.
    {
	Vector allNames = new java.util.Vector ();
	//@set allNames.elementType = \type(String) ;
	//@set allNames.containsNull = false ;
	
	for (Enumeration e = entries.elements (); e.hasMoreElements (); ) {
	    allNames.addElement (((TableEntry) (e.nextElement ())).name);
	}
	
	return new StringIterator (allNames.elements ());
    } //@nowarn Invariant
    /* ESC/Java has a bug in checking the invariant here, but we know it is
       preserved since the code doesn't modify the representation at all. 
       The problem is ESC/Java cannot distinguish between a Vector that is
       used to represent a StringTable, and the local Vector variable that
       happens to have the same type.
    */
    
    public String toString ()
    {
	// Using the mutable StringBuffer type instead of the immutable String
	// type will save the trouble of making lots of new String objects.
	StringBuffer res = new StringBuffer ("{ ");
	boolean firstone = true;

	for (Enumeration e = entries.elements (); e.hasMoreElements (); firstone = false) {
	    TableEntry entry = (TableEntry) e.nextElement ();
	    if (!firstone) { res.append (", "); }
	    res.append (entry.toString ());
	}
	
	res.append (" }");
	return new String (res);
    }
}
