Class library for SPECjbb
authorjzhou <jzhou>
Fri, 4 Feb 2011 01:36:33 +0000 (01:36 +0000)
committerjzhou <jzhou>
Fri, 4 Feb 2011 01:36:33 +0000 (01:36 +0000)
22 files changed:
Robust/src/ClassLibrary/MGC/Object.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/Thread.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/ArrayList.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/BigDecimal.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/BigInteger.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/BufferedReader.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/Calendar.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/Constructor.java [new file with mode: 0755]
Robust/src/ClassLibrary/MGC/gnu/Date.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/DecimalFormat.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/Exception.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/FileDescriptor.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/FilterOutputStream.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/GregorianCalendar.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/Locale.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/MPN.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/PrintStream.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/Reader.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/RuntimeException.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/SimpleTimeZone.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/Throwable.java [new file with mode: 0644]
Robust/src/ClassLibrary/MGC/gnu/TimeZone.java [new file with mode: 0644]

diff --git a/Robust/src/ClassLibrary/MGC/Object.java b/Robust/src/ClassLibrary/MGC/Object.java
new file mode 100644 (file)
index 0000000..0fd6c15
--- /dev/null
@@ -0,0 +1,38 @@
+public class Object {
+  public int cachedCode;   //first field has to be a primitive
+  public boolean cachedHash;
+
+  public native int nativehashCode();
+  private Object nextlockobject;
+  private Object prevlockobject;
+
+  // temporary extra unused int filed to align objects for Java
+  //int wkhqwemnbmwnb;
+
+  public int hashCode() {
+    if (!cachedHash) {
+      cachedCode=nativehashCode();
+      cachedHash=true;
+    }
+    return cachedCode;
+  }
+
+  /* DON'T USE THIS METHOD UNLESS NECESSARY */
+  /* WE WILL DEPRECATE IT AS SOON AS INSTANCEOF WORKS */
+  public native int getType();
+
+  public native int MonitorEnter();
+  public native int MonitorExit();
+
+  public String toString() {
+    return "Object"+hashCode();
+  }
+
+  public boolean equals(Object o) {
+    if (o==this)
+      return true;
+    return false;
+  }
+  
+  //public final native Class getClass();
+}
diff --git a/Robust/src/ClassLibrary/MGC/Thread.java b/Robust/src/ClassLibrary/MGC/Thread.java
new file mode 100644 (file)
index 0000000..50c5580
--- /dev/null
@@ -0,0 +1,27 @@
+public class Thread {
+  private boolean finished;
+
+  public void start() {
+    nativeCreate();
+  }
+
+  private static void staticStart(Thread t) {
+    t.run();
+  }
+
+  public static native void yield();
+
+  public void join() {
+    nativeJoin();
+  }
+
+  private native void nativeJoin();
+
+  public native static void sleep(long millis);
+
+  public void run() {
+  }
+
+  private native void nativeCreate();
+
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/ArrayList.java b/Robust/src/ClassLibrary/MGC/gnu/ArrayList.java
new file mode 100644 (file)
index 0000000..4d17b61
--- /dev/null
@@ -0,0 +1,606 @@
+/* ArrayList.java -- JDK1.2's answer to Vector; this is an array-backed
+   implementation of the List interface
+   Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+//package java.util;
+
+/*import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+*/
+/**
+ * An array-backed implementation of the List interface.  This implements
+ * all optional list operations, and permits null elements, so that it is
+ * better than Vector, which it replaces. Random access is roughly constant
+ * time, and iteration is roughly linear time, so it is nice and fast, with
+ * less overhead than a LinkedList.
+ * <p>
+ *
+ * Each list has a capacity, and as the array reaches that capacity it
+ * is automatically transferred to a larger array. You also have access to
+ * ensureCapacity and trimToSize to control the backing array's size, avoiding
+ * reallocation or wasted memory.
+ * <p>
+ *
+ * ArrayList is not synchronized, so if you need multi-threaded access,
+ * consider using:<br>
+ * <code>List l = Collections.synchronizedList(new ArrayList(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Jon A. Zeppieri
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see List
+ * @see LinkedList
+ * @see Vector
+ * @see Collections#synchronizedList(List)
+ * @see AbstractList
+ * @status updated to 1.4
+ */
+//public class ArrayList<E> extends AbstractList<E>
+//  implements List<E>, RandomAccess, Cloneable, Serializable
+public class ArrayList
+{
+  /**
+   * Compatible with JDK 1.2
+   */
+  private static final long serialVersionUID = 8683452581122892189L;
+
+  /**
+   * The default capacity for new ArrayLists.
+   */
+  private static final int DEFAULT_CAPACITY = 10;
+
+  /**
+   * The number of elements in this list.
+   * @serial the list size
+   */
+  private int size;
+
+  /**
+   * Where the data is stored.
+   */
+  //private transient E[] data;
+  private transient Object[] data;
+
+  /**
+   * Construct a new ArrayList with the supplied initial capacity.
+   *
+   * @param capacity initial capacity of this ArrayList
+   * @throws IllegalArgumentException if capacity is negative
+   */
+  public ArrayList(int capacity)
+  {
+    // Must explicitly check, to get correct exception.
+    if (capacity < 0)
+      throw new Error("Illegal Argument Exception")/*IllegalArgumentException()*/;
+    data = (Object/*E*/[]) new Object[capacity];
+  }
+
+  /**
+   * Construct a new ArrayList with the default capacity (16).
+   */
+  public ArrayList()
+  {
+    this(DEFAULT_CAPACITY);
+  }
+
+  /**
+   * Construct a new ArrayList, and initialize it with the elements
+   * in the supplied Collection. The initial capacity is 110% of the
+   * Collection's size.
+   *
+   * @param c the collection whose elements will initialize this list
+   * @throws NullPointerException if c is null
+   */
+  /*public ArrayList(Collection<? extends E> c)
+  {
+    this((int) (c.size() * 1.1f));
+    addAll(c);
+  }*/
+
+  /**
+   * Trims the capacity of this List to be equal to its size;
+   * a memory saver.
+   */
+  public void trimToSize()
+  {
+    // Not a structural change from the perspective of iterators on this list,
+    // so don't update modCount.
+    if (size != data.length)
+      {
+        Object/*E*/[] newData = /*(ObjectE[])*/ new Object[size];
+        System.arraycopy(data, 0, newData, 0, size);
+        data = newData;
+      }
+  }
+
+  /**
+   * Guarantees that this list will have at least enough capacity to
+   * hold minCapacity elements. This implementation will grow the list to
+   * max(current * 2, minCapacity) if (minCapacity &gt; current). The JCL says
+   * explictly that "this method increases its capacity to minCap", while
+   * the JDK 1.3 online docs specify that the list will grow to at least the
+   * size specified.
+   *
+   * @param minCapacity the minimum guaranteed capacity
+   */
+  public void ensureCapacity(int minCapacity)
+  {
+    int current = data.length;
+
+    if (minCapacity > current)
+      {
+        Object/*E*/[] newData = /*(E[])*/ new Object[Math.max(current * 2, minCapacity)];
+        System.arraycopy(data, 0, newData, 0, size);
+        data = newData;
+      }
+  }
+
+  /**
+   * Returns the number of elements in this list.
+   *
+   * @return the list size
+   */
+  public int size()
+  {
+    return size;
+  }
+
+  /**
+   * Checks if the list is empty.
+   *
+   * @return true if there are no elements
+   */
+  public boolean isEmpty()
+  {
+    return size == 0;
+  }
+
+  /**
+   * Returns true iff element is in this ArrayList.
+   *
+   * @param e the element whose inclusion in the List is being tested
+   * @return true if the list contains e
+   */
+  public boolean contains(Object e)
+  {
+    return indexOf(e) != -1;
+  }
+
+  /**
+   * Returns the lowest index at which element appears in this List, or
+   * -1 if it does not appear.
+   *
+   * @param e the element whose inclusion in the List is being tested
+   * @return the index where e was found
+   */
+  public int indexOf(Object e)
+  {
+    for (int i = 0; i < size; i++)
+      if (equals(e, data[i]))
+        return i;
+    return -1;
+  }
+
+  /**
+   * Returns the highest index at which element appears in this List, or
+   * -1 if it does not appear.
+   *
+   * @param e the element whose inclusion in the List is being tested
+   * @return the index where e was found
+   */
+  public int lastIndexOf(Object e)
+  {
+    for (int i = size - 1; i >= 0; i--)
+      if (equals(e, data[i]))
+        return i;
+    return -1;
+  }
+
+  /**
+   * Creates a shallow copy of this ArrayList (elements are not cloned).
+   *
+   * @return the cloned object
+   */
+  public Object clone()
+  {
+    ArrayList/*<E>*/ clone = null;
+    //try
+      {
+        //clone = (ArrayList<E>) super.clone();
+        clone = new ArrayList();
+        clone.data = /*(E[])*/ data.clone();
+      }
+    /*catch (CloneNotSupportedException e)
+      {
+        // Impossible to get here.
+      }*/
+    return clone;
+  }
+
+  /**
+   * Returns an Object array containing all of the elements in this ArrayList.
+   * The array is independent of this list.
+   *
+   * @return an array representation of this list
+   */
+  public Object[] toArray()
+  {
+    Object/*E*/[] array = /*(E[])*/ new Object[size];
+    System.arraycopy(data, 0, array, 0, size);
+    return array;
+  }
+
+  /**
+   * Returns an Array whose component type is the runtime component type of
+   * the passed-in Array.  The returned Array is populated with all of the
+   * elements in this ArrayList.  If the passed-in Array is not large enough
+   * to store all of the elements in this List, a new Array will be created
+   * and returned; if the passed-in Array is <i>larger</i> than the size
+   * of this List, then size() index will be set to null.
+   *
+   * @param a the passed-in Array
+   * @return an array representation of this list
+   * @throws ArrayStoreException if the runtime type of a does not allow
+   *         an element in this list
+   * @throws NullPointerException if a is null
+   */
+  /*public <T> T[] toArray(T[] a)
+  {
+    if (a.length < size)
+      a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
+    else if (a.length > size)
+      a[size] = null;
+    System.arraycopy(data, 0, a, 0, size);
+    return a;
+  }*/
+
+  /**
+   * Retrieves the element at the user-supplied index.
+   *
+   * @param index the index of the element we are fetching
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   */
+  public Object/*E*/ get(int index)
+  {
+    checkBoundExclusive(index);
+    return data[index];
+  }
+
+  /**
+   * Sets the element at the specified index.  The new element, e,
+   * can be an object of any type or null.
+   *
+   * @param index the index at which the element is being set
+   * @param e the element to be set
+   * @return the element previously at the specified index
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= 0
+   */
+  public Object/*E*/ set(int index, Object/*E*/ e)
+  {
+    checkBoundExclusive(index);
+    Object/*E*/ result = data[index];
+    data[index] = e;
+    return result;
+  }
+
+  /**
+   * Appends the supplied element to the end of this list.
+   * The element, e, can be an object of any type or null.
+   *
+   * @param e the element to be appended to this list
+   * @return true, the add will always succeed
+   */
+  public boolean add(Object/*E*/ e)
+  {
+    modCount++;
+    if (size == data.length)
+      ensureCapacity(size + 1);
+    data[size++] = e;
+    return true;
+  }
+
+  /**
+   * Adds the supplied element at the specified index, shifting all
+   * elements currently at that index or higher one to the right.
+   * The element, e, can be an object of any type or null.
+   *
+   * @param index the index at which the element is being added
+   * @param e the item being added
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+   */
+  public void add(int index, Object/*E*/ e)
+  {
+    checkBoundInclusive(index);
+    modCount++;
+    if (size == data.length)
+      ensureCapacity(size + 1);
+    if (index != size)
+      System.arraycopy(data, index, data, index + 1, size - index);
+    data[index] = e;
+    size++;
+  }
+
+  /**
+   * Removes the element at the user-supplied index.
+   *
+   * @param index the index of the element to be removed
+   * @return the removed Object
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+   */
+  public Object/*E*/ remove(int index)
+  {
+    checkBoundExclusive(index);
+    Object/*E*/ r = data[index];
+    modCount++;
+    if (index != --size)
+      System.arraycopy(data, index + 1, data, index, size - index);
+    // Aid for garbage collection by releasing this pointer.
+    data[size] = null;
+    return r;
+  }
+
+  /**
+   * Removes all elements from this List
+   */
+  public void clear()
+  {
+    if (size > 0)
+      {
+        modCount++;
+        // Allow for garbage collection.
+        Arrays.fill(data, 0, size, null);
+        size = 0;
+      }
+  }
+
+  /**
+   * Add each element in the supplied Collection to this List. It is undefined
+   * what happens if you modify the list while this is taking place; for
+   * example, if the collection contains this list.  c can contain objects
+   * of any type, as well as null values.
+   *
+   * @param c a Collection containing elements to be added to this List
+   * @return true if the list was modified, in other words c is not empty
+   * @throws NullPointerException if c is null
+   */
+  /*public boolean addAll(Collection<? extends E> c)
+  {
+    return addAll(size, c);
+  }*/
+
+  /**
+   * Add all elements in the supplied collection, inserting them beginning
+   * at the specified index.  c can contain objects of any type, as well
+   * as null values.
+   *
+   * @param index the index at which the elements will be inserted
+   * @param c the Collection containing the elements to be inserted
+   * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; 0
+   * @throws NullPointerException if c is null
+   */
+  /*public boolean addAll(int index, Collection<? extends E> c)
+  {
+    checkBoundInclusive(index);
+    Iterator<? extends E> itr = c.iterator();
+    int csize = c.size();
+
+    modCount++;
+    if (csize + size > data.length)
+      ensureCapacity(size + csize);
+    int end = index + csize;
+    if (size > 0 && index != size)
+      System.arraycopy(data, index, data, end, size - index);
+    size += csize;
+    for ( ; index < end; index++)
+      data[index] = itr.next();
+    return csize > 0;
+  }*/
+
+  /**
+   * Removes all elements in the half-open interval [fromIndex, toIndex).
+   * Does nothing when toIndex is equal to fromIndex.
+   *
+   * @param fromIndex the first index which will be removed
+   * @param toIndex one greater than the last index which will be removed
+   * @throws IndexOutOfBoundsException if fromIndex &gt; toIndex
+   */
+  protected void removeRange(int fromIndex, int toIndex)
+  {
+    int change = toIndex - fromIndex;
+    if (change > 0)
+      {
+        modCount++;
+        System.arraycopy(data, toIndex, data, fromIndex, size - toIndex);
+        size -= change;
+      }
+    else if (change < 0)
+      throw new Error("Index Out Of Bounds Exception")/*IndexOutOfBoundsException()*/;
+  }
+
+  /**
+   * Checks that the index is in the range of possible elements (inclusive).
+   *
+   * @param index the index to check
+   * @throws IndexOutOfBoundsException if index &gt; size
+   */
+  private void checkBoundInclusive(int index)
+  {
+    // Implementation note: we do not check for negative ranges here, since
+    // use of a negative index will cause an ArrayIndexOutOfBoundsException,
+    // a subclass of the required exception, with no effort on our part.
+    if (index > size)
+      raiseBoundsError(index);
+  }
+
+  /**
+   * Checks that the index is in the range of existing elements (exclusive).
+   *
+   * @param index the index to check
+   * @throws IndexOutOfBoundsException if index &gt;= size
+   */
+  private void checkBoundExclusive(int index)
+  {
+    // Implementation note: we do not check for negative ranges here, since
+    // use of a negative index will cause an ArrayIndexOutOfBoundsException,
+    // a subclass of the required exception, with no effort on our part.
+    if (index >= size)
+      raiseBoundsError(index);
+  }
+
+  /**
+   * Raise the ArrayIndexOfOutBoundsException.
+   *
+   * @param index the index of the access
+   * @throws IndexOutOfBoundsException unconditionally
+   */
+  private void raiseBoundsError(int index)
+  {
+    // Implementaion note: put in a separate method to make the JITs job easier
+    // (separate common from uncommon code at method boundaries when trivial to
+    // do so).
+    throw new Error/*IndexOutOfBoundsException*/("IndexOutOfBoundsException Index: " + index + ", Size: " + size);
+  }
+  
+  
+  /**
+   * Remove from this list all elements contained in the given collection.
+   * This is not public, due to Sun's API, but this performs in linear
+   * time while the default behavior of AbstractList would be quadratic.
+   *
+   * @param c the collection to filter out
+   * @return true if this list changed
+   * @throws NullPointerException if c is null
+   */
+  /*boolean removeAllInternal(Collection<?> c)
+  {
+    int i;
+    int j;
+    for (i = 0; i < size; i++)
+      if (c.contains(data[i]))
+        break;
+    if (i == size)
+      return false;
+
+    modCount++;
+    for (j = i++; i < size; i++)
+      if (! c.contains(data[i]))
+        data[j++] = data[i];
+    size -= i - j;
+    return true;
+  }*/
+
+  /**
+   * Retain in this vector only the elements contained in the given collection.
+   * This is not public, due to Sun's API, but this performs in linear
+   * time while the default behavior of AbstractList would be quadratic.
+   *
+   * @param c the collection to filter by
+   * @return true if this vector changed
+   * @throws NullPointerException if c is null
+   * @since 1.2
+   */
+  /*boolean retainAllInternal(Collection<?> c)
+  {
+    int i;
+    int j;
+    for (i = 0; i < size; i++)
+      if (! c.contains(data[i]))
+        break;
+    if (i == size)
+      return false;
+
+    modCount++;
+    for (j = i++; i < size; i++)
+      if (c.contains(data[i]))
+        data[j++] = data[i];
+    size -= i - j;
+    return true;
+  }*/
+
+  /**
+   * Serializes this object to the given stream.
+   *
+   * @param s the stream to write to
+   * @throws IOException if the underlying stream fails
+   * @serialData the size field (int), the length of the backing array
+   *             (int), followed by its elements (Objects) in proper order.
+   */
+  /*private void writeObject(ObjectOutputStream s) throws IOException
+  {
+    // The 'size' field.
+    s.defaultWriteObject();
+    // We serialize unused list entries to preserve capacity.
+    int len = data.length;
+    s.writeInt(len);
+    // it would be more efficient to just write "size" items,
+    // this need readObject read "size" items too.
+    for (int i = 0; i < size; i++)
+      s.writeObject(data[i]);
+  }*/
+
+  /**
+   * Deserializes this object from the given stream.
+   *
+   * @param s the stream to read from
+   * @throws ClassNotFoundException if the underlying stream fails
+   * @throws IOException if the underlying stream fails
+   * @serialData the size field (int), the length of the backing array
+   *             (int), followed by its elements (Objects) in proper order.
+   */
+  /*private void readObject(ObjectInputStream s)
+    throws IOException, ClassNotFoundException
+  {
+    // the `size' field.
+    s.defaultReadObject();
+    int capacity = s.readInt();
+    data = (E[]) new Object[capacity];
+    for (int i = 0; i < size; i++)
+      data[i] = (E) s.readObject();
+  }*/
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/BigDecimal.java b/Robust/src/ClassLibrary/MGC/gnu/BigDecimal.java
new file mode 100644 (file)
index 0000000..bc265e8
--- /dev/null
@@ -0,0 +1,1559 @@
+/* java.math.BigDecimal -- Arbitrary precision decimals.
+   Copyright (C) 1999, 2000, 2001, 2003, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+//package java.math;
+
+//import gnu.java.lang.CPStringBuilder;
+
+public class BigDecimal //extends Number implements Comparable<BigDecimal>
+{
+  private BigInteger intVal;
+  private int scale;
+  private int precision = 0;
+  private static final long serialVersionUID = 6108874887143696463L;
+
+  /**
+   * The constant zero as a BigDecimal with scale zero.
+   * @since 1.5
+   */
+  public static final BigDecimal ZERO = 
+    new BigDecimal (BigInteger.ZERO, 0);
+
+  /**
+   * The constant one as a BigDecimal with scale zero.
+   * @since 1.5
+   */
+  public static final BigDecimal ONE = 
+    new BigDecimal (BigInteger.ONE, 0);
+
+  /**
+   * The constant ten as a BigDecimal with scale zero.
+   * @since 1.5
+   */
+  public static final BigDecimal TEN = 
+    new BigDecimal (BigInteger.TEN, 0);
+
+  public static final int ROUND_UP = 0;
+  public static final int ROUND_DOWN = 1;
+  public static final int ROUND_CEILING = 2;
+  public static final int ROUND_FLOOR = 3;
+  public static final int ROUND_HALF_UP = 4;
+  public static final int ROUND_HALF_DOWN = 5;
+  public static final int ROUND_HALF_EVEN = 6;
+  public static final int ROUND_UNNECESSARY = 7;
+
+  /**
+   * Constructs a new BigDecimal whose unscaled value is val and whose
+   * scale is zero.
+   * @param val the value of the new BigDecimal
+   * @since 1.5
+   */
+  public BigDecimal (int val)
+  {
+    this.intVal = BigInteger.valueOf(val);
+    this.scale = 0;
+  }
+  
+  /**
+   * Constructs a BigDecimal using the BigDecimal(int) constructor and then
+   * rounds according to the MathContext.
+   * @param val the value for the initial (unrounded) BigDecimal
+   * @param mc the MathContext specifying the rounding
+   * @throws ArithmeticException if the result is inexact but the rounding type
+   * is RoundingMode.UNNECESSARY
+   * @since 1.5
+   */
+  /*public BigDecimal (int val, MathContext mc)
+  {
+    this (val);
+    if (mc.getPrecision() != 0)
+      {
+        BigDecimal result = this.round(mc);
+        this.intVal = result.intVal;
+        this.scale = result.scale;
+        this.precision = result.precision;
+      }    
+  }*/
+  
+  /**
+   * Constructs a new BigDecimal whose unscaled value is val and whose
+   * scale is zero.
+   * @param val the value of the new BigDecimal
+   */
+  public BigDecimal (long val)
+  {
+    this.intVal = BigInteger.valueOf(val);
+    this.scale = 0;
+  }
+  
+  /**
+   * Constructs a BigDecimal from the long in the same way as BigDecimal(long)
+   * and then rounds according to the MathContext.
+   * @param val the long from which we create the initial BigDecimal
+   * @param mc the MathContext that specifies the rounding behaviour
+   * @throws ArithmeticException if the result is inexact but the rounding type
+   * is RoundingMode.UNNECESSARY
+   * @since 1.5
+   */
+  /*public BigDecimal (long val, MathContext mc)
+  {
+    this(val);
+    if (mc.getPrecision() != 0)
+      {
+        BigDecimal result = this.round(mc);
+        this.intVal = result.intVal;
+        this.scale = result.scale;
+        this.precision = result.precision;
+      }    
+  }*/
+  
+  /**
+   * Constructs a BigDecimal whose value is given by num rounded according to 
+   * mc.  Since num is already a BigInteger, the rounding refers only to the 
+   * precision setting in mc, if mc.getPrecision() returns an int lower than
+   * the number of digits in num, then rounding is necessary.
+   * @param num the unscaledValue, before rounding
+   * @param mc the MathContext that specifies the precision
+   * @throws ArithmeticException if the result is inexact but the rounding type
+   * is RoundingMode.UNNECESSARY
+   * * @since 1.5
+   */
+  /*public BigDecimal (BigInteger num, MathContext mc)
+  {
+    this (num, 0);
+    if (mc.getPrecision() != 0)
+      {
+        BigDecimal result = this.round(mc);
+        this.intVal = result.intVal;
+        this.scale = result.scale;
+        this.precision = result.precision;
+      }
+  }*/
+  
+  /**
+   * Constructs a BigDecimal from the String val according to the same
+   * rules as the BigDecimal(String) constructor and then rounds 
+   * according to the MathContext mc.
+   * @param val the String from which we construct the initial BigDecimal
+   * @param mc the MathContext that specifies the rounding
+   * @throws ArithmeticException if the result is inexact but the rounding type
+   * is RoundingMode.UNNECESSARY   
+   * @since 1.5
+   */
+  /*public BigDecimal (String val, MathContext mc)
+  {
+    this (val);
+    if (mc.getPrecision() != 0)
+      {
+        BigDecimal result = this.round(mc);
+        this.intVal = result.intVal;
+        this.scale = result.scale;
+        this.precision = result.precision;
+      }
+  }*/
+  
+  /**
+   * Constructs a BigDecimal whose unscaled value is num and whose
+   * scale is zero.
+   * @param num the value of the new BigDecimal
+   */
+  public BigDecimal (BigInteger num) 
+  {
+    this (num, 0);
+  }
+
+  /**
+   * Constructs a BigDecimal whose unscaled value is num and whose
+   * scale is scale.
+   * @param num
+   * @param scale
+   */
+  public BigDecimal (BigInteger num, int scale)
+  {
+    this.intVal = num;
+    this.scale = scale;
+  }
+  
+  /**
+   * Constructs a BigDecimal using the BigDecimal(BigInteger, int) 
+   * constructor and then rounds according to the MathContext.
+   * @param num the unscaled value of the unrounded BigDecimal
+   * @param scale the scale of the unrounded BigDecimal
+   * @param mc the MathContext specifying the rounding
+   * @throws ArithmeticException if the result is inexact but the rounding type
+   * is RoundingMode.UNNECESSARY
+   * @since 1.5
+   */
+  /*public BigDecimal (BigInteger num, int scale, MathContext mc)
+  {
+    this (num, scale);
+    if (mc.getPrecision() != 0)
+      {
+        BigDecimal result = this.round(mc);
+        this.intVal = result.intVal;
+        this.scale = result.scale;
+        this.precision = result.precision;
+      }
+  }*/
+
+  /**
+   * Constructs a BigDecimal in the same way as BigDecimal(double) and then
+   * rounds according to the MathContext.
+   * @param num the double from which the initial BigDecimal is created
+   * @param mc the MathContext that specifies the rounding behaviour
+   * @throws ArithmeticException if the result is inexact but the rounding type
+   * is RoundingMode.UNNECESSARY 
+   * @since 1.5
+   */
+  /*public BigDecimal (double num, MathContext mc)
+  {
+    this (num);
+    if (mc.getPrecision() != 0)
+      {
+        BigDecimal result = this.round(mc);
+        this.intVal = result.intVal;
+        this.scale = result.scale;
+        this.precision = result.precision;
+      }
+  }*/
+  
+  public BigDecimal (double num) //throws NumberFormatException 
+  {
+    if (Double.isInfinite (num) || Double.isNaN (num))
+      throw new Error/*NumberFormatException */("invalid argument: " + num);
+    // Note we can't convert NUM to a String and then use the
+    // String-based constructor.  The BigDecimal documentation makes
+    // it clear that the two constructors work differently.
+
+    final int mantissaBits = 52;
+    final int exponentBits = 11;
+    final long mantMask = (1L << mantissaBits) - 1;
+    final long expMask = (1L << exponentBits) - 1;
+
+    long bits = Double.doubleToLongBits (num);
+    long mantissa = bits & mantMask;
+    long exponent = (bits >>> mantissaBits) & expMask;
+    boolean denormal = exponent == 0;
+
+    // Correct the exponent for the bias.
+    exponent -= denormal ? 1022 : 1023;
+
+    // Now correct the exponent to account for the bits to the right
+    // of the decimal.
+    exponent -= mantissaBits;
+    // Ordinary numbers have an implied leading `1' bit.
+    if (! denormal)
+      mantissa |= (1L << mantissaBits);
+
+    // Shave off factors of 10.
+    while (exponent < 0 && (mantissa & 1) == 0)
+      {
+       ++exponent;
+       mantissa >>= 1;
+      }
+
+    intVal = BigInteger.valueOf (bits < 0 ? - mantissa : mantissa);
+    if (exponent < 0)
+      {
+       // We have MANTISSA * 2 ^ (EXPONENT).
+       // Since (1/2)^N == 5^N * 10^-N we can easily convert this
+       // into a power of 10.
+       scale = (int) (- exponent);
+       BigInteger mult = BigInteger.valueOf (5).pow (scale);
+       intVal = intVal.multiply (mult);
+      }
+    else
+      {
+       intVal = intVal.shiftLeft ((int) exponent);
+       scale = 0;
+      }
+  }
+
+  /**
+   * Constructs a BigDecimal from the char subarray and rounding 
+   * according to the MathContext.
+   * @param in the char array
+   * @param offset the start of the subarray
+   * @param len the length of the subarray
+   * @param mc the MathContext for rounding
+   * @throws NumberFormatException if the char subarray is not a valid 
+   * BigDecimal representation
+   * @throws ArithmeticException if the result is inexact but the rounding 
+   * mode is RoundingMode.UNNECESSARY
+   * @since 1.5
+   */
+  /*public BigDecimal(char[] in, int offset, int len, MathContext mc)
+  {
+    this(in, offset, len);
+    // If mc has precision other than zero then we must round.
+    if (mc.getPrecision() != 0)
+      {
+        BigDecimal temp = this.round(mc);
+        this.intVal = temp.intVal;
+        this.scale = temp.scale;
+        this.precision = temp.precision;
+      }
+  }*/
+  
+  /**
+   * Constructs a BigDecimal from the char array and rounding according
+   * to the MathContext. 
+   * @param in the char array
+   * @param mc the MathContext
+   * @throws NumberFormatException if <code>in</code> is not a valid BigDecimal
+   * representation
+   * @throws ArithmeticException if the result is inexact but the rounding mode
+   * is RoundingMode.UNNECESSARY
+   * @since 1.5
+   */
+  /*public BigDecimal(char[] in, MathContext mc)
+  {
+    this(in, 0, in.length);
+    // If mc has precision other than zero then we must round.
+    if (mc.getPrecision() != 0)
+      {
+        BigDecimal temp = this.round(mc);
+        this.intVal = temp.intVal;
+        this.scale = temp.scale;
+        this.precision = temp.precision;
+      } 
+  }*/
+  
+  /**
+   * Constructs a BigDecimal from the given char array, accepting the same
+   * sequence of characters as the BigDecimal(String) constructor.
+   * @param in the char array
+   * @throws NumberFormatException if <code>in</code> is not a valid BigDecimal
+   * representation
+   * @since 1.5
+   */
+  public BigDecimal(char[] in)
+  {
+    this(in, 0, in.length);
+  }
+  
+  /**
+   * Constructs a BigDecimal from a char subarray, accepting the same sequence
+   * of characters as the BigDecimal(String) constructor.  
+   * @param in the char array
+   * @param offset the start of the subarray
+   * @param len the length of the subarray
+   * @throws NumberFormatException if <code>in</code> is not a valid
+   * BigDecimal representation.
+   * @since 1.5
+   */
+  public BigDecimal(char[] in, int offset, int len)
+  {
+    //  start is the index into the char array where the significand starts
+    int start = offset;
+    //  end is one greater than the index of the last character used
+    int end = offset + len;
+    //  point is the index into the char array where the exponent starts
+    //  (or, if there is no exponent, this is equal to end)
+    int point = offset;
+    //  dot is the index into the char array where the decimal point is 
+    //  found, or -1 if there is no decimal point
+    int dot = -1;
+    
+    //  The following examples show what these variables mean.  Note that
+    //  point and dot don't yet have the correct values, they will be 
+    //  properly assigned in a loop later on in this method.
+    //
+    //  Example 1
+    //
+    //         +  1  0  2  .  4  6  9
+    //  __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
+    //
+    //  offset = 2, len = 8, start = 3, dot = 6, point = end = 10
+    //
+    //  Example 2
+    //
+    //         +  2  3  4  .  6  1  3  E  -  1
+    //  __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
+    //
+    //  offset = 2, len = 11, start = 3, dot = 6, point = 10, end = 13
+    //
+    //  Example 3
+    //
+    //         -  1  2  3  4  5  e  7  
+    //  __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
+    //
+    //  offset = 2, len = 8, start = 3, dot = -1, point = 8, end = 10 
+    
+    //  Determine the sign of the number.
+    boolean negative = false;
+    if (in[offset] == '+')
+      {
+        ++start;
+        ++point;
+      }
+    else if (in[offset] == '-')
+      {
+        ++start;
+        ++point;
+        negative = true;
+      }
+
+    //  Check each character looking for the decimal point and the 
+    //  start of the exponent.
+    while (point < end)
+      {
+        char c = in[point];
+        if (c == '.')
+          {
+            // If dot != -1 then we've seen more than one decimal point.
+            if (dot != -1)
+              throw new Error/*NumberFormatException*/("multiple `.'s in number");
+            dot = point;
+          }
+        // Break when we reach the start of the exponent.
+        else if (c == 'e' || c == 'E')
+          break;
+        // Throw an exception if the character was not a decimal or an 
+        // exponent and is not a digit.
+        else if (!Character.isDigit(c))
+          throw new Error/*NumberFormatException*/("unrecognized character at " + point
+                                          + ": " + c);
+        ++point;
+      }
+
+    // val is a StringBuilder from which we'll create a BigInteger
+    // which will be the unscaled value for this BigDecimal
+    CPStringBuilder val = new CPStringBuilder(point - start - 1);
+    if (dot != -1)
+      {
+        // If there was a decimal we must combine the two parts that 
+        // contain only digits and we must set the scale properly.
+        val.append(in, start, dot - start);
+        val.append(in, dot + 1, point - dot - 1);
+        scale = point - 1 - dot;
+      }
+    else
+      {
+        // If there was no decimal then the unscaled value is just the number
+        // formed from all the digits and the scale is zero.
+        val.append(in, start, point - start);
+        scale = 0;
+      }
+    if (val.length() == 0)
+      throw new Error/*NumberFormatException*/("no digits seen");
+
+    // Prepend a negative sign if necessary.
+    if (negative)
+      val.insert(0, '-');
+    intVal = new BigInteger(val.toString());
+
+    // Now parse exponent.
+    // If point < end that means we broke out of the previous loop when we
+    // saw an 'e' or an 'E'.
+    if (point < end)
+      {
+        point++;
+        // Ignore a '+' sign.
+        if (in[point] == '+')
+          point++;
+
+        // Throw an exception if there were no digits found after the 'e'
+        // or 'E'.
+        if (point >= end)
+          throw new Error/*NumberFormatException*/("no exponent following e or E");
+
+        try
+          {
+            // Adjust the scale according to the exponent.  
+            // Remember that the value of a BigDecimal is
+            // unscaledValue x Math.pow(10, -scale)
+            scale -= Integer.parseInt(new String(in, point, end - point));
+          }
+        catch (Error/*NumberFormatException*/ ex)
+          {
+            throw new Error/*NumberFormatException*/("malformed exponent");
+          }
+      }
+  }
+  
+  public BigDecimal (String num) //throws NumberFormatException 
+  {
+    int len = num.length();
+    int start = 0, point = 0;
+    int dot = -1;
+    boolean negative = false;
+    if (num.charAt(0) == '+')
+      {
+       ++start;
+       ++point;
+      }
+    else if (num.charAt(0) == '-')
+      {
+       ++start;
+       ++point;
+       negative = true;
+      }
+
+    while (point < len)
+      {
+       char c = num.charAt (point);
+       if (c == '.')
+         {
+           if (dot >= 0)
+             throw new Error/*NumberFormatException*/ ("multiple `.'s in number");
+           dot = point;
+         }
+       else if (c == 'e' || c == 'E')
+         break;
+       else if (Character.digit (c, 10) < 0)
+         throw new Error/*NumberFormatException*/ ("unrecognized character: " + c);
+       ++point;
+      }
+
+    String val;
+    if (dot >= 0)
+      {
+       val = num.substring (start, dot) + num.substring (dot + 1, point);
+       scale = point - 1 - dot;
+      }
+    else
+      {
+       val = num.substring (start, point);
+       scale = 0;
+      }
+    if (val.length () == 0)
+      throw new Error/*NumberFormatException*/ ("no digits seen");
+
+    if (negative)
+      val = "-" + val;
+    intVal = new BigInteger (val);
+
+    // Now parse exponent.
+    if (point < len)
+      {
+        point++;
+        if (num.charAt(point) == '+')
+          point++;
+
+        if (point >= len )
+          throw new Error/*NumberFormatException*/ ("no exponent following e or E");
+       
+        try 
+         {         
+        scale -= Integer.parseInt (num.substring (point));
+         }
+        catch (Error/*NumberFormatException*/ ex) 
+         {
+           throw new Error/*NumberFormatException*/ ("malformed exponent");
+         }
+      }
+  }
+
+  public static BigDecimal valueOf (long val) 
+  {
+    return valueOf (val, 0);
+  }
+
+  public static BigDecimal valueOf (long val, int scale) 
+    //throws NumberFormatException 
+  {
+    if ((scale == 0) && ((int)val == val))
+      switch ((int) val)
+       {
+       case 0:
+         return ZERO;
+       case 1:
+         return ONE;
+       }
+
+    return new BigDecimal (BigInteger.valueOf (val), scale);
+  }
+
+  public BigDecimal add (BigDecimal val) 
+  {
+    // For addition, need to line up decimals.  Note that the movePointRight
+    // method cannot be used for this as it might return a BigDecimal with
+    // scale == 0 instead of the scale we need.
+    BigInteger op1 = intVal;
+    BigInteger op2 = val.intVal;
+    if (scale < val.scale)
+      op1 = op1.multiply (BigInteger.TEN.pow (val.scale - scale));
+    else if (scale > val.scale)
+      op2 = op2.multiply (BigInteger.TEN.pow (scale - val.scale));
+
+    return new BigDecimal (op1.add (op2), Math.max (scale, val.scale));
+  }
+  
+  /**
+   * Returns a BigDecimal whose value is found first by calling the 
+   * method add(val) and then by rounding according to the MathContext mc.
+   * @param val the augend
+   * @param mc the MathContext for rounding
+   * @throws ArithmeticException if the value is inexact but the rounding is
+   * RoundingMode.UNNECESSARY
+   * @return <code>this</code> + <code>val</code>, rounded if need be
+   * @since 1.5
+   */
+  /*public BigDecimal add (BigDecimal val, MathContext mc)
+  {
+    return add(val).round(mc);
+  }*/
+
+  public BigDecimal subtract (BigDecimal val) 
+  {
+    return this.add(val.negate());
+  }
+
+  /**
+   * Returns a BigDecimal whose value is found first by calling the 
+   * method subtract(val) and then by rounding according to the MathContext mc.
+   * @param val the subtrahend
+   * @param mc the MathContext for rounding
+   * @throws ArithmeticException if the value is inexact but the rounding is
+   * RoundingMode.UNNECESSARY
+   * @return <code>this</code> - <code>val</code>, rounded if need be
+   * @since 1.5
+   */
+  /*public BigDecimal subtract (BigDecimal val, MathContext mc)
+  {
+    return subtract(val).round(mc);
+  }*/
+
+  public BigDecimal multiply (BigDecimal val) 
+  {
+    return new BigDecimal (intVal.multiply (val.intVal), scale + val.scale);
+  }
+  
+  /**
+   * Returns a BigDecimal whose value is (this x val) before it is rounded
+   * according to the MathContext mc. 
+   * @param val the multiplicand
+   * @param mc the MathContext for rounding
+   * @return a new BigDecimal with value approximately (this x val)
+   * @throws ArithmeticException if the value is inexact but the rounding mode
+   * is RoundingMode.UNNECESSARY
+   * @since 1.5
+   */
+  /*public BigDecimal multiply (BigDecimal val, MathContext mc)
+  {
+    return multiply(val).round(mc);
+  }*/
+
+  public BigDecimal divide (BigDecimal val, int roundingMode) 
+    //throws ArithmeticException, IllegalArgumentException 
+  {
+    return divide (val, scale, roundingMode);
+  }
+  
+  /**
+   * Returns a BigDecimal whose value is (this / val), with the specified scale
+   * and rounding according to the RoundingMode 
+   * @param val the divisor
+   * @param scale the scale of the BigDecimal returned
+   * @param roundingMode the rounding mode to use
+   * @return a BigDecimal whose value is approximately (this / val)
+   * @throws ArithmeticException if divisor is zero or the rounding mode is
+   * UNNECESSARY but the specified scale cannot represent the value exactly
+   * @since 1.5
+   */
+  /*public BigDecimal divide(BigDecimal val, 
+                           int scale, RoundingMode roundingMode)
+  {
+    return divide (val, scale, roundingMode.ordinal());
+  }*/
+
+  /**
+   * Returns a BigDecimal whose value is (this / val) rounded according to the
+   * RoundingMode
+   * @param val the divisor
+   * @param roundingMode the rounding mode to use
+   * @return a BigDecimal whose value is approximately (this / val)
+   * @throws ArithmeticException if divisor is zero or the rounding mode is
+   * UNNECESSARY but the specified scale cannot represent the value exactly
+   */
+  /*public BigDecimal divide (BigDecimal val, RoundingMode roundingMode)
+  {
+    return divide (val, scale, roundingMode.ordinal());
+  }*/
+  
+  public BigDecimal divide(BigDecimal val, int newScale, int roundingMode)
+    //throws ArithmeticException, IllegalArgumentException 
+  {
+    if (roundingMode < 0 || roundingMode > 7)
+      throw 
+       new Error/*IllegalArgumentException*/("illegal rounding mode: " + roundingMode);
+
+    if (intVal.signum () == 0) // handle special case of 0.0/0.0
+      return newScale == 0 ? ZERO : new BigDecimal (ZERO.intVal, newScale);
+    
+    // Ensure that pow gets a non-negative value.
+    BigInteger valIntVal = val.intVal;
+    int power = newScale - (scale - val.scale);
+    if (power < 0)
+      {
+       // Effectively increase the scale of val to avoid an
+       // ArithmeticException for a negative power.
+        valIntVal = valIntVal.multiply (BigInteger.TEN.pow (-power));
+       power = 0;
+      }
+
+    BigInteger dividend = intVal.multiply (BigInteger.TEN.pow (power));
+    
+    BigInteger parts[] = dividend.divideAndRemainder (valIntVal);
+
+    BigInteger unrounded = parts[0];
+    if (parts[1].signum () == 0) // no remainder, no rounding necessary
+      return new BigDecimal (unrounded, newScale);
+
+    if (roundingMode == ROUND_UNNECESSARY)
+      throw new Error/*ArithmeticException*/ ("Rounding necessary");
+
+    int sign = intVal.signum () * valIntVal.signum ();
+
+    if (roundingMode == ROUND_CEILING)
+      roundingMode = (sign > 0) ? ROUND_UP : ROUND_DOWN;
+    else if (roundingMode == ROUND_FLOOR)
+      roundingMode = (sign < 0) ? ROUND_UP : ROUND_DOWN;
+    else
+      {
+       // half is -1 if remainder*2 < positive intValue (*power), 0 if equal,
+       // 1 if >. This implies that the remainder to round is less than,
+       // equal to, or greater than half way to the next digit.
+       BigInteger posRemainder
+         = parts[1].signum () < 0 ? parts[1].negate() : parts[1];
+       valIntVal = valIntVal.signum () < 0 ? valIntVal.negate () : valIntVal;
+       int half = posRemainder.shiftLeft(1).compareTo(valIntVal);
+
+       switch(roundingMode)
+         {
+         case ROUND_HALF_UP:
+           roundingMode = (half < 0) ? ROUND_DOWN : ROUND_UP;
+           break;
+         case ROUND_HALF_DOWN:
+           roundingMode = (half > 0) ? ROUND_UP : ROUND_DOWN;
+           break;
+         case ROUND_HALF_EVEN:
+           if (half < 0)
+             roundingMode = ROUND_DOWN;
+           else if (half > 0)
+             roundingMode = ROUND_UP;
+           else if (unrounded.testBit(0)) // odd, then ROUND_HALF_UP
+             roundingMode = ROUND_UP;
+           else                           // even, ROUND_HALF_DOWN
+             roundingMode = ROUND_DOWN;
+           break;
+         }
+      }
+
+    if (roundingMode == ROUND_UP)
+      unrounded = unrounded.add (BigInteger.valueOf (sign > 0 ? 1 : -1));
+
+    // roundingMode == ROUND_DOWN
+    return new BigDecimal (unrounded, newScale);
+  }
+  
+  /**
+   * Performs division, if the resulting quotient requires rounding
+   * (has a nonterminating decimal expansion), 
+   * an ArithmeticException is thrown. 
+   * #see divide(BigDecimal, int, int)
+   * @since 1.5
+   */
+  public BigDecimal divide(BigDecimal divisor)
+    //throws ArithmeticException, IllegalArgumentException 
+  {
+    return divide(divisor, scale, ROUND_UNNECESSARY);
+  }
+
+  /**
+   * Returns a BigDecimal whose value is the remainder in the quotient
+   * this / val.  This is obtained by 
+   * subtract(divideToIntegralValue(val).multiply(val)).  
+   * @param val the divisor
+   * @return a BigDecimal whose value is the remainder
+   * @throws ArithmeticException if val == 0
+   * @since 1.5
+   */
+  public BigDecimal remainder(BigDecimal val)
+  {
+    return subtract(divideToIntegralValue(val).multiply(val));
+  }
+
+  /**
+   * Returns a BigDecimal array, the first element of which is the integer part
+   * of this / val, and the second element of which is the remainder of 
+   * that quotient.
+   * @param val the divisor
+   * @return the above described BigDecimal array
+   * @throws ArithmeticException if val == 0
+   * @since 1.5
+   */
+  public BigDecimal[] divideAndRemainder(BigDecimal val)
+  {
+    BigDecimal[] result = new BigDecimal[2];
+    result[0] = divideToIntegralValue(val);
+    result[1] = subtract(result[0].multiply(val));
+    return result;
+  }
+  
+  /**
+   * Returns a BigDecimal whose value is the integer part of the quotient 
+   * this / val.  The preferred scale is this.scale - val.scale.
+   * @param val the divisor
+   * @return a BigDecimal whose value is the integer part of this / val.
+   * @throws ArithmeticException if val == 0
+   * @since 1.5
+   */
+  public BigDecimal divideToIntegralValue(BigDecimal val)
+  {
+    return divide(val, ROUND_DOWN).floor().setScale(scale - val.scale, ROUND_DOWN);
+  }
+  
+  /**
+   * Mutates this BigDecimal into one with no fractional part, whose value is 
+   * equal to the largest integer that is <= to this BigDecimal.  Note that
+   * since this method is private it is okay to mutate this BigDecimal.
+   * @return the BigDecimal obtained through the floor operation on this 
+   * BigDecimal.
+   */
+  private BigDecimal floor()
+  {
+    if (scale <= 0)
+      return this;
+    String intValStr = intVal.toString();
+    intValStr = intValStr.substring(0, intValStr.length() - scale);
+    intVal = new BigInteger(intValStr).multiply(BigInteger.TEN.pow(scale));
+    return this;
+  }
+    
+  public int compareTo (BigDecimal val) 
+  {
+    if (scale == val.scale)
+      return intVal.compareTo (val.intVal);
+
+    BigInteger thisParts[] = 
+      intVal.divideAndRemainder (BigInteger.TEN.pow (scale));
+    BigInteger valParts[] =
+      val.intVal.divideAndRemainder (BigInteger.TEN.pow (val.scale));
+    
+    int compare;
+    if ((compare = thisParts[0].compareTo (valParts[0])) != 0)
+      return compare;
+
+    // quotients are the same, so compare remainders
+
+    // Add some trailing zeros to the remainder with the smallest scale
+    if (scale < val.scale)
+      thisParts[1] = thisParts[1].multiply
+                       (BigInteger.valueOf (10).pow (val.scale - scale));
+    else if (scale > val.scale)
+      valParts[1] = valParts[1].multiply
+                       (BigInteger.valueOf (10).pow (scale - val.scale));
+
+    // and compare them
+    return thisParts[1].compareTo (valParts[1]);
+  }
+
+  public boolean equals (Object o) 
+  {
+    return (o instanceof BigDecimal 
+           && scale == ((BigDecimal) o).scale
+           && compareTo ((BigDecimal) o) == 0);
+  }
+
+  public int hashCode() 
+  {
+    return intValue() ^ scale;
+  }
+
+  public BigDecimal max (BigDecimal val)
+  {
+    switch (compareTo (val)) 
+      {
+      case 1:
+       return this;
+      default:
+       return val;
+      }
+  }
+
+  public BigDecimal min (BigDecimal val) 
+  {
+    switch (compareTo (val)) 
+      {
+      case -1:
+       return this;
+      default:
+       return val;
+      }
+  }
+
+  public BigDecimal movePointLeft (int n)
+  {
+    return (n < 0) ? movePointRight (-n) : new BigDecimal (intVal, scale + n);
+  }
+
+  public BigDecimal movePointRight (int n)
+  {
+    if (n < 0)
+      return movePointLeft (-n);
+
+    if (scale >= n)
+      return new BigDecimal (intVal, scale - n);
+
+    return new BigDecimal (intVal.multiply 
+                          (BigInteger.TEN.pow (n - scale)), 0);
+  }
+
+  public int signum () 
+  {
+    return intVal.signum ();
+  }
+
+  public int scale () 
+  {
+    return scale;
+  }
+  
+  public BigInteger unscaledValue()
+  {
+    return intVal;
+  }
+
+  public BigDecimal abs () 
+  {
+    return new BigDecimal (intVal.abs (), scale);
+  }
+
+  public BigDecimal negate () 
+  {
+    return new BigDecimal (intVal.negate (), scale);
+  }
+  
+  /**
+   * Returns a BigDecimal whose value is found first by negating this via
+   * the negate() method, then by rounding according to the MathContext mc.
+   * @param mc the MathContext for rounding
+   * @return a BigDecimal whose value is approximately (-this)
+   * @throws ArithmeticException if the value is inexact but the rounding mode
+   * is RoundingMode.UNNECESSARY
+   * @since 1.5
+   */
+  /*public BigDecimal negate(MathContext mc)
+  {
+    BigDecimal result = negate();
+    if (mc.getPrecision() != 0)
+      result = result.round(mc);
+    return result;
+  }*/
+  
+  /**
+   * Returns this BigDecimal.  This is included for symmetry with the 
+   * method negate().
+   * @return this
+   * @since 1.5
+   */
+  public BigDecimal plus()
+  {
+    return this;
+  }
+  
+  /**
+   * Returns a BigDecimal whose value is found by rounding <code>this</code> 
+   * according to the MathContext.  This is the same as round(MathContext).
+   * @param mc the MathContext for rounding
+   * @return a BigDecimal whose value is <code>this</code> before being rounded
+   * @throws ArithmeticException if the value is inexact but the rounding mode
+   * is RoundingMode.UNNECESSARY
+   * @since 1.5
+   */
+  /*public BigDecimal plus(MathContext mc)
+  {
+    return round(mc);
+  }*/
+  
+  /**
+   * Returns a BigDecimal which is this BigDecimal rounded according to the
+   * MathContext rounding settings.
+   * @param mc the MathContext that tells us how to round
+   * @return the rounded BigDecimal
+   */
+  /*public BigDecimal round(MathContext mc)
+  {
+    int mcPrecision = mc.getPrecision();
+    int numToChop = precision() - mcPrecision;
+    // If mc specifies not to chop any digits or if we've already chopped 
+    // enough digits (say by using a MathContext in the constructor for this
+    // BigDecimal) then just return this.
+    if (mcPrecision == 0 || numToChop <= 0)
+      return this;
+    
+    // Make a new BigDecimal which is the correct power of 10 to chop off
+    // the required number of digits and then call divide.
+    BigDecimal div = new BigDecimal(BigInteger.TEN.pow(numToChop));
+    BigDecimal rounded = divide(div, scale, mc.getRoundingMode().ordinal());
+    rounded.scale -= numToChop;
+    rounded.precision = mcPrecision;
+    return rounded;
+  }*/
+  
+  /**
+   * Returns the precision of this BigDecimal (the number of digits in the
+   * unscaled value).  The precision of a zero value is 1.
+   * @return the number of digits in the unscaled value, or 1 if the value 
+   * is zero.
+   */
+  public int precision()
+  {
+    if (precision == 0)
+      {
+       String s = intVal.toString();
+       precision = s.length() - (( s.charAt(0) == '-' ) ? 1 : 0);
+      }
+    return precision;
+  }
+  
+  /**
+   * Returns the String representation of this BigDecimal, using scientific
+   * notation if necessary.  The following steps are taken to generate
+   * the result:
+   * 
+   * 1. the BigInteger unscaledValue's toString method is called and if
+   * <code>scale == 0<code> is returned.
+   * 2. an <code>int adjExp</code> is created which is equal to the negation
+   * of <code>scale</code> plus the number of digits in the unscaled value, 
+   * minus one.
+   * 3. if <code>scale >= 0 && adjExp >= -6</code> then we represent this 
+   * BigDecimal without scientific notation.  A decimal is added if the 
+   * scale is positive and zeros are prepended as necessary.
+   * 4. if scale is negative or adjExp is less than -6 we use scientific
+   * notation.  If the unscaled value has more than one digit, a decimal 
+   * as inserted after the first digit, the character 'E' is appended
+   * and adjExp is appended.
+   */
+  /*public String toString()
+  {
+    // bigStr is the String representation of the unscaled value.  If
+    // scale is zero we simply return this.
+    String bigStr = intVal.toString();
+    if (scale == 0)
+      return bigStr;
+
+    boolean negative = (bigStr.charAt(0) == '-');
+    int point = bigStr.length() - scale - (negative ? 1 : 0);
+
+    CPStringBuilder val = new CPStringBuilder();
+
+    if (scale >= 0 && (point - 1) >= -6)
+      {
+       // Convert to character form without scientific notation.
+        if (point <= 0)
+          {
+            // Zeros need to be prepended to the StringBuilder.
+            if (negative)
+              val.append('-');
+            // Prepend a '0' and a '.' and then as many more '0's as necessary.
+            val.append('0').append('.');
+            while (point < 0)
+              {
+                val.append('0');
+                point++;
+              }
+            // Append the unscaled value.
+            val.append(bigStr.substring(negative ? 1 : 0));
+          }
+        else
+          {
+            // No zeros need to be prepended so the String is simply the 
+            // unscaled value with the decimal point inserted.
+            val.append(bigStr);
+            val.insert(point + (negative ? 1 : 0), '.');
+          }
+      }
+    else
+      {
+        // We must use scientific notation to represent this BigDecimal.
+        val.append(bigStr);
+        // If there is more than one digit in the unscaled value we put a 
+        // decimal after the first digit.
+        if (bigStr.length() > 1)
+          val.insert( ( negative ? 2 : 1 ), '.');
+        // And then append 'E' and the exponent = (point - 1).
+        val.append('E');
+        if (point - 1 >= 0)
+          val.append('+');
+        val.append( point - 1 );
+      }
+    return val.toString();
+  }*/
+
+  /**
+   * Returns the String representation of this BigDecimal, using engineering
+   * notation if necessary.  This is similar to toString() but when exponents 
+   * are used the exponent is made to be a multiple of 3 such that the integer
+   * part is between 1 and 999.
+   * 
+   * @return a String representation of this BigDecimal in engineering notation
+   * @since 1.5
+   */
+  /*public String toEngineeringString()
+  {
+    // bigStr is the String representation of the unscaled value.  If
+    // scale is zero we simply return this.
+    String bigStr = intVal.toString();
+    if (scale == 0)
+      return bigStr;
+
+    boolean negative = (bigStr.charAt(0) == '-');
+    int point = bigStr.length() - scale - (negative ? 1 : 0);
+
+    // This is the adjusted exponent described above.
+    int adjExp = point - 1;
+    CPStringBuilder val = new CPStringBuilder();
+
+    if (scale >= 0 && adjExp >= -6)
+      {
+        // Convert to character form without scientific notation.
+        if (point <= 0)
+          {
+            // Zeros need to be prepended to the StringBuilder.
+            if (negative)
+              val.append('-');
+            // Prepend a '0' and a '.' and then as many more '0's as necessary.
+            val.append('0').append('.');
+            while (point < 0)
+              {
+                val.append('0');
+                point++;
+              }
+            // Append the unscaled value.
+            val.append(bigStr.substring(negative ? 1 : 0));
+          }
+        else
+          {
+            // No zeros need to be prepended so the String is simply the 
+            // unscaled value with the decimal point inserted.
+            val.append(bigStr);
+            val.insert(point + (negative ? 1 : 0), '.');
+          }
+      }
+    else
+      {
+        // We must use scientific notation to represent this BigDecimal.
+        // The exponent must be a multiple of 3 and the integer part
+        // must be between 1 and 999.
+        val.append(bigStr);        
+        int zeros = adjExp % 3;
+        int dot = 1;
+        if (adjExp > 0)
+          {
+            // If the exponent is positive we just move the decimal to the
+            // right and decrease the exponent until it is a multiple of 3.
+            dot += zeros;
+            adjExp -= zeros;
+          }
+        else
+          {
+            // If the exponent is negative then we move the dot to the right
+            // and decrease the exponent (increase its magnitude) until 
+            // it is a multiple of 3.  Note that this is not adjExp -= zeros
+            // because the mod operator doesn't give us the distance to the 
+            // correct multiple of 3.  (-5 mod 3) is -2 but the distance from
+            // -5 to the correct multiple of 3 (-6) is 1, not 2.
+            if (zeros == -2)
+              {
+                dot += 1;
+                adjExp -= 1;
+              }
+            else if (zeros == -1)
+              {
+                dot += 2;
+                adjExp -= 2;
+              }
+          }
+
+        // Either we have to append zeros because, for example, 1.1E+5 should
+        // be 110E+3, or we just have to put the decimal in the right place.
+        if (dot > val.length())
+          {
+            while (dot > val.length())
+              val.append('0');
+          }
+        else if (bigStr.length() > dot)
+          val.insert(dot + (negative ? 1 : 0), '.');
+        
+        // And then append 'E' and the exponent (adjExp).
+        val.append('E');
+        if (adjExp >= 0)
+          val.append('+');
+        val.append(adjExp);
+      }
+    return val.toString();
+  }*/
+  
+  /**
+   * Returns a String representation of this BigDecimal without using 
+   * scientific notation.  This is how toString() worked for releases 1.4
+   * and previous.  Zeros may be added to the end of the String.  For
+   * example, an unscaled value of 1234 and a scale of -3 would result in 
+   * the String 1234000, but the toString() method would return 
+   * 1.234E+6.
+   * @return a String representation of this BigDecimal
+   * @since 1.5
+   */
+  /*public String toPlainString()
+  {
+    // If the scale is zero we simply return the String representation of the 
+    // unscaled value.
+    String bigStr = intVal.toString();
+    if (scale == 0)
+      return bigStr;
+
+    // Remember if we have to put a negative sign at the start.
+    boolean negative = (bigStr.charAt(0) == '-');
+
+    int point = bigStr.length() - scale - (negative ? 1 : 0);
+
+    CPStringBuilder sb = new CPStringBuilder(bigStr.length() + 2
+                                            + (point <= 0 ? (-point + 1) : 0));
+    if (point <= 0)
+      {
+        // We have to prepend zeros and a decimal point.
+        if (negative)
+          sb.append('-');
+        sb.append('0').append('.');
+        while (point < 0)
+          {
+            sb.append('0');
+            point++;
+          }
+        sb.append(bigStr.substring(negative ? 1 : 0));
+      }
+    else if (point < bigStr.length())
+      {
+        // No zeros need to be prepended or appended, just put the decimal
+        // in the right place.
+        sb.append(bigStr);
+        sb.insert(point + (negative ? 1 : 0), '.');
+      }
+    else
+      {
+        // We must append zeros instead of using scientific notation.
+        sb.append(bigStr);
+        for (int i = bigStr.length(); i < point; i++)
+          sb.append('0');
+      }
+    return sb.toString();
+  }*/
+  
+  /**
+   * Converts this BigDecimal to a BigInteger.  Any fractional part will
+   * be discarded.
+   * @return a BigDecimal whose value is equal to floor[this]
+   */
+  public BigInteger toBigInteger () 
+  {
+    // If scale > 0 then we must divide, if scale > 0 then we must multiply,
+    // and if scale is zero then we just return intVal;
+    if (scale > 0)
+      return intVal.divide (BigInteger.TEN.pow (scale));
+    else if (scale < 0)
+      return intVal.multiply(BigInteger.TEN.pow(-scale));
+    return intVal;
+  }
+  
+  /**
+   * Converts this BigDecimal into a BigInteger, throwing an 
+   * ArithmeticException if the conversion is not exact.
+   * @return a BigInteger whose value is equal to the value of this BigDecimal
+   * @since 1.5
+   */
+  public BigInteger toBigIntegerExact()
+  {
+    if (scale > 0)
+      {
+        // If we have to divide, we must check if the result is exact.
+        BigInteger[] result = 
+          intVal.divideAndRemainder(BigInteger.TEN.pow(scale));
+        if (result[1].equals(BigInteger.ZERO))
+          return result[0];
+        throw new Error/*ArithmeticException*/("No exact BigInteger representation");
+      }
+    else if (scale < 0)
+      // If we're multiplying instead, then we needn't check for exactness.
+      return intVal.multiply(BigInteger.TEN.pow(-scale));
+    // If the scale is zero we can simply return intVal.
+    return intVal;
+  }
+
+  public int intValue () 
+  {
+    return toBigInteger ().intValue ();
+  }
+  
+  /**
+   * Returns a BigDecimal which is numerically equal to this BigDecimal but 
+   * with no trailing zeros in the representation.  For example, if this 
+   * BigDecimal has [unscaledValue, scale] = [6313000, 4] this method returns
+   * a BigDecimal with [unscaledValue, scale] = [6313, 1].  As another 
+   * example, [12400, -2] would become [124, -4].
+   * @return a numerically equal BigDecimal with no trailing zeros
+   */
+  public BigDecimal stripTrailingZeros()  
+  {
+    String intValStr = intVal.toString();
+    int newScale = scale;
+    int pointer = intValStr.length() - 1;
+    // This loop adjusts pointer which will be used to give us the substring
+    // of intValStr to use in our new BigDecimal, and also accordingly
+    // adjusts the scale of our new BigDecimal.
+    while (intValStr.charAt(pointer) == '0')
+      {
+        pointer --;
+        newScale --;
+      }
+    // Create a new BigDecimal with the appropriate substring and then
+    // set its scale.
+    BigDecimal result = new BigDecimal(intValStr.substring(0, pointer + 1));    
+    result.scale = newScale;
+    return result;
+  }
+
+  public long longValue ()
+  {
+    return toBigInteger().longValue();
+  }
+
+  public float floatValue() 
+  {
+    return Float.valueOf(toString()).floatValue();
+  }
+
+  public double doubleValue() 
+  {
+    return Double.valueOf(toString()).doubleValue();
+  }
+
+  public BigDecimal setScale (int scale) //throws ArithmeticException
+  {
+    return setScale (scale, ROUND_UNNECESSARY);
+  }
+
+  public BigDecimal setScale (int scale, int roundingMode)
+    //throws ArithmeticException, IllegalArgumentException
+  {
+    // NOTE: The 1.5 JRE doesn't throw this, ones prior to it do and
+    // the spec says it should. Nevertheless, if 1.6 doesn't fix this
+    // we should consider removing it.
+    if( scale < 0 ) throw new Error/*ArithmeticException*/("Scale parameter < 0.");
+    return divide (ONE, scale, roundingMode);
+  }
+  
+  /**
+   * Returns a BigDecimal whose value is the same as this BigDecimal but whose
+   * representation has a scale of <code>newScale</code>.  If the scale is
+   * reduced then rounding may occur, according to the RoundingMode.
+   * @param newScale
+   * @param roundingMode
+   * @return a BigDecimal whose scale is as given, whose value is 
+   * <code>this</code> with possible rounding
+   * @throws ArithmeticException if the rounding mode is UNNECESSARY but 
+   * rounding is required 
+   * @since 1.5
+   */
+  /*public BigDecimal setScale(int newScale, RoundingMode roundingMode)
+  {
+    return setScale(newScale, roundingMode.ordinal());
+  }*/
+  
+  /**
+   * Returns a new BigDecimal constructed from the BigDecimal(String) 
+   * constructor using the Double.toString(double) method to obtain
+   * the String.
+   * @param val the double value used in Double.toString(double)
+   * @return a BigDecimal representation of val
+   * @throws NumberFormatException if val is NaN or infinite
+   * @since 1.5
+   */
+  public static BigDecimal valueOf(double val)
+  {
+    if (Double.isInfinite(val) || Double.isNaN(val))
+      throw new Error/*NumberFormatException*/("argument cannot be NaN or infinite.");
+    return new BigDecimal(Double.toString(val));
+  }
+  
+  /**
+   * Returns a BigDecimal whose numerical value is the numerical value
+   * of this BigDecimal multiplied by 10 to the power of <code>n</code>. 
+   * @param n the power of ten
+   * @return the new BigDecimal
+   * @since 1.5
+   */
+  public BigDecimal scaleByPowerOfTen(int n)
+  {
+    BigDecimal result = new BigDecimal(intVal, scale - n);
+    result.precision = precision;
+    return result;
+  }
+  
+  /**
+   * Returns a BigDecimal whose value is <code>this</code> to the power of 
+   * <code>n</code>. 
+   * @param n the power
+   * @return the new BigDecimal
+   * @since 1.5
+   */
+  public BigDecimal pow(int n)
+  {
+    if (n < 0 || n > 999999999)
+      throw new Error/*ArithmeticException*/("n must be between 0 and 999999999");
+    BigDecimal result = new BigDecimal(intVal.pow(n), scale * n);
+    return result;
+  }
+  
+  /**
+   * Returns a BigDecimal whose value is determined by first calling pow(n)
+   * and then by rounding according to the MathContext mc.
+   * @param n the power
+   * @param mc the MathContext
+   * @return the new BigDecimal
+   * @throws ArithmeticException if n < 0 or n > 999999999 or if the result is
+   * inexact but the rounding is RoundingMode.UNNECESSARY
+   * @since 1.5
+   */
+  /*public BigDecimal pow(int n, MathContext mc)
+  {
+    // FIXME: The specs claim to use the X3.274-1996 algorithm.  We
+    // currently do not.
+    return pow(n).round(mc);
+  }*/
+  
+  /**
+   * Returns a BigDecimal whose value is the absolute value of this BigDecimal
+   * with rounding according to the given MathContext.
+   * @param mc the MathContext
+   * @return the new BigDecimal
+   */
+  /*public BigDecimal abs(MathContext mc)
+  {
+    BigDecimal result = abs();
+    result = result.round(mc);
+    return result;
+  }*/
+  
+  /**
+   * Returns the size of a unit in the last place of this BigDecimal.  This
+   * returns a BigDecimal with [unscaledValue, scale] = [1, this.scale()].
+   * @return the size of a unit in the last place of <code>this</code>.
+   * @since 1.5
+   */
+  public BigDecimal ulp()
+  {
+    return new BigDecimal(BigInteger.ONE, scale);
+  }
+  
+  /**
+   * Converts this BigDecimal to a long value.
+   * @return the long value
+   * @throws ArithmeticException if rounding occurs or if overflow occurs
+   * @since 1.5
+   */
+  public long longValueExact()
+  {
+    // Set scale will throw an exception if rounding occurs.
+    BigDecimal temp = setScale(0, ROUND_UNNECESSARY);
+    BigInteger tempVal = temp.intVal;
+    // Check for overflow.
+    long result = intVal.longValue();
+    if (tempVal.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 1
+        || (result < 0 && signum() == 1) || (result > 0 && signum() == -1))
+      throw new Error/*ArithmeticException*/("this BigDecimal is too " +
+            "large to fit into the return type");
+    
+    return intVal.longValue();
+  }
+  
+  /**
+   * Converts this BigDecimal into an int by first calling longValueExact
+   * and then checking that the <code>long</code> returned from that
+   * method fits into an <code>int</code>.
+   * @return an int whose value is <code>this</code>
+   * @throws ArithmeticException if this BigDecimal has a fractional part
+   * or is too large to fit into an int.
+   * @since 1.5
+   */
+  public int intValueExact()
+  {
+    long temp = longValueExact();
+    int result = (int)temp;
+    if (result != temp)
+      throw new Error/*ArithmeticException*/ ("this BigDecimal cannot fit into an int");
+    return result;
+  }
+  
+  /**
+   * Converts this BigDecimal into a byte by first calling longValueExact
+   * and then checking that the <code>long</code> returned from that
+   * method fits into a <code>byte</code>.
+   * @return a byte whose value is <code>this</code>
+   * @throws ArithmeticException if this BigDecimal has a fractional part
+   * or is too large to fit into a byte.
+   * @since 1.5
+   */
+  public byte byteValueExact()
+  {
+    long temp = longValueExact();
+    byte result = (byte)temp;
+    if (result != temp)
+      throw new Error/*ArithmeticException*/ ("this BigDecimal cannot fit into a byte");
+    return result;
+  }
+  
+  /**
+   * Converts this BigDecimal into a short by first calling longValueExact
+   * and then checking that the <code>long</code> returned from that
+   * method fits into a <code>short</code>.
+   * @return a short whose value is <code>this</code>
+   * @throws ArithmeticException if this BigDecimal has a fractional part
+   * or is too large to fit into a short.
+   * @since 1.5
+   */
+  public short shortValueExact()
+  {
+    long temp = longValueExact();
+    short result = (short)temp;
+    if (result != temp)
+      throw new Error/*ArithmeticException*/ ("this BigDecimal cannot fit into a short");
+    return result;
+  }
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/BigInteger.java b/Robust/src/ClassLibrary/MGC/gnu/BigInteger.java
new file mode 100644 (file)
index 0000000..4dd41fa
--- /dev/null
@@ -0,0 +1,2676 @@
+/* java.math.BigInteger -- Arbitary precision integers
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+//package java.math;
+
+/*import gnu.classpath.Configuration;
+
+import gnu.java.lang.CPStringBuilder;
+import gnu.java.math.GMP;
+import gnu.java.math.MPN;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Random;
+import java.util.logging.Logger;
+*/
+/**
+ * Written using on-line Java Platform 1.2 API Specification, as well
+ * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998) and
+ * "Applied Cryptography, Second Edition" by Bruce Schneier (Wiley, 1996).
+ * 
+ * Based primarily on IntNum.java BitOps.java by Per Bothner (per@bothner.com)
+ * (found in Kawa 1.6.62).
+ *
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @date December 20, 1999.
+ * @status believed complete and correct.
+ */
+public class BigInteger //extends Number implements Comparable<BigInteger>
+{
+  //private static final Logger log = Logger.getLogger(BigInteger.class.getName());
+
+  /** All integers are stored in 2's-complement form.
+   * If words == null, the ival is the value of this BigInteger.
+   * Otherwise, the first ival elements of words make the value
+   * of this BigInteger, stored in little-endian order, 2's-complement form. */
+  private transient int ival;
+  private transient int[] words;
+
+  // Serialization fields.
+  // the first three, although not used in the code, are present for
+  // compatibility with older RI versions of this class. DO NOT REMOVE.
+  private int bitCount = -1;
+  private int bitLength = -1;
+  private int lowestSetBit = -2;
+  private byte[] magnitude;
+  private int signum;
+  private static final long serialVersionUID = -8287574255936472291L;
+
+
+  /** We pre-allocate integers in the range minFixNum..maxFixNum. 
+   * Note that we must at least preallocate 0, 1, and 10.  */
+  private static final int minFixNum = -100;
+  private static final int maxFixNum = 1024;
+  private static final int numFixNum = maxFixNum-minFixNum+1;
+  private static final BigInteger[] smallFixNums;
+  
+  /** The alter-ego GMP instance for this. */
+  //private transient GMP mpz;
+
+  /*private static final boolean USING_NATIVE = Configuration.WANT_NATIVE_BIG_INTEGER
+                                              && initializeLibrary();*/
+
+  static
+  {
+    /*if (USING_NATIVE)
+      {
+        smallFixNums = null;
+        ZERO = valueOf(0L);
+        ONE = valueOf(1L);
+        TEN = valueOf(10L);
+      }
+    else*/
+      {
+        smallFixNums = new BigInteger[numFixNum];
+       for (int i = numFixNum;  --i >= 0; )
+         smallFixNums[i] = new BigInteger(i + minFixNum);
+
+        ZERO = smallFixNums[-minFixNum];
+        ONE = smallFixNums[1 - minFixNum];
+        TEN = smallFixNums[10 - minFixNum];
+      }
+  }
+
+  /**
+   * The constant zero as a BigInteger.
+   * @since 1.2
+   */
+  public static final BigInteger ZERO;
+
+  /**
+   * The constant one as a BigInteger.
+   * @since 1.2
+   */
+  public static final BigInteger ONE;
+
+  /**
+   * The constant ten as a BigInteger.
+   * @since 1.5
+   */
+  public static final BigInteger TEN;
+
+  /* Rounding modes: */
+  private static final int FLOOR = 1;
+  private static final int CEILING = 2;
+  private static final int TRUNCATE = 3;
+  private static final int ROUND = 4;
+
+  /** When checking the probability of primes, it is most efficient to
+   * first check the factoring of small primes, so we'll use this array.
+   */
+  private static final int[] primes =
+    {   2,   3,   5,   7,  11,  13,  17,  19,  23,  29,  31,  37,  41,  43,
+       47,  53,  59,  61,  67,  71,  73,  79,  83,  89,  97, 101, 103, 107,
+      109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
+      191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251 };
+
+  /** HAC (Handbook of Applied Cryptography), Alfred Menezes & al. Table 4.4. */
+  private static final int[] k =
+      {100,150,200,250,300,350,400,500,600,800,1250, Integer.MAX_VALUE};
+  private static final int[] t =
+      { 27, 18, 15, 12,  9,  8,  7,  6,  5,  4,   3, 2};
+
+  private BigInteger()
+  {
+    //super();
+
+    /*if (USING_NATIVE)
+      mpz = new GMP();*/
+  }
+
+  /* Create a new (non-shared) BigInteger, and initialize to an int. */
+  private BigInteger(int value)
+  {
+    //super();
+
+    ival = value;
+  }
+
+  public BigInteger(String s, int radix)
+  {
+    this();
+
+    int len = s.length();
+    int i, digit;
+    boolean negative;
+    byte[] bytes;
+    char ch = s.charAt(0);
+    if (ch == '-')
+      {
+        negative = true;
+        i = 1;
+        bytes = new byte[len - 1];
+      }
+    else
+      {
+        negative = false;
+        i = 0;
+        bytes = new byte[len];
+      }
+    int byte_len = 0;
+    for ( ; i < len;  i++)
+      {
+        ch = s.charAt(i);
+        digit = Character.digit(ch, radix);
+        if (digit < 0)
+          throw new Error/*NumberFormatException*/("Invalid character at position #" + i);
+        bytes[byte_len++] = (byte) digit;
+      }
+
+    /*if (USING_NATIVE)
+      {
+        bytes = null;
+        if (mpz.fromString(s, radix) != 0)
+          throw new NumberFormatException("String \"" + s
+                                          + "\" is NOT a valid number in base "
+                                          + radix);
+      }
+    else*/
+      {
+        BigInteger result;
+        // Testing (len < MPN.chars_per_word(radix)) would be more accurate,
+        // but slightly more expensive, for little practical gain.
+        if (len <= 15 && radix <= 16)
+          result = valueOf(Long.parseLong(s, radix));
+        else
+          result = valueOf(bytes, byte_len, negative, radix);
+
+        this.ival = result.ival;
+        this.words = result.words;
+      }
+  }
+
+  public BigInteger(String val)
+  {
+    this(val, 10);
+  }
+
+  /* Create a new (non-shared) BigInteger, and initialize from a byte array. */
+  public BigInteger(byte[] val)
+  {
+    this();
+
+    if (val == null || val.length < 1)
+      throw new Error("Number Format Exception")/*NumberFormatException()*/;
+
+    /*if (USING_NATIVE)
+      mpz.fromByteArray(val);
+    else*/
+      {
+        words = byteArrayToIntArray(val, val[0] < 0 ? -1 : 0);
+        BigInteger result = make(words, words.length);
+        this.ival = result.ival;
+        this.words = result.words;
+      }
+  }
+
+  public BigInteger(int signum, byte[] magnitude)
+  {
+    this();
+
+    if (magnitude == null || signum > 1 || signum < -1)
+      throw new Error("Number Format Exception")/*NumberFormatException()*/;
+
+    if (signum == 0)
+      {
+       int i;
+       for (i = magnitude.length - 1; i >= 0 && magnitude[i] == 0; --i)
+         ;
+       if (i >= 0)
+         throw new Error("Number Format Exception")/*NumberFormatException()*/;
+        return;
+      }
+
+    /*if (USING_NATIVE)
+      mpz.fromSignedMagnitude(magnitude, signum == -1);
+    else*/
+      {
+        // Magnitude is always positive, so don't ever pass a sign of -1.
+        words = byteArrayToIntArray(magnitude, 0);
+        BigInteger result = make(words, words.length);
+        this.ival = result.ival;
+        this.words = result.words;
+
+        if (signum < 0)
+          setNegative();
+      }
+  }
+
+  public BigInteger(int numBits, Random rnd)
+  {
+    this();
+
+    if (numBits < 0)
+      throw new Error("Illegal Argument Exception")/*IllegalArgumentException()*/;
+
+    init(numBits, rnd);
+  }
+
+  private void init(int numBits, Random rnd)
+  {
+    /*if (USING_NATIVE)
+      {
+        int length = (numBits + 7) / 8;
+        byte[] magnitude = new byte[length];
+        rnd.nextBytes(magnitude);
+        int discardedBitCount = numBits % 8;
+        if (discardedBitCount != 0)
+          {
+            discardedBitCount = 8 - discardedBitCount;
+            magnitude[0] = (byte)((magnitude[0] & 0xFF) >>> discardedBitCount);
+          }
+        mpz.fromSignedMagnitude(magnitude, false);
+        magnitude = null;
+        return;
+      }*/
+
+    int highbits = numBits & 31;
+    // minimum number of bytes to store the above number of bits
+    int highBitByteCount = (highbits + 7) / 8;
+    // number of bits to discard from the last byte
+    int discardedBitCount = highbits % 8;
+    if (discardedBitCount != 0)
+      discardedBitCount = 8 - discardedBitCount;
+    byte[] highBitBytes = new byte[highBitByteCount];
+    if (highbits > 0)
+      {
+        rnd.nextBytes(highBitBytes);
+        highbits = (highBitBytes[highBitByteCount - 1] & 0xFF) >>> discardedBitCount;
+        for (int i = highBitByteCount - 2; i >= 0; i--)
+          highbits = (highbits << 8) | (highBitBytes[i] & 0xFF);
+      }
+    int nwords = numBits / 32;
+
+    while (highbits == 0 && nwords > 0)
+      {
+       highbits = rnd.nextInt();
+       --nwords;
+      }
+    if (nwords == 0 && highbits >= 0)
+      {
+       ival = highbits;
+      }
+    else
+      {
+       ival = highbits < 0 ? nwords + 2 : nwords + 1;
+       words = new int[ival];
+       words[nwords] = highbits;
+       while (--nwords >= 0)
+         words[nwords] = rnd.nextInt();
+      }
+  }
+
+  public BigInteger(int bitLength, int certainty, Random rnd)
+  {
+    this();
+
+    BigInteger result = new BigInteger();
+    while (true)
+      {
+        result.init(bitLength, rnd);
+        result = result.setBit(bitLength - 1);
+        if (result.isProbablePrime(certainty))
+          break;
+      }
+
+    /*if (USING_NATIVE)
+      mpz.fromBI(result.mpz);
+    else*/
+      {
+        this.ival = result.ival;
+        this.words = result.words;
+      }
+  }
+
+  /** 
+   *  Return a BigInteger that is bitLength bits long with a
+   *  probability < 2^-100 of being composite.
+   *
+   *  @param bitLength length in bits of resulting number
+   *  @param rnd random number generator to use
+   *  @throws ArithmeticException if bitLength < 2
+   *  @since 1.4
+   */
+  public static BigInteger probablePrime(int bitLength, Random rnd)
+  {
+    if (bitLength < 2)
+      throw new Error("Arithmetic Exception")/*ArithmeticException()*/;
+
+    return new BigInteger(bitLength, 100, rnd);
+  }
+
+  /** Return a (possibly-shared) BigInteger with a given long value. */
+  public static BigInteger valueOf(long val)
+  {
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+        result.mpz.fromLong(val);
+        return result;
+      }*/
+
+    if (val >= minFixNum && val <= maxFixNum)
+      return smallFixNums[(int) val - minFixNum];
+    int i = (int) val;
+    if ((long) i == val)
+      return new BigInteger(i);
+    BigInteger result = alloc(2);
+    result.ival = 2;
+    result.words[0] = i;
+    result.words[1] = (int)(val >> 32);
+    return result;
+  }
+
+  /**
+   * @return <code>true</code> if the GMP-based native implementation library
+   *         was successfully loaded. Returns <code>false</code> otherwise.
+   */
+  /*private static boolean initializeLibrary()
+  {
+    boolean result;
+    try
+    {
+      System.loadLibrary("javamath");
+      GMP.natInitializeLibrary();
+      result = true;
+    }
+    catch (Throwable x)
+    {
+      result = false;
+      if (Configuration.DEBUG)
+        {
+          log.info("Unable to use native BigInteger: " + x);
+          log.info("Will use a pure Java implementation instead");
+        }
+    }
+    return result;
+  }*/
+
+  /** Make a canonicalized BigInteger from an array of words.
+   * The array may be reused (without copying). */
+  private static BigInteger make(int[] words, int len)
+  {
+    if (words == null)
+      return valueOf(len);
+    len = BigInteger.wordsNeeded(words, len);
+    if (len <= 1)
+      return len == 0 ? ZERO : valueOf(words[0]);
+    BigInteger num = new BigInteger();
+    num.words = words;
+    num.ival = len;
+    return num;
+  }
+
+  /** Convert a big-endian byte array to a little-endian array of words. */
+  private static int[] byteArrayToIntArray(byte[] bytes, int sign)
+  {
+    // Determine number of words needed.
+    int[] words = new int[bytes.length/4 + 1];
+    int nwords = words.length;
+
+    // Create a int out of modulo 4 high order bytes.
+    int bptr = 0;
+    int word = sign;
+    for (int i = bytes.length % 4; i > 0; --i, bptr++)
+      word = (word << 8) | (bytes[bptr] & 0xff);
+    words[--nwords] = word;
+
+    // Elements remaining in byte[] are a multiple of 4.
+    while (nwords > 0)
+      words[--nwords] = bytes[bptr++] << 24 |
+                       (bytes[bptr++] & 0xff) << 16 |
+                       (bytes[bptr++] & 0xff) << 8 |
+                       (bytes[bptr++] & 0xff);
+    return words;
+  }
+
+  /** Allocate a new non-shared BigInteger.
+   * @param nwords number of words to allocate
+   */
+  private static BigInteger alloc(int nwords)
+  {
+    BigInteger result = new BigInteger();
+    if (nwords > 1)
+    result.words = new int[nwords];
+    return result;
+  }
+
+  /** Change words.length to nwords.
+   * We allow words.length to be upto nwords+2 without reallocating.
+   */
+  private void realloc(int nwords)
+  {
+    if (nwords == 0)
+      {
+       if (words != null)
+         {
+           if (ival > 0)
+             ival = words[0];
+           words = null;
+         }
+      }
+    else if (words == null
+            || words.length < nwords
+            || words.length > nwords + 2)
+      {
+       int[] new_words = new int [nwords];
+       if (words == null)
+         {
+           new_words[0] = ival;
+           ival = 1;
+         }
+       else
+         {
+           if (nwords < ival)
+             ival = nwords;
+           System.arraycopy(words, 0, new_words, 0, ival);
+         }
+       words = new_words;
+      }
+  }
+
+  private boolean isNegative()
+  {
+    return (words == null ? ival : words[ival - 1]) < 0;
+  }
+
+  public int signum()
+  {
+    /*if (USING_NATIVE)
+      return mpz.compare(ZERO.mpz);*/
+
+    if (ival == 0 && words == null)
+      return 0;
+    int top = words == null ? ival : words[ival-1];
+    return top < 0 ? -1 : 1;
+  }
+
+  private static int compareTo(BigInteger x, BigInteger y)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = y.signum; // force NPE check
+        return x.mpz.compare(y.mpz);
+      }*/
+
+    if (x.words == null && y.words == null)
+      return x.ival < y.ival ? -1 : x.ival > y.ival ? 1 : 0;
+    boolean x_negative = x.isNegative();
+    boolean y_negative = y.isNegative();
+    if (x_negative != y_negative)
+      return x_negative ? -1 : 1;
+    int x_len = x.words == null ? 1 : x.ival;
+    int y_len = y.words == null ? 1 : y.ival;
+    if (x_len != y_len)
+      return (x_len > y_len) != x_negative ? 1 : -1;
+    return MPN.cmp(x.words, y.words, x_len);
+  }
+
+  /** @since 1.2 */
+  public int compareTo(BigInteger val)
+  {
+    return compareTo(this, val);
+  }
+
+  public BigInteger min(BigInteger val)
+  {
+    return compareTo(this, val) < 0 ? this : val;
+  }
+
+  public BigInteger max(BigInteger val)
+  {
+    return compareTo(this, val) > 0 ? this : val;
+  }
+
+  private boolean isZero()
+  {
+    return words == null && ival == 0;
+  }
+
+  private boolean isOne()
+  {
+    return words == null && ival == 1;
+  }
+
+  /** Calculate how many words are significant in words[0:len-1].
+   * Returns the least value x such that x>0 && words[0:x-1]==words[0:len-1],
+   * when words is viewed as a 2's complement integer.
+   */
+  private static int wordsNeeded(int[] words, int len)
+  {
+    int i = len;
+    if (i > 0)
+      {
+       int word = words[--i];
+       if (word == -1)
+         {
+           while (i > 0 && (word = words[i - 1]) < 0)
+             {
+               i--;
+               if (word != -1) break;
+             }
+         }
+       else
+         {
+           while (word == 0 && i > 0 && (word = words[i - 1]) >= 0)  i--;
+         }
+      }
+    return i + 1;
+  }
+
+  private BigInteger canonicalize()
+  {
+    if (words != null
+       && (ival = BigInteger.wordsNeeded(words, ival)) <= 1)
+      {
+       if (ival == 1)
+         ival = words[0];
+       words = null;
+      }
+    if (words == null && ival >= minFixNum && ival <= maxFixNum)
+      return smallFixNums[ival - minFixNum];
+    return this;
+  }
+
+  /** Add two ints, yielding a BigInteger. */
+  private static BigInteger add(int x, int y)
+  {
+    return valueOf((long) x + (long) y);
+  }
+
+  /** Add a BigInteger and an int, yielding a new BigInteger. */
+  private static BigInteger add(BigInteger x, int y)
+  {
+    if (x.words == null)
+      return BigInteger.add(x.ival, y);
+    BigInteger result = new BigInteger(0);
+    result.setAdd(x, y);
+    return result.canonicalize();
+  }
+
+  /** Set this to the sum of x and y.
+   * OK if x==this. */
+  private void setAdd(BigInteger x, int y)
+  {
+    if (x.words == null)
+      {
+       set((long) x.ival + (long) y);
+       return;
+      }
+    int len = x.ival;
+    realloc(len + 1);
+    long carry = y;
+    for (int i = 0;  i < len;  i++)
+      {
+       carry += ((long) x.words[i] & 0xffffffffL);
+       words[i] = (int) carry;
+       carry >>= 32;
+      }
+    if (x.words[len - 1] < 0)
+      carry--;
+    words[len] = (int) carry;
+    ival = wordsNeeded(words, len + 1);
+  }
+
+  /** Destructively add an int to this. */
+  private void setAdd(int y)
+  {
+    setAdd(this, y);
+  }
+
+  /** Destructively set the value of this to a long. */
+  private void set(long y)
+  {
+    int i = (int) y;
+    if ((long) i == y)
+      {
+       ival = i;
+       words = null;
+      }
+    else
+      {
+       realloc(2);
+       words[0] = i;
+       words[1] = (int) (y >> 32);
+       ival = 2;
+      }
+  }
+
+  /** Destructively set the value of this to the given words.
+  * The words array is reused, not copied. */
+  private void set(int[] words, int length)
+  {
+    this.ival = length;
+    this.words = words;
+  }
+
+  /** Destructively set the value of this to that of y. */
+  private void set(BigInteger y)
+  {
+    if (y.words == null)
+      set(y.ival);
+    else if (this != y)
+      {
+       realloc(y.ival);
+       System.arraycopy(y.words, 0, words, 0, y.ival);
+       ival = y.ival;
+      }
+  }
+
+  /** Add two BigIntegers, yielding their sum as another BigInteger. */
+  private static BigInteger add(BigInteger x, BigInteger y, int k)
+  {
+    if (x.words == null && y.words == null)
+      return valueOf((long) k * (long) y.ival + (long) x.ival);
+    if (k != 1)
+      {
+       if (k == -1)
+         y = BigInteger.neg(y);
+       else
+         y = BigInteger.times(y, valueOf(k));
+      }
+    if (x.words == null)
+      return BigInteger.add(y, x.ival);
+    if (y.words == null)
+      return BigInteger.add(x, y.ival);
+    // Both are big
+    if (y.ival > x.ival)
+      { // Swap so x is longer then y.
+       BigInteger tmp = x;  x = y;  y = tmp;
+      }
+    BigInteger result = alloc(x.ival + 1);
+    int i = y.ival;
+    long carry = MPN.add_n(result.words, x.words, y.words, i);
+    long y_ext = y.words[i - 1] < 0 ? 0xffffffffL : 0;
+    for (; i < x.ival;  i++)
+      {
+       carry += ((long) x.words[i] & 0xffffffffL) + y_ext;
+       result.words[i] = (int) carry;
+       carry >>>= 32;
+      }
+    if (x.words[i - 1] < 0)
+      y_ext--;
+    result.words[i] = (int) (carry + y_ext);
+    result.ival = i+1;
+    return result.canonicalize();
+  }
+
+  public BigInteger add(BigInteger val)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = val.signum; // force NPE check
+        BigInteger result = new BigInteger();
+        mpz.add(val.mpz, result.mpz);
+        return result;
+      }*/
+
+    return add(this, val, 1);
+  }
+
+  public BigInteger subtract(BigInteger val)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = val.signum; // force NPE check
+        BigInteger result = new BigInteger();
+        mpz.subtract(val.mpz, result.mpz);
+        return result;
+      }*/
+
+    return add(this, val, -1);
+  }
+
+  private static BigInteger times(BigInteger x, int y)
+  {
+    if (y == 0)
+      return ZERO;
+    if (y == 1)
+      return x;
+    int[] xwords = x.words;
+    int xlen = x.ival;
+    if (xwords == null)
+      return valueOf((long) xlen * (long) y);
+    boolean negative;
+    BigInteger result = BigInteger.alloc(xlen + 1);
+    if (xwords[xlen - 1] < 0)
+      {
+       negative = true;
+       negate(result.words, xwords, xlen);
+       xwords = result.words;
+      }
+    else
+      negative = false;
+    if (y < 0)
+      {
+       negative = !negative;
+       y = -y;
+      }
+    result.words[xlen] = MPN.mul_1(result.words, xwords, xlen, y);
+    result.ival = xlen + 1;
+    if (negative)
+      result.setNegative();
+    return result.canonicalize();
+  }
+
+  private static BigInteger times(BigInteger x, BigInteger y)
+  {
+    if (y.words == null)
+      return times(x, y.ival);
+    if (x.words == null)
+      return times(y, x.ival);
+    boolean negative = false;
+    int[] xwords;
+    int[] ywords;
+    int xlen = x.ival;
+    int ylen = y.ival;
+    if (x.isNegative())
+      {
+       negative = true;
+       xwords = new int[xlen];
+       negate(xwords, x.words, xlen);
+      }
+    else
+      {
+       negative = false;
+       xwords = x.words;
+      }
+    if (y.isNegative())
+      {
+       negative = !negative;
+       ywords = new int[ylen];
+       negate(ywords, y.words, ylen);
+      }
+    else
+      ywords = y.words;
+    // Swap if x is shorter then y.
+    if (xlen < ylen)
+      {
+       int[] twords = xwords;  xwords = ywords;  ywords = twords;
+       int tlen = xlen;  xlen = ylen;  ylen = tlen;
+      }
+    BigInteger result = BigInteger.alloc(xlen+ylen);
+    MPN.mul(result.words, xwords, xlen, ywords, ylen);
+    result.ival = xlen+ylen;
+    if (negative)
+      result.setNegative();
+    return result.canonicalize();
+  }
+
+  public BigInteger multiply(BigInteger y)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = y.signum; // force NPE check
+        BigInteger result = new BigInteger();
+        mpz.multiply(y.mpz, result.mpz);
+        return result;
+      }*/
+
+    return times(this, y);
+  }
+
+  private static void divide(long x, long y,
+                            BigInteger quotient, BigInteger remainder,
+                            int rounding_mode)
+  {
+    boolean xNegative, yNegative;
+    if (x < 0)
+      {
+       xNegative = true;
+       if (x == Long.MIN_VALUE)
+         {
+           divide(valueOf(x), valueOf(y),
+                  quotient, remainder, rounding_mode);
+           return;
+         }
+       x = -x;
+      }
+    else
+      xNegative = false;
+
+    if (y < 0)
+      {
+       yNegative = true;
+       if (y == Long.MIN_VALUE)
+         {
+           if (rounding_mode == TRUNCATE)
+             { // x != Long.Min_VALUE implies abs(x) < abs(y)
+               if (quotient != null)
+                 quotient.set(0);
+               if (remainder != null)
+                 remainder.set(x);
+             }
+           else
+             divide(valueOf(x), valueOf(y),
+                     quotient, remainder, rounding_mode);
+           return;
+         }
+       y = -y;
+      }
+    else
+      yNegative = false;
+
+    long q = x / y;
+    long r = x % y;
+    boolean qNegative = xNegative ^ yNegative;
+
+    boolean add_one = false;
+    if (r != 0)
+      {
+       switch (rounding_mode)
+         {
+         case TRUNCATE:
+           break;
+         case CEILING:
+         case FLOOR:
+           if (qNegative == (rounding_mode == FLOOR))
+             add_one = true;
+           break;
+         case ROUND:
+           add_one = r > ((y - (q & 1)) >> 1);
+           break;
+         }
+      }
+    if (quotient != null)
+      {
+       if (add_one)
+         q++;
+       if (qNegative)
+         q = -q;
+       quotient.set(q);
+      }
+    if (remainder != null)
+      {
+       // The remainder is by definition: X-Q*Y
+       if (add_one)
+         {
+           // Subtract the remainder from Y.
+           r = y - r;
+           // In this case, abs(Q*Y) > abs(X).
+           // So sign(remainder) = -sign(X).
+           xNegative = ! xNegative;
+         }
+       else
+         {
+           // If !add_one, then: abs(Q*Y) <= abs(X).
+           // So sign(remainder) = sign(X).
+         }
+       if (xNegative)
+         r = -r;
+       remainder.set(r);
+      }
+  }
+
+  /** Divide two integers, yielding quotient and remainder.
+   * @param x the numerator in the division
+   * @param y the denominator in the division
+   * @param quotient is set to the quotient of the result (iff quotient!=null)
+   * @param remainder is set to the remainder of the result
+   *  (iff remainder!=null)
+   * @param rounding_mode one of FLOOR, CEILING, TRUNCATE, or ROUND.
+   */
+  private static void divide(BigInteger x, BigInteger y,
+                            BigInteger quotient, BigInteger remainder,
+                            int rounding_mode)
+  {
+    if ((x.words == null || x.ival <= 2)
+       && (y.words == null || y.ival <= 2))
+      {
+       long x_l = x.longValue();
+       long y_l = y.longValue();
+       if (x_l != Long.MIN_VALUE && y_l != Long.MIN_VALUE)
+         {
+           divide(x_l, y_l, quotient, remainder, rounding_mode);
+           return;
+         }
+      }
+
+    boolean xNegative = x.isNegative();
+    boolean yNegative = y.isNegative();
+    boolean qNegative = xNegative ^ yNegative;
+
+    int ylen = y.words == null ? 1 : y.ival;
+    int[] ywords = new int[ylen];
+    y.getAbsolute(ywords);
+    while (ylen > 1 && ywords[ylen - 1] == 0)  ylen--;
+
+    int xlen = x.words == null ? 1 : x.ival;
+    int[] xwords = new int[xlen+2];
+    x.getAbsolute(xwords);
+    while (xlen > 1 && xwords[xlen-1] == 0)  xlen--;
+
+    int qlen, rlen;
+
+    int cmpval = MPN.cmp(xwords, xlen, ywords, ylen);
+    if (cmpval < 0)  // abs(x) < abs(y)
+      { // quotient = 0;  remainder = num.
+       int[] rwords = xwords;  xwords = ywords;  ywords = rwords;
+       rlen = xlen;  qlen = 1;  xwords[0] = 0;
+      }
+    else if (cmpval == 0)  // abs(x) == abs(y)
+      {
+       xwords[0] = 1;  qlen = 1;  // quotient = 1
+       ywords[0] = 0;  rlen = 1;  // remainder = 0;
+      }
+    else if (ylen == 1)
+      {
+       qlen = xlen;
+       // Need to leave room for a word of leading zeros if dividing by 1
+       // and the dividend has the high bit set.  It might be safe to
+       // increment qlen in all cases, but it certainly is only necessary
+       // in the following case.
+       if (ywords[0] == 1 && xwords[xlen-1] < 0)
+         qlen++;
+       rlen = 1;
+       ywords[0] = MPN.divmod_1(xwords, xwords, xlen, ywords[0]);
+      }
+    else  // abs(x) > abs(y)
+      {
+       // Normalize the denominator, i.e. make its most significant bit set by
+       // shifting it normalization_steps bits to the left.  Also shift the
+       // numerator the same number of steps (to keep the quotient the same!).
+
+       int nshift = MPN.count_leading_zeros(ywords[ylen - 1]);
+       if (nshift != 0)
+         {
+           // Shift up the denominator setting the most significant bit of
+           // the most significant word.
+           MPN.lshift(ywords, 0, ywords, ylen, nshift);
+
+           // Shift up the numerator, possibly introducing a new most
+           // significant word.
+           int x_high = MPN.lshift(xwords, 0, xwords, xlen, nshift);
+           xwords[xlen++] = x_high;
+         }
+
+       if (xlen == ylen)
+         xwords[xlen++] = 0;
+       MPN.divide(xwords, xlen, ywords, ylen);
+       rlen = ylen;
+       MPN.rshift0 (ywords, xwords, 0, rlen, nshift);
+
+       qlen = xlen + 1 - ylen;
+       if (quotient != null)
+         {
+           for (int i = 0;  i < qlen;  i++)
+             xwords[i] = xwords[i+ylen];
+         }
+      }
+
+    if (ywords[rlen-1] < 0)
+      {
+        ywords[rlen] = 0;
+        rlen++;
+      }
+
+    // Now the quotient is in xwords, and the remainder is in ywords.
+
+    boolean add_one = false;
+    if (rlen > 1 || ywords[0] != 0)
+      { // Non-zero remainder i.e. in-exact quotient.
+       switch (rounding_mode)
+         {
+         case TRUNCATE:
+           break;
+         case CEILING:
+         case FLOOR:
+           if (qNegative == (rounding_mode == FLOOR))
+             add_one = true;
+           break;
+         case ROUND:
+           // int cmp = compareTo(remainder<<1, abs(y));
+           BigInteger tmp = remainder == null ? new BigInteger() : remainder;
+           tmp.set(ywords, rlen);
+           tmp = shift(tmp, 1);
+           if (yNegative)
+             tmp.setNegative();
+           int cmp = compareTo(tmp, y);
+           // Now cmp == compareTo(sign(y)*(remainder<<1), y)
+           if (yNegative)
+             cmp = -cmp;
+           add_one = (cmp == 1) || (cmp == 0 && (xwords[0]&1) != 0);
+         }
+      }
+    if (quotient != null)
+      {
+       quotient.set(xwords, qlen);
+       if (qNegative)
+         {
+           if (add_one)  // -(quotient + 1) == ~(quotient)
+             quotient.setInvert();
+           else
+             quotient.setNegative();
+         }
+       else if (add_one)
+         quotient.setAdd(1);
+      }
+    if (remainder != null)
+      {
+       // The remainder is by definition: X-Q*Y
+       remainder.set(ywords, rlen);
+       if (add_one)
+         {
+           // Subtract the remainder from Y:
+           // abs(R) = abs(Y) - abs(orig_rem) = -(abs(orig_rem) - abs(Y)).
+           BigInteger tmp;
+           if (y.words == null)
+             {
+               tmp = remainder;
+               tmp.set(yNegative ? ywords[0] + y.ival : ywords[0] - y.ival);
+             }
+           else
+             tmp = BigInteger.add(remainder, y, yNegative ? 1 : -1);
+           // Now tmp <= 0.
+           // In this case, abs(Q) = 1 + floor(abs(X)/abs(Y)).
+           // Hence, abs(Q*Y) > abs(X).
+           // So sign(remainder) = -sign(X).
+           if (xNegative)
+             remainder.setNegative(tmp);
+           else
+             remainder.set(tmp);
+         }
+       else
+         {
+           // If !add_one, then: abs(Q*Y) <= abs(X).
+           // So sign(remainder) = sign(X).
+           if (xNegative)
+             remainder.setNegative();
+         }
+      }
+  }
+
+  public BigInteger divide(BigInteger val)
+  {
+    /*if (USING_NATIVE)
+      {
+        if (val.compareTo(ZERO) == 0)
+          throw new ArithmeticException("divisor is zero");
+
+        BigInteger result = new BigInteger();
+        mpz.quotient(val.mpz, result.mpz);
+        return result;
+      }*/
+
+    if (val.isZero())
+      throw new Error/*ArithmeticException*/("divisor is zero");
+
+    BigInteger quot = new BigInteger();
+    divide(this, val, quot, null, TRUNCATE);
+    return quot.canonicalize();
+  }
+
+  public BigInteger remainder(BigInteger val)
+  {
+    /*if (USING_NATIVE)
+      {
+        if (val.compareTo(ZERO) == 0)
+          throw new ArithmeticException("divisor is zero");
+
+        BigInteger result = new BigInteger();
+        mpz.remainder(val.mpz, result.mpz);
+        return result;
+      }*/
+
+    if (val.isZero())
+      throw new Error/*ArithmeticException*/("divisor is zero");
+
+    BigInteger rem = new BigInteger();
+    divide(this, val, null, rem, TRUNCATE);
+    return rem.canonicalize();
+  }
+
+  public BigInteger[] divideAndRemainder(BigInteger val)
+  {
+    /*if (USING_NATIVE)
+      {
+        if (val.compareTo(ZERO) == 0)
+          throw new ArithmeticException("divisor is zero");
+
+        BigInteger q = new BigInteger();
+        BigInteger r = new BigInteger();
+        mpz.quotientAndRemainder(val.mpz, q.mpz, r.mpz);
+        return new BigInteger[] { q, r };
+      }*/
+
+    if (val.isZero())
+      throw new Error/*ArithmeticException*/("divisor is zero");
+
+    BigInteger[] result = new BigInteger[2];
+    result[0] = new BigInteger();
+    result[1] = new BigInteger();
+    divide(this, val, result[0], result[1], TRUNCATE);
+    result[0].canonicalize();
+    result[1].canonicalize();
+    return result;
+  }
+
+  public BigInteger mod(BigInteger m)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = m.signum; // force NPE check
+        if (m.compareTo(ZERO) < 1)
+          throw new ArithmeticException("non-positive modulus");
+
+        BigInteger result = new BigInteger();
+        mpz.modulo(m.mpz, result.mpz);
+        return result;
+      }*/
+
+    if (m.isNegative() || m.isZero())
+      throw new Error/*ArithmeticException*/("non-positive modulus");
+
+    BigInteger rem = new BigInteger();
+    divide(this, m, null, rem, FLOOR);
+    return rem.canonicalize();
+  }
+
+  /** Calculate the integral power of a BigInteger.
+   * @param exponent the exponent (must be non-negative)
+   */
+  public BigInteger pow(int exponent)
+  {
+    if (exponent <= 0)
+      {
+       if (exponent == 0)
+         return ONE;
+         throw new Error/*ArithmeticException*/("negative exponent");
+      }
+
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+        mpz.pow(exponent, result.mpz);
+        return result;
+      }*/
+
+    if (isZero())
+      return this;
+    int plen = words == null ? 1 : ival;  // Length of pow2.
+    int blen = ((bitLength() * exponent) >> 5) + 2 * plen;
+    boolean negative = isNegative() && (exponent & 1) != 0;
+    int[] pow2 = new int [blen];
+    int[] rwords = new int [blen];
+    int[] work = new int [blen];
+    getAbsolute(pow2); // pow2 = abs(this);
+    int rlen = 1;
+    rwords[0] = 1; // rwords = 1;
+    for (;;)  // for (i = 0;  ; i++)
+      {
+       // pow2 == this**(2**i)
+       // prod = this**(sum(j=0..i-1, (exponent>>j)&1))
+       if ((exponent & 1) != 0)
+         { // r *= pow2
+           MPN.mul(work, pow2, plen, rwords, rlen);
+           int[] temp = work;  work = rwords;  rwords = temp;
+           rlen += plen;
+           while (rwords[rlen - 1] == 0)  rlen--;
+         }
+       exponent >>= 1;
+       if (exponent == 0)
+         break;
+       // pow2 *= pow2;
+       MPN.mul(work, pow2, plen, pow2, plen);
+       int[] temp = work;  work = pow2;  pow2 = temp;  // swap to avoid a copy
+       plen *= 2;
+       while (pow2[plen - 1] == 0)  plen--;
+      }
+    if (rwords[rlen - 1] < 0)
+      rlen++;
+    if (negative)
+      negate(rwords, rwords, rlen);
+    return BigInteger.make(rwords, rlen);
+  }
+
+  private static int[] euclidInv(int a, int b, int prevDiv)
+  {
+    if (b == 0)
+      throw new Error/*ArithmeticException*/("not invertible");
+
+    if (b == 1)
+       // Success:  values are indeed invertible!
+       // Bottom of the recursion reached; start unwinding.
+       return new int[] { -prevDiv, 1 };
+
+    int[] xy = euclidInv(b, a % b, a / b);     // Recursion happens here.
+    a = xy[0]; // use our local copy of 'a' as a work var
+    xy[0] = a * -prevDiv + xy[1];
+    xy[1] = a;
+    return xy;
+  }
+
+  private static void euclidInv(BigInteger a, BigInteger b,
+                                BigInteger prevDiv, BigInteger[] xy)
+  {
+    if (b.isZero())
+      throw new Error/*ArithmeticException*/("not invertible");
+
+    if (b.isOne())
+      {
+       // Success:  values are indeed invertible!
+       // Bottom of the recursion reached; start unwinding.
+       xy[0] = neg(prevDiv);
+        xy[1] = ONE;
+       return;
+      }
+
+    // Recursion happens in the following conditional!
+
+    // If a just contains an int, then use integer math for the rest.
+    if (a.words == null)
+      {
+        int[] xyInt = euclidInv(b.ival, a.ival % b.ival, a.ival / b.ival);
+       xy[0] = new BigInteger(xyInt[0]);
+        xy[1] = new BigInteger(xyInt[1]);
+      }
+    else
+      {
+       BigInteger rem = new BigInteger();
+       BigInteger quot = new BigInteger();
+       divide(a, b, quot, rem, FLOOR);
+        // quot and rem may not be in canonical form. ensure
+        rem.canonicalize();
+        quot.canonicalize();
+       euclidInv(b, rem, quot, xy);
+      }
+
+    BigInteger t = xy[0];
+    xy[0] = add(xy[1], times(t, prevDiv), -1);
+    xy[1] = t;
+  }
+
+  public BigInteger modInverse(BigInteger y)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = y.signum; // force NPE check
+        if (mpz.compare(ZERO.mpz) < 1)
+          throw new ArithmeticException("non-positive modulo");
+
+        BigInteger result = new BigInteger();
+        mpz.modInverse(y.mpz, result.mpz);
+        return result;
+      }*/
+
+    if (y.isNegative() || y.isZero())
+      throw new Error/*ArithmeticException*/("non-positive modulo");
+
+    // Degenerate cases.
+    if (y.isOne())
+      return ZERO;
+    if (isOne())
+      return ONE;
+
+    // Use Euclid's algorithm as in gcd() but do this recursively
+    // rather than in a loop so we can use the intermediate results as we
+    // unwind from the recursion.
+    // Used http://www.math.nmsu.edu/~crypto/EuclideanAlgo.html as reference.
+    BigInteger result = new BigInteger();
+    boolean swapped = false;
+
+    if (y.words == null)
+      {
+       // The result is guaranteed to be less than the modulus, y (which is
+       // an int), so simplify this by working with the int result of this
+       // modulo y.  Also, if this is negative, make it positive via modulo
+       // math.  Note that BigInteger.mod() must be used even if this is
+       // already an int as the % operator would provide a negative result if
+       // this is negative, BigInteger.mod() never returns negative values.
+        int xval = (words != null || isNegative()) ? mod(y).ival : ival;
+        int yval = y.ival;
+
+       // Swap values so x > y.
+       if (yval > xval)
+         {
+           int tmp = xval; xval = yval; yval = tmp;
+           swapped = true;
+         }
+       // Normally, the result is in the 2nd element of the array, but
+       // if originally x < y, then x and y were swapped and the result
+       // is in the 1st element of the array.
+       result.ival =
+         euclidInv(yval, xval % yval, xval / yval)[swapped ? 0 : 1];
+
+       // Result can't be negative, so make it positive by adding the
+       // original modulus, y.ival (not the possibly "swapped" yval).
+       if (result.ival < 0)
+         result.ival += y.ival;
+      }
+    else
+      {
+       // As above, force this to be a positive value via modulo math.
+       BigInteger x = isNegative() ? this.mod(y) : this;
+
+       // Swap values so x > y.
+       if (x.compareTo(y) < 0)
+         {
+           result = x; x = y; y = result; // use 'result' as a work var
+           swapped = true;
+         }
+       // As above (for ints), result will be in the 2nd element unless
+       // the original x and y were swapped.
+       BigInteger rem = new BigInteger();
+       BigInteger quot = new BigInteger();
+       divide(x, y, quot, rem, FLOOR);
+        // quot and rem may not be in canonical form. ensure
+        rem.canonicalize();
+        quot.canonicalize();
+       BigInteger[] xy = new BigInteger[2];
+       euclidInv(y, rem, quot, xy);
+       result = swapped ? xy[0] : xy[1];
+
+       // Result can't be negative, so make it positive by adding the
+       // original modulus, y (which is now x if they were swapped).
+       if (result.isNegative())
+         result = add(result, swapped ? x : y, 1);
+      }
+    
+    return result;
+  }
+
+  public BigInteger modPow(BigInteger exponent, BigInteger m)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = exponent.signum; // force NPE check
+        if (m.mpz.compare(ZERO.mpz) < 1)
+          throw new ArithmeticException("non-positive modulo");
+
+        BigInteger result = new BigInteger();
+        mpz.modPow(exponent.mpz, m.mpz, result.mpz);
+        return result;
+      }*/
+
+    if (m.isNegative() || m.isZero())
+      throw new Error/*ArithmeticException*/("non-positive modulo");
+
+    if (exponent.isNegative())
+      return modInverse(m).modPow(exponent.negate(), m);
+    if (exponent.isOne())
+      return mod(m);
+
+    // To do this naively by first raising this to the power of exponent
+    // and then performing modulo m would be extremely expensive, especially
+    // for very large numbers.  The solution is found in Number Theory
+    // where a combination of partial powers and moduli can be done easily.
+    //
+    // We'll use the algorithm for Additive Chaining which can be found on
+    // p. 244 of "Applied Cryptography, Second Edition" by Bruce Schneier.
+    BigInteger s = ONE;
+    BigInteger t = this;
+    BigInteger u = exponent;
+
+    while (!u.isZero())
+      {
+       if (u.and(ONE).isOne())
+         s = times(s, t).mod(m);
+       u = u.shiftRight(1);
+       t = times(t, t).mod(m);
+      }
+
+    return s;
+  }
+
+  /** Calculate Greatest Common Divisor for non-negative ints. */
+  private static int gcd(int a, int b)
+  {
+    // Euclid's algorithm, copied from libg++.
+    int tmp;
+    if (b > a)
+      {
+       tmp = a; a = b; b = tmp;
+      }
+    for(;;)
+      {
+       if (b == 0)
+         return a;
+        if (b == 1)
+         return b;
+        tmp = b;
+           b = a % b;
+           a = tmp;
+         }
+      }
+
+  public BigInteger gcd(BigInteger y)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = y.signum; // force NPE check
+        BigInteger result = new BigInteger();
+        mpz.gcd(y.mpz, result.mpz);
+        return result;
+      }*/
+
+    int xval = ival;
+    int yval = y.ival;
+    if (words == null)
+      {
+       if (xval == 0)
+         return abs(y);
+       if (y.words == null
+           && xval != Integer.MIN_VALUE && yval != Integer.MIN_VALUE)
+         {
+           if (xval < 0)
+             xval = -xval;
+           if (yval < 0)
+             yval = -yval;
+           return valueOf(gcd(xval, yval));
+         }
+       xval = 1;
+      }
+    if (y.words == null)
+      {
+       if (yval == 0)
+         return abs(this);
+       yval = 1;
+      }
+    int len = (xval > yval ? xval : yval) + 1;
+    int[] xwords = new int[len];
+    int[] ywords = new int[len];
+    getAbsolute(xwords);
+    y.getAbsolute(ywords);
+    len = MPN.gcd(xwords, ywords, len);
+    BigInteger result = new BigInteger(0);
+    result.ival = len;
+    result.words = xwords;
+    return result.canonicalize();
+  }
+
+  /**
+   * <p>Returns <code>true</code> if this BigInteger is probably prime,
+   * <code>false</code> if it's definitely composite. If <code>certainty</code>
+   * is <code><= 0</code>, <code>true</code> is returned.</p>
+   *
+   * @param certainty a measure of the uncertainty that the caller is willing
+   * to tolerate: if the call returns <code>true</code> the probability that
+   * this BigInteger is prime exceeds <code>(1 - 1/2<sup>certainty</sup>)</code>.
+   * The execution time of this method is proportional to the value of this
+   * parameter.
+   * @return <code>true</code> if this BigInteger is probably prime,
+   * <code>false</code> if it's definitely composite.
+   */
+  public boolean isProbablePrime(int certainty)
+  {
+    if (certainty < 1)
+      return true;
+
+    /*if (USING_NATIVE)
+      return mpz.testPrimality(certainty) != 0;*/
+
+    /** We'll use the Rabin-Miller algorithm for doing a probabilistic
+     * primality test.  It is fast, easy and has faster decreasing odds of a
+     * composite passing than with other tests.  This means that this
+     * method will actually have a probability much greater than the
+     * 1 - .5^certainty specified in the JCL (p. 117), but I don't think
+     * anyone will complain about better performance with greater certainty.
+     *
+     * The Rabin-Miller algorithm can be found on pp. 259-261 of "Applied
+     * Cryptography, Second Edition" by Bruce Schneier.
+     */
+
+    // First rule out small prime factors
+    BigInteger rem = new BigInteger();
+    int i;
+    for (i = 0; i < primes.length; i++)
+      {
+       if (words == null && ival == primes[i])
+         return true;
+
+        divide(this, smallFixNums[primes[i] - minFixNum], null, rem, TRUNCATE);
+        if (rem.canonicalize().isZero())
+         return false;
+      }
+
+    // Now perform the Rabin-Miller test.
+
+    // Set b to the number of times 2 evenly divides (this - 1).
+    // I.e. 2^b is the largest power of 2 that divides (this - 1).
+    BigInteger pMinus1 = add(this, -1);
+    int b = pMinus1.getLowestSetBit();
+
+    // Set m such that this = 1 + 2^b * m.
+    BigInteger m = pMinus1.divide(valueOf(2L).pow(b));
+
+    // The HAC (Handbook of Applied Cryptography), Alfred Menezes & al. Note
+    // 4.49 (controlling the error probability) gives the number of trials
+    // for an error probability of 1/2**80, given the number of bits in the
+    // number to test.  we shall use these numbers as is if/when 'certainty'
+    // is less or equal to 80, and twice as much if it's greater.
+    int bits = this.bitLength();
+    for (i = 0; i < k.length; i++)
+      if (bits <= k[i])
+        break;
+    int trials = t[i];
+    if (certainty > 80)
+      trials *= 2;
+    BigInteger z;
+    for (int t = 0; t < trials; t++)
+      {
+        // The HAC (Handbook of Applied Cryptography), Alfred Menezes & al.
+        // Remark 4.28 states: "...A strategy that is sometimes employed
+        // is to fix the bases a to be the first few primes instead of
+        // choosing them at random.
+       z = smallFixNums[primes[t] - minFixNum].modPow(m, this);
+       if (z.isOne() || z.equals(pMinus1))
+         continue;                     // Passes the test; may be prime.
+
+       for (i = 0; i < b; )
+         {
+           if (z.isOne())
+             return false;
+           i++;
+           if (z.equals(pMinus1))
+             break;                    // Passes the test; may be prime.
+
+           z = z.modPow(valueOf(2), this);
+         }
+
+       if (i == b && !z.equals(pMinus1))
+         return false;
+      }
+    return true;
+  }
+
+  private void setInvert()
+  {
+    if (words == null)
+      ival = ~ival;
+    else
+      {
+       for (int i = ival;  --i >= 0; )
+         words[i] = ~words[i];
+      }
+  }
+
+  private void setShiftLeft(BigInteger x, int count)
+  {
+    int[] xwords;
+    int xlen;
+    if (x.words == null)
+      {
+       if (count < 32)
+         {
+           set((long) x.ival << count);
+           return;
+         }
+       xwords = new int[1];
+       xwords[0] = x.ival;
+       xlen = 1;
+      }
+    else
+      {
+       xwords = x.words;
+       xlen = x.ival;
+      }
+    int word_count = count >> 5;
+    count &= 31;
+    int new_len = xlen + word_count;
+    if (count == 0)
+      {
+       realloc(new_len);
+       for (int i = xlen;  --i >= 0; )
+         words[i+word_count] = xwords[i];
+      }
+    else
+      {
+       new_len++;
+       realloc(new_len);
+       int shift_out = MPN.lshift(words, word_count, xwords, xlen, count);
+       count = 32 - count;
+       words[new_len-1] = (shift_out << count) >> count;  // sign-extend.
+      }
+    ival = new_len;
+    for (int i = word_count;  --i >= 0; )
+      words[i] = 0;
+  }
+
+  private void setShiftRight(BigInteger x, int count)
+  {
+    if (x.words == null)
+      set(count < 32 ? x.ival >> count : x.ival < 0 ? -1 : 0);
+    else if (count == 0)
+      set(x);
+    else
+      {
+       boolean neg = x.isNegative();
+       int word_count = count >> 5;
+       count &= 31;
+       int d_len = x.ival - word_count;
+       if (d_len <= 0)
+         set(neg ? -1 : 0);
+       else
+         {
+           if (words == null || words.length < d_len)
+             realloc(d_len);
+           MPN.rshift0 (words, x.words, word_count, d_len, count);
+           ival = d_len;
+           if (neg)
+             words[d_len-1] |= -2 << (31 - count);
+         }
+      }
+  }
+
+  private void setShift(BigInteger x, int count)
+  {
+    if (count > 0)
+      setShiftLeft(x, count);
+    else
+      setShiftRight(x, -count);
+  }
+
+  private static BigInteger shift(BigInteger x, int count)
+  {
+    if (x.words == null)
+      {
+       if (count <= 0)
+         return valueOf(count > -32 ? x.ival >> (-count) : x.ival < 0 ? -1 : 0);
+       if (count < 32)
+         return valueOf((long) x.ival << count);
+      }
+    if (count == 0)
+      return x;
+    BigInteger result = new BigInteger(0);
+    result.setShift(x, count);
+    return result.canonicalize();
+  }
+
+  public BigInteger shiftLeft(int n)
+  {
+    if (n == 0)
+      return this;
+
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+        if (n < 0)
+          mpz.shiftRight(-n, result.mpz);
+        else
+          mpz.shiftLeft(n, result.mpz);
+        return result;
+      }*/
+
+    return shift(this, n);
+  }
+
+  public BigInteger shiftRight(int n)
+  {
+    if (n == 0)
+      return this;
+
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+        if (n < 0)
+          mpz.shiftLeft(-n, result.mpz);
+        else
+          mpz.shiftRight(n, result.mpz);
+        return result;
+      }*/
+
+    return shift(this, -n);
+  }
+
+  /*private void format(int radix, CPStringBuilder buffer)
+  {
+    if (words == null)
+      buffer.append(Integer.toString(ival, radix));
+    else if (ival <= 2)
+      buffer.append(Long.toString(longValue(), radix));
+    else
+      {
+       boolean neg = isNegative();
+       int[] work;
+       if (neg || radix != 16)
+         {
+           work = new int[ival];
+           getAbsolute(work);
+         }
+       else
+         work = words;
+       int len = ival;
+
+       if (radix == 16)
+         {
+           if (neg)
+             buffer.append('-');
+           int buf_start = buffer.length();
+           for (int i = len;  --i >= 0; )
+             {
+               int word = work[i];
+               for (int j = 8;  --j >= 0; )
+                 {
+                   int hex_digit = (word >> (4 * j)) & 0xF;
+                   // Suppress leading zeros:
+                   if (hex_digit > 0 || buffer.length() > buf_start)
+                     buffer.append(Character.forDigit(hex_digit, 16));
+                 }
+             }
+         }
+       else
+         {
+           int i = buffer.length();
+           for (;;)
+             {
+               int digit = MPN.divmod_1(work, work, len, radix);
+               buffer.append(Character.forDigit(digit, radix));
+               while (len > 0 && work[len-1] == 0) len--;
+               if (len == 0)
+                 break;
+             }
+           if (neg)
+             buffer.append('-');
+           // Reverse buffer. 
+           int j = buffer.length() - 1;
+           while (i < j)
+             {
+               char tmp = buffer.charAt(i);
+               buffer.setCharAt(i, buffer.charAt(j));
+               buffer.setCharAt(j, tmp);
+               i++;  j--;
+             }
+         }
+      }
+  }*/
+
+  /*public String toString()
+  {
+    return toString(10);
+  }*/
+
+  /*public String toString(int radix)
+  {
+    if (USING_NATIVE)
+      return mpz.toString(radix);
+
+    if (words == null)
+      return Integer.toString(ival, radix);
+    if (ival <= 2)
+      return Long.toString(longValue(), radix);
+    int buf_size = ival * (MPN.chars_per_word(radix) + 1);
+    CPStringBuilder buffer = new CPStringBuilder(buf_size);
+    format(radix, buffer);
+    return buffer.toString();
+  }*/
+
+  public int intValue()
+  {
+    /*if (USING_NATIVE)
+      {
+        int result = mpz.absIntValue();
+        return mpz.compare(ZERO.mpz) < 0 ? - result : result;
+      }*/
+
+    if (words == null)
+      return ival;
+    return words[0];
+  }
+
+  public long longValue()
+  {
+    /*if (USING_NATIVE)
+      {
+        long result;
+        result = (abs().shiftRight(32)).mpz.absIntValue();
+        result <<= 32;
+        result |= mpz.absIntValue() & 0xFFFFFFFFL;
+        return this.compareTo(ZERO) < 0 ? - result : result;
+      }*/
+
+    if (words == null)
+      return ival;
+    if (ival == 1)
+      return words[0];
+    return ((long)words[1] << 32) + ((long)words[0] & 0xffffffffL);
+  }
+
+  public int hashCode()
+  {
+    // FIXME: May not match hashcode of JDK.
+    /*if (USING_NATIVE)
+      {
+        // TODO: profile to decide whether to make it native
+        byte[] bytes = this.toByteArray();
+        int result = 0;
+        for (int i = 0; i < bytes.length; i++)
+          result ^= (bytes[i] & 0xFF) << (8 * (i % 4));
+        return result;
+      }*/
+
+    return words == null ? ival : (words[0] + words[ival - 1]);
+  }
+
+  /* Assumes x and y are both canonicalized. */
+  private static boolean equals(BigInteger x, BigInteger y)
+  {
+    /*if (USING_NATIVE)
+      return x.mpz.compare(y.mpz) == 0;*/
+
+    if (x.words == null && y.words == null)
+      return x.ival == y.ival;
+    if (x.words == null || y.words == null || x.ival != y.ival)
+      return false;
+    for (int i = x.ival; --i >= 0; )
+      {
+       if (x.words[i] != y.words[i])
+         return false;
+      }
+    return true;
+  }
+
+  /* Assumes this and obj are both canonicalized. */
+  public boolean equals(Object obj)
+  {
+    if (! (obj instanceof BigInteger))
+      return false;
+    return equals(this, (BigInteger) obj);
+  }
+
+  private static BigInteger valueOf(byte[] digits, int byte_len,
+                                   boolean negative, int radix)
+  {
+    int chars_per_word = MPN.chars_per_word(radix);
+    int[] words = new int[byte_len / chars_per_word + 1];
+    int size = MPN.set_str(words, digits, byte_len, radix);
+    if (size == 0)
+      return ZERO;
+    if (words[size-1] < 0)
+      words[size++] = 0;
+    if (negative)
+      negate(words, words, size);
+    return make(words, size);
+  }
+
+  public double doubleValue()
+  {
+    /*if (USING_NATIVE)
+      return mpz.doubleValue();*/
+
+    if (words == null)
+      return (double) ival;
+    if (ival <= 2)
+      return (double) longValue();
+    if (isNegative())
+      return neg(this).roundToDouble(0, true, false);
+      return roundToDouble(0, false, false);
+  }
+
+  public float floatValue()
+  {
+    return (float) doubleValue();
+  }
+
+  /** Return true if any of the lowest n bits are one.
+   * (false if n is negative).  */
+  private boolean checkBits(int n)
+  {
+    if (n <= 0)
+      return false;
+    if (words == null)
+      return n > 31 || ((ival & ((1 << n) - 1)) != 0);
+    int i;
+    for (i = 0; i < (n >> 5) ; i++)
+      if (words[i] != 0)
+       return true;
+    return (n & 31) != 0 && (words[i] & ((1 << (n & 31)) - 1)) != 0;
+  }
+
+  /** Convert a semi-processed BigInteger to double.
+   * Number must be non-negative.  Multiplies by a power of two, applies sign,
+   * and converts to double, with the usual java rounding.
+   * @param exp power of two, positive or negative, by which to multiply
+   * @param neg true if negative
+   * @param remainder true if the BigInteger is the result of a truncating
+   * division that had non-zero remainder.  To ensure proper rounding in
+   * this case, the BigInteger must have at least 54 bits.  */
+  private double roundToDouble(int exp, boolean neg, boolean remainder)
+  {
+    // Compute length.
+    int il = bitLength();
+
+    // Exponent when normalized to have decimal point directly after
+    // leading one.  This is stored excess 1023 in the exponent bit field.
+    exp += il - 1;
+
+    // Gross underflow.  If exp == -1075, we let the rounding
+    // computation determine whether it is minval or 0 (which are just
+    // 0x0000 0000 0000 0001 and 0x0000 0000 0000 0000 as bit
+    // patterns).
+    if (exp < -1075)
+      return neg ? -0.0 : 0.0;
+
+    // gross overflow
+    if (exp > 1023)
+      return neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+
+    // number of bits in mantissa, including the leading one.
+    // 53 unless it's denormalized
+    int ml = (exp >= -1022 ? 53 : 53 + exp + 1022);
+
+    // Get top ml + 1 bits.  The extra one is for rounding.
+    long m;
+    int excess_bits = il - (ml + 1);
+    if (excess_bits > 0)
+      m = ((words == null) ? ival >> excess_bits
+          : MPN.rshift_long(words, ival, excess_bits));
+    else
+      m = longValue() << (- excess_bits);
+
+    // Special rounding for maxval.  If the number exceeds maxval by
+    // any amount, even if it's less than half a step, it overflows.
+    if (exp == 1023 && ((m >> 1) == (1L << 53) - 1))
+      {
+       if (remainder || checkBits(il - ml))
+         return neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+       else
+         return neg ? - Double.MAX_VALUE : Double.MAX_VALUE;
+      }
+
+    // Normal round-to-even rule: round up if the bit dropped is a one, and
+    // the bit above it or any of the bits below it is a one.
+    if ((m & 1) == 1
+       && ((m & 2) == 2 || remainder || checkBits(excess_bits)))
+      {
+       m += 2;
+       // Check if we overflowed the mantissa
+       if ((m & (1L << 54)) != 0)
+         {
+           exp++;
+           // renormalize
+           m >>= 1;
+         }
+       // Check if a denormalized mantissa was just rounded up to a
+       // normalized one.
+       else if (ml == 52 && (m & (1L << 53)) != 0)
+         exp++;
+      }
+       
+    // Discard the rounding bit
+    m >>= 1;
+
+    long bits_sign = neg ? (1L << 63) : 0;
+    exp += 1023;
+    long bits_exp = (exp <= 0) ? 0 : ((long)exp) << 52;
+    long bits_mant = m & ~(1L << 52);
+    return Double.longBitsToDouble(bits_sign | bits_exp | bits_mant);
+  }
+
+  /** Copy the abolute value of this into an array of words.
+   * Assumes words.length >= (this.words == null ? 1 : this.ival).
+   * Result is zero-extended, but need not be a valid 2's complement number.
+   */
+  private void getAbsolute(int[] words)
+  {
+    int len;
+    if (this.words == null)
+      {
+       len = 1;
+       words[0] = this.ival;
+      }
+    else
+      {
+       len = this.ival;
+       for (int i = len;  --i >= 0; )
+         words[i] = this.words[i];
+      }
+    if (words[len - 1] < 0)
+      negate(words, words, len);
+    for (int i = words.length;  --i > len; )
+      words[i] = 0;
+  }
+
+  /** Set dest[0:len-1] to the negation of src[0:len-1].
+   * Return true if overflow (i.e. if src is -2**(32*len-1)).
+   * Ok for src==dest. */
+  private static boolean negate(int[] dest, int[] src, int len)
+  {
+    long carry = 1;
+    boolean negative = src[len-1] < 0;
+    for (int i = 0;  i < len;  i++)
+      {
+        carry += ((long) (~src[i]) & 0xffffffffL);
+        dest[i] = (int) carry;
+        carry >>= 32;
+      }
+    return (negative && dest[len-1] < 0);
+  }
+
+  /** Destructively set this to the negative of x.
+   * It is OK if x==this.*/
+  private void setNegative(BigInteger x)
+  {
+    int len = x.ival;
+    if (x.words == null)
+      {
+       if (len == Integer.MIN_VALUE)
+         set(- (long) len);
+       else
+         set(-len);
+       return;
+      }
+    realloc(len + 1);
+    if (negate(words, x.words, len))
+      words[len++] = 0;
+    ival = len;
+  }
+
+  /** Destructively negate this. */
+  private void setNegative()
+  {
+    setNegative(this);
+  }
+
+  private static BigInteger abs(BigInteger x)
+  {
+    return x.isNegative() ? neg(x) : x;
+  }
+
+  public BigInteger abs()
+  {
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+        mpz.abs(result.mpz);
+        return result;
+      }*/
+
+    return abs(this);
+  }
+
+  private static BigInteger neg(BigInteger x)
+  {
+    if (x.words == null && x.ival != Integer.MIN_VALUE)
+      return valueOf(- x.ival);
+    BigInteger result = new BigInteger(0);
+    result.setNegative(x);
+    return result.canonicalize();
+  }
+
+  public BigInteger negate()
+  {
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+       mpz.negate(result.mpz);
+        return result;
+      }*/
+
+    return neg(this);
+  }
+
+  /** Calculates ceiling(log2(this < 0 ? -this : this+1))
+   * See Common Lisp: the Language, 2nd ed, p. 361.
+   */
+  public int bitLength()
+  {
+    /*if (USING_NATIVE)
+      return mpz.bitLength();*/
+
+    if (words == null)
+      return MPN.intLength(ival);
+      return MPN.intLength(words, ival);
+  }
+
+  public byte[] toByteArray()
+  {
+    if (signum() == 0)
+      return new byte[1];
+
+    /*if (USING_NATIVE)
+      {
+        // the minimal number of bytes required to represent the MPI is function
+        // of (a) its bit-length, and (b) its sign.  only when this MPI is both
+        // positive, and its bit-length is a multiple of 8 do we add one zero
+        // bit for its sign.  we do this so if we construct a new MPI from the
+        // resulting byte array, we wouldn't mistake a positive number, whose
+        // bit-length is a multiple of 8, for a similar-length negative one.
+        int bits = bitLength();
+        if (bits % 8 == 0 || this.signum() == 1)
+          bits++;
+        byte[] bytes = new byte[(bits + 7) / 8];
+        mpz.toByteArray(bytes);
+        return bytes;
+      }*/
+
+    // Determine number of bytes needed.  The method bitlength returns
+    // the size without the sign bit, so add one bit for that and then
+    // add 7 more to emulate the ceil function using integer math.
+    byte[] bytes = new byte[(bitLength() + 1 + 7) / 8];
+    int nbytes = bytes.length;
+
+    int wptr = 0;
+    int word;
+
+    // Deal with words array until one word or less is left to process.
+    // If BigInteger is an int, then it is in ival and nbytes will be <= 4.
+    while (nbytes > 4)
+      {
+       word = words[wptr++];
+       for (int i = 4; i > 0; --i, word >>= 8)
+          bytes[--nbytes] = (byte) word;
+      }
+
+    // Deal with the last few bytes.  If BigInteger is an int, use ival.
+    word = (words == null) ? ival : words[wptr];
+    for ( ; nbytes > 0; word >>= 8)
+      bytes[--nbytes] = (byte) word;
+
+    return bytes;
+  }
+
+  /** Return the boolean opcode (for bitOp) for swapped operands.
+   * I.e. bitOp(swappedOp(op), x, y) == bitOp(op, y, x).
+   */
+  private static int swappedOp(int op)
+  {
+    return
+    "\000\001\004\005\002\003\006\007\010\011\014\015\012\013\016\017"
+    .charAt(op);
+  }
+
+  /** Do one the the 16 possible bit-wise operations of two BigIntegers. */
+  private static BigInteger bitOp(int op, BigInteger x, BigInteger y)
+  {
+    switch (op)
+      {
+        case 0:  return ZERO;
+        case 1:  return x.and(y);
+        case 3:  return x;
+        case 5:  return y;
+        case 15: return valueOf(-1);
+      }
+    BigInteger result = new BigInteger();
+    setBitOp(result, op, x, y);
+    return result.canonicalize();
+  }
+
+  /** Do one the the 16 possible bit-wise operations of two BigIntegers. */
+  private static void setBitOp(BigInteger result, int op,
+                              BigInteger x, BigInteger y)
+  {
+    if ((y.words != null) && (x.words == null || x.ival < y.ival))
+      {
+       BigInteger temp = x;  x = y;  y = temp;
+       op = swappedOp(op);
+      }
+    int xi;
+    int yi;
+    int xlen, ylen;
+    if (y.words == null)
+      {
+       yi = y.ival;
+       ylen = 1;
+      }
+    else
+      {
+       yi = y.words[0];
+       ylen = y.ival;
+      }
+    if (x.words == null)
+      {
+       xi = x.ival;
+       xlen = 1;
+      }
+    else
+      {
+       xi = x.words[0];
+       xlen = x.ival;
+      }
+    if (xlen > 1)
+      result.realloc(xlen);
+    int[] w = result.words;
+    int i = 0;
+    // Code for how to handle the remainder of x.
+    // 0:  Truncate to length of y.
+    // 1:  Copy rest of x.
+    // 2:  Invert rest of x.
+    int finish = 0;
+    int ni;
+    switch (op)
+      {
+      case 0:  // clr
+       ni = 0;
+       break;
+      case 1: // and
+       for (;;)
+         {
+           ni = xi & yi;
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       if (yi < 0) finish = 1;
+       break;
+      case 2: // andc2
+       for (;;)
+         {
+           ni = xi & ~yi;
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       if (yi >= 0) finish = 1;
+       break;
+      case 3:  // copy x
+       ni = xi;
+       finish = 1;  // Copy rest
+       break;
+      case 4: // andc1
+       for (;;)
+         {
+           ni = ~xi & yi;
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       if (yi < 0) finish = 2;
+       break;
+      case 5: // copy y
+       for (;;)
+         {
+           ni = yi;
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       break;
+      case 6:  // xor
+       for (;;)
+         {
+           ni = xi ^ yi;
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       finish = yi < 0 ? 2 : 1;
+       break;
+      case 7:  // ior
+       for (;;)
+         {
+           ni = xi | yi;
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       if (yi >= 0) finish = 1;
+       break;
+      case 8:  // nor
+       for (;;)
+         {
+           ni = ~(xi | yi);
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       if (yi >= 0)  finish = 2;
+       break;
+      case 9:  // eqv [exclusive nor]
+       for (;;)
+         {
+           ni = ~(xi ^ yi);
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       finish = yi >= 0 ? 2 : 1;
+       break;
+      case 10:  // c2
+       for (;;)
+         {
+           ni = ~yi;
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       break;
+      case 11:  // orc2
+       for (;;)
+         {
+           ni = xi | ~yi;
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       if (yi < 0)  finish = 1;
+       break;
+      case 12:  // c1
+       ni = ~xi;
+       finish = 2;
+       break;
+      case 13:  // orc1
+       for (;;)
+         {
+           ni = ~xi | yi;
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       if (yi >= 0) finish = 2;
+       break;
+      case 14:  // nand
+       for (;;)
+         {
+           ni = ~(xi & yi);
+           if (i+1 >= ylen) break;
+           w[i++] = ni;  xi = x.words[i];  yi = y.words[i];
+         }
+       if (yi < 0) finish = 2;
+       break;
+      default:
+      case 15:  // set
+       ni = -1;
+       break;
+      }
+    // Here i==ylen-1; w[0]..w[i-1] have the correct result;
+    // and ni contains the correct result for w[i+1].
+    if (i+1 == xlen)
+      finish = 0;
+    switch (finish)
+      {
+      case 0:
+       if (i == 0 && w == null)
+         {
+           result.ival = ni;
+           return;
+         }
+       w[i++] = ni;
+       break;
+      case 1:  w[i] = ni;  while (++i < xlen)  w[i] = x.words[i];  break;
+      case 2:  w[i] = ni;  while (++i < xlen)  w[i] = ~x.words[i];  break;
+      }
+    result.ival = i;
+  }
+
+  /** Return the logical (bit-wise) "and" of a BigInteger and an int. */
+  private static BigInteger and(BigInteger x, int y)
+  {
+    if (x.words == null)
+      return valueOf(x.ival & y);
+    if (y >= 0)
+      return valueOf(x.words[0] & y);
+    int len = x.ival;
+    int[] words = new int[len];
+    words[0] = x.words[0] & y;
+    while (--len > 0)
+      words[len] = x.words[len];
+    return make(words, x.ival);
+  }
+
+  /** Return the logical (bit-wise) "and" of two BigIntegers. */
+  public BigInteger and(BigInteger y)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = y.signum; // force NPE check
+        BigInteger result = new BigInteger();
+        mpz.and(y.mpz, result.mpz);
+        return result;
+      }*/
+
+    if (y.words == null)
+      return and(this, y.ival);
+    else if (words == null)
+      return and(y, ival);
+
+    BigInteger x = this;
+    if (ival < y.ival)
+      {
+        BigInteger temp = this;  x = y;  y = temp;
+      }
+    int i;
+    int len = y.isNegative() ? x.ival : y.ival;
+    int[] words = new int[len];
+    for (i = 0;  i < y.ival;  i++)
+      words[i] = x.words[i] & y.words[i];
+    for ( ; i < len;  i++)
+      words[i] = x.words[i];
+    return make(words, len);
+  }
+
+  /** Return the logical (bit-wise) "(inclusive) or" of two BigIntegers. */
+  public BigInteger or(BigInteger y)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = y.signum; // force NPE check
+        BigInteger result = new BigInteger();
+        mpz.or(y.mpz, result.mpz);
+        return result;
+      }*/
+
+    return bitOp(7, this, y);
+  }
+
+  /** Return the logical (bit-wise) "exclusive or" of two BigIntegers. */
+  public BigInteger xor(BigInteger y)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = y.signum; // force NPE check
+        BigInteger result = new BigInteger();
+        mpz.xor(y.mpz, result.mpz);
+        return result;
+      }*/
+
+    return bitOp(6, this, y);
+  }
+
+  /** Return the logical (bit-wise) negation of a BigInteger. */
+  public BigInteger not()
+  {
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+        mpz.not(result.mpz);
+        return result;
+      }*/
+
+    return bitOp(12, this, ZERO);
+  }
+
+  public BigInteger andNot(BigInteger val)
+  {
+    /*if (USING_NATIVE)
+      {
+        int dummy = val.signum; // force NPE check
+        BigInteger result = new BigInteger();
+        mpz.andNot(val.mpz, result.mpz);
+        return result;
+      }*/
+
+    return and(val.not());
+  }
+
+  public BigInteger clearBit(int n)
+  {
+    if (n < 0)
+      throw new ArithmeticException();
+
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+        mpz.setBit(n, false, result.mpz);
+        return result;
+      }*/
+
+    return and(ONE.shiftLeft(n).not());
+  }
+
+  public BigInteger setBit(int n)
+  {
+    if (n < 0)
+      throw new ArithmeticException();
+
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+        mpz.setBit(n, true, result.mpz);
+        return result;
+      }*/
+
+    return or(ONE.shiftLeft(n));
+  }
+
+  public boolean testBit(int n)
+  {
+    if (n < 0)
+      throw new Error("Arithmetic Exception")/*ArithmeticException()*/;
+
+    /*if (USING_NATIVE)
+      return mpz.testBit(n) != 0;*/
+
+    return !and(ONE.shiftLeft(n)).isZero();
+  }
+
+  public BigInteger flipBit(int n)
+  {
+    if (n < 0)
+      throw new Error("Arithmetic Exception")/*ArithmeticException()*/;
+
+    /*if (USING_NATIVE)
+      {
+        BigInteger result = new BigInteger();
+        mpz.flipBit(n, result.mpz);
+        return result;
+      }*/
+
+    return xor(ONE.shiftLeft(n));
+  }
+
+  public int getLowestSetBit()
+  {
+    /*if (USING_NATIVE)
+      return mpz.compare(ZERO.mpz) == 0 ? -1 : mpz.lowestSetBit();*/
+
+    if (isZero())
+      return -1;
+
+    if (words == null)
+      return MPN.findLowestBit(ival);
+    else
+      return MPN.findLowestBit(words);
+  }
+
+  // bit4count[I] is number of '1' bits in I.
+  private static final byte[] bit4_count = { 0, 1, 1, 2,  1, 2, 2, 3,
+                                            1, 2, 2, 3,  2, 3, 3, 4};
+
+  private static int bitCount(int i)
+  {
+    int count = 0;
+    while (i != 0)
+      {
+       count += bit4_count[i & 15];
+       i >>>= 4;
+      }
+    return count;
+  }
+
+  private static int bitCount(int[] x, int len)
+  {
+    int count = 0;
+    while (--len >= 0)
+      count += bitCount(x[len]);
+    return count;
+  }
+
+  /** Count one bits in a BigInteger.
+   * If argument is negative, count zero bits instead. */
+  public int bitCount()
+  {
+    /*if (USING_NATIVE)
+      return mpz.bitCount();*/
+
+    int i, x_len;
+    int[] x_words = words;
+    if (x_words == null)
+      {
+       x_len = 1;
+       i = bitCount(ival);
+      }
+    else
+      {
+       x_len = ival;
+       i = bitCount(x_words, x_len);
+      }
+    return isNegative() ? x_len * 32 - i : i;
+  }
+
+  /*private void readObject(ObjectInputStream s)
+    //throws IOException, ClassNotFoundException
+  {
+    if (USING_NATIVE)
+      {
+        mpz = new GMP();
+        s.defaultReadObject();
+        if (signum != 0)
+          mpz.fromByteArray(magnitude);
+        // else it's zero and we need to do nothing
+      }
+    else
+      {
+       s.defaultReadObject();
+       if (magnitude.length == 0 || signum == 0)
+         {
+           this.ival = 0;
+           this.words = null;
+         }
+       else
+         {
+           words = byteArrayToIntArray(magnitude, signum < 0 ? -1 : 0);
+           BigInteger result = make(words, words.length);
+           this.ival = result.ival;
+           this.words = result.words;        
+         }    
+      }
+  }*/
+
+  /*private void writeObject(ObjectOutputStream s)
+    //throws IOException, ClassNotFoundException
+  {
+    signum = signum();
+    magnitude = signum == 0 ? new byte[0] : toByteArray();
+    s.defaultWriteObject();
+    magnitude = null; // not needed anymore
+  }*/
+
+  // inner class(es) ..........................................................
+
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/BufferedReader.java b/Robust/src/ClassLibrary/MGC/gnu/BufferedReader.java
new file mode 100644 (file)
index 0000000..f91bc4c
--- /dev/null
@@ -0,0 +1,576 @@
+/* BufferedReader.java
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+     Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+//package java.io;
+
+//import gnu.java.lang.CPStringBuilder;
+
+/* Written using "Java Class Libraries", 2nd edition, plus online
+ * API docs for JDK 1.2 beta from http://www.javasoft.com.
+ * Status:  Believed complete and correct.
+ */
+
+/**
+ * This subclass of <code>FilterReader</code> buffers input from an 
+ * underlying implementation to provide a possibly more efficient read
+ * mechanism.  It maintains the buffer and buffer state in instance 
+ * variables that are available to subclasses.  The default buffer size
+ * of 8192 chars can be overridden by the creator of the stream.
+ * <p>
+ * This class also implements mark/reset functionality.  It is capable
+ * of remembering any number of input chars, to the limits of
+ * system memory or the size of <code>Integer.MAX_VALUE</code>
+ *
+ * @author Per Bothner (bothner@cygnus.com)
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ */
+public class BufferedReader extends Reader
+{
+  Reader in;
+  char[] buffer;
+  /* Index of current read position.  Must be >= 0 and <= limit. */
+  /* There is a special case where pos may be equal to limit+1; this
+   * is used as an indicator that a readLine was done with a '\r' was
+   * the very last char in the buffer.  Since we don't want to read-ahead
+   * and potentially block, we set pos this way to indicate the situation
+   * and deal with it later.  Doing it this way rather than having a
+   * separate boolean field to indicate the condition has the advantage
+   * that it is self-clearing on things like mark/reset.
+   */
+  int pos;
+  /* Limit of valid data in buffer.  Must be >= pos and <= buffer.length. */
+  /* This can be < pos in the one special case described above. */
+  int limit;
+
+  /* The value -1 means there is no mark, or the mark has been invalidated.
+     Otherwise, markPos is the index in the buffer of the marked position.
+     Must be >= 0 and <= pos.
+     Note we do not explicitly store the read-limit.
+     The implicit read-limit is (buffer.length - markPos), which is
+     guaranteed to be >= the read-limit requested in the call to mark. */
+  int markPos = -1;
+
+  // The JCL book specifies the default buffer size as 8K characters.
+  // This is package-private because it is used by LineNumberReader.
+  static final int DEFAULT_BUFFER_SIZE = 8192;
+
+  /**
+    * Create a new <code>BufferedReader</code> that will read from the 
+    * specified subordinate stream with a default buffer size of 8192 chars.
+    *
+    * @param in The subordinate stream to read from
+    */
+  public BufferedReader(Reader in)
+  {
+    this(in, DEFAULT_BUFFER_SIZE);
+  }
+
+  /**
+   * Create a new <code>BufferedReader</code> that will read from the 
+   * specified subordinate stream with a buffer size that is specified by the 
+   * caller.
+   *
+   * @param in The subordinate stream to read from
+   * @param size The buffer size to use
+   *
+   * @exception IllegalArgumentException if size &lt;= 0
+   */
+  public BufferedReader(Reader in, int size)
+  {
+    super(in.lock);
+    if (size <= 0)
+      throw new Error/*IllegalArgumentException*/("IllegalArgumentException Illegal buffer size: " + size);
+    this.in = in;
+    buffer = new char[size];
+  }
+
+  /**
+   * This method closes the underlying stream and frees any associated
+   * resources.
+   *
+   * @exception IOException If an error occurs
+   */
+  public void close() //throws IOException
+  {
+    synchronized (lock)
+      {
+       if (in != null)
+         in.close();
+       in = null;
+       buffer = null;
+      }
+  }
+
+  /**
+   * Returns <code>true</code> to indicate that this class supports mark/reset 
+   * functionality.
+   *
+   * @return <code>true</code>
+   */
+  public boolean markSupported()
+  {
+    return true;
+  }
+
+  /**
+   * Mark a position in the input to which the stream can be
+   * "reset" by calling the <code>reset()</code> method.  The parameter
+   * <code>readLimit</code> is the number of chars that can be read from the 
+   * stream after setting the mark before the mark becomes invalid.  For
+   * example, if <code>mark()</code> is called with a read limit of 10, then 
+   * when 11 chars of data are read from the stream before the 
+   * <code>reset()</code> method is called, then the mark is invalid and the 
+   * stream object instance is not required to remember the mark.
+   * <p>
+   * Note that the number of chars that can be remembered by this method
+   * can be greater than the size of the internal read buffer.  It is also
+   * not dependent on the subordinate stream supporting mark/reset
+   * functionality.
+   *
+   * @param readLimit The number of chars that can be read before the mark 
+   *        becomes invalid
+   *
+   * @exception IOException If an error occurs
+   * @exception IllegalArgumentException if readLimit is negative.
+   */
+  public void mark(int readLimit) //throws IOException
+  {
+    if (readLimit < 0)
+      throw new Error/*IllegalArgumentException*/("IllegalArgumentException Read-ahead limit is negative");
+
+    synchronized (lock)
+      {
+       checkStatus();
+       // In this method we need to be aware of the special case where
+       // pos + 1 == limit.  This indicates that a '\r' was the last char
+       // in the buffer during a readLine.  We'll want to maintain that
+       // condition after we shift things around and if a larger buffer is
+       // needed to track readLimit, we'll have to make it one element
+       // larger to ensure we don't invalidate the mark too early, if the
+       // char following the '\r' is NOT a '\n'.  This is ok because, per
+       // the spec, we are not required to invalidate when passing readLimit.
+       //
+       // Note that if 'pos > limit', then doing 'limit -= pos' will cause
+       // limit to be negative.  This is the only way limit will be < 0.
+
+       if (pos + readLimit > limit)
+         {
+           char[] old_buffer = buffer;
+           int extraBuffSpace = 0;
+           if (pos > limit)
+             extraBuffSpace = 1;
+           if (readLimit + extraBuffSpace > limit)
+             buffer = new char[readLimit + extraBuffSpace];
+           limit -= pos;
+           if (limit >= 0)
+             {
+               System.arraycopy(old_buffer, pos, buffer, 0, limit);
+               pos = 0;
+             }
+         }
+
+       if (limit < 0)
+         {
+           // Maintain the relationship of 'pos > limit'.
+           pos = 1;
+           limit = markPos = 0;
+         }
+       else
+         markPos = pos;
+       // Now pos + readLimit <= buffer.length. thus if we need to read
+       // beyond buffer.length, then we are allowed to invalidate markPos.
+      }
+  }
+
+  /**
+   * Reset the stream to the point where the <code>mark()</code> method
+   * was called.  Any chars that were read after the mark point was set will
+   * be re-read during subsequent reads.
+   * <p>
+   * This method will throw an IOException if the number of chars read from
+   * the stream since the call to <code>mark()</code> exceeds the mark limit
+   * passed when establishing the mark.
+   *
+   * @exception IOException If an error occurs;
+   */
+  public void reset() //throws IOException
+  {
+    synchronized (lock)
+      {
+       checkStatus();
+       if (markPos < 0)
+         throw new Error/*IOException*/("mark never set or invalidated");
+
+       // Need to handle the extremely unlikely case where a readLine was
+       // done with a '\r' as the last char in the buffer; which was then
+       // immediately followed by a mark and a reset with NO intervening
+       // read of any sort.  In that case, setting pos to markPos would
+       // lose that info and a subsequent read would thus not skip a '\n'
+       // (if one exists).  The value of limit in this rare case is zero.
+       // We can assume that if limit is zero for other reasons, then
+       // pos is already set to zero and doesn't need to be readjusted.
+       if (limit > 0)
+         pos = markPos;
+      }
+  }
+
+  /**
+   * This method determines whether or not a stream is ready to be read.  If
+   * this method returns <code>false</code> then this stream could (but is
+   * not guaranteed to) block on the next read attempt.
+   *
+   * @return <code>true</code> if this stream is ready to be read, 
+   * <code>false</code> otherwise
+   *
+   * @exception IOException If an error occurs
+   */
+  public boolean ready() //throws IOException
+  {
+    synchronized (lock)
+      {
+       checkStatus();
+       return pos < limit || in.ready();
+      }
+  }
+
+  /**
+   * This method read chars from a stream and stores them into a caller
+   * supplied buffer.  It starts storing the data at index 
+   * <code>offset</code> into
+   * the buffer and attempts to read <code>len</code> chars.  This method can
+   * return before reading the number of chars requested.  The actual number
+   * of chars read is returned as an int.  A -1 is returned to indicate the
+   * end of the stream.
+   * <p>
+   * This method will block until some data can be read.
+   *
+   * @param buf The array into which the chars read should be stored
+   * @param offset The offset into the array to start storing chars
+   * @param count The requested number of chars to read
+   *
+   * @return The actual number of chars read, or -1 if end of stream.
+   *
+   * @exception IOException If an error occurs.
+   * @exception IndexOutOfBoundsException If offset and count are not
+   * valid regarding buf.
+   */
+  public int read(char[] buf, int offset, int count) //throws IOException
+  {
+    if (offset < 0 || offset + count > buf.length || count < 0)
+      throw new Error/*IndexOutOfBoundsException*/("IndexOutOfBoundsException");
+
+    synchronized (lock)
+      {
+       checkStatus();
+       // Once again, we need to handle the special case of a readLine
+       // that has a '\r' at the end of the buffer.  In this case, we'll
+       // need to skip a '\n' if it is the next char to be read.
+       // This special case is indicated by 'pos > limit'.
+       boolean retAtEndOfBuffer = false;
+
+       int avail = limit - pos;
+       if (count > avail)
+         {
+           if (avail > 0)
+             count = avail;
+           else // pos >= limit
+             {
+               if (limit == buffer.length)
+                 markPos = -1; // read too far - invalidate the mark.
+               if (pos > limit)
+                 {
+                   // Set a boolean and make pos == limit to simplify things.
+                   retAtEndOfBuffer = true;
+                   --pos;
+                 }
+               if (markPos < 0)
+                 {
+                   // Optimization:  can read directly into buf.
+                   if (count >= buffer.length && !retAtEndOfBuffer)
+                     return in.read(buf, offset, count);
+                   pos = limit = 0;
+                 }
+               avail = in.read(buffer, limit, buffer.length - limit);
+               if (retAtEndOfBuffer && avail > 0 && buffer[limit] == '\n')
+                 {
+                   --avail;
+                   limit++;
+                 }
+               if (avail < count)
+                 {
+                   if (avail <= 0)
+                     return avail;
+                   count = avail;
+                 }
+               limit += avail;
+             }
+         }
+       System.arraycopy(buffer, pos, buf, offset, count);
+       pos += count;
+       return count;
+      }
+  }
+
+  /* Read more data into the buffer.  Update pos and limit appropriately.
+     Assumes pos==limit initially.  May invalidate the mark if read too much.
+     Return number of chars read (never 0), or -1 on eof. */
+  private int fill() //throws IOException
+  {
+    checkStatus();
+    // Handle the special case of a readLine that has a '\r' at the end of
+    // the buffer.  In this case, we'll need to skip a '\n' if it is the
+    // next char to be read.  This special case is indicated by 'pos > limit'.
+    boolean retAtEndOfBuffer = false;
+    if (pos > limit)
+      {
+        retAtEndOfBuffer = true;
+       --pos;
+      }
+
+    if (markPos >= 0 && limit == buffer.length)
+      markPos = -1;
+    if (markPos < 0)
+      pos = limit = 0;
+    int count = in.read(buffer, limit, buffer.length - limit);
+    if (count > 0)
+      limit += count;
+
+    if (retAtEndOfBuffer && buffer[pos] == '\n')
+      {
+       --count;
+       // If the mark was set to the location of the \n, then we
+       // must change it to fully pretend that the \n does not
+       // exist.
+       if (markPos == pos)
+         ++markPos;
+       ++pos;
+      }
+
+    return count;
+  }
+  
+  public int read() //throws IOException
+  {
+    synchronized (lock)
+      {
+       checkStatus();
+       if (pos >= limit && fill () <= 0)
+         return -1;
+       return buffer[pos++];
+      }
+  }
+
+  /* Return the end of the line starting at this.pos and ending at limit.
+   * The index returns is *before* any line terminators, or limit
+   * if no line terminators were found.
+   */
+  private int lineEnd(int limit)
+  {
+    int i = pos;
+    for (; i < limit; i++)
+      {
+       char ch = buffer[i];
+       if (ch == '\n' || ch == '\r')
+         break;
+      }
+    return i;
+  }
+
+  /**
+   * This method reads a single line of text from the input stream, returning
+   * it as a <code>String</code>.  A line is terminated by "\n", a "\r", or
+   * an "\r\n" sequence.  The system dependent line separator is not used.
+   * The line termination characters are not returned in the resulting
+   * <code>String</code>.
+   * 
+   * @return The line of text read, or <code>null</code> if end of stream.
+   * 
+   * @exception IOException If an error occurs
+   */
+  public String readLine() //throws IOException
+  {/*
+    checkStatus();
+    // Handle the special case where a previous readLine (with no intervening
+    // reads/skips) had a '\r' at the end of the buffer.
+    // In this case, we'll need to skip a '\n' if it's the next char to be read.
+    // This special case is indicated by 'pos > limit'.
+    if (pos > limit)
+      {
+       int ch = read();
+       if (ch < 0)
+         return null;
+       if (ch != '\n')
+         --pos;
+      }
+    int i = lineEnd(limit);
+    if (i < limit)
+      {
+       String str = String.valueOf(buffer, pos, i - pos);
+       pos = i + 1;
+       // If the last char in the buffer is a '\r', we must remember
+       // to check if the next char to be read after the buffer is refilled
+       // is a '\n'.  If so, skip it.  To indicate this condition, we set pos
+       // to be limit + 1, which normally is never possible.
+       if (buffer[i] == '\r')
+         if (pos == limit || buffer[pos] == '\n')
+           pos++;
+       return str;
+      }
+    CPStringBuilder sbuf = new CPStringBuilder(200);
+    sbuf.append(buffer, pos, i - pos);
+    pos = i;
+    // We only want to return null when no characters were read before
+    // EOF.  So we must keep track of this separately.  Otherwise we
+    // would treat an empty `sbuf' as an EOF condition, which is wrong
+    // when there is just a newline.
+    boolean eof = false;
+    for (;;)
+      {
+       // readLine should block. So we must not return until a -1 is reached.
+       if (pos >= limit)
+         {
+           // here count == 0 isn't sufficient to give a failure.
+           int count = fill();
+           if (count < 0)
+             {
+               eof = true;
+               break;
+             }
+           continue;
+         }
+       int ch = buffer[pos++];
+       if (ch == '\n' || ch == '\r')
+         {
+           // Check here if a '\r' was the last char in the buffer; if so,
+           // mark it as in the comment above to indicate future reads
+           // should skip a newline that is the next char read after
+           // refilling the buffer.
+           if (ch == '\r')
+             if (pos == limit || buffer[pos] == '\n')
+               pos++;
+           break;
+         }
+       i = lineEnd(limit);
+       sbuf.append(buffer, pos - 1, i - (pos - 1));
+       pos = i;
+      }
+    return (sbuf.length() == 0 && eof) ? null : sbuf.toString();*/
+    return null;
+  }
+
+  /**
+   * This method skips the specified number of chars in the stream.  It
+   * returns the actual number of chars skipped, which may be less than the
+   * requested amount.
+   * <p>
+   * This method first discards chars in the buffer, then calls the
+   * <code>skip</code> method on the underlying stream to skip the 
+   * remaining chars.
+   *
+   * @param count The requested number of chars to skip
+   *
+   * @return The actual number of chars skipped.
+   *
+   * @exception IOException If an error occurs.
+   * @exception IllegalArgumentException If count is negative.
+   */
+  public long skip(long count) //throws IOException
+  {
+    synchronized (lock)
+      {
+       checkStatus();
+       if (count < 0)
+         throw new Error/*IllegalArgumentException*/("skip value is negative");
+       if (count == 0)
+         return 0;
+       // Yet again, we need to handle the special case of a readLine
+       // that has a '\r' at the end of the buffer.  In this case, we need
+       // to ignore a '\n' if it is the next char to be read.
+       // This special case is indicated by 'pos > limit' (i.e. avail < 0).
+       // To simplify things, if we're dealing with the special case for
+       // readLine, just read the next char (since the fill method will
+       // skip the '\n' for us).  By doing this, we'll have to back up pos.
+       // That's easier than trying to keep track of whether we've skipped
+       // one element or not.
+       if (pos > limit)
+         {
+           if (read() < 0)
+             return 0;
+           else
+             --pos; 
+         }
+
+       int avail = limit - pos;
+
+       if (count < avail)
+         {
+           pos += count;
+           return count;
+         }
+
+       pos = limit;
+       long todo = count - avail;
+       if (todo > buffer.length)
+         {
+           markPos = -1;
+           todo -= in.skip(todo);
+         }
+       else
+         {
+           while (todo > 0)
+             {
+               avail = fill();
+               if (avail <= 0)
+                 break;
+               if (avail > todo)
+                 avail = (int) todo;
+               pos += avail;
+               todo -= avail;
+             }
+         }
+       return count - todo;
+      }
+  }
+  
+  private void checkStatus() //throws IOException
+  {
+    if (in == null)
+      throw new Error/*IOException*/("Stream closed");
+  }  
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/Calendar.java b/Robust/src/ClassLibrary/MGC/gnu/Calendar.java
new file mode 100644 (file)
index 0000000..7dc6cf2
--- /dev/null
@@ -0,0 +1,1621 @@
+/* Calendar.java --
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006,  
+   Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util;
+
+/*import gnu.java.lang.CPStringBuilder;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import java.text.DateFormatSymbols;*/
+
+/**
+ * This class is an abstract base class for Calendars, which can be
+ * used to convert between <code>Date</code> objects and a set of
+ * integer fields which represent <code>YEAR</code>,
+ * <code>MONTH</code>, <code>DAY</code>, etc.  The <code>Date</code>
+ * object represents a time in milliseconds since the Epoch. <br>
+ *
+ * This class is locale sensitive.  To get the Object matching the
+ * current locale you can use <code>getInstance</code>.  You can even provide
+ * a locale or a timezone.  <code>getInstance</code> returns currently
+ * a <code>GregorianCalendar</code> for the current date. <br>
+ *
+ * If you want to convert a date from the Year, Month, Day, DayOfWeek,
+ * etc.  Representation to a <code>Date</code>-Object, you can create
+ * a new Calendar with <code>getInstance()</code>,
+ * <code>clear()</code> all fields, <code>set(int,int)</code> the
+ * fields you need and convert it with <code>getTime()</code>. <br>
+ *
+ * If you want to convert a <code>Date</code>-object to the Calendar
+ * representation, create a new Calendar, assign the
+ * <code>Date</code>-Object with <code>setTime()</code>, and read the
+ * fields with <code>get(int)</code>. <br>
+ *
+ * When computing the date from time fields, it may happen, that there
+ * are either two few fields set, or some fields are inconsistent.  This
+ * cases will handled in a calendar specific way.  Missing fields are
+ * replaced by the fields of the epoch: 1970 January 1 00:00. <br>
+ *
+ * To understand, how the day of year is computed out of the fields
+ * look at the following table.  It is traversed from top to bottom,
+ * and for the first line all fields are set, that line is used to
+ * compute the day. <br>
+ *
+ *
+<pre>month + day_of_month
+month + week_of_month + day_of_week
+month + day_of_week_of_month + day_of_week
+day_of_year
+day_of_week + week_of_year</pre>
+ *
+ * The hour_of_day-field takes precedence over the ampm and
+ * hour_of_ampm fields. <br>
+ *
+ * <STRONG>Note:</STRONG> This can differ for non-Gregorian calendar. <br>
+ *
+ * To convert a calendar to a human readable form and vice versa,  use
+ * the <code>java.text.DateFormat</code> class. <br>
+ *
+ * Other useful things you can do with an calendar, is
+ * <code>roll</code>ing fields (that means increase/decrease a
+ * specific field by one, propagating overflows), or
+ * <code>add</code>ing/substracting a fixed amount to a field.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Jochen Hoenicke (Jochen.Hoenicke@Informatik.Uni-Oldenburg.de)
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @author Jeff Sturm (jsturm@one-point.com)
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Bryce McKinlay (mckinlay@redhat.com)
+ * @author Ingo Proetel (proetel@aicas.com)
+ * @author Jerry Quinn (jlquinn@optonline.net)
+ * @author Jeroen Frijters (jeroen@frijters.net)
+ * @author Noa Resare (noa@resare.com)
+ * @author Sven de Marothy (sven@physto.se)
+ * @author David Gilbert (david.gilbert@object-refinery.com)
+ * @author Olivier Jolly (olivier.jolly@pcedev.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see Date
+ * @see GregorianCalendar
+ * @see TimeZone
+ * @see java.text.DateFormat
+ */
+public /*abstract*/ class Calendar
+  //implements Serializable, Cloneable, Comparable<Calendar>
+{
+  /**
+   * Constant representing the era time field.
+   */
+  public static final int ERA = 0;
+
+  /**
+   * Constant representing the year time field.
+   */
+  public static final int YEAR = 1;
+
+  /**
+   * Constant representing the month time field.  This field
+   * should contain one of the JANUARY,...,DECEMBER constants below.
+   */
+  public static final int MONTH = 2;
+
+  /**
+   * Constant representing the week of the year field.
+   * @see #setFirstDayOfWeek(int)
+   */
+  public static final int WEEK_OF_YEAR = 3;
+
+  /**
+   * Constant representing the week of the month time field.
+   * @see #setFirstDayOfWeek(int)
+   */
+  public static final int WEEK_OF_MONTH = 4;
+
+  /**
+   * Constant representing the day time field, synonym for DAY_OF_MONTH.
+   */
+  public static final int DATE = 5;
+
+  /**
+   * Constant representing the day time field.
+   */
+  public static final int DAY_OF_MONTH = 5;
+
+  /**
+   * Constant representing the day of year time field.  This is
+   * 1 for the first day in month.
+   */
+  public static final int DAY_OF_YEAR = 6;
+
+  /**
+   * Constant representing the day of week time field.  This field
+   * should contain one of the SUNDAY,...,SATURDAY constants below.
+   */
+  public static final int DAY_OF_WEEK = 7;
+
+  /**
+   * Constant representing the day-of-week-in-month field.  For
+   * instance this field contains 2 for the second thursday in a
+   * month.  If you give a negative number here, the day will count
+   * from the end of the month.
+   */
+  public static final int DAY_OF_WEEK_IN_MONTH = 8;
+
+  /**
+   * Constant representing the part of the day for 12-hour clock.  This
+   * should be one of AM or PM.
+   */
+  public static final int AM_PM = 9;
+
+  /**
+   * Constant representing the hour time field for 12-hour clock.
+   */
+  public static final int HOUR = 10;
+
+  /**
+   * Constant representing the hour of day time field for 24-hour clock.
+   */
+  public static final int HOUR_OF_DAY = 11;
+
+  /**
+   * Constant representing the minute of hour time field.
+   */
+  public static final int MINUTE = 12;
+
+  /**
+   * Constant representing the second time field.
+   */
+  public static final int SECOND = 13;
+
+  /**
+   * Constant representing the millisecond time field.
+   */
+  public static final int MILLISECOND = 14;
+
+  /**
+   * Constant representing the time zone offset time field for the
+   * time given in the other fields.  It is measured in
+   * milliseconds.  The default is the offset of the time zone.
+   */
+  public static final int ZONE_OFFSET = 15;
+
+  /**
+   * Constant representing the daylight saving time offset in
+   * milliseconds.  The default is the value given by the time zone.
+   */
+  public static final int DST_OFFSET = 16;
+
+  /**
+   * Number of time fields.
+   */
+  public static final int FIELD_COUNT = 17;
+
+  /**
+   * Constant representing Sunday.
+   */
+  public static final int SUNDAY = 1;
+
+  /**
+   * Constant representing Monday.
+   */
+  public static final int MONDAY = 2;
+
+  /**
+   * Constant representing Tuesday.
+   */
+  public static final int TUESDAY = 3;
+
+  /**
+   * Constant representing Wednesday.
+   */
+  public static final int WEDNESDAY = 4;
+
+  /**
+   * Constant representing Thursday.
+   */
+  public static final int THURSDAY = 5;
+
+  /**
+   * Constant representing Friday.
+   */
+  public static final int FRIDAY = 6;
+
+  /**
+   * Constant representing Saturday.
+   */
+  public static final int SATURDAY = 7;
+
+  /**
+   * Constant representing January.
+   */
+  public static final int JANUARY = 0;
+
+  /**
+   * Constant representing February.
+   */
+  public static final int FEBRUARY = 1;
+
+  /**
+   * Constant representing March.
+   */
+  public static final int MARCH = 2;
+
+  /**
+   * Constant representing April.
+   */
+  public static final int APRIL = 3;
+
+  /**
+   * Constant representing May.
+   */
+  public static final int MAY = 4;
+
+  /**
+   * Constant representing June.
+   */
+  public static final int JUNE = 5;
+
+  /**
+   * Constant representing July.
+   */
+  public static final int JULY = 6;
+
+  /**
+   * Constant representing August.
+   */
+  public static final int AUGUST = 7;
+
+  /**
+   * Constant representing September.
+   */
+  public static final int SEPTEMBER = 8;
+
+  /**
+   * Constant representing October.
+   */
+  public static final int OCTOBER = 9;
+
+  /**
+   * Constant representing November.
+   */
+  public static final int NOVEMBER = 10;
+
+  /**
+   * Constant representing December.
+   */
+  public static final int DECEMBER = 11;
+
+  /**
+   * Constant representing Undecimber. This is an artificial name useful
+   * for lunar calendars.
+   */
+  public static final int UNDECIMBER = 12;
+
+  /**
+   * Useful constant for 12-hour clock.
+   */
+  public static final int AM = 0;
+
+  /**
+   * Useful constant for 12-hour clock.
+   */
+  public static final int PM = 1;
+
+  /**
+   * A style specifier for {@link #getDisplayNames(int,int,Locale)}
+   * stating that names should be returned in both long and short variants.
+   *
+   * @since 1.6
+   * @see #SHORT
+   * @see #LONG
+   */
+  public static final int ALL_STYLES = 0;
+
+  /**
+   * A style specifier for {@link #getDisplayName(int,int,Locale)}
+   * and {@link #getDisplayNames(int,int,Locale)} stating that names
+   * should be returned in their short variant if applicable.
+   *
+   * @since 1.6
+   */
+  public static final int SHORT = 1;
+
+  /**
+   * A style specifier for {@link #getDisplayName(int,int,Locale)}
+   * and {@link #getDisplayNames(int,int,Locale)} stating that names
+   * should be returned in their long variant if applicable.
+   *
+   * @since 1.6
+   */
+  public static final int LONG = 2;
+
+  /**
+   * The time fields.  The array is indexed by the constants YEAR to
+   * DST_OFFSET.
+   * @serial
+   */
+  protected int[] fields = new int[FIELD_COUNT];
+
+  /**
+   * The flags which tell if the fields above have a value.
+   * @serial
+   */
+  protected boolean[] isSet = new boolean[FIELD_COUNT];
+
+  /**
+   * The time in milliseconds since the epoch.
+   * @serial
+   */
+  protected long time;
+
+  /**
+   * Tells if the above field has a valid value.
+   * @serial
+   */
+  protected boolean isTimeSet;
+
+  /**
+   * Tells if the fields have a valid value.  This superseeds the isSet
+   * array.
+   * @serial
+   */
+  protected boolean areFieldsSet;
+
+  /**
+   * The time zone of this calendar.  Used by sub classes to do UTC / local
+   * time conversion.  Sub classes can access this field with getTimeZone().
+   * @serial
+   */
+  private TimeZone zone;
+
+  /**
+   * This is the default calendar class, that is returned on
+   * java.util.Calendar.getInstance().
+   * XXX - this isn't localized anywhere, is it?
+   * @see java.util.Calendar#getInstance()
+   */
+  private static final String calendarClassName = "java.util.GregorianCalendar";
+
+  /**
+   * Specifies if the date/time interpretation should be lenient.
+   * If the flag is set, a date such as "February 30, 1996" will be
+   * treated as the 29th day after the February 1.  If this flag
+   * is false, such dates will cause an exception.
+   * @serial
+   */
+  private boolean lenient;
+
+  /**
+   * Sets what the first day of week is.  This is used for
+   * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
+   * @serial
+   */
+  private int firstDayOfWeek;
+
+  /**
+   * Sets how many days are required in the first week of the year.
+   * If the first day of the year should be the first week you should
+   * set this value to 1.  If the first week must be a full week, set
+   * it to 7.
+   * @serial
+   */
+  private int minimalDaysInFirstWeek;
+
+  /**
+   * Is set to true if DST_OFFSET is explicitly set. In that case
+   * it's value overrides the value computed from the current
+   * time and the timezone.
+   */
+  private boolean explicitDSTOffset = false;
+
+  /**
+   * The version of the serialized data on the stream.
+   * <dl><dt>0 or not present</dt>
+   * <dd> JDK 1.1.5 or later.</dd>
+   * <dt>1</dt>
+   * <dd>JDK 1.1.6 or later.  This always writes a correct `time' value
+   * on the stream, as well as the other fields, to be compatible with
+   * earlier versions</dd></dl>
+   * @since JDK1.1.6
+   * @serial
+   */
+  private int serialVersionOnStream = 1;
+
+  /**
+   * XXX - I have not checked the compatibility.  The documentation of
+   * the serialized-form is quite hairy...
+   */
+  static final long serialVersionUID = -1807547505821590642L;
+
+  /**
+   * The name of the resource bundle. Used only by getBundle()
+   */
+  private static final String bundleName = "gnu.java.locale.LocaleInformation";
+
+  /**
+   * get resource bundle:
+   * The resources should be loaded via this method only. Iff an application
+   * uses this method, the resourcebundle is required.
+   */
+  /*private static ResourceBundle getBundle(Locale locale)
+  {
+    return ResourceBundle.getBundle(bundleName, locale,
+                                    ClassLoader.getSystemClassLoader());
+  }*/
+
+  /**
+   * The set of properties for obtaining the minimum number of days in 
+   * the first week.
+   */
+  //private static transient final Properties properties;
+
+  /**
+   * Reads in the properties.
+   */
+  /*static
+  {
+    properties = new Properties();
+    try 
+      {
+        properties.load(Calendar.class.getResourceAsStream("weeks.properties"));
+      }
+    catch (IOException exception)
+      {
+        System.out.println("Failed to load weeks resource: " + exception);
+      }
+  }*/
+
+  /**
+   * Constructs a new Calendar with the default time zone and the default
+   * locale.
+   */
+  protected Calendar()
+  {
+    this(/*TimeZone.getDefault(), */Locale.getDefault());
+  }
+
+  /**
+   * Constructs a new Calendar with the given time zone and the given
+   * locale.
+   * @param zone a time zone.
+   * @param locale a locale.
+   */
+  protected Calendar(/*TimeZone zone, */Locale locale)
+  {
+    //this.zone = zone;
+    lenient = true;
+    String[] days = { "", "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
+
+    /*String country = locale.getCountry();
+    String min = properties.getProperty("minDays." + country);
+    if (min == null)
+      min = properties.getProperty("minDays.DEFAULT");
+    String first = properties.getProperty("firstDay." + country);
+    if (first == null)
+      first = properties.getProperty("firstDay.DEFAULT");
+    try
+      {
+       if (min != null)
+         minimalDaysInFirstWeek = Integer.parseInt(min);
+      }
+    catch (NumberFormatException ex)
+      {
+       minimalDaysInFirstWeek = 1;
+      }*/
+
+    firstDayOfWeek = 1;
+    /*if (first != null)
+      for (int i = 0; i < 8; i++)
+       if (days[i].equals(first))
+         firstDayOfWeek = i;*/
+
+    clear();
+  }
+
+  /**
+   * Creates a calendar representing the actual time, using the default
+   * time zone and locale.
+   * 
+   * @return The new calendar.
+   */
+  public static synchronized Calendar getInstance()
+  {
+    return getInstance(/*TimeZone.getDefault(), */Locale.getDefault());
+  }
+
+  /**
+   * Creates a calendar representing the actual time, using the given
+   * time zone and the default locale.
+   * 
+   * @param zone a time zone (<code>null</code> not permitted).
+   * 
+   * @return The new calendar.
+   * 
+   * @throws NullPointerException if <code>zone</code> is <code>null</code>.
+   */
+  /*public static synchronized Calendar getInstance(TimeZone zone)
+  {
+    return getInstance(zone, Locale.getDefault());
+  }*/
+
+  /**
+   * Creates a calendar representing the actual time, using the default
+   * time zone and the given locale.
+   * 
+   * @param locale a locale (<code>null</code> not permitted).
+   * 
+   * @return The new calendar.
+   * 
+   * @throws NullPointerException if <code>locale</code> is <code>null</code>.
+   */
+  /*public static synchronized Calendar getInstance(Locale locale)
+  {
+    return getInstance(TimeZone.getDefault(), locale);
+  }*/
+
+  /**
+   * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle
+   * lookup for every getInstance call.
+   */
+  private static final HashMap/*<Locale,Class>*/ cache = new HashMap/*<Locale,Class>*/();
+
+  /** Preset argument types for calendar-class constructor lookup.  */
+  /*private static Class[] ctorArgTypes = new Class[]
+                                        {
+                                          TimeZone.class, Locale.class
+                                        };*/
+
+  /**
+   * Creates a calendar representing the actual time, using the given
+   * time zone and locale.
+   * 
+   * @param zone a time zone (<code>null</code> not permitted).
+   * @param locale a locale (<code>null</code> not permitted).
+   * 
+   * @return The new calendar.
+   * 
+   * @throws NullPointerException if <code>zone</code> or <code>locale</code>
+   *     is <code>null</code>.
+   */
+  public static synchronized Calendar getInstance(/*TimeZone zone, */Locale locale)
+  {
+    Class calendarClass = (Class)cache.get(locale);
+    //Throwable exception = null;
+
+    /*try
+      {
+       if (calendarClass == null)
+         {
+           calendarClass = Class.forName(calendarClassName);
+           if (Calendar.class.isAssignableFrom(calendarClass))
+             cache.put(locale, calendarClass);
+         }*/
+
+       // GregorianCalendar is by far the most common case. Optimize by 
+       // avoiding reflection.
+       //if (calendarClass == GregorianCalendar.class)
+         return new GregorianCalendar(zone, locale);
+
+       /*if (Calendar.class.isAssignableFrom(calendarClass))
+         {
+           Constructor ctor = calendarClass.getConstructor(ctorArgTypes);
+           return (Calendar) ctor.newInstance(new Object[] { zone, locale });
+         }
+      }
+    catch (ClassNotFoundException ex)
+      {
+       exception = ex;
+      }
+    catch (IllegalAccessException ex)
+      {
+       exception = ex;
+      }
+    catch (NoSuchMethodException ex)
+      {
+       exception = ex;
+      }
+    catch (InstantiationException ex)
+      {
+       exception = ex;
+      }
+    catch (InvocationTargetException ex)
+      {
+       exception = ex;
+      }
+
+    throw new RuntimeException("Error instantiating calendar for locale "
+                               + locale, exception);*/
+  }
+
+  /**
+   * Gets the set of locales for which a Calendar is available.
+   * @exception MissingResourceException if locale data couldn't be found.
+   * @return the set of locales.
+   */
+  /*public static synchronized Locale[] getAvailableLocales()
+  {
+    ResourceBundle rb = getBundle(new Locale("", ""));
+    return (Locale[]) rb.getObject("availableLocales");
+  }*/
+
+  /**
+   * Converts the time field values (<code>fields</code>) to
+   * milliseconds since the epoch UTC (<code>time</code>).  Override
+   * this method if you write your own Calendar.  */
+  protected /*abstract*/ void computeTime();
+
+  /**
+   * Converts the milliseconds since the epoch UTC
+   * (<code>time</code>) to time fields
+   * (<code>fields</code>). Override this method if you write your
+   * own Calendar.
+   */
+  protected /*abstract*/ void computeFields();
+
+  /**
+   * Converts the time represented by this object to a
+   * <code>Date</code>-Object.
+   * @return the Date.
+   */
+  /*public final Date getTime()
+  {
+    if (! isTimeSet)
+      computeTime();
+    return new Date(time);
+  }*/
+
+  /**
+   * Sets this Calendar's time to the given Date.  All time fields
+   * are invalidated by this method.
+   * 
+   * @param date  the date (<code>null</code> not permitted).
+   * 
+   * @throws NullPointerException if <code>date</code> is <code>null</code>.
+   */
+  public final void setTime(Date date)
+  {
+    setTimeInMillis(date.getTime());
+  }
+
+  /**
+   * Returns the time represented by this Calendar.
+   * @return the time in milliseconds since the epoch.
+   * @specnote This was made public in 1.4.
+   */
+  /*public long getTimeInMillis()
+  {
+    if (! isTimeSet)
+      computeTime();
+    return time;
+  }*/
+
+  /**
+   * Sets this Calendar's time to the given Time.  All time fields
+   * are invalidated by this method.
+   * @param time the time in milliseconds since the epoch
+   * @specnote This was made public in 1.4.
+   */
+  public void setTimeInMillis(long time)
+  {
+    clear();
+    this.time = time;
+    isTimeSet = true;
+    computeFields();
+  }
+
+  /**
+   * Gets the value of the specified field.  They are recomputed
+   * if they are invalid.
+   * @param field the time field. One of the time field constants.
+   * @return the value of the specified field
+   * @throws ArrayIndexOutOfBoundsException if the field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   * @specnote Not final since JDK 1.4
+   */
+  public int get(int field)
+  {
+    // If the requested field is invalid, force all fields to be recomputed.
+    if (! isSet[field])
+      areFieldsSet = false;
+    complete();
+    return fields[field];
+  }
+
+  /**
+   * Gets the value of the specified field. This method doesn't
+   * recompute the fields, if they are invalid.
+   * @param field the time field. One of the time field constants.
+   * @return the value of the specified field, undefined if
+   * <code>areFieldsSet</code> or <code>isSet[field]</code> is false.
+   * @throws ArrayIndexOutOfBoundsException if the field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   */
+  protected final int internalGet(int field)
+  {
+    return fields[field];
+  }
+
+  /**
+   * Sets the time field with the given value.  This does invalidate
+   * the time in milliseconds.
+   * @param field the time field. One of the time field constants
+   * @param value the value to be set.
+   * @throws ArrayIndexOutOfBoundsException if field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   * @specnote Not final since JDK 1.4
+   */
+  /*public void set(int field, int value)
+  {
+    if (isTimeSet)
+      for (int i = 0; i < FIELD_COUNT; i++)
+       isSet[i] = false;
+    isTimeSet = false;
+    fields[field] = value;
+    isSet[field] = true;
+
+    // The five valid date patterns, in order of priority
+    // 1  YEAR + MONTH + DAY_OF_MONTH
+    // 2  YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
+    // 3  YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
+    // 4  YEAR + DAY_OF_YEAR
+    // 5  YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
+    switch (field)
+      {
+      case MONTH: // pattern 1,2 or 3
+       isSet[DAY_OF_YEAR] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       break;
+      case DAY_OF_MONTH: // pattern 1
+       isSet[YEAR] = true;
+       isSet[MONTH] = true;
+       isSet[WEEK_OF_MONTH] = true;
+       isSet[DAY_OF_WEEK] = false;
+       isSet[DAY_OF_WEEK_IN_MONTH] = false;
+       isSet[DAY_OF_YEAR] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       break;
+      case WEEK_OF_MONTH: // pattern 2
+       if (! isSet[DAY_OF_WEEK])
+         fields[DAY_OF_WEEK] = getFirstDayOfWeek();
+       isSet[YEAR] = true;
+       isSet[MONTH] = true;
+       isSet[DAY_OF_WEEK] = true;
+       isSet[DAY_OF_MONTH] = false;
+       isSet[DAY_OF_WEEK_IN_MONTH] = false;
+       isSet[DAY_OF_YEAR] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       break;
+      case DAY_OF_WEEK_IN_MONTH: // pattern 3
+       if (! isSet[DAY_OF_WEEK])
+         fields[DAY_OF_WEEK] = getFirstDayOfWeek();
+       isSet[YEAR] = true;
+       isSet[MONTH] = true;
+       isSet[DAY_OF_WEEK] = true;
+       isSet[DAY_OF_YEAR] = false;
+       isSet[DAY_OF_MONTH] = false;
+       isSet[WEEK_OF_MONTH] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       break;
+      case DAY_OF_YEAR: // pattern 4
+       isSet[YEAR] = true;
+       isSet[MONTH] = false;
+       isSet[WEEK_OF_MONTH] = false;
+       isSet[DAY_OF_MONTH] = false;
+       isSet[DAY_OF_WEEK] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       isSet[DAY_OF_WEEK_IN_MONTH] = false;
+       break;
+      case WEEK_OF_YEAR: // pattern 5
+       if (! isSet[DAY_OF_WEEK])
+         fields[DAY_OF_WEEK] = getFirstDayOfWeek();
+       isSet[YEAR] = true;
+       isSet[DAY_OF_WEEK] = true;
+       isSet[MONTH] = false;
+       isSet[DAY_OF_MONTH] = false;
+       isSet[WEEK_OF_MONTH] = false;
+       isSet[DAY_OF_YEAR] = false;
+       isSet[DAY_OF_WEEK_IN_MONTH] = false;
+       break;
+      case AM_PM:
+       isSet[HOUR] = true;
+       isSet[HOUR_OF_DAY] = false;
+       break;
+      case HOUR_OF_DAY:
+       isSet[AM_PM] = false;
+       isSet[HOUR] = false;
+       break;
+      case HOUR:
+       isSet[AM_PM] = true;
+       isSet[HOUR_OF_DAY] = false;
+       break;
+      case DST_OFFSET:
+       explicitDSTOffset = true;
+      }
+
+    // May have crossed over a DST boundary.
+    if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET))
+      isSet[DST_OFFSET] = false;
+  }*/
+
+  /**
+   * Sets the fields for year, month, and date
+   * @param year the year.
+   * @param month the month, one of the constants JANUARY..UNDICEMBER.
+   * @param date the day of the month
+   */
+  /*public final void set(int year, int month, int date)
+  {
+    isTimeSet = false;
+    fields[YEAR] = year;
+    fields[MONTH] = month;
+    fields[DATE] = date;
+    isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true;
+    isSet[WEEK_OF_YEAR] = false;
+    isSet[DAY_OF_YEAR] = false;
+    isSet[WEEK_OF_MONTH] = false;
+    isSet[DAY_OF_WEEK] = false;
+    isSet[DAY_OF_WEEK_IN_MONTH] = false;
+    isSet[ERA] = false;
+
+    if (! explicitDSTOffset)
+      isSet[DST_OFFSET] = false; // May have crossed a DST boundary.
+  }*/
+
+  /**
+   * Sets the fields for year, month, date, hour, and minute
+   * @param year the year.
+   * @param month the month, one of the constants JANUARY..UNDICEMBER.
+   * @param date the day of the month
+   * @param hour the hour of day.
+   * @param minute the minute.
+   */
+  /*public final void set(int year, int month, int date, int hour, int minute)
+  {
+    set(year, month, date);
+    fields[HOUR_OF_DAY] = hour;
+    fields[MINUTE] = minute;
+    isSet[HOUR_OF_DAY] = isSet[MINUTE] = true;
+    isSet[AM_PM] = false;
+    isSet[HOUR] = false;
+  }*/
+
+  /**
+   * Sets the fields for year, month, date, hour, and minute
+   * @param year the year.
+   * @param month the month, one of the constants JANUARY..UNDICEMBER.
+   * @param date the day of the month
+   * @param hour the hour of day.
+   * @param minute the minute.
+   * @param second the second.
+   */
+  /*public final void set(int year, int month, int date, int hour, int minute,
+                        int second)
+  {
+    set(year, month, date, hour, minute);
+    fields[SECOND] = second;
+    isSet[SECOND] = true;
+  }*/
+
+  /**
+   * Clears the values of all the time fields.
+   */
+  public final void clear()
+  {
+    isTimeSet = false;
+    areFieldsSet = false;
+    int zoneOffs = 0;//zone.getRawOffset();
+    int[] tempFields = 
+                       {
+                         1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
+                         0, 0, zoneOffs, 0
+                       };
+    fields = tempFields;
+    for (int i = 0; i < FIELD_COUNT; i++)
+      isSet[i] = false;
+  }
+
+  /**
+   * Clears the values of the specified time field.
+   * @param field the time field. One of the time field constants.
+   * @throws ArrayIndexOutOfBoundsException if field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   */
+  public final void clear(int field)
+  {
+    int[] tempFields = 
+                       {
+                         1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
+                         0, 0, 0/*zone.getRawOffset()*/, 0
+                       };
+    complete();
+    isTimeSet = false;
+    areFieldsSet = false;
+    isSet[field] = false;
+    fields[field] = tempFields[field];
+  }
+
+  /**
+   * Determines if the specified field has a valid value.
+   * @return true if the specified field has a value.
+   * @throws ArrayIndexOutOfBoundsException if the field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   */
+  public final boolean isSet(int field)
+  {
+    return isSet[field];
+  }
+
+  /**
+   * Fills any unset fields in the time field list
+   */
+  protected void complete()
+  {
+    if (! isTimeSet)
+      computeTime();
+    if (! areFieldsSet)
+      computeFields();
+  }
+
+  /**
+   * Compares the given calendar with this.
+   * @param o the object to that we should compare.
+   * @return true, if the given object is a calendar, that represents
+   * the same time (but doesn't necessary have the same fields).
+   */
+  // TODO always false now
+  public boolean equals(Object o)
+  {
+    if (! (o instanceof Calendar))
+      return false;
+    Calendar cal = (Calendar) o;
+    /*if (getTimeInMillis() == ((Calendar) o).getTimeInMillis()
+        && cal.getFirstDayOfWeek() == getFirstDayOfWeek()
+        && cal.isLenient() == isLenient()
+        && cal.getMinimalDaysInFirstWeek() == getMinimalDaysInFirstWeek())
+      {
+        TimeZone self = getTimeZone();
+        TimeZone oth = cal.getTimeZone();
+        return self == null ? oth == null : self.equals(oth);
+      }*/
+    return false;
+  }
+
+  /**
+   * Returns a hash code for this calendar.
+   * @return a hash code, which fullfits the general contract of
+   * <code>hashCode()</code>
+   */
+  public int hashCode()
+  {
+    long time = getTimeInMillis();
+    int val = (int) ((time & 0xffffffffL) ^ (time >> 32));
+    /*val += (getFirstDayOfWeek() + (isLenient() ? 1230 : 1237)
+            + getMinimalDaysInFirstWeek());
+    TimeZone self = getTimeZone();
+    if (self != null)
+      val ^= self.hashCode();*/
+    return val;
+  }
+
+  /**
+   * Compares the given calendar with this.
+   * @param o the object to that we should compare.
+   * @return true, if the given object is a calendar, and this calendar
+   * represents a smaller time than the calendar o.
+   * @exception ClassCastException if o is not an calendar.
+   * @since JDK1.2 you don't need to override this method
+   */
+  /*public boolean before(Object o)
+  {
+    return getTimeInMillis() < ((Calendar) o).getTimeInMillis();
+  }*/
+
+  /**
+   * Compares the given calendar with this.
+   * @param o the object to that we should compare.
+   * @return true, if the given object is a calendar, and this calendar
+   * represents a bigger time than the calendar o.
+   * @exception ClassCastException if o is not an calendar.
+   * @since JDK1.2 you don't need to override this method
+   */
+  /*public boolean after(Object o)
+  {
+    return getTimeInMillis() > ((Calendar) o).getTimeInMillis();
+  }*/
+
+  /**
+   * Adds the specified amount of time to the given time field.  The
+   * amount may be negative to subtract the time.  If the field overflows
+   * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
+   * @param field the time field. One of the time field constants.
+   * @param amount the amount of time.
+   * @throws ArrayIndexOutOfBoundsException if the field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   */
+  public /*abstract*/ void add(int field, int amount);
+
+  /**
+   * Rolls the specified time field up or down.  This means add one
+   * to the specified field, but don't change the other fields.  If
+   * the maximum for this field is reached, start over with the
+   * minimum value.  <br>
+   *
+   * <strong>Note:</strong> There may be situation, where the other
+   * fields must be changed, e.g rolling the month on May, 31.
+   * The date June, 31 is automatically converted to July, 1.
+   * @param field the time field. One of the time field constants.
+   * @param up the direction, true for up, false for down.
+   * @throws ArrayIndexOutOfBoundsException if the field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   */
+  public /*abstract*/ void roll(int field, boolean up);
+
+  /**
+   * Rolls up or down the specified time field by the given amount.
+   * A negative amount rolls down.  The default implementation is
+   * call <code>roll(int, boolean)</code> for the specified amount.
+   *
+   * Subclasses should override this method to do more intuitiv things.
+   *
+   * @param field the time field. One of the time field constants.
+   * @param amount the amount to roll by, positive for rolling up,
+   * negative for rolling down.
+   * @throws ArrayIndexOutOfBoundsException if the field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   * @since JDK1.2
+   */
+  public void roll(int field, int amount)
+  {
+    while (amount > 0)
+      {
+       roll(field, true);
+       amount--;
+      }
+    while (amount < 0)
+      {
+       roll(field, false);
+       amount++;
+      }
+  }
+
+  /**
+   * Sets the time zone to the specified value.
+   * @param zone the new time zone
+   */
+  /*public void setTimeZone(TimeZone zone)
+  {
+    this.zone = zone;
+    computeTime();
+    computeFields();
+  }*/
+
+  /**
+   * Gets the time zone of this calendar
+   * @return the current time zone.
+   */
+  /*public TimeZone getTimeZone()
+  {
+    return zone;
+  }*/
+
+  /**
+   * Specifies if the date/time interpretation should be lenient.
+   * If the flag is set, a date such as "February 30, 1996" will be
+   * treated as the 29th day after the February 1.  If this flag
+   * is false, such dates will cause an exception.
+   * @param lenient true, if the date should be interpreted linient,
+   * false if it should be interpreted strict.
+   */
+  public void setLenient(boolean lenient)
+  {
+    this.lenient = lenient;
+  }
+
+  /**
+   * Tells if the date/time interpretation is lenient.
+   * @return true, if the date should be interpreted linient,
+   * false if it should be interpreted strict.
+   */
+  public boolean isLenient()
+  {
+    return lenient;
+  }
+
+  /**
+   * Sets what the first day of week is.  This is used for
+   * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
+   * @param value the first day of week.  One of SUNDAY to SATURDAY.
+   */
+  public void setFirstDayOfWeek(int value)
+  {
+    firstDayOfWeek = value;
+  }
+
+  /**
+   * Gets what the first day of week is.  This is used for
+   * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
+   * @return the first day of week.  One of SUNDAY to SATURDAY.
+   */
+  public int getFirstDayOfWeek()
+  {
+    return firstDayOfWeek;
+  }
+
+  /**
+   * Sets how many days are required in the first week of the year.
+   * If the first day of the year should be the first week you should
+   * set this value to 1.  If the first week must be a full week, set
+   * it to 7.
+   * @param value the minimal days required in the first week.
+   */
+  public void setMinimalDaysInFirstWeek(int value)
+  {
+    minimalDaysInFirstWeek = value;
+  }
+
+  /**
+   * Gets how many days are required in the first week of the year.
+   * @return the minimal days required in the first week.
+   * @see #setMinimalDaysInFirstWeek
+   */
+  public int getMinimalDaysInFirstWeek()
+  {
+    return minimalDaysInFirstWeek;
+  }
+
+  /**
+   * Gets the smallest value that is allowed for the specified field.
+   * @param field the time field. One of the time field constants.
+   * @return the smallest value.
+   */
+  public /*abstract*/ int getMinimum(int field);
+
+  /**
+   * Gets the biggest value that is allowed for the specified field.
+   * @param field the time field. One of the time field constants.
+   * @return the biggest value.
+   */
+  public /*abstract*/ int getMaximum(int field);
+
+  /**
+   * Gets the greatest minimum value that is allowed for the specified field.
+   * @param field the time field. One of the time field constants.
+   * @return the greatest minimum value.
+   */
+  public /*abstract*/ int getGreatestMinimum(int field);
+
+  /**
+   * Gets the smallest maximum value that is allowed for the
+   * specified field.  For example this is 28 for DAY_OF_MONTH.
+   * @param field the time field. One of the time field constants.
+   * @return the least maximum value.
+   */
+  public /*abstract*/ int getLeastMaximum(int field);
+
+  /**
+   * Gets the actual minimum value that is allowed for the specified field.
+   * This value is dependent on the values of the other fields.
+   * @param field the time field. One of the time field constants.
+   * @return the actual minimum value.
+   * @throws ArrayIndexOutOfBoundsException if the field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   * @since jdk1.2
+   */
+  /*public int getActualMinimum(int field)
+  {
+    Calendar tmp = (Calendar) clone(); // To avoid restoring state
+    int min = tmp.getGreatestMinimum(field);
+    int end = tmp.getMinimum(field);
+    tmp.set(field, min);
+    for (; min > end; min--)
+      {
+       tmp.add(field, -1); // Try to get smaller
+       if (tmp.get(field) != min - 1)
+         break; // Done if not successful
+      }
+    return min;
+  }*/
+
+  /**
+   * Gets the actual maximum value that is allowed for the specified field.
+   * This value is dependent on the values of the other fields.
+   * @param field the time field. One of the time field constants.
+   * @return the actual maximum value.
+   * @throws ArrayIndexOutOfBoundsException if the field is outside
+   *         the valid range.  The value of field must be >= 0 and
+   *         <= <code>FIELD_COUNT</code>.
+   * @since jdk1.2
+   */
+  /*public int getActualMaximum(int field)
+  {
+    Calendar tmp = (Calendar) clone(); // To avoid restoring state
+    int max = tmp.getLeastMaximum(field);
+    int end = tmp.getMaximum(field);
+    tmp.set(field, max);
+    for (; max < end; max++)
+      {
+       tmp.add(field, 1);
+       if (tmp.get(field) != max + 1)
+         break;
+      }
+    return max;
+  }*/
+
+  /**
+   * Compares the time of two calendar instances.
+   * @param cal the calendar to which the time should be compared.
+   * @return 0 if the two calendars are set to the same time, 
+   * less than 0 if the time of this calendar is before that of 
+   * <code>cal</code>, or more than 0 if the time of this calendar is after
+   * that of <code>cal</code>.
+   *
+   * @param cal the calendar to compare this instance with.
+   * @throws NullPointerException if <code>cal</code> is null.
+   * @throws IllegalArgumentException if either calendar has fields set to 
+   * invalid values.
+   * @since 1.5
+   */
+  /*public int compareTo(Calendar cal)
+  {
+    long t1 = getTimeInMillis();
+    long t2 = cal.getTimeInMillis();
+    if(t1 == t2)
+      return 0;
+    if(t1 > t2)
+      return 1;
+    return -1;
+  }*/
+
+  /**
+   * Return a clone of this object.
+   */
+  public Object clone()
+  {
+    try
+      {
+       Calendar cal = (Calendar) super.clone();
+       cal.fields = (int[]) fields.clone();
+       cal.isSet = (boolean[]) isSet.clone();
+       return cal;
+      }
+    catch (CloneNotSupportedException ex)
+      {
+       return null;
+      }
+  }
+
+  private static final String[] fieldNames = 
+                                             {
+                                               ",ERA=", ",YEAR=", ",MONTH=",
+                                               ",WEEK_OF_YEAR=",
+                                               ",WEEK_OF_MONTH=",
+                                               ",DAY_OF_MONTH=",
+                                               ",DAY_OF_YEAR=", ",DAY_OF_WEEK=",
+                                               ",DAY_OF_WEEK_IN_MONTH=",
+                                               ",AM_PM=", ",HOUR=",
+                                               ",HOUR_OF_DAY=", ",MINUTE=",
+                                               ",SECOND=", ",MILLISECOND=",
+                                               ",ZONE_OFFSET=", ",DST_OFFSET="
+                                             };
+
+  /**
+   * Returns a string representation of this object.  It is mainly
+   * for debugging purposes and its content is implementation
+   * specific.
+   */
+  /*public String toString()
+  {
+    CPStringBuilder sb = new CPStringBuilder(getClass().getName());
+    sb.append('[');
+    sb.append("time=");
+    if (isTimeSet)
+      sb.append(time);
+    else
+      sb.append("?");
+    sb.append(",zone=" + zone);
+    sb.append(",areFieldsSet=" + areFieldsSet);
+    for (int i = 0; i < FIELD_COUNT; i++)
+      {
+       sb.append(fieldNames[i]);
+       if (isSet[i])
+         sb.append(fields[i]);
+       else
+         sb.append("?");
+      }
+    sb.append(",lenient=").append(lenient);
+    sb.append(",firstDayOfWeek=").append(firstDayOfWeek);
+    sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek);
+    sb.append("]");
+    return sb.toString();
+  }*/
+
+  /**
+   * Saves the state of the object to the stream.  Ideally we would
+   * only write the time field, but we need to be compatible with
+   * earlier versions. <br>
+   *
+   * This doesn't write the JDK1.1 field nextStamp to the stream, as
+   * I don't know what it is good for, and because the documentation
+   * says, that it could be omitted.  */
+  /*private void writeObject(ObjectOutputStream stream) throws IOException
+  {
+    if (! isTimeSet)
+      computeTime();
+    stream.defaultWriteObject();
+  }*/
+
+  /**
+   * Reads the object back from stream (deserialization).
+   */
+  /*private void readObject(ObjectInputStream stream)
+    throws IOException, ClassNotFoundException
+  {
+    stream.defaultReadObject();
+    if (! isTimeSet)
+      computeTime();
+
+    if (serialVersionOnStream > 1)
+      {
+       // This is my interpretation of the serial number:
+       // Sun wants to remove all fields from the stream someday
+       // and will then increase the serialVersion number again.
+       // We prepare to be compatible.
+       fields = new int[FIELD_COUNT];
+       isSet = new boolean[FIELD_COUNT];
+       areFieldsSet = false;
+      }
+  }*/
+
+  /**
+   * Returns a localised textual representation of the current value
+   * of the given field using the specified style.  If there is no
+   * applicable textual representation (e.g. the field has a numeric
+   * value), then <code>null</code> is returned.  If one does exist,
+   * then the value is obtained from {@link #get(int)} and converted
+   * appropriately.  For example, if the <code>MONTH</code> field is
+   * requested, then <code>get(MONTH)</code> is called.  This is then
+   * converted to a textual representation based on its value and
+   * the style requested; if the <code>LONG</code> style is requested
+   * and the returned value is <code>11</code> from a
+   * {@link GregorianCalendar} implementation, then <code>"December"</code>
+   * is returned.  By default, a textual representation is available
+   * for all fields which have an applicable value obtainable from
+   * {@link java.text.DateFormatSymbols}.
+   *
+   * @param field the calendar field whose textual representation should
+   *              be obtained.
+   * @param style the style to use; either {@link #LONG} or {@link #SHORT}.
+   * @param locale the locale to use for translation.
+   * @return the textual representation of the given field in the specified
+   *         style, or <code>null</code> if none is applicable.
+   * @throws IllegalArgumentException if <code>field</code> or <code>style</code>
+   *                                  or invalid, or the calendar is non-lenient
+   *                                  and has invalid values.
+   * @throws NullPointerException if <code>locale</code> is <code>null</code>.
+   * @since 1.6
+   */
+  /*public String getDisplayName(int field, int style, Locale locale)
+  {
+    if (field < 0 || field >= FIELD_COUNT)
+      throw new IllegalArgumentException("The field value, " + field +
+                                        ", is invalid.");
+    if (style != SHORT && style != LONG)
+      throw new IllegalArgumentException("The style must be either " +
+                                        "short or long.");
+    if (field == YEAR || field == WEEK_OF_YEAR ||
+       field == WEEK_OF_MONTH || field == DAY_OF_MONTH ||
+       field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH ||
+       field == HOUR || field == HOUR_OF_DAY || field == MINUTE ||
+       field == SECOND || field == MILLISECOND)
+      return null;
+
+    int value = get(field);
+    DateFormatSymbols syms = DateFormatSymbols.getInstance(locale);
+    if (field == ERA)
+      return syms.getEras()[value];
+    if (field == MONTH)
+      if (style == LONG)
+       return syms.getMonths()[value];
+      else 
+       return syms.getShortMonths()[value];
+    if (field == DAY_OF_WEEK)
+      if (style == LONG)
+       return syms.getWeekdays()[value];
+      else
+       return syms.getShortWeekdays()[value];
+    if (field == AM_PM)
+      return syms.getAmPmStrings()[value];
+    if (field == ZONE_OFFSET)
+      if (style == LONG)
+       return syms.getZoneStrings()[value][1];
+      else
+       return syms.getZoneStrings()[value][2];
+    if (field == DST_OFFSET)
+      if (style == LONG)
+       return syms.getZoneStrings()[value][3];
+      else
+       return syms.getZoneStrings()[value][4];
+
+    throw new InternalError("Failed to resolve field " + field +
+                           " with style " + style + " for locale " +
+                           locale);
+  }*/
+
+  /**
+   * Returns a map linking all specified textual representations
+   * of the given field to their numerical values.  The textual
+   * representations included are determined by the specified
+   * style and locale.  For example, if the style <code>LONG</code>
+   * is specified and the German locale, then the map will
+   * contain "Montag" to {@link #MONDAY}, "Dienstag" to
+   * {@link #TUESDAY}, "Mittwoch" to {@link #WEDNESDAY} and
+   * so on.  The default implementation uses the values returned
+   * by {@link DateFormatSymbols} so, for example, the style
+   * {@link #ALL_STYLES} and the field {@link #MONTH} will return
+   * a map filled with the values returned from
+   * {@link DateFormatSymbols#getMonths()} and
+   * {@link DateFormatSymbols#getShortMonths()}.  If there are
+   * no textual representations for a given field (usually because
+   * it is purely numeric, such as the year in the
+   * {@link GregorianCalendar}), <code>null</code> is returned.
+   *
+   * @param field the calendar field whose textual representation should
+   *              be obtained.
+   * @param style the style to use; either {@link #LONG}, {@link #SHORT}
+   *              or {@link ALL_STYLES}.
+   * @param locale the locale to use for translation.
+   * @return a map of the textual representations of the given field in the
+   *         specified style to their numeric values, or <code>null</code>
+   *         if none is applicable.
+   * @throws IllegalArgumentException if <code>field</code> or <code>style</code>
+   *                                  or invalid, or the calendar is non-lenient
+   *                                  and has invalid values.
+   * @throws NullPointerException if <code>locale</code> is <code>null</code>.
+   * @since 1.6
+   */
+  /*public Map<String,Integer> getDisplayNames(int field, int style, Locale locale)
+  {
+    if (field < 0 || field >= FIELD_COUNT)
+      throw new IllegalArgumentException("The field value, " + field +
+                                        ", is invalid.");
+    if (style != SHORT && style != LONG && style != ALL_STYLES)
+      throw new IllegalArgumentException("The style must be either " +
+                                        "short, long or all styles.");
+    if (field == YEAR || field == WEEK_OF_YEAR ||
+       field == WEEK_OF_MONTH || field == DAY_OF_MONTH ||
+       field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH ||
+       field == HOUR || field == HOUR_OF_DAY || field == MINUTE ||
+       field == SECOND || field == MILLISECOND)
+      return null;
+
+    DateFormatSymbols syms = DateFormatSymbols.getInstance(locale);
+    Map<String,Integer> map = new HashMap<String,Integer>();
+    if (field == ERA)
+      {
+       String[] eras = syms.getEras();
+       for (int a = 0; a < eras.length; ++a)
+         map.put(eras[a], a);
+       return map;
+      }
+    if (field == MONTH)
+      {
+       if (style == LONG || style == ALL_STYLES)
+         {
+           String[] months = syms.getMonths();
+           for (int a = 0; a < months.length; ++a)
+             map.put(months[a], a);
+         }
+       if (style == SHORT || style == ALL_STYLES)
+         {
+           String[] months = syms.getShortMonths();
+           for (int a = 0; a < months.length; ++a)
+             map.put(months[a], a);
+         }
+       return map;
+      }
+    if (field == DAY_OF_WEEK)
+      {
+       if (style == LONG || style == ALL_STYLES)
+         {
+           String[] weekdays = syms.getWeekdays();
+           for (int a = SUNDAY; a < weekdays.length; ++a)
+             map.put(weekdays[a], a);
+         }
+       if (style == SHORT || style == ALL_STYLES)
+         {
+           String[] weekdays = syms.getShortWeekdays();
+           for (int a = SUNDAY; a < weekdays.length; ++a)
+             map.put(weekdays[a], a);
+         }
+       return map;
+      }
+    if (field == AM_PM)
+      {
+       String[] ampms = syms.getAmPmStrings();
+       for (int a = 0; a < ampms.length; ++a)
+         map.put(ampms[a], a);
+       return map;
+      }
+    if (field == ZONE_OFFSET)
+      {
+       String[][] zones = syms.getZoneStrings();
+       for (int a = 0; a < zones.length; ++a)
+         {
+           if (style == LONG || style == ALL_STYLES) 
+             map.put(zones[a][1], a);
+           if (style == SHORT || style == ALL_STYLES)
+             map.put(zones[a][2], a);
+         }
+       return map;
+      }
+    if (field == DST_OFFSET)
+      {
+       String[][] zones = syms.getZoneStrings();
+       for (int a = 0; a < zones.length; ++a)
+         {
+           if (style == LONG || style == ALL_STYLES) 
+             map.put(zones[a][3], a);
+           if (style == SHORT || style == ALL_STYLES)
+             map.put(zones[a][4], a);
+         }
+       return map;
+      }
+    
+    throw new InternalError("Failed to resolve field " + field +
+                           " with style " + style + " for locale " +
+                           locale);
+  }*/
+
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/Constructor.java b/Robust/src/ClassLibrary/MGC/gnu/Constructor.java
new file mode 100755 (executable)
index 0000000..e72a991
--- /dev/null
@@ -0,0 +1,454 @@
+/* java.lang.reflect.Constructor - reflection of Java constructors
+   Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.lang.reflect;
+
+/*import gnu.java.lang.ClassHelper;
+import gnu.java.lang.CPStringBuilder;
+
+import gnu.java.lang.reflect.MethodSignatureParser;
+
+import java.lang.annotation.Annotation;
+*/
+/**
+ * The Constructor class represents a constructor of a class. It also allows
+ * dynamic creation of an object, via reflection. Invocation on Constructor
+ * objects knows how to do widening conversions, but throws
+ * {@link IllegalArgumentException} if a narrowing conversion would be
+ * necessary. You can query for information on this Constructor regardless
+ * of location, but construction access may be limited by Java language
+ * access controls. If you can't do it in the compiler, you can't normally
+ * do it here either.<p>
+ *
+ * <B>Note:</B> This class returns and accepts types as Classes, even
+ * primitive types; there are Class types defined that represent each
+ * different primitive type.  They are <code>java.lang.Boolean.TYPE,
+ * java.lang.Byte.TYPE,</code>, also available as <code>boolean.class,
+ * byte.class</code>, etc.  These are not to be confused with the
+ * classes <code>java.lang.Boolean, java.lang.Byte</code>, etc., which are
+ * real classes.<p>
+ *
+ * Also note that this is not a serializable class.  It is entirely feasible
+ * to make it serializable using the Externalizable interface, but this is
+ * on Sun, not me.
+ *
+ * @author John Keiser
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Member
+ * @see Class
+ * @see java.lang.Class#getConstructor(Class[])
+ * @see java.lang.Class#getDeclaredConstructor(Class[])
+ * @see java.lang.Class#getConstructors()
+ * @see java.lang.Class#getDeclaredConstructors()
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public final class Constructor//<T>
+  //extends AccessibleObject
+  //implements GenericDeclaration, Member
+{  
+  /*private static final int CONSTRUCTOR_MODIFIERS
+    = Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC;*/
+
+  //private MethodSignatureParser p;
+
+  //VMConstructor cons;
+
+  /**
+   * This class is uninstantiable outside this package.
+   */
+  Constructor(/*VMConstructor cons*/)
+  {
+    //this.cons = cons;
+    //cons.cons = this;
+  }
+
+  /*private Constructor()
+  {
+  }*/
+
+  /**
+   * Gets the class that declared this constructor.
+   * @return the class that declared this member
+   */
+  public Class/*<T>*/ getDeclaringClass()
+  {
+    // Inescapable as the VM layer is 1.4 based. 
+    //@SuppressWarnings("unchecked")
+      //Class/*<T>*/ declClass = /*(Class<T>)*/ cons.getDeclaringClass();
+    //return declClass;
+    return null;
+  }
+
+  /**
+   * Gets the name of this constructor (the non-qualified name of the class
+   * it was declared in).
+   * @return the name of this constructor
+   */
+  public String getName()
+  {
+    return null; //cons.getDeclaringClass().getName();
+  }
+
+  /**
+   * Gets the modifiers this constructor uses.  Use the <code>Modifier</code>
+   * class to interpret the values. A constructor can only have a subset of the
+   * following modifiers: public, private, protected.
+   *
+   * @return an integer representing the modifiers to this Member
+   * @see Modifier
+   */
+  /*public int getModifiers()
+  {
+    return cons.getModifiersInternal() & CONSTRUCTOR_MODIFIERS;
+  }*/
+
+  /**
+   * Return true if this constructor is synthetic, false otherwise.
+   * A synthetic member is one which is created by the compiler,
+   * and which does not appear in the user's source code.
+   * @since 1.5
+   */
+  /*public boolean isSynthetic()
+  {
+    return (cons.getModifiersInternal() & Modifier.SYNTHETIC) != 0;
+  }*/
+
+  /**
+   * Return true if this is a varargs constructor, that is if
+   * the constructor takes a variable number of arguments.
+   * @since 1.5
+   */
+  /*public boolean isVarArgs()
+  {
+    return (cons.getModifiersInternal() & Modifier.VARARGS) != 0;
+  }*/
+
+  /**
+   * Get the parameter list for this constructor, in declaration order. If the
+   * constructor takes no parameters, returns a 0-length array (not null).
+   *
+   * @return a list of the types of the constructor's parameters
+   */
+  public Class/*<?>*/[] getParameterTypes()
+  {
+    return /*(Class<?>[])*/ null; //cons.getParameterTypes();
+  }
+
+  /**
+   * Get the exception types this constructor says it throws, in no particular
+   * order. If the constructor has no throws clause, returns a 0-length array
+   * (not null).
+   *
+   * @return a list of the types in the constructor's throws clause
+   */
+  /*public Class<?>[] getExceptionTypes()
+  {
+    return (Class<?>[]) cons.getExceptionTypes();
+  }*/
+
+  /**
+   * Compare two objects to see if they are semantically equivalent.
+   * Two Constructors are semantically equivalent if they have the same
+   * declaring class and the same parameter list.  This ignores different
+   * exception clauses, but since you can't create a Method except through the
+   * VM, this is just the == relation.
+   *
+   * @param o the object to compare to
+   * @return <code>true</code> if they are equal; <code>false</code> if not.
+   */
+  public boolean equals(Object o)
+  {
+    return false; //cons.equals(o);
+  }
+
+  /**
+   * Get the hash code for the Constructor. The Constructor hash code is the
+   * hash code of the declaring class's name.
+   *
+   * @return the hash code for the object
+   */
+  public int hashCode()
+  {
+    return getName().hashCode();
+  }
+
+  /**
+   * Get a String representation of the Constructor. A Constructor's String
+   * representation is "&lt;modifier&gt; &lt;classname&gt;(&lt;paramtypes&gt;)
+   * throws &lt;exceptions&gt;", where everything after ')' is omitted if
+   * there are no exceptions.<br> Example:
+   * <code>public java.io.FileInputStream(java.lang.Runnable)
+   * throws java.io.FileNotFoundException</code>
+   *
+   * @return the String representation of the Constructor
+   */
+  /*public String toString()
+  {
+    // 128 is a reasonable buffer initial size for constructor
+    CPStringBuilder sb = new CPStringBuilder(128);
+    Modifier.toString(getModifiers(), sb).append(' ');
+    sb.append(getDeclaringClass().getName()).append('(');
+    Class[] c = getParameterTypes();
+    if (c.length > 0)
+      {
+        sb.append(ClassHelper.getUserName(c[0]));
+        for (int i = 1; i < c.length; i++)
+          sb.append(',').append(ClassHelper.getUserName(c[i]));
+      }
+    sb.append(')');
+    c = getExceptionTypes();
+    if (c.length > 0)
+      {
+        sb.append(" throws ").append(c[0].getName());
+        for (int i = 1; i < c.length; i++)
+          sb.append(',').append(c[i].getName());
+      }
+    return sb.toString();
+  }*/
+
+  /*static <X extends GenericDeclaration>
+  void addTypeParameters(CPStringBuilder sb, TypeVariable<X>[] typeArgs)
+  {
+    if (typeArgs.length == 0)
+      return;
+    sb.append('<');
+    for (int i = 0; i < typeArgs.length; ++i)
+      {
+        if (i > 0)
+          sb.append(',');
+        sb.append(typeArgs[i]);
+      }
+    sb.append("> ");
+  }*/
+
+  /*public String toGenericString()
+  {
+    CPStringBuilder sb = new CPStringBuilder(128);
+    Modifier.toString(getModifiers(), sb).append(' ');
+    addTypeParameters(sb, getTypeParameters());
+    sb.append(getDeclaringClass().getName()).append('(');
+    Type[] types = getGenericParameterTypes();
+    if (types.length > 0)
+      {
+        sb.append(types[0]);
+        for (int i = 1; i < types.length; ++i)
+          sb.append(',').append(types[i]);
+      }
+    sb.append(')');
+    types = getGenericExceptionTypes();
+    if (types.length > 0)
+      {
+        sb.append(" throws ").append(types[0]);
+        for (int i = 1; i < types.length; i++)
+          sb.append(',').append(types[i]);
+      }
+    return sb.toString();
+  }*/
+
+  /**
+   * Create a new instance by invoking the constructor. Arguments are
+   * automatically unwrapped and widened, if needed.<p>
+   *
+   * If this class is abstract, you will get an
+   * <code>InstantiationException</code>. If the constructor takes 0
+   * arguments, you may use null or a 0-length array for <code>args</code>.<p>
+   *
+   * If this Constructor enforces access control, your runtime context is
+   * evaluated, and you may have an <code>IllegalAccessException</code> if
+   * you could not create this object in similar compiled code. If the class
+   * is uninitialized, you trigger class initialization, which may end in a
+   * <code>ExceptionInInitializerError</code>.<p>
+   *
+   * Then, the constructor is invoked. If it completes normally, the return
+   * value will be the new object. If it completes abruptly, the exception is
+   * wrapped in an <code>InvocationTargetException</code>.
+   *
+   * @param args the arguments to the constructor
+   * @return the newly created object
+   * @throws IllegalAccessException if the constructor could not normally be
+   *         called by the Java code (i.e. it is not public)
+   * @throws IllegalArgumentException if the number of arguments is incorrect;
+   *         or if the arguments types are wrong even with a widening
+   *         conversion
+   * @throws InstantiationException if the class is abstract
+   * @throws InvocationTargetException if the constructor throws an exception
+   * @throws ExceptionInInitializerError if construction triggered class
+   *         initialization, which then failed
+   */
+  public Object/*T*/ newInstance(Object[]/*...*/ args)
+    //throws InstantiationException, IllegalAccessException,
+    //       InvocationTargetException
+  {
+    // Inescapable as the VM layer is 1.4 based. 
+    //@SuppressWarnings("unchecked")
+      Object/*T*/ ins = null; ///*(T)*/ cons.construct(args);
+    return ins;
+  }
+
+  /**
+   * Returns an array of <code>TypeVariable</code> objects that represents
+   * the type variables declared by this constructor, in declaration order.
+   * An array of size zero is returned if this constructor has no type
+   * variables.
+   *
+   * @return the type variables associated with this constructor.
+   * @throws GenericSignatureFormatError if the generic signature does
+   *         not conform to the format specified in the Virtual Machine
+   *         specification, version 3.
+   * @since 1.5
+   */
+  /*public TypeVariable<Constructor<T>>[] getTypeParameters()
+  {
+    if (p == null)
+      {
+       String sig = cons.getSignature();
+       if (sig == null)
+         return new TypeVariable[0];
+       p = new MethodSignatureParser(this, sig);
+      }
+    return p.getTypeParameters();
+  }*/
+
+  /**
+   * Returns an array of <code>Type</code> objects that represents
+   * the exception types declared by this constructor, in declaration order.
+   * An array of size zero is returned if this constructor declares no
+   * exceptions.
+   *
+   * @return the exception types declared by this constructor. 
+   * @throws GenericSignatureFormatError if the generic signature does
+   *         not conform to the format specified in the Virtual Machine
+   *         specification, version 3.
+   * @since 1.5
+   */
+  /*public Type[] getGenericExceptionTypes()
+  {
+    if (p == null)
+      {
+       String sig = cons.getSignature();
+       if (sig == null)
+         return getExceptionTypes();
+       p = new MethodSignatureParser(this, sig);
+      }
+    return p.getGenericExceptionTypes();
+  }*/
+
+  /**
+   * Returns an array of <code>Type</code> objects that represents
+   * the parameter list for this constructor, in declaration order.
+   * An array of size zero is returned if this constructor takes no
+   * parameters.
+   *
+   * @return a list of the types of the constructor's parameters
+   * @throws GenericSignatureFormatError if the generic signature does
+   *         not conform to the format specified in the Virtual Machine
+   *         specification, version 3.
+   * @since 1.5
+   */
+  /*public Type[] getGenericParameterTypes()
+  {
+    if (p == null)
+      {
+       String sig = cons.getSignature();
+       if (sig == null)
+         return getParameterTypes();
+       p = new MethodSignatureParser(this, sig);
+      }
+    return p.getGenericParameterTypes();
+  }*/
+
+  /**
+   * <p>
+   * Return an array of arrays representing the annotations on each
+   * of the constructor's parameters.  The outer array is aligned against
+   * the parameters of the constructors and is thus equal in length to
+   * the number of parameters (thus having a length zero if there are none).
+   * Each array element in the outer array contains an inner array which
+   * holds the annotations.  This array has a length of zero if the parameter
+   * has no annotations.
+   * </p>
+   * <p>
+   * The returned annotations are serialized.  Changing the annotations has
+   * no affect on the return value of future calls to this method.
+   * </p>
+   * 
+   * @return an array of arrays which represents the annotations used on the
+   *         parameters of this constructor.  The order of the array elements
+   *         matches the declaration order of the parameters.
+   * @since 1.5
+   */
+  /*public Annotation[][] getParameterAnnotations()
+  {
+    return cons.getParameterAnnotations();
+  }*/
+
+  /**
+   * Returns the element's annotation for the specified annotation type,
+   * or <code>null</code> if no such annotation exists.
+   *
+   * @param annotationClass the type of annotation to look for.
+   * @return this element's annotation for the specified type, or
+   *         <code>null</code> if no such annotation exists.
+   * @throws NullPointerException if the annotation class is <code>null</code>.
+   */
+  /*public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+  {
+    // Inescapable as the VM layer is 1.4 based. 
+    @SuppressWarnings("unchecked")
+      T ann = (T) cons.getAnnotation(annotationClass);
+    return ann;
+  }*/
+
+  /**
+   * Returns all annotations directly defined by the element.  If there are
+   * no annotations directly associated with the element, then a zero-length
+   * array will be returned.  The returned array may be modified by the client
+   * code, but this will have no effect on the annotation content of this
+   * class, and hence no effect on the return value of this method for
+   * future callers.
+   *
+   * @return the annotations directly defined by the element.
+   * @since 1.5
+   */
+  /*public Annotation[] getDeclaredAnnotations()
+  {
+    return cons.getDeclaredAnnotations();
+  }*/
+
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/Date.java b/Robust/src/ClassLibrary/MGC/gnu/Date.java
new file mode 100644 (file)
index 0000000..801c377
--- /dev/null
@@ -0,0 +1,1257 @@
+/* java.util.Date
+   Copyright (C) 1998, 1999, 2000, 2001, 2005  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util;
+
+/*import gnu.java.lang.CPStringBuilder;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+*/
+/**
+ * <p>
+ * This class represents a specific time in milliseconds since the epoch.
+ * The epoch is 1970, January 1 00:00:00.0000 UTC.  
+ * </p>
+ * <p>
+ * <code>Date</code> is intended to reflect universal time coordinate (UTC),
+ * but this depends on the underlying host environment.  Most operating systems 
+ * don't handle the leap second, which occurs about once every year or
+ * so.  The leap second is added to the last minute of the day on either
+ * the 30th of June or the 31st of December, creating a minute 61 seconds
+ * in length.
+ * </p>
+ * <p>
+ * The representations of the date fields are as follows:
+ * <ul>
+ * <li>
+ * Years are specified as the difference between the year
+ * and 1900.  Thus, the final year used is equal to
+ * 1900 + y, where y is the input value.
+ * </li>
+ * <li>
+ * Months are represented using zero-based indexing,
+ * making 0 January and 11 December.
+ * </li>
+ * <li>
+ * Dates are represented with the usual values of
+ * 1 through to 31.
+ * </li>
+ * <li>
+ * Hours are represented in the twenty-four hour clock,
+ * with integer values from 0 to 23.  12am is 0, and
+ * 12pm is 12.
+ * </li>
+ * <li>
+ * Minutes are again as usual, with values from 0 to 59.
+ * </li>
+ * <li>
+ * Seconds are represented with the values 0 through to 61,
+ * with 60 and 61 being leap seconds (as per the ISO C standard).
+ * </li>
+ * </ul>
+ * </p>
+ * <p>
+ * Prior to JDK 1.1, this class was the sole class handling date and time
+ * related functionality.  However, this particular solution was not
+ * amenable to internationalization.  The new <code>Calendar</code>
+ * class should now be used to handle dates and times, with <code>Date</code>
+ * being used only for values in milliseconds since the epoch.  The
+ * <code>Calendar</code> class, and its concrete implementations, handle
+ * the interpretation of these values into minutes, hours, days, months
+ * and years.  The formatting and parsing of dates is left to the
+ * <code>DateFormat</code> class, which is able to handle the different
+ * types of date format which occur in different locales.
+ * </p>
+ *
+ * @see Calendar
+ * @see GregorianCalendar
+ * @see java.text.DateFormat
+ * @author Jochen Hoenicke
+ * @author Per Bothner (bothner@cygnus.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+public class Date
+    //implements Cloneable, Comparable<Date>, Serializable
+{
+  /**
+   * This is the serialization UID for this class
+   * for compatability with Sun's JDK.
+   */
+  private static final long serialVersionUID = 7523967970034938905L;
+
+  /**
+   * The time in milliseconds since the epoch.
+   */
+  private transient long time;
+
+  /**
+   * An array of week names used to map names to integer values.
+   */
+  private static final String[] weekNames = { "Sun", "Mon", "Tue", "Wed",
+                                             "Thu", "Fri", "Sat" };
+  /**
+   * An array of month names used to map names to integer values.
+   */
+  private static final String[] monthNames = { "Jan", "Feb", "Mar", "Apr",
+                                              "May", "Jun", "Jul", "Aug",
+                                              "Sep", "Oct", "Nov", "Dec" };
+  /**
+   * Creates a new Date Object representing the current time.
+   */
+  public Date()
+  {
+    time = System.currentTimeMillis();
+  }
+
+  /**
+   * Creates a new Date Object representing the given time.
+   *
+   * @param time the time in milliseconds since the epoch.
+   */
+  public Date(long time)
+  {
+    this.time = time;
+  }
+
+  /**
+   * Creates a new Date Object representing the given time.
+   *
+   * @deprecated use <code>new GregorianCalendar(year+1900, month,
+   * day)</code> instead.
+   * @param year the difference between the required year and 1900.
+   * @param month the month as a value between 0 and 11.
+   * @param day the day as a value between 0 and 31.
+   */
+  public Date(int year, int month, int day)
+  {
+    this(year, month, day, 0, 0, 0);
+  }
+
+  /**
+   * Creates a new Date Object representing the given time.
+   *
+   * @deprecated use <code>new GregorianCalendar(year+1900, month,
+   * day, hour, min)</code> instead.
+   * @param year the difference between the required year and 1900.
+   * @param month the month as a value between 0 and 11.
+   * @param day the day as a value between 0 and 31.
+   * @param hour the hour as a value between 0 and 23, in 24-hour
+   *        clock notation.
+   * @param min the minute as a value between 0 and 59.
+   */
+  public Date(int year, int month, int day, int hour, int min)
+  {
+    this(year, month, day, hour, min, 0);
+  }
+
+  /**
+   * Creates a new Date Object representing the given time.
+   *
+   * @deprecated use <code>new GregorianCalendar(year+1900, month,
+   * day, hour, min, sec)</code> instead. 
+   * @param year the difference between the required year and 1900.
+   * @param month the month as a value between 0 and 11.
+   * @param day the day as a value between 0 and 31.
+   * @param hour the hour as a value between 0 and 23, in 24-hour
+   *        clock notation.
+   * @param min the minute as a value between 0 and 59.
+   * @param sec the second as a value between 0 and 61 (with 60
+   *        and 61 being leap seconds).
+   */
+  public Date(int year, int month, int day, int hour, int min, int sec)
+  {
+    GregorianCalendar cal =
+       new GregorianCalendar(year + 1900, month, day, hour, min, sec);
+    time = cal.getTimeInMillis();
+  }
+
+  /**
+   * Creates a new Date from the given string representation.  This
+   * does the same as <code>new Date(Date.parse(s))</code>
+   * @see #parse
+   * @deprecated use <code>java.text.DateFormat.parse(s)</code> instead.  
+   */
+  /*public Date(String s)
+  {
+    time = parse(s);
+  }*/
+
+  /**
+   * Returns a copy of this <code>Date</code> object.
+   *
+   * @return a copy, or null if the object couldn't be
+   *         cloned.
+   * @see Object#clone()
+   */
+  /*public Object clone()
+  {
+    try
+      {
+       return super.clone();
+      }
+    catch (CloneNotSupportedException ex)
+      {
+       return null;
+      }
+  }*/
+
+  /**
+   * Returns the number of milliseconds since the epoch
+   * specified by the given arguments.  The arguments are
+   * interpreted relative to UTC rather than the local
+   * time zone.
+   *
+   * @deprecated Use <code>Calendar</code> with a UTC
+   *             <code>TimeZone</code> instead.
+   * @param year the difference between the required year and 1900.
+   * @param month the month as a value between 0 and 11.
+   * @param date the day as a value between 0 and 31.
+   * @param hrs the hour as a value between 0 and 23, in 24-hour
+   *        clock notation.
+   * @param min the minute as a value between 0 and 59.
+   * @param sec the second as a value between 0 and 61 (with 60
+   *        and 61 being leap seconds).
+   * @return the time in milliseconds since the epoch.
+   */
+  /*public static long UTC(int year, int month, int date,
+                        int hrs, int min, int sec)
+  {
+    GregorianCalendar cal =
+      new GregorianCalendar(year + 1900, month, date, hrs, min, sec);
+    cal.set(Calendar.ZONE_OFFSET, 0);
+    cal.set(Calendar.DST_OFFSET, 0);
+    return cal.getTimeInMillis();
+  }*/
+
+  /**
+   * Gets the time represented by this object.
+   *
+   * @return the time in milliseconds since the epoch.
+   */
+  public long getTime()
+  {
+    return time;
+  }
+
+  /**
+   * Returns the number of minutes offset used with UTC to give the time
+   * represented by this object in the current time zone.  The date information
+   * from this object is also used to determine whether or not daylight savings
+   * time is in effect.  For example, the offset for the UK would be 0 if the
+   * month of the date object was January, and 1 if the month was August.
+   * 
+   * @deprecated use
+   * <code>Calendar.get(Calendar.ZONE_OFFSET)+Calendar.get(Calendar.DST_OFFSET)</code>
+   * instead.
+   * @return The time zone offset in minutes of the local time zone
+   * relative to UTC.  The time represented by this object is used to
+   * determine if we should use daylight savings.
+   */
+  /*public int getTimezoneOffset()
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    return - (cal.get(Calendar.ZONE_OFFSET)
+           + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
+  }*/
+
+  /**
+   * Sets the time which this object should represent.
+   *
+   * @param time the time in milliseconds since the epoch.  
+   */
+  public void setTime(long time)
+  {
+    this.time = time;
+  }
+
+  /**
+   * Tests if this date is after the specified date.
+   *
+   * @param when the other date
+   * @return true, if the date represented by this object is
+   * strictly later than the time represented by when.  
+   */
+  public boolean after(Date when)
+  {
+    return time > when.time;
+  }
+
+  /**
+   * Tests if this date is before the specified date.
+   *
+   * @param when the other date
+   * @return true, if the date represented by when is strictly later
+   * than the time represented by this object.
+   */
+  public boolean before(Date when)
+  {
+    return time < when.time;
+  }
+
+  /**
+   * Compares two dates for equality.
+   *
+   * @param obj the object to compare.
+   * @return true, if obj is a Date object and the time represented
+   * by obj is exactly the same as the time represented by this
+   * object.  
+   */
+  public boolean equals(Object obj)
+  {
+    return (obj instanceof Date && time == ((Date) obj).time);
+  }
+
+  /**
+   * Compares two dates.
+   *
+   * @param when the other date.
+   * @return 0, if the date represented
+   * by obj is exactly the same as the time represented by this
+   * object, a negative if this Date is before the other Date, and
+   * a positive value otherwise.  
+   */
+  public int compareTo(Date when)
+  {
+    return (time < when.time) ? -1 : (time == when.time) ? 0 : 1;
+  }
+
+  /**
+   * Computes the hash code of this <code>Date</code> as the
+   * XOR of the most significant and the least significant
+   * 32 bits of the 64 bit milliseconds value.
+   *
+   * @return the hash code.
+   */
+  public int hashCode()
+  {
+    return (int) time ^ (int) (time >>> 32);
+  }
+
+  /**
+   * <p>
+   * Returns a string representation of this date using
+   * the following date format:
+   * </p>
+   * <p>
+   * <code>day mon dd hh:mm:ss zz yyyy</code>
+   * </p>
+   * <p>where the fields used here are:
+   * <ul>
+   * <li>
+   * <code>day</code> -- the day of the week
+   * (Sunday through to Saturday).
+   * </li>
+   * <li>
+   * <code>mon</code> -- the month (Jan to Dec).
+   * </li>
+   * <li>
+   * <code>dd</code> -- the day of the month
+   * as two decimal digits (01 to 31).
+   * </li>
+   * <li>
+   * <code>hh</code> -- the hour of the day
+   * as two decimal digits in 24-hour clock notation
+   * (01 to 23).
+   * </li>
+   * <li>
+   * <code>mm</code> -- the minute of the day
+   * as two decimal digits (01 to 59).
+   * </li>
+   * <li>
+   * <code>ss</code> -- the second of the day
+   * as two decimal digits (01 to 61).
+   * </li>
+   * <li>
+   * <code>zz</code> -- the time zone information if available.
+   * The possible time zones used include the abbreviations
+   * recognised by <code>parse()</code> (e.g. GMT, CET, etc.)
+   * and may reflect the fact that daylight savings time is in
+   * effect.  The empty string is used if there is no time zone
+   * information.
+   * </li>
+   * <li>
+   * <code>yyyy</code> -- the year as four decimal digits.
+   * </li>
+   * </ul>
+   * <p>
+   * The <code>DateFormat</code> class should now be 
+   * preferred over using this method.
+   * </p>
+   *
+   * @return A string of the form 'day mon dd hh:mm:ss zz yyyy'
+   * @see #parse(String)
+   * @see DateFormat
+   */
+  /*public String toString()
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    String day = "0" + cal.get(Calendar.DATE);
+    String hour = "0" + cal.get(Calendar.HOUR_OF_DAY);
+    String min = "0" + cal.get(Calendar.MINUTE);
+    String sec = "0" + cal.get(Calendar.SECOND);
+    String year = "000" + cal.get(Calendar.YEAR);
+    return weekNames[cal.get(Calendar.DAY_OF_WEEK) - 1] + " "
+      + monthNames[cal.get(Calendar.MONTH)] + " "
+      + day.substring(day.length() - 2) + " "
+      + hour.substring(hour.length() - 2) + ":"
+      + min.substring(min.length() - 2) + ":"
+      + sec.substring(sec.length() - 2) + " "
+      +
+      cal.getTimeZone().getDisplayName(cal.getTimeZone().inDaylightTime(this),
+                                      TimeZone.SHORT) + " " +
+      year.substring(year.length() - 4);
+  }*/
+
+  /** 
+   * Returns a locale-dependent string representation of this
+   * <code>Date</code> object.
+   *
+   * @deprecated Use DateFormat.format(Date)
+   * @return A locale-dependent string representation.
+   * @see #parse(String)
+   * @see DateFormat
+   */
+  /*public String toLocaleString()
+  {
+    return java.text.DateFormat.getInstance().format(this);
+  }*/
+
+  /** 
+   * <p>
+   * Returns a string representation of this <code>Date</code>
+   * object using GMT rather than the local timezone.
+   * The following date format is used:
+   * </p>
+   * <p>
+   * <code>d mon yyyy hh:mm:ss GMT</code>
+   * </p>
+   * <p>where the fields used here are:
+   * <ul>
+   * <li>
+   * <code>d</code> -- the day of the month
+   * as one or two decimal digits (1 to 31).
+   * </li>
+   * <li>
+   * <code>mon</code> -- the month (Jan to Dec).
+   * </li>
+   * <li>
+   * <code>yyyy</code> -- the year as four decimal digits.
+   * </li>
+   * <li>
+   * <code>hh</code> -- the hour of the day
+   * as two decimal digits in 24-hour clock notation
+   * (01 to 23).
+   * </li>
+   * <li>
+   * <code>mm</code> -- the minute of the day
+   * as two decimal digits (01 to 59).
+   * </li>
+   * <li>
+   * <code>ss</code> -- the second of the day
+   * as two decimal digits (01 to 61).
+   * </li>
+   * <li>
+   * <code>GMT</code> -- the literal string "GMT"
+   * indicating Greenwich Mean Time as opposed to
+   * the local timezone.
+   * </li>
+   * </ul>
+   * 
+   * @deprecated Use DateFormat.format(Date) with a GMT TimeZone.
+   * @return A string of the form 'd mon yyyy hh:mm:ss GMT' using
+   *         GMT as opposed to the local timezone.
+   * @see #parse(String)
+   * @see DateFormat
+   */
+  /*public String toGMTString()
+  {
+    java.text.DateFormat format = java.text.DateFormat.getInstance();
+    format.setTimeZone(TimeZone.getTimeZone("GMT"));
+    return format.format(this);
+  }*/
+
+  /**
+   * Parses the time zone string.
+   *
+   * @param tok The token containing the time zone.
+   * @param sign The sign (+ or -) used by the time zone.
+   * @return An integer representing the number of minutes offset
+   *         from GMT for the time zone.
+   */
+  /*private static int parseTz(String tok, char sign)
+    throws IllegalArgumentException
+  {
+    int num;
+
+    try
+      {
+       // parseInt doesn't handle '+' so strip off sign.
+       num = Integer.parseInt(tok.substring(1));
+      }
+    catch (NumberFormatException ex)
+      {
+       throw new IllegalArgumentException(tok);
+      }
+
+    // Convert hours to minutes.
+    if (num < 24)
+      num *= 60;
+    else
+      num = (num / 100) * 60 + num % 100;
+
+    return sign == '-' ? -num : num;
+  }*/
+
+  /**
+   * Parses the month string.
+   *
+   * @param tok the token containing the month.
+   * @return An integer between 0 and 11, representing
+   *         a month from January (0) to December (11),
+   *         or -1 if parsing failed.
+   */
+  /*private static int parseMonth(String tok)
+  {
+    // Initialize strings for month names.
+    // We could possibly use the fields of DateFormatSymbols but that is
+    // localized and thus might not match the English words specified.
+    String months[] = { "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY",
+                       "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER",
+                       "NOVEMBER", "DECEMBER" };
+
+    int i;
+    for (i = 0; i < 12; i++)
+      if (months[i].startsWith(tok))
+        return i;
+
+    // Return -1 if not found.
+    return -1;
+  }*/
+
+  /**
+   * Parses the day of the week string.
+   *
+   * @param tok the token containing the day of the week.
+   * @return true if the token was parsed successfully.
+   */
+  /*private static boolean parseDayOfWeek(String tok)
+  {
+    // Initialize strings for days of the week names.
+    // We could possibly use the fields of DateFormatSymbols but that is
+    // localized and thus might not match the English words specified.
+    String daysOfWeek[] = { "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
+                           "THURSDAY", "FRIDAY", "SATURDAY" };
+
+    int i;
+    for (i = 0; i < 7; i++)
+      if (daysOfWeek[i].startsWith(tok))
+        return true;
+
+    return false;
+  }*/
+
+  /** 
+   * <p>
+   * Parses a String and returns the time, in milliseconds since the
+   * epoch, it represents.  Most syntaxes are handled, including
+   * the IETF date standard "day, dd mon yyyy hh:mm:ss zz" (see
+   * <code>toString()</code> for definitions of these fields).
+   * Standard U.S. time zone abbreviations are recognised, in
+   * addition to time zone offsets in positive or negative minutes.
+   * If a time zone is specified, the specified time is assumed to
+   * be in UTC and the appropriate conversion is applied, following
+   * parsing, to convert this to the local time zone.  If no zone
+   * is specified, the time is assumed to already be in the local
+   * time zone.
+   * </p>
+   * <p>
+   * The method parses the string progressively from left to right.
+   * At the end of the parsing process, either a time is returned
+   * or an <code>IllegalArgumentException</code> is thrown to signify
+   * failure.  The ASCII characters A-Z, a-z, 0-9, and ',', '+', '-',
+   * ':' and '/' are the only characters permitted within the string,
+   * besides whitespace and characters enclosed within parantheses
+   * '(' and ')'.  
+   * </p>
+   * <p>
+   * A sequence of consecutive digits are recognised as a number,
+   * and interpreted as follows:
+   * <ul>
+   * <li>
+   * A number preceded by a sign (+ or -) is taken to be a time zone
+   * offset.  The time zone offset can be specified in either hours
+   * or minutes.  The former is assumed if the number is less than 24.
+   * Otherwise, the offset is assumed to be in minutes.  A - indicates
+   * a time zone west of GMT, while a + represents a time zone to the
+   * east of GMT.  The time zones are always assumed to be relative
+   * to GMT, and a (redundant) specification of this can be included
+   * with the time zone.  For example, '-9', 'utc-9' and 'GMT-9' all
+   * represent a time zone nine hours west of GMT.  Similarly,
+   * '+4', 'ut+4' and 'UTC+4' all give 4 hours east of GMT.
+   * </li>
+   * <li>
+   * A number equal to or greater than 70 is regarded as a year specification.
+   * Values lower than 70 are only assumed to indicate a year if both the
+   * day of the month and the month itself have already been recognised.
+   * Year values less than 100 are interpreted as being relative to the current
+   * century when the <code>Date</code> class is initialised..  Given a century,
+   * x, the year is assumed to be within the range x - 80 to x + 19.  The value
+   * itself is then used as a match against the two last digits of one of these
+   * years.  For example, take x to be 2004.  A two-digit year is assumed to fall
+   * within the range x - 80 (1924) and x + 19 (2023).  Thus, any intepreted value
+   * between 0 and 23 is assumed to be 2000 to 2023 and values between 24 and 99
+   * are taken as being 1924 to 1999.  This only applies for the case of 2004.
+   * With a different year, the values will be interpreted differently. 2005
+   * will used 0 to 24 as 2000 to 2024 and 25 to 99 as 1925 to 1999, for example.
+   * This behaviour differs from that of <code>SimpleDateFormat</code> and is
+   * time-dependent (a two-digit year will be interpreted differently depending
+   * on the time the code is run).
+   * </li>
+   * <li>
+   * Numbers followed by a colon are interpreted by first an hour, and then
+   * as a minute, once an hour has been found.
+   * </li>
+   * <li>
+   * <li>
+   * Numbers followed by a slash are regarded first as a month, and then as
+   * a day of the month once the month has been found.  This follows the
+   * U.S. date format of mm/dd, rather than the European dd/mm.  Months
+   * are converted to the recognised value - 1 before storage, in order
+   * to put the number within the range 0 to 11.
+   * </li>
+   * <li>
+   * Numbers followed by commas, whitespace, hyphens or the end of the string
+   * are interpreted in the following order: hour, minute, second, day of month.
+   * The first type not already recognised in the current string being parsed is
+   * assumed.
+   * </li>
+   * </ul>
+   * </p>
+   * <p>
+   * A sequence of consecutive alphabetic characters is recognised as a word,
+   * and interpreted as follows, in a case-insentive fashion:
+   * <ul>
+   * <li>
+   * The characters 'AM' or 'PM' restrict the hour value to a value between 0
+   * and 12.  In the latter case, 12 is added to the hour value before storage.
+   * </li>
+   * <li>
+   * Any words which match any prefix of one of the days of the week ('Monday',
+   * 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' and 'Sunday'),
+   * are simply ignored.
+   * </li>
+   * <li>
+   * Any words which match any prefix of one of the months of the year ('January',
+   * 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September',
+   * 'October', 'November', 'December') are recognised and interpreted as the
+   * appropriate value between 0 and 11.  The first match made against a
+   * month is the one used, in the order specified here.  For example, 'Ma' is
+   * intepreted as 'March' (2) and not as 'May' (4).  Similarly, 'Ju' is 'June',
+   * and not 'July'.
+   * </li>
+   * <li>
+   * The words 'GMT', 'UT' and 'UTC' are interpreted as specifying UTC as the
+   * time zone in use for this date.
+   * </li>
+   * <li>
+   * The word pairs 'EST'/'EDT', 'CST'/'CDT', 'MST'/'MDT' and 'PST'/'PDT' are
+   * interpreted as the appropriate U.S. time zone abbreviation.  Each pair
+   * is the standard and daylight savings time zone specification, respectively,
+   * for each zone within the U.S, these being Eastern Standard/Daylight Time
+   * (-5), Central Standard/Daylight Time (-6), Mountain Standard/Daylight Time
+   * (-7) and Pacific Standard/Daylight Time (-8).
+   * </li>
+   * </ul>
+   *
+   * @param string The String to parse.
+   * @return The time in milliseconds since the epoch.
+   * @throws IllegalArgumentException if the string fails to parse.
+   * @deprecated Use DateFormat.parse(String)
+   * @see #toString()
+   * @see SimpleDateFormat
+   */
+  /*public static long parse(String string)
+  {
+    // Initialize date/time fields before parsing begins.
+    int year = -1;
+    int month = -1;
+    int day = -1;
+    int hour = -1;
+    int minute = -1;
+    int second = -1;
+    int timezone = 0;
+    boolean localTimezone = true;
+
+    // Trim out any nested stuff in parentheses now to make parsing easier.
+    CPStringBuilder buf = new CPStringBuilder();
+    int parenNesting = 0;
+    int len = string.length();
+    for (int i = 0;  i < len;  i++)
+      {
+       char ch = string.charAt(i);
+       if (ch >= 'a' && ch <= 'z')
+         ch -= 'a' - 'A';
+       if (ch == '(')
+         parenNesting++;
+       else if (parenNesting == 0)
+         buf.append(ch);
+       else if (ch == ')')
+         parenNesting--;
+      }
+    int tmpMonth;
+
+    // Make all chars upper case to simplify comparisons later.
+    // Also ignore commas; treat them as delimiters.
+    StringTokenizer strtok = new StringTokenizer(buf.toString(), " \t\n\r,");
+
+    while (strtok.hasMoreTokens())
+      {
+       String tok = strtok.nextToken();
+       char firstch = tok.charAt(0);
+       if ((firstch == '+' || firstch == '-') && year >= 0)
+         {
+           timezone = parseTz(tok, firstch);
+           localTimezone = false;
+         }
+       else if (firstch >= '0' && firstch <= '9')
+         {
+           int lastPunct = -1;
+           while (tok != null && tok.length() > 0)
+             {
+               int punctOffset = tok.length();
+               int num = 0;
+               int punct;
+               for (int i = 0;  ;  i++)
+                 {
+                   if (i >= punctOffset)
+                     {
+                       punct = -1;
+                       break;
+                     }
+                   else
+                     {
+                       punct = tok.charAt(i);
+                       if (punct >= '0' && punct <= '9')
+                         {
+                           if (num > 999999999) // in case of overflow
+                             throw new IllegalArgumentException(tok);
+                           num = 10 * num + (punct - '0');
+                         }
+                       else
+                         {
+                           punctOffset = i;
+                           break;
+                         }
+                     }
+                     
+                 }
+
+               if (punct == ':')
+                 {
+                   if (hour < 0)
+                     hour = num;
+                   else
+                     minute = num;
+                 }
+               else if (lastPunct == ':' && hour >= 0 && (minute < 0 || second < 0))
+                 {
+                   if (minute < 0)
+                     minute = num;
+                   else
+                     second = num;
+                 }
+               else if ((num >= 70
+                         && (punct == ' ' || punct == ','
+                             || punct == '/' || punct < 0))
+                        || (num < 70 && day >= 0 && month >= 0 && year < 0))
+                 {
+                   if (num >= 100)
+                     year = num;
+                   else
+                     {
+                       int curYear = 1900 + new Date().getYear();
+                       int firstYear = curYear - 80;
+                       year = firstYear / 100 * 100 + num;
+                       if (year < firstYear)
+                         year += 100;
+                     }
+                 }
+               else if (punct == '/')
+                 {
+                   if (month < 0)
+                     month = num - 1;
+                   else
+                     day = num;
+                 }
+               else if (hour >= 0 && minute < 0)
+                 minute = num;
+               else if (minute >= 0 && second < 0)
+                 second = num;
+               else if (day < 0)
+                 day = num;
+               else
+                 throw new IllegalArgumentException(tok);
+
+               // Advance string if there's more to process in this token.
+               if (punct < 0 || punctOffset + 1 >= tok.length())
+                 tok = null;
+               else
+                 tok = tok.substring(punctOffset + 1);
+               lastPunct = punct;
+             }
+         }
+       else if (firstch >= 'A' && firstch <= 'Z')
+         {
+           if (tok.equals("AM"))
+             {
+               if (hour < 1 || hour > 12)
+                 throw new IllegalArgumentException(tok);
+               if (hour == 12)
+                 hour = 0;
+             }
+           else if (tok.equals("PM"))
+             {
+               if (hour < 1 || hour > 12)
+                 throw new IllegalArgumentException(tok);
+               if (hour < 12)
+                 hour += 12;
+             }
+           else if (parseDayOfWeek(tok))
+             { // Ignore it; throw the token away. 
+          }
+           else if (tok.equals("UT") || tok.equals("UTC") || tok.equals("GMT"))
+             localTimezone = false;
+           else if (tok.startsWith("UT") || tok.startsWith("GMT"))
+             {
+               int signOffset = 3;
+               if (tok.charAt(1) == 'T' && tok.charAt(2) != 'C')
+                 signOffset = 2;
+
+               char sign = tok.charAt(signOffset);
+               if (sign != '+' && sign != '-')
+                 throw new IllegalArgumentException(tok);
+
+               timezone = parseTz(tok.substring(signOffset), sign);
+               localTimezone = false;
+             }
+           else if ((tmpMonth = parseMonth(tok)) >= 0)
+             month = tmpMonth;
+           else if (tok.length() == 3 && tok.charAt(2) == 'T')
+             {
+               // Convert timezone offset from hours to minutes.
+               char ch = tok.charAt(0);
+               if (ch == 'E')
+                 timezone = -5 * 60;
+               else if (ch == 'C')
+                 timezone = -6 * 60;
+               else if (ch == 'M')
+                 timezone = -7 * 60;
+               else if (ch == 'P')
+                 timezone = -8 * 60;
+               else
+                 throw new IllegalArgumentException(tok);
+
+               // Shift 60 minutes for Daylight Savings Time.
+               if (tok.charAt(1) == 'D')
+                 timezone += 60;
+               else if (tok.charAt(1) != 'S')
+                 throw new IllegalArgumentException(tok);
+
+               localTimezone = false;
+             }
+           else
+             throw new IllegalArgumentException(tok);
+         }
+       else
+         throw new IllegalArgumentException(tok);
+      }
+
+    // Unspecified hours, minutes, or seconds should default to 0.
+    if (hour < 0)
+      hour = 0;
+    if (minute < 0)
+      minute = 0;
+    if (second < 0)
+      second = 0;
+
+    // Throw exception if any other fields have not been recognized and set.
+    if (year < 0 || month < 0 || day < 0)
+      throw new IllegalArgumentException("Missing field");
+
+    // Return the time in either local time or relative to GMT as parsed.
+    // If no time-zone was specified, get the local one (in minutes) and
+    // convert to milliseconds before adding to the UTC.
+    GregorianCalendar cal
+      = new GregorianCalendar(year, month, day, hour, minute, second);
+    if (!localTimezone)
+      {
+       cal.set(Calendar.ZONE_OFFSET, timezone * 60 * 1000);
+       cal.set(Calendar.DST_OFFSET, 0);
+      }
+    return cal.getTimeInMillis();
+  }*/
+
+  /**
+   * Returns the difference between the year represented by this
+   * <code>Date</code> object and 1900.
+   *
+   * @return the year minus 1900 represented by this date object.
+   * @deprecated Use Calendar instead of Date, and use get(Calendar.YEAR)
+   * instead.  Note the 1900 difference in the year.
+   * @see Calendar
+   * @see #setYear(int)
+   */
+  /*public int getYear()
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    return cal.get(Calendar.YEAR) - 1900;
+  }*/
+
+  /**
+   * Sets the year to the specified year, plus 1900.  The other
+   * fields are only altered as required to match the same date
+   * and time in the new year.  Usually, this will mean that
+   * the fields are not changed at all, but in the case of
+   * a leap day or leap second, the fields will change in
+   * relation to the existence of such an event in the new year.
+   * For example, if the date specifies February the 29th, 2000,
+   * then this will become March the 1st if the year is changed
+   * to 2001, as 2001 is not a leap year.  Similarly, a seconds
+   * value of 60 or 61 may result in the seconds becoming 0 and
+   * the minute increasing by 1, if the new time does not include
+   * a leap second.
+   *
+   * @param year the year minus 1900.
+   * @deprecated Use Calendar instead of Date, and use
+   * set(Calendar.YEAR, year) instead.  Note about the 1900
+   * difference in year.  
+   * @see #getYear()
+   * @see Calendar
+   */
+  /*public void setYear(int year)
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    cal.set(Calendar.YEAR, 1900 + year);
+    time = cal.getTimeInMillis();
+  }*/
+
+  /**
+   * Returns the month represented by this <code>Date</code> object,
+   * as a value between 0 (January) and 11 (December).
+   *
+   * @return the month represented by this date object (zero based).
+   * @deprecated Use Calendar instead of Date, and use get(Calendar.MONTH)
+   * instead.
+   * @see #setMonth(int)
+   * @see Calendar
+   */
+  /*public int getMonth()
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    return cal.get(Calendar.MONTH);
+  }*/
+
+  /**
+   * Sets the month to the given value.  The other
+   * fields are only altered as necessary to match
+   * the same date and time in the new month.  In most
+   * cases, the other fields won't change at all.  However,
+   * in the case of a shorter month or a leap second, values
+   * may be adjusted.  For example, if the day of the month
+   * is currently 31, and the month value is changed from
+   * January (0) to September (8), the date will become
+   * October the 1st, as September only has 30 days.  Similarly,
+   * a seconds value of 60 or 61 (a leap second) may result
+   * in the seconds value being reset to 0 and the minutes
+   * value being incremented by 1, if the new time does
+   * not include a leap second.
+   * 
+   * @param month the month, with a zero-based index
+   *        from January.
+   * @deprecated Use Calendar instead of Date, and use
+   * set(Calendar.MONTH, month) instead.
+   * @see #getMonth()
+   * @see Calendar 
+   */
+  /*public void setMonth(int month)
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    cal.set(Calendar.MONTH, month);
+    time = cal.getTimeInMillis();
+  }*/
+
+  /**
+   * Returns the day of the month of this <code>Date</code>
+   * object, as a value between 0 and 31.
+   *
+   * @return the day of month represented by this date object.
+   * @deprecated Use Calendar instead of Date, and use get(Calendar.DATE)
+   * instead.
+   * @see Calendar
+   * @see #setDate(int)
+   */
+  /*public int getDate()
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    return cal.get(Calendar.DATE);
+  }*/
+
+  /**
+   * Sets the date to the given value. The other
+   * fields are only altered as necessary to match
+   * the same date and time on the new day of the month.  In most
+   * cases, the other fields won't change at all.  However,
+   * in the case of a leap second or the day being out of
+   * the range of the current month, values
+   * may be adjusted.  For example, if the day of the month
+   * is currently 30 and the month is June, a new day of the
+   * month value of 31 will cause the month to change to July,
+   * as June only has 30 days .  Similarly,
+   * a seconds value of 60 or 61 (a leap second) may result
+   * in the seconds value being reset to 0 and the minutes
+   * value being incremented by 1, if the new time does
+   * not include a leap second.
+   *
+   * @param date the date.
+   * @deprecated Use Calendar instead of Date, and use
+   * set(Calendar.DATE, date) instead. 
+   * @see Calendar
+   * @see #getDate()
+   */
+  /*public void setDate(int date)
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    cal.set(Calendar.DATE, date);
+    time = cal.getTimeInMillis();
+  }*/
+
+  /**
+   * Returns the day represented by this <code>Date</code>
+   * object as an integer between 0 (Sunday) and 6 (Saturday).
+   *
+   * @return the day represented by this date object.
+   * @deprecated Use Calendar instead of Date, and use get(Calendar.DAY_OF_WEEK)
+   * instead.
+   * @see Calendar
+   */
+  /*public int getDay()
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    // For Calendar, Sunday is 1.  For Date, Sunday is 0.
+    return cal.get(Calendar.DAY_OF_WEEK) - 1;
+  }*/
+
+  /**
+   * Returns the hours represented by this <code>Date</code>
+   * object as an integer between 0 and 23.
+   *
+   * @return the hours represented by this date object.
+   * @deprecated Use Calendar instead of Date, and use get(Calendar.HOUR_OF_DAY)
+   * instead.
+   * @see Calendar
+   * @see #setHours(int)
+   */
+  /*public int getHours()
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    return cal.get(Calendar.HOUR_OF_DAY);
+  }*/
+
+  /**
+   * Sets the hours to the given value.  The other
+   * fields are only altered as necessary to match
+   * the same date and time in the new hour.  In most
+   * cases, the other fields won't change at all.  However,
+   * in the case of a leap second, values
+   * may be adjusted.  For example,
+   * a seconds value of 60 or 61 (a leap second) may result
+   * in the seconds value being reset to 0 and the minutes
+   * value being incremented by 1 if the new hour does
+   * not contain a leap second.
+   *
+   * @param hours the hours.
+   * @deprecated Use Calendar instead of Date, and use
+   * set(Calendar.HOUR_OF_DAY, hours) instead.
+   * @see Calendar
+   * @see #getHours() 
+   */
+  /*public void setHours(int hours)
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    cal.set(Calendar.HOUR_OF_DAY, hours);
+    time = cal.getTimeInMillis();
+  }*/
+
+  /**
+   * Returns the number of minutes represented by the <code>Date</code>
+   * object, as an integer between 0 and 59.
+   *
+   * @return the minutes represented by this date object.
+   * @deprecated Use Calendar instead of Date, and use get(Calendar.MINUTE)
+   * instead.
+   * @see Calendar
+   * @see #setMinutes(int)
+   */
+  /*public int getMinutes()
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    return cal.get(Calendar.MINUTE);
+  }*/
+
+  /**
+   * Sets the minutes to the given value.  The other
+   * fields are only altered as necessary to match
+   * the same date and time in the new minute.  In most
+   * cases, the other fields won't change at all.  However,
+   * in the case of a leap second, values
+   * may be adjusted.  For example,
+   * a seconds value of 60 or 61 (a leap second) may result
+   * in the seconds value being reset to 0 and the minutes
+   * value being incremented by 1 if the new minute does
+   * not contain a leap second.
+   *
+   * @param minutes the minutes.
+   * @deprecated Use Calendar instead of Date, and use
+   * set(Calendar.MINUTE, minutes) instead. 
+   * @see Calendar
+   * @see #getMinutes()
+   */
+  /*public void setMinutes(int minutes)
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    cal.set(Calendar.MINUTE, minutes);
+    time = cal.getTimeInMillis();
+  }*/
+
+  /**
+   * Returns the number of seconds represented by the <code>Date</code>
+   * object, as an integer between 0 and 61 (60 and 61 being leap seconds).
+   *
+   * @return the seconds represented by this date object.
+   * @deprecated Use Calendar instead of Date, and use get(Calendar.SECOND)
+   * instead.
+   * @see Calendar
+   * @see #setSeconds(int)
+   */
+  /*public int getSeconds()
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    return cal.get(Calendar.SECOND);
+  }*/
+
+  /**
+   * Sets the seconds to the given value.  The other
+   * fields are only altered as necessary to match
+   * the same date and time in the new minute.  In most
+   * cases, the other fields won't change at all.  However,
+   * in the case of a leap second, values
+   * may be adjusted.  For example, setting the
+   * seconds value to 60 or 61 (a leap second) may result
+   * in the seconds value being reset to 0 and the minutes
+   * value being incremented by 1, if the current time does
+   * not contain a leap second.
+   *
+   * @param seconds the seconds.
+   * @deprecated Use Calendar instead of Date, and use
+   * set(Calendar.SECOND, seconds) instead.
+   * @see Calendar
+   * @see #getSeconds() 
+   */
+  /*public void setSeconds(int seconds)
+  {
+    Calendar cal = Calendar.getInstance();
+    cal.setTimeInMillis(time);
+    cal.set(Calendar.SECOND, seconds);
+    time = cal.getTimeInMillis();
+  }*/
+
+  /**
+   * Deserializes a <code>Date</code> object from an
+   * input stream, setting the time (in milliseconds
+   * since the epoch) to the long value read from the
+   * stream.
+   *
+   * @param input the input stream.
+   * @throws IOException if an I/O error occurs in the stream.
+   * @throws ClassNotFoundException if the class of the
+   *         serialized object could not be found.
+   */
+  /*private void readObject(ObjectInputStream input)
+    throws IOException, ClassNotFoundException
+  {
+    input.defaultReadObject();
+    time = input.readLong();
+  }*/
+
+  /**
+   * Serializes a <code>Date</code> object to an output stream,
+   * storing the time (in milliseconds since the epoch) as a long
+   * value in the stream.
+   *
+   * @serialdata A long value representing the offset from the epoch
+   * in milliseconds.  This is the same value that is returned by the
+   * method getTime().
+   * @param output the output stream.
+   * @throws IOException if an I/O error occurs in the stream.
+   */
+  /*private void writeObject(ObjectOutputStream output)
+    throws IOException
+  {
+    output.defaultWriteObject();
+    output.writeLong(time);
+  }*/
+
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/DecimalFormat.java b/Robust/src/ClassLibrary/MGC/gnu/DecimalFormat.java
new file mode 100644 (file)
index 0000000..57cd7b9
--- /dev/null
@@ -0,0 +1,2284 @@
+/* DecimalFormat.java -- Formats and parses numbers
+   Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+/* 
+ * This class contains few bits from ICU4J (http://icu.sourceforge.net/),
+ * Copyright by IBM and others and distributed under the
+ * distributed under MIT/X.
+ */
+
+//package java.text;
+
+/*import gnu.java.lang.CPStringBuilder;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import java.util.ArrayList;
+import java.util.Currency;
+import java.util.Locale;
+*/
+/*
+ * This note is here for historical reasons and because I had not the courage
+ * to remove it :)
+ *  
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @date March 4, 1999
+ *
+ * Written using "Java Class Libraries", 2nd edition, plus online
+ * API docs for JDK 1.2 from http://www.javasoft.com.
+ * Status:  Believed complete and correct to 1.2.
+ * Note however that the docs are very unclear about how format parsing
+ * should work.  No doubt there are problems here.
+ */
+
+/**
+ * This class is a concrete implementation of NumberFormat used to format
+ * decimal numbers. The class can format numbers given a specific locale.
+ * Generally, to get an instance of DecimalFormat you should call the factory
+ * methods in the <code>NumberFormat</code> base class.
+ * 
+ * @author Mario Torre (neugens@limasoftware.net)
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+public class DecimalFormat //extends NumberFormat
+{
+  /** serialVersionUID for serializartion. */
+  private static final long serialVersionUID = 864413376551465018L;
+  
+  /** Defines the default number of digits allowed while formatting integers. */
+  private static final int DEFAULT_INTEGER_DIGITS = 309; 
+
+  /**
+   * Defines the default number of digits allowed while formatting
+   * fractions.
+   */
+  private static final int DEFAULT_FRACTION_DIGITS = 340;
+  
+  /**
+   * Locale-independent pattern symbols.
+   */
+  // Happen to be the same as the US symbols.
+  /*private static final DecimalFormatSymbols nonLocalizedSymbols
+    = new DecimalFormatSymbols (Locale.US);*/
+  
+  /**
+   * Defines if parse should return a BigDecimal or not.
+   */
+  private boolean parseBigDecimal;
+  
+  /**
+   * Defines if we have to use the monetary decimal separator or
+   * the decimal separator while formatting numbers.
+   */
+  private boolean useCurrencySeparator;
+    
+  /** Defines if the decimal separator is always shown or not. */
+  private boolean decimalSeparatorAlwaysShown;
+  
+  /**
+   * Defines if the decimal separator has to be shown.
+   * 
+   * This is different then <code>decimalSeparatorAlwaysShown</code>,
+   * as it defines if the format string contains a decimal separator or no.
+   */
+  private boolean showDecimalSeparator;
+  
+  /**
+   * This field is used to determine if the grouping
+   * separator is included in the format string or not.
+   * This is only needed to match the behaviour of the RI.
+   */
+  private boolean groupingSeparatorInPattern;
+  
+  /** Defines the size of grouping groups when grouping is used. */
+  private byte groupingSize;
+  
+  /**
+   * This is an internal parameter used to keep track of the number
+   * of digits the form the exponent, when exponential notation is used.
+   * It is used with <code>exponentRound</code>
+   */
+  private byte minExponentDigits;
+  /** This field is used to set the exponent in the engineering notation. */
+  private int exponentRound;
+  
+  /** Multiplier used in percent style formats. */
+  private int multiplier;
+  
+  /** Multiplier used in percent style formats. */
+  private int negativePatternMultiplier;
+  
+  /** The negative prefix. */
+  private String negativePrefix;
+  
+  /** The negative suffix. */
+  private String negativeSuffix;
+  
+  /** The positive prefix. */
+  private String positivePrefix;
+  
+  /** The positive suffix. */
+  private String positiveSuffix;
+  
+  /** Decimal Format Symbols for the given locale. */
+  //private DecimalFormatSymbols symbols;
+  
+  /** Determine if we have to use exponential notation or not. */
+  private boolean useExponentialNotation;
+  
+  /**
+   * Defines the maximum number of integer digits to show when we use
+   * the exponential notation.
+   */
+  private int maxIntegerDigitsExponent;
+  
+  /** Defines if the format string has a negative prefix or not. */
+  private boolean hasNegativePrefix;
+  
+  /** Defines if the format string has a fractional pattern or not. */
+  private boolean hasFractionalPattern;
+  /** Stores a list of attributes for use by formatToCharacterIterator. */
+  //private ArrayList attributes = new ArrayList();
+  
+  /**
+   * Constructs a <code>DecimalFormat</code> which uses the default
+   * pattern and symbols.
+   */
+  public DecimalFormat()
+  {
+    //this ("#,##0.###");
+  }
+  
+  /**
+   * Constructs a <code>DecimalFormat</code> which uses the given
+   * pattern and the default symbols for formatting and parsing.
+   *
+   * @param pattern the non-localized pattern to use.
+   * @throws NullPointerException if any argument is null.
+   * @throws IllegalArgumentException if the pattern is invalid.
+   */
+  public DecimalFormat(String pattern)
+  {
+    //this (pattern, new DecimalFormatSymbols());
+  }
+
+  /**
+   * Constructs a <code>DecimalFormat</code> using the given pattern
+   * and formatting symbols.  This construction method is used to give
+   * complete control over the formatting process.  
+   *
+   * @param pattern the non-localized pattern to use.
+   * @param symbols the set of symbols used for parsing and formatting.
+   * @throws NullPointerException if any argument is null.
+   * @throws IllegalArgumentException if the pattern is invalid.
+   */
+  /*public DecimalFormat(String pattern, DecimalFormatSymbols symbols)
+  {
+    this.symbols = (DecimalFormatSymbols) symbols.clone();
+    applyPatternWithSymbols(pattern, nonLocalizedSymbols);
+  }*/
+  
+  /**
+   * Apply the given localized patern to the current DecimalFormat object.
+   * 
+   * @param pattern The localized pattern to apply.
+   * @throws IllegalArgumentException if the given pattern is invalid.
+   * @throws NullPointerException if the input pattern is null.
+   */
+  public void applyLocalizedPattern (String pattern)
+  {
+    //applyPatternWithSymbols(pattern, this.symbols);
+  }
+
+  /**
+   * Apply the given localized pattern to the current DecimalFormat object.
+   * 
+   * @param pattern The localized pattern to apply.
+   * @throws IllegalArgumentException if the given pattern is invalid.
+   * @throws NullPointerException if the input pattern is null.
+   */
+  public void applyPattern(String pattern)
+  {
+    //applyPatternWithSymbols(pattern, nonLocalizedSymbols);
+  }
+
+  public Object clone()
+  {
+    /*DecimalFormat c = (DecimalFormat) super.clone();
+    c.symbols = (DecimalFormatSymbols) symbols.clone();
+    return c;*/
+    return null;
+  }
+
+  /**
+   * Tests this instance for equality with an arbitrary object.  This method
+   * returns <code>true</code> if:
+   * <ul>
+   * <li><code>obj</code> is not <code>null</code>;</li>
+   * <li><code>obj</code> is an instance of <code>DecimalFormat</code>;</li>
+   * <li>this instance and <code>obj</code> have the same attributes;</li>
+   * </ul>
+   * 
+   * @param obj  the object (<code>null</code> permitted).
+   * 
+   * @return A boolean.
+   */
+  public boolean equals(Object obj)
+  {
+    /*if (! (obj instanceof DecimalFormat))
+      return false;
+    DecimalFormat dup = (DecimalFormat) obj;
+    return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown 
+           && groupingUsed == dup.groupingUsed
+           && groupingSeparatorInPattern == dup.groupingSeparatorInPattern 
+           && groupingSize == dup.groupingSize
+           && multiplier == dup.multiplier
+           && useExponentialNotation == dup.useExponentialNotation
+           && minExponentDigits == dup.minExponentDigits
+           && minimumIntegerDigits == dup.minimumIntegerDigits
+           && maximumIntegerDigits == dup.maximumIntegerDigits
+           && minimumFractionDigits == dup.minimumFractionDigits
+           && maximumFractionDigits == dup.maximumFractionDigits
+           && parseBigDecimal == dup.parseBigDecimal
+           && useCurrencySeparator == dup.useCurrencySeparator
+           && showDecimalSeparator == dup.showDecimalSeparator
+           && exponentRound == dup.exponentRound
+           && negativePatternMultiplier == dup.negativePatternMultiplier
+           && maxIntegerDigitsExponent == dup.maxIntegerDigitsExponent
+           // XXX: causes equivalent patterns to fail
+           // && hasNegativePrefix == dup.hasNegativePrefix
+           && equals(negativePrefix, dup.negativePrefix)
+           && equals(negativeSuffix, dup.negativeSuffix)
+           && equals(positivePrefix, dup.positivePrefix)
+           && equals(positiveSuffix, dup.positiveSuffix)
+           && symbols.equals(dup.symbols));*/
+    return false;
+  }
+
+  /**
+   * Returns a hash code for this object.
+   *
+   * @return A hash code.
+   */
+  public int hashCode()
+  {
+    return toPattern().hashCode();
+  }
+  
+  public StringBuffer format(long l) {
+    return null;
+  }
+  
+  /**
+   * Produce a formatted {@link String} representation of this object.
+   * The passed object must be of type number. 
+   * 
+   * @param obj The {@link Number} to format.
+   * @param sbuf The destination String; text will be appended to this String. 
+   * @param pos If used on input can be used to define an alignment
+   * field. If used on output defines the offsets of the alignment field.
+   * @return The String representation of this long.
+   */
+  /*public final StringBuffer format(Object obj, StringBuffer sbuf, FieldPosition pos)
+  {
+    if (obj instanceof BigInteger)
+      {
+        BigDecimal decimal = new BigDecimal((BigInteger) obj);
+        formatInternal(decimal, true, sbuf, pos);
+        return sbuf;
+      }
+    else if (obj instanceof BigDecimal)
+      {
+        formatInternal((BigDecimal) obj, true, sbuf, pos);
+        return sbuf;
+      }
+    
+    return super.format(obj, sbuf, pos);
+  }*/
+  
+  /**
+   * Produce a formatted {@link String} representation of this double.
+   * 
+   * @param number The double to format.
+   * @param dest The destination String; text will be appended to this String. 
+   * @param fieldPos If used on input can be used to define an alignment
+   * field. If used on output defines the offsets of the alignment field.
+   * @return The String representation of this long.
+   * @throws NullPointerException if <code>dest</code> or fieldPos are null
+   */
+  /*public StringBuffer format(double number, StringBuffer dest,
+                            FieldPosition fieldPos)
+  {
+    // special cases for double: NaN and negative or positive infinity
+    if (Double.isNaN(number))
+      {
+        // 1. NaN
+        String nan = symbols.getNaN();
+        dest.append(nan);
+        
+        // update field position if required
+        if ((fieldPos.getField() == INTEGER_FIELD ||
+             fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
+          {
+            int index = dest.length();
+            fieldPos.setBeginIndex(index - nan.length());
+            fieldPos.setEndIndex(index);
+          }
+      }
+    else if (Double.isInfinite(number))
+      {
+        // 2. Infinity
+        if (number < 0)
+          dest.append(this.negativePrefix);
+        else
+          dest.append(this.positivePrefix);
+        
+        dest.append(symbols.getInfinity());
+        
+        if (number < 0)
+          dest.append(this.negativeSuffix);
+        else
+          dest.append(this.positiveSuffix);
+        
+        if ((fieldPos.getField() == INTEGER_FIELD ||
+            fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
+         {
+           fieldPos.setBeginIndex(dest.length());
+           fieldPos.setEndIndex(0);
+         }
+      }
+    else
+      {
+        // get the number as a BigDecimal
+        BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
+        formatInternal(bigDecimal, false, dest, fieldPos);
+      }
+    
+    return dest;
+  }*/
+
+  /**
+   * Produce a formatted {@link String} representation of this long.
+   * 
+   * @param number The long to format.
+   * @param dest The destination String; text will be appended to this String. 
+   * @param fieldPos If used on input can be used to define an alignment
+   * field. If used on output defines the offsets of the alignment field.
+   * @return The String representation of this long.
+   */
+  /*public StringBuffer format(long number, StringBuffer dest,
+                             FieldPosition fieldPos)
+  {
+    BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
+    formatInternal(bigDecimal, true, dest, fieldPos);
+    return dest;
+  }*/
+  
+  /**
+   * Return an <code>AttributedCharacterIterator</code> as a result of
+   * the formatting of the passed {@link Object}.
+   * 
+   * @return An {@link AttributedCharacterIterator}.
+   * @throws NullPointerException if value is <code>null</code>. 
+   * @throws IllegalArgumentException if value is not an instance of
+   * {@link Number}.
+   */
+  /*public AttributedCharacterIterator formatToCharacterIterator(Object value)
+  {
+    /*
+     * This method implementation derives directly from the
+     * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
+     */
+    
+    /*if (value == null)
+      throw new NullPointerException("Passed Object is null");
+    
+    if (!(value instanceof Number)) throw new
+      IllegalArgumentException("Cannot format given Object as a Number");
+    
+    StringBuffer text = new StringBuffer();
+    attributes.clear();
+    super.format(value, text, new FieldPosition(0));
+
+    AttributedString as = new AttributedString(text.toString());
+
+    // add NumberFormat field attributes to the AttributedString
+    for (int i = 0; i < attributes.size(); i++)
+      {
+        FieldPosition pos = (FieldPosition) attributes.get(i);
+        Format.Field attribute = pos.getFieldAttribute();
+        
+        as.addAttribute(attribute, attribute, pos.getBeginIndex(),
+                        pos.getEndIndex());
+      }
+    
+    // return the CharacterIterator from AttributedString
+    return as.getIterator();
+  }*/
+  
+  /**
+   * Returns the currency corresponding to the currency symbol stored
+   * in the instance of <code>DecimalFormatSymbols</code> used by this
+   * <code>DecimalFormat</code>.
+   *
+   * @return A new instance of <code>Currency</code> if
+   * the currency code matches a known one, null otherwise.
+   */
+  /*public Currency getCurrency()
+  {
+    return symbols.getCurrency();
+  }*/
+  
+  /**
+   * Returns a copy of the symbols used by this instance.
+   * 
+   * @return A copy of the symbols.
+   */
+  /*public DecimalFormatSymbols getDecimalFormatSymbols()
+  {
+    return (DecimalFormatSymbols) symbols.clone();
+  }*/
+  
+  /**
+   * Gets the interval used between a grouping separator and the next.
+   * For example, a grouping size of 3 means that the number 1234 is
+   * formatted as 1,234.
+   * 
+   * The actual character used as grouping separator depends on the
+   * locale and is defined by {@link DecimalFormatSymbols#getDecimalSeparator()}
+   * 
+   * @return The interval used between a grouping separator and the next.
+   */
+  public int getGroupingSize()
+  {
+    return groupingSize;
+  }
+
+  /**
+   * Gets the multiplier used in percent and similar formats.
+   * 
+   * @return The multiplier used in percent and similar formats.
+   */
+  public int getMultiplier()
+  {
+    return multiplier;
+  }
+  
+  /**
+   * Gets the negative prefix.
+   * 
+   * @return The negative prefix.
+   */
+  public String getNegativePrefix()
+  {
+    return negativePrefix;
+  }
+
+  /**
+   * Gets the negative suffix.
+   * 
+   * @return The negative suffix.
+   */
+  public String getNegativeSuffix()
+  {
+    return negativeSuffix;
+  }
+  
+  /**
+   * Gets the positive prefix.
+   * 
+   * @return The positive prefix.
+   */
+  public String getPositivePrefix()
+  {
+    return positivePrefix;
+  }
+  
+  /**
+   * Gets the positive suffix.
+   *  
+   * @return The positive suffix.
+   */
+  public String getPositiveSuffix()
+  {
+    return positiveSuffix;
+  }
+  
+  public boolean isDecimalSeparatorAlwaysShown()
+  {
+    return decimalSeparatorAlwaysShown;
+  }
+  
+  /**
+   * Define if <code>parse(java.lang.String, java.text.ParsePosition)</code>
+   * should return a {@link BigDecimal} or not. 
+   * 
+   * @param newValue
+   */
+  public void setParseBigDecimal(boolean newValue)
+  {
+    this.parseBigDecimal = newValue;
+  }
+  
+  /**
+   * Returns <code>true</code> if
+   * <code>parse(java.lang.String, java.text.ParsePosition)</code> returns
+   * a <code>BigDecimal</code>, <code>false</code> otherwise.
+   * The default return value for this method is <code>false</code>.
+   * 
+   * @return <code>true</code> if the parse method returns a {@link BigDecimal},
+   * <code>false</code> otherwise.
+   * @since 1.5
+   * @see #setParseBigDecimal(boolean)
+   */
+  public boolean isParseBigDecimal()
+  {
+    return this.parseBigDecimal;
+  }
+  
+  /**
+   * This method parses the specified string into a <code>Number</code>.
+   * 
+   * The parsing starts at <code>pos</code>, which is updated as the parser
+   * consume characters in the passed string.
+   * On error, the <code>Position</code> object index is not updated, while
+   * error position is set appropriately, an <code>null</code> is returned.
+   * 
+   * @param str The string to parse.
+   * @param pos The desired <code>ParsePosition</code>.
+   *
+   * @return The parsed <code>Number</code>
+   */
+  /*public Number parse(String str, ParsePosition pos)
+  {
+    // a special values before anything else
+    // NaN
+    if (str.contains(this.symbols.getNaN()))
+      return Double.valueOf(Double.NaN);
+   
+    // this will be our final number
+    CPStringBuilder number = new CPStringBuilder();
+    
+    // special character
+    char minus = symbols.getMinusSign();
+    
+    // starting parsing position
+    int start = pos.getIndex();
+    
+    // validate the string, it have to be in the 
+    // same form as the format string or parsing will fail
+    String _negativePrefix = (this.negativePrefix.compareTo("") == 0
+                              ? minus + positivePrefix
+                              : this.negativePrefix);
+    
+    // we check both prefixes, because one might be empty.
+    // We want to pick the longest prefix that matches.
+    int positiveLen = positivePrefix.length();
+    int negativeLen = _negativePrefix.length();
+    
+    boolean isNegative = str.startsWith(_negativePrefix);
+    boolean isPositive = str.startsWith(positivePrefix);
+    
+    if (isPositive && isNegative)
+      {
+        // By checking this way, we preserve ambiguity in the case
+        // where the negative format differs only in suffix.
+        if (negativeLen > positiveLen)
+          {
+            start += _negativePrefix.length();
+            isNegative = true;
+          }
+        else
+          {
+            start += positivePrefix.length();
+            isPositive = true;
+            if (negativeLen < positiveLen)
+              isNegative = false;
+          }
+      }
+    else if (isNegative)
+      {
+        start += _negativePrefix.length();
+        isPositive = false;
+      }
+    else if (isPositive)
+      {
+        start += positivePrefix.length();
+        isNegative = false;
+      }
+    else
+      {
+        pos.setErrorIndex(start);
+        return null;
+      }
+    
+    // other special characters used by the parser
+    char decimalSeparator = symbols.getDecimalSeparator();
+    char zero = symbols.getZeroDigit();
+    char exponent = symbols.getExponential(); 
+    
+    // stop parsing position in the string
+    int stop = start + this.maximumIntegerDigits + maximumFractionDigits + 2;
+    
+    if (useExponentialNotation)
+      stop += minExponentDigits + 1;
+    
+    boolean inExponent = false;
+
+    // correct the size of the end parsing flag
+    int len = str.length();
+    if (len < stop) stop = len;
+    char groupingSeparator = symbols.getGroupingSeparator();
+    
+    int i = start;
+    while (i < stop)
+      {
+        char ch = str.charAt(i);
+        i++;
+       
+        if (ch >= zero && ch <= (zero + 9))
+          {
+            number.append(ch);
+          }
+        else if (this.parseIntegerOnly)
+          {
+            i--;
+            break;
+          }
+        else if (ch == decimalSeparator)
+          {
+            number.append('.');
+          }
+        else if (ch == exponent)
+          {
+            number.append(ch);
+            inExponent = !inExponent;
+          }
+        else if ((ch == '+' || ch == '-' || ch == minus))
+          {
+            if (inExponent)
+              number.append(ch);
+            else
+             {
+               i--;
+               break;
+             }
+          }
+       else
+         {
+           if (!groupingUsed || ch != groupingSeparator)
+             {
+               i--;
+               break;
+             }
+         }
+      }
+
+    // 2nd special case: infinity
+    // XXX: need to be tested
+    if (str.contains(symbols.getInfinity()))
+      {
+        int inf = str.indexOf(symbols.getInfinity()); 
+        pos.setIndex(inf);
+        
+        // FIXME: ouch, this is really ugly and lazy code...
+        if (this.parseBigDecimal)
+          {
+            if (isNegative)
+              return BigDecimal.valueOf(Double.NEGATIVE_INFINITY);
+            
+            return BigDecimal.valueOf(Double.POSITIVE_INFINITY);
+          }
+        
+        if (isNegative)
+          return Double.valueOf(Double.NEGATIVE_INFINITY);
+
+        return Double.valueOf(Double.POSITIVE_INFINITY);
+      }
+    
+    // no number...
+    if (i == start || number.length() == 0)
+      {
+        pos.setErrorIndex(i);
+        return null;
+      }
+
+    // now we have to check the suffix, done here after number parsing
+    // or the index will not be updated correctly...
+    boolean hasNegativeSuffix = str.endsWith(this.negativeSuffix);
+    boolean hasPositiveSuffix = str.endsWith(this.positiveSuffix);
+    boolean positiveEqualsNegative = negativeSuffix.equals(positiveSuffix);
+
+    positiveLen = positiveSuffix.length();
+    negativeLen = negativeSuffix.length();
+    
+    if (isNegative && !hasNegativeSuffix)
+      {
+        pos.setErrorIndex(i);
+        return null;
+      }
+    else if (hasNegativeSuffix &&
+             !positiveEqualsNegative &&
+             (negativeLen > positiveLen))
+      {
+        isNegative = true;
+      }
+    else if (!hasPositiveSuffix)
+      {
+        pos.setErrorIndex(i);
+        return null;
+      }
+    
+    if (isNegative) number.insert(0, '-');
+   
+    pos.setIndex(i);
+    
+    // now we handle the return type
+    BigDecimal bigDecimal = new BigDecimal(number.toString());
+    if (this.parseBigDecimal)
+      return bigDecimal;
+    
+    // want integer?
+    if (this.parseIntegerOnly)
+      return Long.valueOf(bigDecimal.longValue());
+
+    // 3th special case -0.0
+    if (isNegative && (bigDecimal.compareTo(BigDecimal.ZERO) == 0))
+      return Double.valueOf(-0.0);
+    
+    try
+      {
+        BigDecimal integer
+          = bigDecimal.setScale(0, BigDecimal.ROUND_UNNECESSARY);
+        return Long.valueOf(integer.longValue());
+      }
+    catch (ArithmeticException e)
+      {
+        return Double.valueOf(bigDecimal.doubleValue());
+      }
+  }*/
+
+  /**
+   * Sets the <code>Currency</code> on the
+   * <code>DecimalFormatSymbols</code> used, which also sets the
+   * currency symbols on those symbols.
+   * 
+   * @param currency The new <code>Currency</code> on the
+   * <code>DecimalFormatSymbols</code>.
+   */
+  /*public void setCurrency(Currency currency)
+  {
+    Currency current = symbols.getCurrency();
+    if (current != currency)
+      {
+       String oldSymbol = symbols.getCurrencySymbol();
+       int len = oldSymbol.length();
+       symbols.setCurrency(currency);
+       String newSymbol = symbols.getCurrencySymbol();
+       int posPre = positivePrefix.indexOf(oldSymbol);
+       if (posPre != -1)
+         positivePrefix = positivePrefix.substring(0, posPre) +
+           newSymbol + positivePrefix.substring(posPre+len);
+       int negPre = negativePrefix.indexOf(oldSymbol);
+       if (negPre != -1)
+         negativePrefix = negativePrefix.substring(0, negPre) +
+           newSymbol + negativePrefix.substring(negPre+len);
+       int posSuf = positiveSuffix.indexOf(oldSymbol);
+       if (posSuf != -1)
+         positiveSuffix = positiveSuffix.substring(0, posSuf) +
+           newSymbol + positiveSuffix.substring(posSuf+len);
+       int negSuf = negativeSuffix.indexOf(oldSymbol);
+       if (negSuf != -1)
+         negativeSuffix = negativeSuffix.substring(0, negSuf) +
+           newSymbol + negativeSuffix.substring(negSuf+len);
+      }
+  }*/
+  
+  /**
+   * Sets the symbols used by this instance.  This method makes a copy of 
+   * the supplied symbols.
+   * 
+   * @param newSymbols  the symbols (<code>null</code> not permitted).
+   */
+  /*public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)
+  {
+    symbols = (DecimalFormatSymbols) newSymbols.clone();
+  }*/
+  
+  /**
+   * Define if the decimal separator should be always visible or only
+   * visible when needed. This method as effect only on integer values.
+   * Pass <code>true</code> if you want the decimal separator to be
+   * always shown, <code>false</code> otherwise.
+   * 
+   * @param newValue true</code> if you want the decimal separator to be
+   * always shown, <code>false</code> otherwise.
+   */
+  /*public void setDecimalSeparatorAlwaysShown(boolean newValue)
+  {
+    decimalSeparatorAlwaysShown = newValue;
+  }*/
+  
+  /**
+   * Sets the number of digits used to group portions of the integer part of
+   * the number. For example, the number <code>123456</code>, with a grouping
+   * size of 3, is rendered <code>123,456</code>.
+   * 
+   * @param groupSize The number of digits used while grouping portions
+   * of the integer part of a number.
+   */
+  public void setGroupingSize(int groupSize)
+  {
+    groupingSize = (byte) groupSize;
+  }
+  
+  /**
+   * Sets the maximum number of digits allowed in the integer
+   * portion of a number to the specified value.
+   * The new value will be the choosen as the minimum between
+   * <code>newvalue</code> and 309. Any value below zero will be
+   * replaced by zero.
+   * 
+   * @param newValue The new maximum integer digits value.
+   */
+  /*public void setMaximumIntegerDigits(int newValue)
+  {
+    newValue = (newValue > 0) ? newValue : 0;
+    super.setMaximumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS));
+  }*/
+  
+  /**
+   * Sets the minimum number of digits allowed in the integer
+   * portion of a number to the specified value.
+   * The new value will be the choosen as the minimum between
+   * <code>newvalue</code> and 309. Any value below zero will be
+   * replaced by zero.
+   * 
+   * @param newValue The new minimum integer digits value.
+   */
+  /*public void setMinimumIntegerDigits(int newValue)
+  {
+    newValue = (newValue > 0) ? newValue : 0;
+    super.setMinimumIntegerDigits(Math.min(newValue,  DEFAULT_INTEGER_DIGITS));
+  }*/
+  
+  /**
+   * Sets the maximum number of digits allowed in the fraction
+   * portion of a number to the specified value.
+   * The new value will be the choosen as the minimum between
+   * <code>newvalue</code> and 309. Any value below zero will be
+   * replaced by zero.
+   * 
+   * @param newValue The new maximum fraction digits value.
+   */
+  /*public void setMaximumFractionDigits(int newValue)
+  {
+    newValue = (newValue > 0) ? newValue : 0;
+    super.setMaximumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
+  }*/
+  
+  /**
+   * Sets the minimum number of digits allowed in the fraction
+   * portion of a number to the specified value.
+   * The new value will be the choosen as the minimum between
+   * <code>newvalue</code> and 309. Any value below zero will be
+   * replaced by zero.
+   * 
+   * @param newValue The new minimum fraction digits value.
+   */
+  /*public void setMinimumFractionDigits(int newValue)
+  {
+    newValue = (newValue > 0) ? newValue : 0;
+    super.setMinimumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS));
+  }*/
+  
+  /**
+   * Sets the multiplier for use in percent and similar formats.
+   * For example, for percent set the multiplier to 100, for permille, set the
+   * miltiplier to 1000.
+   * 
+   * @param newValue the new value for multiplier.
+   */
+  public void setMultiplier(int newValue)
+  {
+    multiplier = newValue;
+  }
+  
+  /**
+   * Sets the negative prefix.
+   * 
+   * @param newValue The new negative prefix.
+   */
+  public void setNegativePrefix(String newValue)
+  {
+    negativePrefix = newValue;
+  }
+
+  /**
+   * Sets the negative suffix.
+   * 
+   * @param newValue The new negative suffix.
+   */
+  public void setNegativeSuffix(String newValue)
+  {
+    negativeSuffix = newValue;
+  }
+  
+  /**
+   * Sets the positive prefix.
+   * 
+   * @param newValue The new positive prefix.
+   */
+  public void setPositivePrefix(String newValue)
+  {
+    positivePrefix = newValue;
+  }
+  
+  /**
+   * Sets the new positive suffix.
+   * 
+   * @param newValue The new positive suffix.
+   */
+  public void setPositiveSuffix(String newValue)
+  {
+    positiveSuffix = newValue;
+  }
+  
+  /**
+   * This method returns a string with the formatting pattern being used
+   * by this object. The string is localized.
+   * 
+   * @return A localized <code>String</code> with the formatting pattern.
+   * @see #toPattern()
+   */
+  /*public String toLocalizedPattern()
+  {
+    return computePattern(this.symbols);
+  }*/
+  
+  /**
+   * This method returns a string with the formatting pattern being used
+   * by this object. The string is not localized.
+   * 
+   * @return A <code>String</code> with the formatting pattern.
+   * @see #toLocalizedPattern()
+   */
+  /*public String toPattern()
+  {
+    return computePattern(nonLocalizedSymbols);
+  }*/
+  
+  /* ***** private methods ***** */
+  
+  /**
+   * This is an shortcut helper method used to test if two given strings are
+   * equals.
+   * 
+   * @param s1 The first string to test for equality.
+   * @param s2 The second string to test for equality.
+   * @return <code>true</code> if the strings are both <code>null</code> or
+   * equals.
+   */
+  private boolean equals(String s1, String s2)
+  {
+    if (s1 == null || s2 == null)
+      return s1 == s2;
+    return s1.equals(s2);
+  }
+  
+  
+  /* ****** PATTERN ****** */
+  
+  /**
+   * This helper function creates a string consisting of all the
+   * characters which can appear in a pattern and must be quoted.
+   */
+  /*private String patternChars (DecimalFormatSymbols syms)
+  {
+    CPStringBuilder buf = new CPStringBuilder ();
+    
+    buf.append(syms.getDecimalSeparator());
+    buf.append(syms.getDigit());
+    buf.append(syms.getExponential());
+    buf.append(syms.getGroupingSeparator());
+    buf.append(syms.getMinusSign());
+    buf.append(syms.getPatternSeparator());
+    buf.append(syms.getPercent());
+    buf.append(syms.getPerMill());
+    buf.append(syms.getZeroDigit());
+    buf.append('\'');
+    buf.append('\u00a4');
+    
+    return buf.toString();
+  }*/
+
+  /**
+   * Quote special characters as defined by <code>patChars</code> in the
+   * input string.
+   * 
+   * @param text
+   * @param patChars
+   * @return A StringBuffer with special characters quoted.
+   */
+  /*private CPStringBuilder quoteFix(String text, String patChars)
+  {
+    CPStringBuilder buf = new CPStringBuilder();
+    
+    int len = text.length();
+    char ch;
+    for (int index = 0; index < len; ++index)
+      {
+        ch = text.charAt(index);
+        if (patChars.indexOf(ch) != -1)
+          {
+            buf.append('\'');
+            buf.append(ch);
+            if (ch != '\'') buf.append('\'');
+          }
+        else
+          {
+            buf.append(ch);
+          }
+      }
+    
+    return buf;
+  }*/
+  
+  /**
+   * Returns the format pattern, localized to follow the given
+   * symbols.
+   */
+  /*private String computePattern(DecimalFormatSymbols symbols)
+  {
+    StringBuilder mainPattern = new StringBuilder();
+    
+    // We have to at least emit a zero for the minimum number of
+    // digits. Past that we need hash marks up to the grouping
+    // separator (and one beyond).
+    int _groupingSize = groupingUsed ? groupingSize + 1: groupingSize;
+    int totalDigits = Math.max(minimumIntegerDigits, _groupingSize);
+    
+    // if it is not in exponential notiation,
+    // we always have a # prebended
+    if (!useExponentialNotation) mainPattern.append(symbols.getDigit());
+    
+    for (int i = 1; i < totalDigits - minimumIntegerDigits; i++)
+      mainPattern.append(symbols.getDigit());
+    
+    for (int i = totalDigits - minimumIntegerDigits; i < totalDigits; i++)
+      mainPattern.append(symbols.getZeroDigit());
+    
+    if (groupingUsed)
+      {
+        mainPattern.insert(mainPattern.length() - groupingSize,
+                           symbols.getGroupingSeparator());
+      }
+
+    // See if we need decimal info.
+    if (minimumFractionDigits > 0 || maximumFractionDigits > 0 ||
+        decimalSeparatorAlwaysShown)
+      {
+        mainPattern.append(symbols.getDecimalSeparator());
+      }
+    
+    for (int i = 0; i < minimumFractionDigits; ++i)
+      mainPattern.append(symbols.getZeroDigit());
+    
+    for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i)
+      mainPattern.append(symbols.getDigit());
+    
+    if (useExponentialNotation)
+      {
+        mainPattern.append(symbols.getExponential());
+        
+        for (int i = 0; i < minExponentDigits; ++i)
+          mainPattern.append(symbols.getZeroDigit());
+        
+        if (minExponentDigits == 0)
+          mainPattern.append(symbols.getDigit());
+      }
+    
+    // save the pattern
+    String pattern = mainPattern.toString();
+    
+    // so far we have the pattern itself, now we need to add
+    // the positive and the optional negative prefixes and suffixes
+    String patternChars = patternChars(symbols);
+    mainPattern.insert(0, quoteFix(positivePrefix, patternChars));
+    mainPattern.append(quoteFix(positiveSuffix, patternChars));
+    
+    if (hasNegativePrefix)
+      {
+        mainPattern.append(symbols.getPatternSeparator());
+        mainPattern.append(quoteFix(negativePrefix, patternChars));
+        mainPattern.append(pattern);
+        mainPattern.append(quoteFix(negativeSuffix, patternChars));
+      }
+    
+    // finally, return the pattern string
+    return mainPattern.toString();
+  }*/
+  
+  /* ****** FORMAT PARSING ****** */
+  
+  /**
+   * Scan the input string and define a pattern suitable for use
+   * with this decimal format.
+   * 
+   * @param pattern
+   * @param symbols
+   */
+  /*private void applyPatternWithSymbols(String pattern,
+                                       DecimalFormatSymbols symbols)
+  {
+    // The pattern string is described by a BNF diagram.
+    // we could use a recursive parser to read and prepare
+    // the string, but this would be too slow and resource
+    // intensive, while this code is quite critical as it is
+    // called always when the class is instantiated and every
+    // time a new pattern is given.
+    // Our strategy is to divide the string into section as given by
+    // the BNF diagram, iterating through the string and setting up
+    // the parameters we need for formatting (which is basicly what
+    // a descendent recursive parser would do - but without recursion).
+    // I'm sure that there are smarter methods to do this.
+    
+    // Restore default values. Most of these will be overwritten
+    // but we want to be sure that nothing is left out.
+    setDefaultValues();
+    
+    int len = pattern.length();
+    if (len == 0)
+      {
+        // this is another special case...
+        this.minimumIntegerDigits = 1;
+        this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
+        this.minimumFractionDigits = 0;
+        this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
+        
+        // FIXME: ...and these values may not be valid in all locales
+        this.minExponentDigits = 0;
+        this.showDecimalSeparator = true;
+        this.groupingUsed = true;
+        this.groupingSize = 3;
+        
+        return;
+      }
+    
+    int start = scanFix(pattern, symbols, 0, true);
+    if (start < len) start = scanNumberInteger(pattern, symbols, start);
+    if (start < len)
+      {
+        start = scanFractionalPortion(pattern, symbols, start);
+      }
+    else
+      {
+        // special case, pattern that ends here does not have a fractional
+        // portion
+        this.minimumFractionDigits = 0;
+        this.maximumFractionDigits = 0;
+        //this.decimalSeparatorAlwaysShown = false;
+        //this.showDecimalSeparator = false;
+      }
+    
+    // XXX: this fixes a compatibility test with the RI.
+    // If new uses cases fail, try removing this line first.
+    //if (!this.hasIntegerPattern && !this.hasFractionalPattern)
+    //  throw new IllegalArgumentException("No valid pattern found!");
+    
+    if (start < len) start = scanExponent(pattern, symbols, start);
+    if (start < len) start = scanFix(pattern, symbols, start, false);
+    if (start < len) scanNegativePattern(pattern, symbols, start);
+    
+    if (useExponentialNotation &&
+        (maxIntegerDigitsExponent > minimumIntegerDigits) &&
+        (maxIntegerDigitsExponent > 1))
+      {
+        minimumIntegerDigits = 1;
+        exponentRound = maxIntegerDigitsExponent;
+      }
+    
+    if (useExponentialNotation)
+      maximumIntegerDigits = maxIntegerDigitsExponent;
+    
+    if (!this.hasFractionalPattern && this.showDecimalSeparator == true)
+      {
+        this.decimalSeparatorAlwaysShown = true;
+      }
+  }*/
+  
+  /**
+   * Scans for the prefix or suffix portion of the pattern string.
+   * This method handles the positive subpattern of the pattern string.
+   *  
+   * @param pattern The pattern string to parse.
+   * @return The position in the pattern string where parsing ended.
+   */
+  /*private int scanFix(String pattern, DecimalFormatSymbols sourceSymbols,
+                      int start, boolean prefix)
+  {
+    CPStringBuilder buffer = new CPStringBuilder();
+    
+    // the number portion is always delimited by one of those
+    // characters
+    char decimalSeparator = sourceSymbols.getDecimalSeparator();
+    char patternSeparator = sourceSymbols.getPatternSeparator();
+    char groupingSeparator = sourceSymbols.getGroupingSeparator();
+    char digit = sourceSymbols.getDigit();
+    char zero = sourceSymbols.getZeroDigit();
+    char minus = sourceSymbols.getMinusSign();
+    
+    // other special characters, cached here to avoid method calls later
+    char percent = sourceSymbols.getPercent();
+    char permille = sourceSymbols.getPerMill();
+    
+    String currencySymbol = this.symbols.getCurrencySymbol();
+    
+    boolean quote = false;
+    
+    char ch = pattern.charAt(start);
+    if (ch == patternSeparator)
+      {
+        // negative subpattern
+        this.hasNegativePrefix = true;
+        ++start;
+        return start;
+      }
+    
+    int len = pattern.length();
+    int i;
+    for (i = start; i < len; i++)
+      {
+        ch = pattern.charAt(i);
+
+        // we are entering into the negative subpattern
+        if (!quote && ch == patternSeparator)
+          {
+            if (this.hasNegativePrefix)
+              {
+                throw new IllegalArgumentException("Invalid pattern found: "
+                                                   + start);
+              }
+            
+            this.hasNegativePrefix = true;
+            ++i;
+            break;
+          }
+        
+        // this means we are inside the number portion
+        if (!quote &&
+            (ch == minus || ch == digit || ch == zero ||
+             ch == groupingSeparator))
+          break;
+
+        if (!quote && ch == decimalSeparator)
+          {
+            this.showDecimalSeparator = true;
+            break;
+          }
+        else if (quote && ch != '\'')
+          {
+            buffer.append(ch);
+            continue;
+          }
+        
+        if (ch == '\u00A4')
+          {
+            // CURRENCY
+            currencySymbol = this.symbols.getCurrencySymbol();
+
+            // if \u00A4 is doubled, we use the international currency symbol
+            if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4')
+              {
+                currencySymbol = this.symbols.getInternationalCurrencySymbol();
+                i++;
+              }
+
+            this.useCurrencySeparator = true;
+            buffer.append(currencySymbol);
+          }
+        else if (ch == percent)
+          {
+            // PERCENT
+            this.multiplier = 100;
+            buffer.append(this.symbols.getPercent());
+          }
+        else if (ch == permille)
+          {
+            // PERMILLE
+            this.multiplier = 1000;
+            buffer.append(this.symbols.getPerMill());
+          }
+        else if (ch == '\'')
+          {
+            // QUOTE
+            if ((i + 1) < len && pattern.charAt(i + 1) == '\'')
+              {
+                // we need to add ' to the buffer 
+                buffer.append(ch);
+                i++;
+              }
+            else
+              {
+                quote = !quote;
+                continue;
+              }
+          }
+        else
+          {
+            buffer.append(ch);
+          }
+      }
+    
+    if (prefix)
+      {
+        this.positivePrefix = buffer.toString();
+        this.negativePrefix = minus + "" + positivePrefix;
+      }
+    else
+      {
+        this.positiveSuffix = buffer.toString();
+      }
+    
+    return i;
+  }*/
+  
+  /**
+   * Scan the given string for number patterns, starting
+   * from <code>start</code>.
+   * This method searches the integer part of the pattern only.
+   * 
+   * @param pattern The pattern string to parse.
+   * @param start The starting parse position in the string.
+   * @return The position in the pattern string where parsing ended,
+   * counted from the beginning of the string (that is, 0).
+   */
+  /*private int scanNumberInteger(String pattern, DecimalFormatSymbols symbols,
+                                int start)
+  {
+    char digit = symbols.getDigit();
+    char zero = symbols.getZeroDigit();
+    char groupingSeparator = symbols.getGroupingSeparator();
+    char decimalSeparator = symbols.getDecimalSeparator();
+    char exponent = symbols.getExponential();
+    char patternSeparator = symbols.getPatternSeparator();
+    
+    // count the number of zeroes in the pattern
+    // this number defines the minum digits in the integer portion
+    int zeros = 0;
+    
+    // count the number of digits used in grouping
+    int _groupingSize = 0;
+    
+    this.maxIntegerDigitsExponent = 0;
+    
+    boolean intPartTouched = false;
+    
+    char ch;
+    int len = pattern.length();
+    int i;
+    for (i = start; i < len; i++)
+      {
+        ch = pattern.charAt(i);
+        // break on decimal separator or exponent or pattern separator
+        if (ch == decimalSeparator || ch == exponent)
+          break;
+        
+        if (this.hasNegativePrefix && ch == patternSeparator)
+          throw new IllegalArgumentException("Invalid pattern found: "
+                                             + start);
+        
+        if (ch == digit)
+          {
+            // in our implementation we could relax this strict
+            // requirement, but this is used to keep compatibility with
+            // the RI
+            if (zeros > 0) throw new
+              IllegalArgumentException("digit mark following zero in " +
+                        "positive subpattern, not allowed. Position: " + i);
+            
+            _groupingSize++;
+            intPartTouched = true;
+            this.maxIntegerDigitsExponent++;
+          }
+        else if (ch == zero)
+          {
+            zeros++;
+            _groupingSize++;
+            this.maxIntegerDigitsExponent++;
+          }
+        else if (ch == groupingSeparator)
+          {
+            this.groupingSeparatorInPattern = true;
+            this.groupingUsed = true;
+            _groupingSize = 0;
+          }
+        else
+          {
+            // any other character not listed above
+            // means we are in the suffix portion
+            break;
+          }
+      }
+    
+    if (groupingSeparatorInPattern) this.groupingSize = (byte) _groupingSize;
+    this.minimumIntegerDigits = zeros;
+    
+    // XXX: compatibility code with the RI: the number of minimum integer
+    // digits is at least one when maximumIntegerDigits is more than zero
+    if (intPartTouched && this.maximumIntegerDigits > 0 &&
+        this.minimumIntegerDigits == 0)
+      this.minimumIntegerDigits = 1;
+
+    return i;
+  }*/
+  
+  /**
+   * Scan the given string for number patterns, starting
+   * from <code>start</code>.
+   * This method searches the fractional part of the pattern only.
+   * 
+   * @param pattern The pattern string to parse.
+   * @param start The starting parse position in the string.
+   * @return The position in the pattern string where parsing ended,
+   * counted from the beginning of the string (that is, 0).
+   */
+  /*private int scanFractionalPortion(String pattern,
+                                    DecimalFormatSymbols symbols,
+                                    int start)
+  {
+    char digit = symbols.getDigit();
+    char zero = symbols.getZeroDigit();
+    char groupingSeparator = symbols.getGroupingSeparator();
+    char decimalSeparator = symbols.getDecimalSeparator();
+    char exponent = symbols.getExponential();
+    char patternSeparator = symbols.getPatternSeparator();
+    
+    // first character needs to be '.' otherwise we are not parsing the
+    // fractional portion
+    char ch = pattern.charAt(start);
+    if (ch != decimalSeparator)
+      {
+        this.minimumFractionDigits = 0;
+        this.maximumFractionDigits = 0;
+        return start;
+      }
+    
+    ++start;
+    
+    this.hasFractionalPattern = true;
+    
+    this.minimumFractionDigits = 0;
+    int digits = 0;
+    
+    int len = pattern.length();
+    int i;
+    for (i = start; i < len; i++)
+      {
+        ch = pattern.charAt(i);
+        
+        // we hit the exponential or negative subpattern
+        if (ch == exponent || ch == patternSeparator)
+          break;
+        
+        // pattern error
+        if (ch == groupingSeparator || ch == decimalSeparator) throw new
+          IllegalArgumentException("unexpected character '" + ch + "' " +
+                                   "in fractional subpattern. Position: " + i);
+        
+        if (ch == digit)
+          {
+            digits++;
+          }
+        else if (ch == zero)
+          {
+            if (digits > 0) throw new
+            IllegalArgumentException("digit mark following zero in " +
+                      "positive subpattern, not allowed. Position: " + i);
+            
+            this.minimumFractionDigits++;
+          }
+        else
+          {
+            // we are in the suffix section of pattern
+            break;
+          }
+      }
+    
+    if (i == start) this.hasFractionalPattern = false;
+    
+    this.maximumFractionDigits = this.minimumFractionDigits + digits;
+    this.showDecimalSeparator = true;
+    
+    return i;
+  }*/
+  
+  /**
+   * Scan the given string for number patterns, starting
+   * from <code>start</code>.
+   * This method searches the expoential part of the pattern only.
+   * 
+   * @param pattern The pattern string to parse.
+   * @param start The starting parse position in the string.
+   * @return The position in the pattern string where parsing ended,
+   * counted from the beginning of the string (that is, 0).
+   */
+  /*private int scanExponent(String pattern, DecimalFormatSymbols symbols,
+                           int start)
+  {
+    char digit = symbols.getDigit();
+    char zero = symbols.getZeroDigit();
+    char groupingSeparator = symbols.getGroupingSeparator();
+    char decimalSeparator = symbols.getDecimalSeparator();
+    char exponent = symbols.getExponential();
+    
+    char ch = pattern.charAt(start);
+    
+    if (ch == decimalSeparator)
+      {
+        // ignore dots
+        ++start;
+      }
+    
+    if (ch != exponent)
+      {
+        this.useExponentialNotation = false;
+        return start;
+      }
+    
+    ++start;
+    
+    this.minExponentDigits = 0;
+    
+    int len = pattern.length();
+    int i;
+    for (i = start; i < len; i++)
+      {
+        ch = pattern.charAt(i);
+        
+        if (ch == groupingSeparator || ch == decimalSeparator ||
+            ch == digit || ch == exponent) throw new
+        IllegalArgumentException("unexpected character '" + ch + "' " + 
+                                 "in exponential subpattern. Position: " + i);
+        
+        if (ch == zero)
+          {
+            this.minExponentDigits++;
+          }
+        else
+          {
+            // any character other than zero is an exit point
+            break;
+          }
+      }
+    
+    this.useExponentialNotation = true; 
+    
+    return i;
+  }*/
+  
+  /**
+   * Scan the given string for number patterns, starting
+   * from <code>start</code>.
+   * This method searches the negative part of the pattern only and scan
+   * throught the end of the string.
+   * 
+   * @param pattern The pattern string to parse.
+   * @param start The starting parse position in the string.
+   */
+  /*private void scanNegativePattern(String pattern,
+                                   DecimalFormatSymbols sourceSymbols,
+                                   int start)
+  {
+    StringBuilder buffer = new StringBuilder();
+    
+    // the number portion is always delimited by one of those
+    // characters
+    char decimalSeparator = sourceSymbols.getDecimalSeparator();
+    char patternSeparator = sourceSymbols.getPatternSeparator();
+    char groupingSeparator = sourceSymbols.getGroupingSeparator();
+    char digit = sourceSymbols.getDigit();
+    char zero = sourceSymbols.getZeroDigit();
+    char minus = sourceSymbols.getMinusSign();
+    
+    // other special charcaters, cached here to avoid method calls later
+    char percent = sourceSymbols.getPercent();
+    char permille = sourceSymbols.getPerMill();
+    
+    String CURRENCY_SYMBOL = this.symbols.getCurrencySymbol();
+    String currencySymbol = CURRENCY_SYMBOL;
+    
+    boolean quote = false;
+    boolean prefixDone = false;
+    
+    int len = pattern.length();
+    if (len > 0) this.hasNegativePrefix = true;
+    
+    char ch = pattern.charAt(start);
+    if (ch == patternSeparator)
+      {
+        // no pattern separator in the negative pattern
+        if ((start + 1) > len) throw new
+          IllegalArgumentException("unexpected character '" + ch + "' " +
+                                   "in negative subpattern.");    
+        start++;
+      }
+    
+    int i;
+    for (i = start; i < len; i++)
+      {
+        ch = pattern.charAt(i);
+        
+        // this means we are inside the number portion
+        if (!quote &&
+            (ch == digit || ch == zero || ch == decimalSeparator ||
+             ch == patternSeparator || ch == groupingSeparator))
+          {
+            if (!prefixDone)
+              {
+                this.negativePrefix = buffer.toString();
+                buffer.delete(0, buffer.length());
+                prefixDone = true;
+              }
+          }
+        else if (ch == minus)
+          {
+            buffer.append(this.symbols.getMinusSign());
+          }
+        else if (quote && ch != '\'')
+          {
+            buffer.append(ch);
+          }
+        else if (ch == '\u00A4')
+          {
+            // CURRENCY
+            currencySymbol = CURRENCY_SYMBOL;
+
+            // if \u00A4 is doubled, we use the international currency symbol
+            if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4')
+              {
+                currencySymbol = this.symbols.getInternationalCurrencySymbol();
+                i = i + 2;
+              }
+
+            // FIXME: not sure about this, the specs says that we only have to
+            // change prefix and suffix, so leave it as commented
+            // unless in case of bug report/errors
+            //this.useCurrencySeparator = true;
+            
+            buffer.append(currencySymbol);
+          }
+        else if (ch == percent)
+          {
+            // PERCENT
+            this.negativePatternMultiplier = 100;
+            buffer.append(this.symbols.getPercent());
+          }
+        else if (ch == permille)
+          {
+            // PERMILLE
+            this.negativePatternMultiplier = 1000;
+            buffer.append(this.symbols.getPerMill());
+          }
+        else if (ch == '\'')
+          {
+            // QUOTE
+            if ((i + 1) < len && pattern.charAt(i + 1) == '\'')
+              {
+                // we need to add ' to the buffer 
+                buffer.append(ch);
+                i++;
+              }
+            else
+              {
+                quote = !quote;
+              }
+          }
+        else if (ch == patternSeparator)
+          {
+            // no pattern separator in the negative pattern
+            throw new IllegalArgumentException("unexpected character '" + ch +
+                                               "' in negative subpattern.");
+          }
+        else
+          {
+            buffer.append(ch);
+          }
+      }
+    
+    if (prefixDone)
+      this.negativeSuffix = buffer.toString();
+    else
+      this.negativePrefix = buffer.toString();
+  }*/
+  
+  /* ****** FORMATTING ****** */
+  
+  /**
+   * Handles the real formatting.
+   * 
+   * We use a BigDecimal to format the number without precision loss.
+   * All the rounding is done by methods in BigDecimal.
+   * The <code>isLong</code> parameter is used to determine if we are
+   * formatting a long or BigInteger. In this case, we avoid to format
+   * the fractional part of the number (unless specified otherwise in the
+   * format string) that would consist only of a 0 digit.
+   * 
+   * @param number A BigDecimal representation fo the input number.
+   * @param dest The destination buffer.
+   * @param isLong A boolean that indicates if this BigDecimal is a real
+   * decimal or an integer.
+   * @param fieldPos Use to keep track of the formatting position.
+   */
+  /*private void formatInternal(BigDecimal number, boolean isLong,
+                              StringBuffer dest, FieldPosition fieldPos)
+  {  
+    // The specs says that fieldPos should not be null, and that we
+    // should throw a NPE, but it seems that in few classes that
+    // reference this one, fieldPos is set to null.
+    // This is even defined in the javadoc, see for example MessageFormat.
+    // I think the best here is to check for fieldPos and build one if it is
+    // null. If it cause harms or regressions, just remove this line and
+    // fix the classes in the point of call, insted.
+    if (fieldPos == null) fieldPos = new FieldPosition(0);
+    
+    int _multiplier = this.multiplier;
+    
+    // used to track attribute starting position for each attribute
+    int attributeStart = -1;
+    
+    // now get the sign this will be used by the special case Inifinity
+    // and by the normal cases.
+    boolean isNegative = (number.signum() < 0) ? true : false;
+    if (isNegative)
+      {
+        attributeStart = dest.length();
+        
+        // append the negative prefix to the string
+        dest.append(negativePrefix);
+        
+        // once got the negative prefix, we can use
+        // the absolute value.
+        number = number.abs();
+        
+        _multiplier = negativePatternMultiplier;
+        
+        addAttribute(Field.SIGN, attributeStart, dest.length());
+      }
+    else
+      {
+        // not negative, use the positive prefix
+        dest.append(positivePrefix);
+      }
+    
+    // these are used ot update the field position
+    int beginIndexInt = dest.length();
+    int endIndexInt = 0;
+    int beginIndexFract = 0;
+    int endIndexFract = 0;
+    
+    // compute the multiplier to use with percent and similar
+    number = number.multiply(BigDecimal.valueOf(_multiplier));
+    
+    // XXX: special case, not sure if it belongs here or if it is
+    // correct at all. There may be other special cases as well
+    // these should be handled in the format string parser.
+    if (this.maximumIntegerDigits == 0 && this.maximumFractionDigits == 0)
+      {
+        number = BigDecimal.ZERO;
+        this.maximumIntegerDigits = 1;
+        this.minimumIntegerDigits = 1;
+      }
+    
+    //  get the absolute number
+    number = number.abs();
+
+    // the scaling to use while formatting this number
+    int scale = this.maximumFractionDigits;
+    
+    // this is the actual number we will use
+    // it is corrected later on to handle exponential
+    // notation, if needed
+    long exponent = 0;
+    
+    // are we using exponential notation?
+    if (this.useExponentialNotation)
+      {
+        exponent = getExponent(number);
+        number = number.movePointLeft((int) exponent);
+        
+        // FIXME: this makes the test ##.###E0 to pass,
+        // but all all the other tests to fail...
+        // this should be really something like
+        // min + max - what is already shown...
+        //scale = this.minimumIntegerDigits + this.maximumFractionDigits;
+      }
+    
+    // round the number to the nearest neighbor
+    number = number.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
+
+    // now get the integer and fractional part of the string
+    // that will be processed later
+    String plain = number.toPlainString();
+    
+    String intPart = null;
+    String fractPart = null;
+    
+    // remove - from the integer part, this is needed as
+    // the Narrowing Primitive Conversions algorithm used may loose
+    // information about the sign
+    int minusIndex = plain.lastIndexOf('-', 0);
+    if (minusIndex > -1) plain = plain.substring(minusIndex + 1);
+    
+    // strip the decimal portion
+    int dot = plain.indexOf('.');
+    if (dot > -1)
+      {
+        intPart = plain.substring(0, dot);
+        dot++;
+        
+        if (useExponentialNotation)
+          fractPart = plain.substring(dot, dot + scale);
+        else
+          fractPart = plain.substring(dot);
+      }
+    else
+      {
+        intPart = plain;
+      }
+    
+    // used in various places later on
+    int intPartLen = intPart.length();
+    endIndexInt = intPartLen;
+    
+    // if the number of digits in our intPart is not greater than the
+    // minimum we have to display, we append zero to the destination
+    // buffer before adding the integer portion of the number.
+    int zeroes = minimumIntegerDigits - intPartLen;
+    if (zeroes > 0)
+      {
+        attributeStart = Math.max(dest.length() - 1, 0);
+        appendZero(dest, zeroes, minimumIntegerDigits);
+      }
+
+    if (this.useExponentialNotation)
+      {
+        // For exponential numbers, the significant in mantissa are
+        // the sum of the minimum integer and maximum fraction
+        // digits, and does not take into account the maximun integer
+        // digits to display.
+        
+        if (attributeStart < 0)
+          attributeStart = Math.max(dest.length() - 1, 0);
+        appendDigit(intPart, dest, this.groupingUsed);
+      }
+    else
+      {
+        // non exponential notation
+        intPartLen = intPart.length();
+        int canary = Math.min(intPartLen, this.maximumIntegerDigits);
+        
+        // remove from the string the number in excess
+        // use only latest digits
+        intPart = intPart.substring(intPartLen - canary);
+        endIndexInt = intPart.length() + 1;
+        
+        // append it
+        if (maximumIntegerDigits > 0 &&
+            !(this.minimumIntegerDigits == 0 &&
+             intPart.compareTo(String.valueOf(symbols.getZeroDigit())) == 0))
+          {
+            if (attributeStart < 0)
+              attributeStart = Math.max(dest.length() - 1, 0);
+            appendDigit(intPart, dest, this.groupingUsed);
+          }
+      }
+    
+    // add the INTEGER attribute
+    addAttribute(Field.INTEGER, attributeStart, dest.length());
+    
+    // ...update field position, if needed, and return...
+    if ((fieldPos.getField() == INTEGER_FIELD ||
+        fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER))
+      {
+        fieldPos.setBeginIndex(beginIndexInt);
+        fieldPos.setEndIndex(endIndexInt);
+      }
+    
+    handleFractionalPart(dest, fractPart, fieldPos, isLong);
+        
+    // and the exponent
+    if (this.useExponentialNotation)
+      {
+        attributeStart = dest.length();
+        
+        dest.append(symbols.getExponential());
+        
+        addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length());
+        attributeStart = dest.length();
+        
+        if (exponent < 0)
+          {
+            dest.append(symbols.getMinusSign());
+            exponent = -exponent;
+            
+            addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length());
+          }
+        
+        attributeStart = dest.length();
+        
+        String exponentString = String.valueOf(exponent);
+        int exponentLength = exponentString.length();
+        
+        for (int i = 0; i < minExponentDigits - exponentLength; i++)
+          dest.append(symbols.getZeroDigit());
+        
+        for (int i = 0; i < exponentLength; ++i)
+          dest.append(exponentString.charAt(i));
+        
+        addAttribute(Field.EXPONENT, attributeStart, dest.length());
+      }
+    // now include the suffixes...
+    if (isNegative)
+      {
+        dest.append(negativeSuffix);
+      }
+    else
+      {
+        dest.append(positiveSuffix);
+      }
+  }*/
+
+  /**
+   * Add to the input buffer the result of formatting the fractional
+   * portion of the number.
+   * 
+   * @param dest
+   * @param fractPart
+   * @param fieldPos
+   * @param isLong
+   */
+  /*private void handleFractionalPart(StringBuffer dest, String fractPart,
+                                    FieldPosition fieldPos, boolean isLong)
+  {
+    int dotStart = 0;
+    int dotEnd = 0;
+    boolean addDecimal = false;
+    
+    if (this.decimalSeparatorAlwaysShown  ||
+         ((!isLong || this.useExponentialNotation) &&
+           this.showDecimalSeparator && this.maximumFractionDigits > 0) ||
+        this.minimumFractionDigits > 0)
+      {
+        dotStart = dest.length();
+        
+        if (this.useCurrencySeparator)
+          dest.append(symbols.getMonetaryDecimalSeparator());
+        else
+          dest.append(symbols.getDecimalSeparator());
+        
+        dotEnd = dest.length();
+        addDecimal = true;
+      }
+    
+    // now handle the fraction portion of the number
+    int fractStart = 0;
+    int fractEnd = 0;
+    boolean addFractional = false;
+    
+    if ((!isLong || this.useExponentialNotation)
+        && this.maximumFractionDigits > 0
+        || this.minimumFractionDigits > 0)
+      {
+        fractStart = dest.length();
+        fractEnd = fractStart;
+        
+        int digits = this.minimumFractionDigits;
+        
+        if (this.useExponentialNotation)
+          {
+            digits = (this.minimumIntegerDigits + this.minimumFractionDigits)
+              - dest.length();
+            if (digits < 0) digits = 0;
+          }
+        
+        fractPart = adjustTrailingZeros(fractPart, digits);
+        
+        // FIXME: this code must be improved
+        // now check if the factional part is just 0, in this case
+        // we need to remove the '.' unless requested
+        boolean allZeros = true;
+        char fracts[] = fractPart.toCharArray();
+        for (int i = 0; i < fracts.length; i++)
+          {
+            if (fracts[i] != '0')
+              allZeros = false;
+          }
+        
+        if (!allZeros || (minimumFractionDigits > 0))
+          {
+            appendDigit(fractPart, dest, false);
+            fractEnd = dest.length();
+            
+            addDecimal = true;
+            addFractional = true;
+          }
+        else if (!this.decimalSeparatorAlwaysShown)
+          {
+            dest.deleteCharAt(dest.length() - 1);
+            addDecimal = false;
+          }
+        else
+          {
+            fractEnd = dest.length();
+            addFractional = true;
+          }
+      }
+    
+    if (addDecimal)
+      addAttribute(Field.DECIMAL_SEPARATOR, dotStart, dotEnd);
+    
+    if (addFractional)
+      addAttribute(Field.FRACTION, fractStart, fractEnd);
+    
+    if ((fieldPos.getField() == FRACTION_FIELD ||
+        fieldPos.getFieldAttribute() == NumberFormat.Field.FRACTION))
+      {
+        fieldPos.setBeginIndex(fractStart);
+        fieldPos.setEndIndex(fractEnd);
+      }
+  }*/
+  
+  /**
+   * Append to <code>dest</code>the give number of zeros.
+   * Grouping is added if needed.
+   * The integer totalDigitCount defines the total number of digits
+   * of the number to which we are appending zeroes.
+   */
+  private void appendZero(StringBuffer dest, int zeroes, int totalDigitCount)
+  {
+    char ch = symbols.getZeroDigit();
+    char gSeparator = symbols.getGroupingSeparator();
+    
+    int i = 0;
+    int gPos = totalDigitCount;
+    for (i = 0; i < zeroes; i++, gPos--)
+      {
+        if (this.groupingSeparatorInPattern &&
+            (this.groupingUsed && this.groupingSize != 0) &&
+            (gPos % groupingSize == 0 && i > 0))
+          dest.append(gSeparator);
+        
+        dest.append(ch);
+      }
+    
+    // special case, that requires adding an additional separator
+    if (this.groupingSeparatorInPattern &&
+        (this.groupingUsed && this.groupingSize != 0) &&
+        (gPos % groupingSize == 0))
+      dest.append(gSeparator);
+  }
+  
+  /**
+   * Append src to <code>dest</code>.
+   * 
+   * Grouping is added if <code>groupingUsed</code> is set
+   * to <code>true</code>.
+   */
+  private void appendDigit(String src, StringBuffer dest,
+                             boolean groupingUsed)
+  {
+    int zero = symbols.getZeroDigit() - '0';
+    
+    int ch;
+    char gSeparator = symbols.getGroupingSeparator();
+        
+    int len = src.length();
+    for (int i = 0, gPos = len; i < len; i++, gPos--)
+      {
+        ch = src.charAt(i);
+        if (groupingUsed && this.groupingSize != 0 &&
+            gPos % groupingSize == 0 && i > 0)
+          dest.append(gSeparator);
+
+        dest.append((char) (zero + ch));
+      }
+  }
+  
+  /**
+   * Calculate the exponent to use if eponential notation is used.
+   * The exponent is calculated as a power of ten.
+   * <code>number</code> should be positive, if is zero, or less than zero,
+   * zero is returned.
+   */
+  private long getExponent(BigDecimal number)
+  {
+    long exponent = 0;
+    
+    if (number.signum() > 0)
+      {
+        double _number = number.doubleValue();
+        exponent = (long) Math.floor (Math.log10(_number));
+        
+        // get the right value for the exponent
+        exponent = exponent - (exponent % this.exponentRound);
+        
+        // if the minimumIntegerDigits is more than zero
+        // we display minimumIntegerDigits of digits.
+        // so, for example, if minimumIntegerDigits == 2
+        // and the actual number is 0.123 it will be
+        // formatted as 12.3E-2
+        // this means that the exponent have to be shifted
+        // to the correct value.
+        if (minimumIntegerDigits > 0)
+              exponent -= minimumIntegerDigits - 1;
+      }
+    
+    return exponent;
+  }
+  /**
+   * Remove contiguos zeros from the end of the <code>src</code> string,
+   * if src contains more than <code>minimumDigits</code> digits.
+   * if src contains less that <code>minimumDigits</code>,
+   * then append zeros to the string.
+   * 
+   * Only the first block of zero digits is removed from the string
+   * and only if they fall in the src.length - minimumDigits
+   * portion of the string.
+   * 
+   * @param src The string with the correct number of zeros.
+   */
+  /*private String adjustTrailingZeros(String src, int minimumDigits)
+  {
+    int len = src.length();
+    String result;
+    
+    // remove all trailing zero
+    if (len > minimumDigits)
+      {
+        int zeros = 0;    
+        for (int i = len - 1; i > minimumDigits; i--)
+          {
+            if (src.charAt(i) == '0')
+              ++zeros;
+            else
+              break;
+          }
+        result =  src.substring(0, len - zeros);                
+      }
+    else
+      {
+        char zero = symbols.getZeroDigit();
+        CPStringBuilder _result = new CPStringBuilder(src);
+        for (int i = len; i < minimumDigits; i++)
+          {
+            _result.append(zero);
+          }
+        result = _result.toString();
+      }
+    
+    return result;
+  }*/
+  
+  /**
+   * Adds an attribute to the attributes list.
+   * 
+   * @param field
+   * @param begin
+   * @param end
+   */
+  /*private void addAttribute(Field field, int begin, int end)
+  {
+    /*
+     * This method and its implementation derives directly from the
+     * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X.
+     */
+    
+    /*FieldPosition pos = new FieldPosition(field);
+    pos.setBeginIndex(begin);
+    pos.setEndIndex(end);
+    attributes.add(pos);
+  }*/
+  
+  /**
+   * Sets the default values for the various properties in this DecimaFormat.
+   */
+  private void setDefaultValues()
+  {
+    // Maybe we should add these values to the message bundle and take
+    // the most appropriate for them for any locale.
+    // Anyway, these seem to be good values for a default in most languages.
+    // Note that most of these will change based on the format string.
+    
+    this.negativePrefix = String.valueOf(symbols.getMinusSign());
+    this.negativeSuffix = "";
+    this.positivePrefix = "";
+    this.positiveSuffix = "";
+    
+    this.multiplier = 1;
+    this.negativePatternMultiplier = 1;
+    this.exponentRound = 1;
+    
+    this.hasNegativePrefix = false;
+    
+    this.minimumIntegerDigits = 1;
+    this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS;
+    this.minimumFractionDigits = 0;
+    this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS;
+    this.minExponentDigits = 0;
+    
+    this.groupingSize = 0;
+    
+    this.decimalSeparatorAlwaysShown = false;
+    this.showDecimalSeparator = false;
+    this.useExponentialNotation = false;
+    this.groupingUsed = false;
+    this.groupingSeparatorInPattern = false;
+    
+    this.useCurrencySeparator = false;
+    
+    this.hasFractionalPattern = false;
+  }
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/Exception.java b/Robust/src/ClassLibrary/MGC/gnu/Exception.java
new file mode 100644 (file)
index 0000000..42f7c64
--- /dev/null
@@ -0,0 +1,104 @@
+/* Exception.java -- generic exception thrown to indicate an exceptional
+   condition has occurred.
+   Copyright (C) 1998, 1999, 2001, 2002, 2005  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.lang;
+
+/**
+ * The root class of all exceptions worth catching in a program.  This
+ * includes the special category of <code>RuntimeException</code>, which
+ * does not need to be declared in a throws clause.  Exceptions can be used
+ * to represent almost any exceptional behavior, such as programming errors,
+ * mouse movements, keyboard clicking, etc.
+ *
+ * @author Brian Jones
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @status updated to 1.4
+ */
+public class Exception extends Throwable
+{
+  /**
+   * Compatible with JDK 1.0+.
+   */
+  private static final long serialVersionUID = -3387516993124229948L;
+
+  /**
+   * Create an exception without a message. The cause remains uninitialized.
+   *
+   * @see #initCause(Throwable)
+   */
+  public Exception()
+  {
+  }
+
+  /**
+   * Create an exception with a message. The cause remains uninitialized.
+   *
+   * @param s the message
+   * @see #initCause(Throwable)
+   */
+  public Exception(String s)
+  {
+    super(s);
+  }
+
+  /**
+   * Create an exception with a message and a cause.
+   *
+   * @param s the message string
+   * @param cause the cause of this error
+   * @since 1.4
+   */
+  public Exception(String s, Throwable cause)
+  {
+    super(s, cause);
+  }
+
+  /**
+   * Create an exception with a given cause, and a message of
+   * <code>cause == null ? null : cause.toString()</code>.
+   *
+   * @param cause the cause of this exception
+   * @since 1.4
+   */
+  public Exception(Throwable cause)
+  {
+    super(cause);
+  }
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/FileDescriptor.java b/Robust/src/ClassLibrary/MGC/gnu/FileDescriptor.java
new file mode 100644 (file)
index 0000000..7b9c146
--- /dev/null
@@ -0,0 +1,146 @@
+/* FileDescriptor.java -- Opaque file handle class
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004
+   Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.io;
+
+/*import gnu.java.nio.FileChannelImpl;
+
+import java.nio.channels.ByteChannel;
+import java.nio.channels.FileChannel;
+*/
+/**
+ * This class represents an opaque file handle as a Java class.  It should
+ * be used only to pass to other methods that expect an object of this
+ * type.  No system specific information can be obtained from this object.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @date September 24, 1998 
+ */
+public final class FileDescriptor
+{
+  /**
+   * A <code>FileDescriptor</code> representing the system standard input
+   * stream.  This will usually be accessed through the
+   * <code>System.in</code>variable.
+   */
+  public static final FileDescriptor in
+  = new FileDescriptor ("System.in"/*FileChannelImpl.in*/);
+
+  /**
+   * A <code>FileDescriptor</code> representing the system standard output
+   * stream.  This will usually be accessed through the
+   * <code>System.out</code>variable.
+   */
+  public static final FileDescriptor out
+  = new FileDescriptor ("System.out"/*FileChannelImpl.out*/);
+
+  /**
+   * A <code>FileDescriptor</code> representing the system standard error
+   * stream.  This will usually be accessed through the
+   * <code>System.err</code>variable.
+   */
+  public static final FileDescriptor err
+  = new FileDescriptor ("System.err"/*FileChannelImpl.err*/);
+
+  //final ByteChannel channel;
+  final String channel;
+
+  /**
+   * This method is used to initialize an invalid FileDescriptor object.
+   */
+  public FileDescriptor()
+  {
+    channel = null;
+  }
+
+  /**
+   * This method is used to initialize a FileDescriptor object.
+   */
+  /*FileDescriptor(ByteChannel channel)
+  {
+    this.channel = channel;
+  }*/
+  
+  FileDescriptor(String channel)
+  {
+    this.channel = channel;
+  }
+
+
+  /**
+   * This method forces all data that has not yet been physically written to
+   * the underlying storage medium associated with this 
+   * <code>FileDescriptor</code>
+   * to be written out.  This method will not return until all data has
+   * been fully written to the underlying device.  If the device does not
+   * support this functionality or if an error occurs, then an exception
+   * will be thrown.
+   */
+  /*public void sync () throws SyncFailedException
+  {
+    if (channel instanceof FileChannel)
+      {
+       try
+         {
+           ((FileChannel) channel).force(true); 
+         }
+       catch (IOException ex)
+         {
+           if (ex instanceof SyncFailedException)
+             throw (SyncFailedException) ex;
+           else
+             throw new SyncFailedException(ex.toString());
+         }
+      }
+  }*/
+
+  /**
+   * This methods tests whether or not this object represents a valid open
+   * native file handle.
+   *
+   * @return <code>true</code> if this object represents a valid 
+   * native file handle, <code>false</code> otherwise
+   */
+  /*public boolean valid ()
+  { 
+    ByteChannel c = channel;
+    return (c != null) && (c.isOpen());
+  }*/
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/FilterOutputStream.java b/Robust/src/ClassLibrary/MGC/gnu/FilterOutputStream.java
new file mode 100644 (file)
index 0000000..dd5ad6e
--- /dev/null
@@ -0,0 +1,150 @@
+/* FilterOutputStream.java -- Parent class for output streams that filter
+   Copyright (C) 1998, 1999, 2001, 2003, 2005  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.io;
+
+/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
+ * "The Java Language Specification", ISBN 0-201-63451-1
+ * Status:  Complete to version 1.1.
+ */
+
+/**
+  * This class is the common superclass of output stream classes that 
+  * filter the output they write.  These classes typically transform the
+  * data in some way prior to writing it out to another underlying
+  * <code>OutputStream</code>.  This class simply overrides all the 
+  * methods in <code>OutputStream</code> to redirect them to the
+  * underlying stream.  Subclasses provide actual filtering.
+  *
+  * @author Aaron M. Renn (arenn@urbanophile.com)
+  * @author Tom Tromey (tromey@cygnus.com)
+  */
+public class FilterOutputStream extends OutputStream
+{
+  /**
+    * This is the subordinate <code>OutputStream</code> that this class
+    * redirects its method calls to.
+    */
+  protected OutputStream out;
+
+  /**
+    * This method initializes an instance of <code>FilterOutputStream</code>
+    * to write to the specified subordinate <code>OutputStream</code>.
+    *
+    * @param out The <code>OutputStream</code> to write to
+    */
+  public FilterOutputStream(OutputStream out)
+  {
+    this.out = out;
+  }
+
+  /**
+    * This method closes the underlying <code>OutputStream</code>.  Any
+    * further attempts to write to this stream may throw an exception.
+    *
+    * @exception IOException If an error occurs
+    */
+  public void close() //throws IOException
+  {
+    flush();
+    out.close();
+  }
+
+  /**
+    * This method attempt to flush all buffered output to be written to the
+    * underlying output sink.
+    *
+    * @exception IOException If an error occurs
+    */
+  public void flush() //throws IOException
+  {
+    out.flush();
+  }
+
+  /**
+    * This method writes a single byte of output to the underlying
+    * <code>OutputStream</code>.
+    *
+    * @param b The byte to write, passed as an int.
+    *
+    * @exception IOException If an error occurs
+    */
+  public void write(int b) //throws IOException
+  {
+    out.write(b);
+  }
+
+  /**
+    * This method writes all the bytes in the specified array to the underlying
+    * <code>OutputStream</code>.  It does this by calling the three parameter
+    * version of this method - <code>write(byte[], int, int)</code> in this
+    * class instead of writing to the underlying <code>OutputStream</code>
+    * directly.  This allows most subclasses to avoid overriding this method.
+    *
+    * @param buf The byte array to write bytes from
+    *
+    * @exception IOException If an error occurs
+    */
+  public void write(byte[] buf) //throws IOException
+  {
+    // Don't do checking here, per Java Lang Spec.
+    write(buf, 0, buf.length);
+  }
+
+  /**
+    * This method calls the <code>write(int)</code> method <code>len</code>
+    * times for all bytes from the array <code>buf</code> starting at index
+    * <code>offset</code>. Subclasses should overwrite this method to get a
+    * more efficient implementation.
+    *
+    * @param buf The byte array to write bytes from
+    * @param offset The index into the array to start writing bytes from
+    * @param len The number of bytes to write
+    *
+    * @exception IOException If an error occurs
+    */
+  public void write(byte[] buf, int offset, int len) //throws IOException
+  {
+    // Don't do checking here, per Java Lang Spec.
+    for (int i=0; i < len; i++) 
+      write(buf[offset + i]);
+
+  }
+
+} // class FilterOutputStream
+
diff --git a/Robust/src/ClassLibrary/MGC/gnu/GregorianCalendar.java b/Robust/src/ClassLibrary/MGC/gnu/GregorianCalendar.java
new file mode 100644 (file)
index 0000000..ebad058
--- /dev/null
@@ -0,0 +1,1365 @@
+/* java.util.GregorianCalendar
+   Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2007
+   Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util;
+
+
+/**
+ * <p>
+ * This class represents the Gregorian calendar, that is used in most
+ * countries all over the world.  It does also handle the Julian calendar
+ * for dates smaller than the date of the change to the Gregorian calendar.
+ * The Gregorian calendar differs from the Julian calendar by a different
+ * leap year rule (no leap year every 100 years, except if year is divisible
+ * by 400).
+ * </p>
+ * <p>
+ * This change date is different from country to country, and can be changed with
+ * <code>setGregorianChange</code>.  The first countries to adopt the Gregorian
+ * calendar did so on the 15th of October, 1582.  This date followed October
+ * the 4th, 1582 in the Julian calendar system.  The non-existant days that were
+ * omitted when the change took place are interpreted as Gregorian dates.
+ * </p>
+ * <p>
+ * Prior to the changeover date, New Year's Day occurred on the 25th of March.
+ * However, this class always takes New Year's Day as being the 1st of January.
+ * Client code should manually adapt the year value, if required, for dates
+ * between January the 1st and March the 24th in years prior to the changeover.
+ * </p>
+ * <p>
+ * Any date infinitely forwards or backwards in time can be represented by
+ * this class.  A <em>proleptic</em> calendar system is used, which allows
+ * future dates to be created via the existing rules.  This allows meaningful
+ * and consistent dates to be produced for all years.  However, dates are only
+ * historically accurate following March the 1st, 4AD when the Julian calendar
+ * system was adopted.  Prior to this, leap year rules were applied erraticly.
+ * </p>
+ * <p>
+ * There are two eras available for the Gregorian calendar, namely BC and AD.
+ * </p>
+ * <p>
+ * Weeks are defined as a period of seven days, beginning on the first day
+ * of the week, as returned by <code>getFirstDayOfWeek()</code>, and ending
+ * on the day prior to this.
+ * </p>
+ * <p>
+ * The weeks of the year are numbered from 1 to a possible 53.  The first week
+ * of the year is defined as the first week that contains at least the minimum
+ * number of days of the first week in the new year (retrieved via
+ * <code>getMinimalDaysInFirstWeek()</code>).  All weeks after this are numbered
+ * from 2 onwards.
+ * </p>
+ * <p>
+ * For example, take the year 2004.  It began on a Thursday.  The first week
+ * of 2004 depends both on where a week begins and how long it must minimally
+ * last.  Let's say that the week begins on a Monday and must have a minimum
+ * of 5 days.  In this case, the first week begins on Monday, the 5th of January.
+ * The first 4 days (Thursday to Sunday) are not eligible, as they are too few
+ * to make up the minimum number of days of the first week which must be in
+ * the new year.  If the minimum was lowered to 4 days, then the first week
+ * would instead begin on Monday, the 29th of December, 2003.  This first week
+ * has 4 of its days in the new year, and is now eligible.
+ * </p>
+ * <p>
+ * The weeks of the month are numbered from 0 to a possible 6.  The first week
+ * of the month (numbered 1) is a set of days, prior to the first day of the week,
+ * which number at least the minimum number of days in a week.  Unlike the first
+ * week of the year, the first week of the month only uses days from that particular
+ * month.  As a consequence, it may have a variable number of days (from the minimum
+ * number required up to a full week of 7) and it need not start on the first day of
+ * the week.  It must, however, be following by the first day of the week, as this
+ * marks the beginning of week 2.  Any days of the month which occur prior to the
+ * first week (because the first day of the week occurs before the minimum number
+ * of days is met) are seen as week 0.
+ * </p>
+ * <p>
+ * Again, we will take the example of the year 2004 to demonstrate this.  September
+ * 2004 begins on a Wednesday.  Taking our first day of the week as Monday, and the
+ * minimum length of the first week as 6, we find that week 1 runs from Monday,
+ * the 6th of September to Sunday the 12th.  Prior to the 6th, there are only
+ * 5 days (Wednesday through to Sunday).  This is too small a number to meet the
+ * minimum, so these are classed as being days in week 0.  Week 2 begins on the
+ * 13th, and so on.  This changes if we reduce the minimum to 5.  In this case,
+ * week 1 is a truncated week from Wednesday the 1st to Sunday the 5th, and week
+ * 0 doesn't exist.  The first seven day week is week 2, starting on the 6th.
+ * </p>
+ * <p>
+ * On using the <code>clear()</code> method, the Gregorian calendar returns
+ * to its default value of the 1st of January, 1970 AD 00:00:00 (the epoch).
+ * The day of the week is set to the correct day for that particular time.
+ * The day is also the first of the month, and the date is in week 0.
+ * </p>
+ *
+ * @see Calendar
+ * @see TimeZone
+ * @see Calendar#getFirstDayOfWeek()
+ * @see Calendar#getMinimalDaysInFirstWeek()
+ */
+public class GregorianCalendar extends Calendar
+{
+  /**
+   * Constant representing the era BC (Before Christ).
+   */
+  public static final int BC = 0;
+
+  /**
+   * Constant representing the era AD (Anno Domini).
+   */
+  public static final int AD = 1;
+
+  /**
+   * The point at which the Gregorian calendar rules were used.
+   * This may be changed by using setGregorianChange;
+   * The default is midnight (UTC) on October 5, 1582 (Julian),
+   * or October 15, 1582 (Gregorian).
+   *
+   * @serial the changeover point from the Julian calendar
+   *         system to the Gregorian.
+   */
+  private long gregorianCutover = (new Date((24 * 60 * 60 * 1000L) * (((1582 * (365 * 4
+                                            + 1)) / 4
+                                            + (java.util.Calendar.OCTOBER * (31
+                                            + 30 + 31 + 30 + 31) - 9) / 5 + 5)
+                                            - ((1970 * (365 * 4 + 1)) / 4 + 1
+                                            - 13)))).getTime();
+
+  /**
+   * For compatability with Sun's JDK.
+   */
+  static final long serialVersionUID = -8125100834729963327L;
+
+  /**
+   * Days in the epoch. Relative Jan 1, year '0' which is not a leap year.
+   * (although there is no year zero, this does not matter.)
+   * This is consistent with the formula:
+   * = (year-1)*365L + ((year-1) >> 2)
+   *
+   * Plus the gregorian correction:
+   *  Math.floor((year-1) / 400.) - Math.floor((year-1) / 100.);
+   * For a correct julian date, the correction is -2 instead.
+   *
+   * The gregorian cutover in 1582 was 10 days, so by calculating the
+   * correction from year zero, we have 15 non-leap days (even centuries)
+   * minus 3 leap days (year 400,800,1200) = 12. Subtracting two corrects
+   * this to the correct number 10.
+   */
+  private static final int EPOCH_DAYS = 719162;
+
+  /**
+   * Constructs a new GregorianCalender representing the current
+   * time, using the default time zone and the default locale.
+   */
+  public GregorianCalendar()
+  {
+    this(/*TimeZone.getDefault(), */Locale.getDefault());
+  }
+
+  /**
+   * Constructs a new GregorianCalender representing the current
+   * time, using the specified time zone and the default locale.
+   *
+   * @param zone a time zone.
+   */
+  /*public GregorianCalendar(TimeZone zone)
+  {
+    this(zone, Locale.getDefault());
+  }*/
+
+  /**
+   * Constructs a new GregorianCalender representing the current
+   * time, using the default time zone and the specified locale.
+   *
+   * @param locale a locale.
+   */
+  public GregorianCalendar(Locale locale)
+  {
+    this(/*TimeZone.getDefault(), */locale);
+  }
+
+  /**
+   * Constructs a new GregorianCalender representing the current
+   * time with the given time zone and the given locale.
+   *
+   * @param zone a time zone.
+   * @param locale a locale.
+   */
+  /*public GregorianCalendar(TimeZone zone, Locale locale)
+  {
+    this(zone, locale, false);
+    setTimeInMillis(System.currentTimeMillis());
+  }*/
+
+  /**
+   * Common constructor that all constructors should call.
+   * @param zone a time zone.
+   * @param locale a locale.
+   * @param unused unused parameter to make the signature differ from
+   * the public constructor (TimeZone, Locale).
+   */
+  private GregorianCalendar(/*TimeZone zone, */Locale locale, boolean unused)
+  {
+    super(/*zone, */locale);
+  }
+
+  /**
+   * Constructs a new GregorianCalendar representing midnight on the
+   * given date with the default time zone and locale.
+   *
+   * @param year corresponds to the YEAR time field.
+   * @param month corresponds to the MONTH time field.
+   * @param day corresponds to the DAY time field.
+   */
+  public GregorianCalendar(int year, int month, int day)
+  {
+    this(/*TimeZone.getDefault(), */Locale.getDefault(), false);
+    set(year, month, day);
+  }
+
+  /**
+   * Constructs a new GregorianCalendar representing midnight on the
+   * given date with the default time zone and locale.
+   *
+   * @param year corresponds to the YEAR time field.
+   * @param month corresponds to the MONTH time field.
+   * @param day corresponds to the DAY time field.
+   * @param hour corresponds to the HOUR_OF_DAY time field.
+   * @param minute corresponds to the MINUTE time field.
+   */
+  public GregorianCalendar(int year, int month, int day, int hour, int minute)
+  {
+    this(/*TimeZone.getDefault(), */Locale.getDefault(), false);
+    set(year, month, day, hour, minute);
+  }
+
+  /**
+   * Constructs a new GregorianCalendar representing midnight on the
+   * given date with the default time zone and locale.
+   *
+   * @param year corresponds to the YEAR time field.
+   * @param month corresponds to the MONTH time field.
+   * @param day corresponds to the DAY time field.
+   * @param hour corresponds to the HOUR_OF_DAY time field.
+   * @param minute corresponds to the MINUTE time field.
+   * @param second corresponds to the SECOND time field.
+   */
+  public GregorianCalendar(int year, int month, int day, int hour, int minute,
+                           int second)
+  {
+    this(/*TimeZone.getDefault(), */Locale.getDefault(), false);
+    set(year, month, day, hour, minute, second);
+  }
+
+  /**
+   * Sets the date of the switch from Julian dates to Gregorian dates.
+   * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure
+   * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian
+   * calendar.
+   *
+   * @param date the date of the change.
+   */
+  public void setGregorianChange(Date date)
+  {
+    gregorianCutover = date.getTime();
+  }
+
+  /**
+   * Gets the date of the switch from Julian dates to Gregorian dates.
+   *
+   * @return the date of the change.
+   */
+  public final Date getGregorianChange()
+  {
+    return new Date(gregorianCutover);
+  }
+
+  /**
+   * <p>
+   * Determines if the given year is a leap year.  The result is
+   * undefined if the Gregorian change took place in 1800, so that
+   * the end of February is skipped, and that year is specified.
+   * (well...).
+   * </p>
+   * <p>
+   * To specify a year in the BC era, use a negative value calculated
+   * as 1 - y, where y is the required year in BC.  So, 1 BC is 0,
+   * 2 BC is -1, 3 BC is -2, etc.
+   * </p>
+   *
+   * @param year a year (use a negative value for BC).
+   * @return true, if the given year is a leap year, false otherwise.
+   */
+  public boolean isLeapYear(int year)
+  {
+    // Only years divisible by 4 can be leap years
+    if ((year & 3) != 0)
+      return false;
+
+    // Is the leap-day a Julian date? Then it's a leap year
+    if (! isGregorian(year, 31 + 29 - 1))
+      return true;
+
+    // Apply gregorian rules otherwise
+    return ((year % 100) != 0 || (year % 400) == 0);
+  }
+
+  /**
+   * Retrieves the day of the week corresponding to the specified
+   * day of the specified year.
+   *
+   * @param year the year in which the dayOfYear occurs.
+   * @param dayOfYear the day of the year (an integer between 0 and
+   *        and 366)
+   */
+  private int getWeekDay(int year, int dayOfYear)
+  {
+    boolean greg = isGregorian(year, dayOfYear);
+    int day = (int) getLinearDay(year, dayOfYear, greg);
+
+    // The epoch was a thursday.
+    int weekday = (day + THURSDAY) % 7;
+    if (weekday <= 0)
+      weekday += 7;
+    return weekday;
+  }
+
+  /**
+   * Returns the day of the week for the first day of a given month (0..11)
+   */
+  private int getFirstDayOfMonth(int year, int month)
+  {
+    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+    if (month > 11)
+      {
+       year += (month / 12);
+       month = month % 12;
+      }
+
+    if (month < 0)
+      {
+       year += (int) month / 12;
+       month = month % 12;
+       if (month < 0)
+         {
+           month += 12;
+           year--;
+         }
+      }
+
+    int dayOfYear = dayCount[month] + 1;
+    if (month > 1)
+      if (isLeapYear(year))
+       dayOfYear++;
+
+    boolean greg = isGregorian(year, dayOfYear);
+    int day = (int) getLinearDay(year, dayOfYear, greg);
+
+    // The epoch was a thursday.
+    int weekday = (day + THURSDAY) % 7;
+    if (weekday <= 0)
+      weekday += 7;
+    return weekday;
+  }
+
+  /**
+   * Takes a year, and a (zero based) day of year and determines
+   * if it is gregorian or not.
+   */
+  private boolean isGregorian(int year, int dayOfYear)
+  {
+    int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
+                      - EPOCH_DAYS; // gregorian days from 1 to epoch.
+    int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
+                     - (int) Math.floor((double) (year - 1) / 100.);
+
+    return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover);
+  }
+
+  /**
+   * Check set fields for validity, without leniency.
+   *
+   * @throws IllegalArgumentException if a field is invalid
+   */
+  private void nonLeniencyCheck() throws IllegalArgumentException
+  {
+    int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+    int year = fields[YEAR];
+    int month = fields[MONTH];
+    int leap = isLeapYear(year) ? 1 : 0;
+
+    if (isSet[ERA] && fields[ERA] != AD && fields[ERA] != BC)
+      throw new IllegalArgumentException("Illegal ERA.");
+    if (isSet[YEAR] && fields[YEAR] < 1)
+      throw new IllegalArgumentException("Illegal YEAR.");
+    if (isSet[MONTH] && (month < 0 || month > 11))
+      throw new IllegalArgumentException("Illegal MONTH.");
+    if (isSet[WEEK_OF_YEAR])
+      {
+       int daysInYear = 365 + leap;
+       daysInYear += (getFirstDayOfMonth(year, 0) - 1); // pad first week
+       int last = getFirstDayOfMonth(year, 11) + 4;
+       if (last > 7)
+         last -= 7;
+       daysInYear += 7 - last;
+       int weeks = daysInYear / 7;
+       if (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > weeks)
+         throw new IllegalArgumentException("Illegal WEEK_OF_YEAR.");
+      }
+
+    if (isSet[WEEK_OF_MONTH])
+      {
+       int weeks = (month == 1 && leap == 0) ? 5 : 6;
+       if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks)
+         throw new IllegalArgumentException("Illegal WEEK_OF_MONTH.");
+      }
+
+    if (isSet[DAY_OF_MONTH])
+      if (fields[DAY_OF_MONTH] < 1
+          || fields[DAY_OF_MONTH] > month_days[month]
+          + ((month == 1) ? leap : 0))
+       throw new IllegalArgumentException("Illegal DAY_OF_MONTH.");
+
+    if (isSet[DAY_OF_YEAR]
+        && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > 365 + leap))
+      throw new IllegalArgumentException("Illegal DAY_OF_YEAR.");
+
+    if (isSet[DAY_OF_WEEK]
+        && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7))
+      throw new IllegalArgumentException("Illegal DAY_OF_WEEK.");
+
+    if (isSet[DAY_OF_WEEK_IN_MONTH])
+      {
+       int weeks = (month == 1 && leap == 0) ? 4 : 5;
+       if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks
+           || fields[DAY_OF_WEEK_IN_MONTH] > weeks)
+         throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH.");
+      }
+
+    if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM)
+      throw new IllegalArgumentException("Illegal AM_PM.");
+    if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11))
+      throw new IllegalArgumentException("Illegal HOUR.");
+    if (isSet[HOUR_OF_DAY]
+        && (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23))
+      throw new IllegalArgumentException("Illegal HOUR_OF_DAY.");
+    if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59))
+      throw new IllegalArgumentException("Illegal MINUTE.");
+    if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59))
+      throw new IllegalArgumentException("Illegal SECOND.");
+    if (isSet[MILLISECOND]
+        && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999))
+      throw new IllegalArgumentException("Illegal MILLISECOND.");
+    if (isSet[ZONE_OFFSET]
+        && (fields[ZONE_OFFSET] < -12 * 60 * 60 * 1000L
+        || fields[ZONE_OFFSET] > 12 * 60 * 60 * 1000L))
+      throw new IllegalArgumentException("Illegal ZONE_OFFSET.");
+    if (isSet[DST_OFFSET]
+        && (fields[DST_OFFSET] < -12 * 60 * 60 * 1000L
+        || fields[DST_OFFSET] > 12 * 60 * 60 * 1000L))
+      throw new IllegalArgumentException("Illegal DST_OFFSET.");
+  }
+
+  /**
+   * Converts the time field values (<code>fields</code>) to
+   * milliseconds since the epoch UTC (<code>time</code>).
+   *
+   * @throws IllegalArgumentException if any calendar fields
+   *         are invalid.
+   */
+  protected synchronized void computeTime()
+  {
+    int millisInDay = 0;
+    int era = fields[ERA];
+    int year = fields[YEAR];
+    int month = fields[MONTH];
+    int day = fields[DAY_OF_MONTH];
+
+    int minute = fields[MINUTE];
+    int second = fields[SECOND];
+    int millis = fields[MILLISECOND];
+    int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+    int hour = 0;
+
+    if (! isLenient())
+      nonLeniencyCheck();
+
+    if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR]))
+      {
+       // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
+       if (isSet[WEEK_OF_YEAR])
+         {
+           int first = getFirstDayOfMonth(year, 0);
+           int offs = 1;
+           int daysInFirstWeek = getFirstDayOfWeek() - first;
+           if (daysInFirstWeek <= 0)
+             daysInFirstWeek += 7;
+
+           if (daysInFirstWeek < getMinimalDaysInFirstWeek())
+             offs += daysInFirstWeek;
+           else
+             offs -= 7 - daysInFirstWeek;
+           month = 0;
+           day = offs + 7 * (fields[WEEK_OF_YEAR] - 1);
+           offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
+
+           if (offs < 0)
+             offs += 7;
+           day += offs;
+         }
+       else
+         {
+           // 4:  YEAR + DAY_OF_YEAR
+           month = 0;
+           day = fields[DAY_OF_YEAR];
+         }
+      }
+    else
+      {
+       if (isSet[DAY_OF_WEEK])
+         {
+           int first = getFirstDayOfMonth(year, month);
+
+           // 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
+           if (isSet[DAY_OF_WEEK_IN_MONTH])
+             {
+               if (fields[DAY_OF_WEEK_IN_MONTH] < 0)
+                 {
+                   month++;
+                   first = getFirstDayOfMonth(year, month);
+                   day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH]);
+                 }
+               else
+                 day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1);
+
+               int offs = fields[DAY_OF_WEEK] - first;
+               if (offs < 0)
+                 offs += 7;
+               day += offs;
+             }
+           else
+             { // 2: YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
+               int offs = 1;
+               int daysInFirstWeek = getFirstDayOfWeek() - first;
+               if (daysInFirstWeek <= 0)
+                 daysInFirstWeek += 7;
+
+               if (daysInFirstWeek < getMinimalDaysInFirstWeek())
+                 offs += daysInFirstWeek;
+               else
+                 offs -= 7 - daysInFirstWeek;
+
+               day = offs + 7 * (fields[WEEK_OF_MONTH] - 1);
+               offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
+               if (offs < 0)
+                 offs += 7;
+               day += offs;
+             }
+         }
+
+       // 1:  YEAR + MONTH + DAY_OF_MONTH
+      }
+    if (era == BC && year > 0)
+      year = 1 - year;
+
+    // rest of code assumes day/month/year set
+    // should negative BC years be AD?
+    // get the hour (but no check for validity)
+    if (isSet[HOUR])
+      {
+       hour = fields[HOUR];
+       if (fields[AM_PM] == PM)
+         hour += 12;
+      }
+    else
+      hour = fields[HOUR_OF_DAY];
+
+    // Read the era,year,month,day fields and convert as appropriate.
+    // Calculate number of milliseconds into the day
+    // This takes care of both h, m, s, ms over/underflows.
+    long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis;
+    day += allMillis / (24 * 60 * 60 * 1000L);
+    millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L));
+
+    if (month < 0)
+      {
+       year += (int) month / 12;
+       month = month % 12;
+       if (month < 0)
+         {
+           month += 12;
+           year--;
+         }
+      }
+    if (month > 11)
+      {
+       year += (month / 12);
+       month = month % 12;
+      }
+
+    month_days[1] = isLeapYear(year) ? 29 : 28;
+
+    while (day <= 0)
+      {
+       if (month == 0)
+         {
+           year--;
+           month_days[1] = isLeapYear(year) ? 29 : 28;
+         }
+       month = (month + 11) % 12;
+       day += month_days[month];
+      }
+    while (day > month_days[month])
+      {
+       day -= (month_days[month]);
+       month = (month + 1) % 12;
+       if (month == 0)
+         {
+           year++;
+           month_days[1] = isLeapYear(year) ? 29 : 28;
+         }
+      }
+
+    // ok, by here we have valid day,month,year,era and millisinday
+    int dayOfYear = dayCount[month] + day - 1; // (day starts on 1)
+    if (isLeapYear(year) && month > 1)
+      dayOfYear++;
+
+    int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
+                      - EPOCH_DAYS; // gregorian days from 1 to epoch.
+    int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
+                     - (int) Math.floor((double) (year - 1) / 100.);
+
+    if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover)
+      relativeDay += gregFactor;
+    else
+      relativeDay -= 2;
+
+    time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay;
+
+    // the epoch was a Thursday.
+    int weekday = (int) (relativeDay + THURSDAY) % 7;
+    if (weekday <= 0)
+      weekday += 7;
+    fields[DAY_OF_WEEK] = weekday;
+
+    // Time zone corrections.
+    TimeZone zone = getTimeZone();
+    int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET]
+                                       : zone.getRawOffset();
+
+    int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET]
+                                      : (zone.getOffset((year < 0) ? BC : AD,
+                                                        (year < 0) ? 1 - year
+                                                                   : year,
+                                                        month, day, weekday,
+                                                        millisInDay)
+                                      - zone.getRawOffset());
+
+    time -= rawOffset + dstOffset;
+
+    isTimeSet = true;
+  }
+
+  /**
+   * Get the linear day in days since the epoch, using the
+   * Julian or Gregorian calendar as specified.  If you specify a
+   * nonpositive year it is interpreted as BC as following: 0 is 1
+   * BC, -1 is 2 BC and so on.
+   *
+   * @param year the year of the date.
+   * @param dayOfYear the day of year of the date; 1 based.
+   * @param gregorian <code>true</code>, if we should use the Gregorian rules.
+   * @return the days since the epoch, may be negative.
+   */
+  private long getLinearDay(int year, int dayOfYear, boolean gregorian)
+  {
+    // The 13 is the number of days, that were omitted in the Gregorian
+    // Calender until the epoch.
+    // We shift right by 2 instead of dividing by 4, to get correct
+    // results for negative years (and this is even more efficient).
+    long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1)
+                     - EPOCH_DAYS; // gregorian days from 1 to epoch.
+
+    if (gregorian)
+      {
+       // subtract the days that are missing in gregorian calendar
+       // with respect to julian calendar.
+       //
+       // Okay, here we rely on the fact that the gregorian
+       // calendar was introduced in the AD era.  This doesn't work
+       // with negative years.
+       //
+       // The additional leap year factor accounts for the fact that
+       // a leap day is not seen on Jan 1 of the leap year.
+       int gregOffset = (int) Math.floor((double) (year - 1) / 400.)
+                        - (int) Math.floor((double) (year - 1) / 100.);
+
+       return julianDay + gregOffset;
+      }
+    else
+      julianDay -= 2;
+    return julianDay;
+  }
+
+  /**
+   * Converts the given linear day into era, year, month,
+   * day_of_year, day_of_month, day_of_week, and writes the result
+   * into the fields array.
+   *
+   * @param day the linear day.
+   * @param gregorian true, if we should use Gregorian rules.
+   */
+  private void calculateDay(int[] fields, long day, boolean gregorian)
+  {
+    // the epoch was a Thursday.
+    int weekday = (int) (day + THURSDAY) % 7;
+    if (weekday <= 0)
+      weekday += 7;
+    fields[DAY_OF_WEEK] = weekday;
+
+    // get a first approximation of the year.  This may be one 
+    // year too big.
+    int year = 1970
+               + (int) (gregorian
+                        ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L
+                        + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L));
+    if (day >= 0)
+      year++;
+
+    long firstDayOfYear = getLinearDay(year, 1, gregorian);
+
+    // Now look in which year day really lies.
+    if (day < firstDayOfYear)
+      {
+       year--;
+       firstDayOfYear = getLinearDay(year, 1, gregorian);
+      }
+
+    day -= firstDayOfYear - 1; // day of year,  one based.
+
+    fields[DAY_OF_YEAR] = (int) day;
+    if (year <= 0)
+      {
+       fields[ERA] = BC;
+       fields[YEAR] = 1 - year;
+      }
+    else
+      {
+       fields[ERA] = AD;
+       fields[YEAR] = year;
+      }
+
+    int leapday = isLeapYear(year) ? 1 : 0;
+    if (day <= 31 + 28 + leapday)
+      {
+       fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY
+       fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH];
+      }
+    else
+      {
+       // A few more magic formulas
+       int scaledDay = ((int) day - leapday) * 5 + 8;
+       fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31);
+       fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1;
+      }
+  }
+
+  /**
+   * Converts the milliseconds since the epoch UTC
+   * (<code>time</code>) to time fields
+   * (<code>fields</code>).
+   */
+  protected synchronized void computeFields()
+  {
+    boolean gregorian = (time >= gregorianCutover);
+
+    TimeZone zone = getTimeZone();
+    fields[ZONE_OFFSET] = zone.getRawOffset();
+    long localTime = time + fields[ZONE_OFFSET];
+
+    long day = localTime / (24 * 60 * 60 * 1000L);
+    int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L));
+
+    if (millisInDay < 0)
+      {
+       millisInDay += (24 * 60 * 60 * 1000);
+       day--;
+      }
+
+    calculateDay(fields, day, gregorian);
+    fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR],
+                                        fields[MONTH], fields[DAY_OF_MONTH],
+                                        fields[DAY_OF_WEEK], millisInDay)
+                         - fields[ZONE_OFFSET];
+
+    millisInDay += fields[DST_OFFSET];
+    if (millisInDay >= 24 * 60 * 60 * 1000)
+      {
+       millisInDay -= 24 * 60 * 60 * 1000;
+       calculateDay(fields, ++day, gregorian);
+      }
+
+    fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7;
+
+    // which day of the week are we (0..6), relative to getFirstDayOfWeek
+    int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7;
+
+    // which day of the week is the first of this month?
+    // nb 35 is the smallest multiple of 7 that ensures that
+    // the left hand side of the modulo operator is positive.
+    int relativeWeekdayOfFirst = (relativeWeekday - fields[DAY_OF_MONTH]
+                                 + 1 + 35) % 7;
+
+    // which week of the month is the first of this month in?
+    int minDays = getMinimalDaysInFirstWeek();
+    int weekOfFirst = ((7 - relativeWeekdayOfFirst) >= minDays) ? 1 : 0;
+
+    // which week of the month is this day in?
+    fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH]
+                            + relativeWeekdayOfFirst - 1) / 7 + weekOfFirst;
+
+    int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7;
+
+    // Do the Correction: getMinimalDaysInFirstWeek() is always in the 
+    // first week.
+    int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays)
+                       - getFirstDayOfWeek()) % 7;
+    if (minDays - firstWeekday < 1)
+      weekOfYear++;
+    fields[WEEK_OF_YEAR] = weekOfYear;
+
+    int hourOfDay = millisInDay / (60 * 60 * 1000);
+    fields[AM_PM] = (hourOfDay < 12) ? AM : PM;
+    int hour = hourOfDay % 12;
+    fields[HOUR] = hour;
+    fields[HOUR_OF_DAY] = hourOfDay;
+    millisInDay %= (60 * 60 * 1000);
+    fields[MINUTE] = millisInDay / (60 * 1000);
+    millisInDay %= (60 * 1000);
+    fields[SECOND] = millisInDay / (1000);
+    fields[MILLISECOND] = millisInDay % 1000;
+
+    areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] = isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] = isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] = isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] = isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true;
+  }
+  
+  /**
+   * Return a hash code for this object, following the general contract
+   * specified by {@link Object#hashCode()}.
+   * @return the hash code
+   */
+  public int hashCode()
+  {
+    int val = (int) ((gregorianCutover >>> 32) ^ (gregorianCutover & 0xffffffff));
+    return super.hashCode() ^ val;
+  }
+
+  /**
+   * Compares the given calendar with this.  An object, o, is
+   * equivalent to this if it is also a <code>GregorianCalendar</code>
+   * with the same time since the epoch under the same conditions
+   * (same change date and same time zone).
+   *
+   * @param o the object to that we should compare.
+   * @return true, if the given object is a calendar, that represents
+   * the same time (but doesn't necessarily have the same fields).
+   * @throws IllegalArgumentException if one of the fields
+   *         <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
+   *         specified, if an unknown field is specified or if one
+   *         of the calendar fields receives an illegal value when
+   *         leniancy is not enabled.
+   */
+  public boolean equals(Object o)
+  {
+    if (! (o instanceof GregorianCalendar))
+      return false;
+
+    GregorianCalendar cal = (GregorianCalendar) o;
+    return (cal.gregorianCutover == gregorianCutover
+            && super.equals(o));
+  }
+
+  /**
+   * Adds the specified amount of time to the given time field.  The
+   * amount may be negative to subtract the time.  If the field overflows
+   * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
+   * @param field one of the time field constants.
+   * @param amount the amount of time to add.
+   * @exception IllegalArgumentException if <code>field</code> is
+   *   <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or
+   *   if <code>amount</code> contains an out-of-range value and the calendar
+   *   is not in lenient mode.
+   */
+  public void add(int field, int amount)
+  {
+    switch (field)
+      {
+      case YEAR:
+       complete();
+       fields[YEAR] += amount;
+       isTimeSet = false;
+       break;
+      case MONTH:
+       complete();
+       int months = fields[MONTH] + amount;
+       fields[YEAR] += months / 12;
+       fields[MONTH] = months % 12;
+       if (fields[MONTH] < 0)
+         {
+           fields[MONTH] += 12;
+           fields[YEAR]--;
+         }
+       int maxDay = getActualMaximum(DAY_OF_MONTH);
+       if (fields[DAY_OF_MONTH] > maxDay)
+         fields[DAY_OF_MONTH] = maxDay;
+       set(YEAR, fields[YEAR]);
+       set(MONTH, fields[MONTH]);
+       break;
+      case DAY_OF_MONTH:
+      case DAY_OF_YEAR:
+      case DAY_OF_WEEK:
+       if (! isTimeSet)
+         computeTime();
+       time += amount * (24 * 60 * 60 * 1000L);
+       areFieldsSet = false;
+       break;
+      case WEEK_OF_YEAR:
+      case WEEK_OF_MONTH:
+      case DAY_OF_WEEK_IN_MONTH:
+       if (! isTimeSet)
+         computeTime();
+       time += amount * (7 * 24 * 60 * 60 * 1000L);
+       areFieldsSet = false;
+       break;
+      case AM_PM:
+       if (! isTimeSet)
+         computeTime();
+       time += amount * (12 * 60 * 60 * 1000L);
+       areFieldsSet = false;
+       break;
+      case HOUR:
+      case HOUR_OF_DAY:
+       if (! isTimeSet)
+         computeTime();
+       time += amount * (60 * 60 * 1000L);
+       areFieldsSet = false;
+       break;
+      case MINUTE:
+       if (! isTimeSet)
+         computeTime();
+       time += amount * (60 * 1000L);
+       areFieldsSet = false;
+       break;
+      case SECOND:
+       if (! isTimeSet)
+         computeTime();
+       time += amount * (1000L);
+       areFieldsSet = false;
+       break;
+      case MILLISECOND:
+       if (! isTimeSet)
+         computeTime();
+       time += amount;
+       areFieldsSet = false;
+       break;
+      case ZONE_OFFSET:
+      case DST_OFFSET:default:
+       throw new IllegalArgumentException("Invalid or unknown field");
+      }
+  }
+
+  /**
+   * Rolls the specified time field up or down.  This means add one
+   * to the specified field, but don't change the other fields.  If
+   * the maximum for this field is reached, start over with the
+   * minimum value.
+   *
+   * <strong>Note:</strong> There may be situation, where the other
+   * fields must be changed, e.g rolling the month on May, 31.
+   * The date June, 31 is automatically converted to July, 1.
+   * This requires lenient settings.
+   *
+   * @param field the time field. One of the time field constants.
+   * @param up the direction, true for up, false for down.
+   * @throws IllegalArgumentException if one of the fields
+   *         <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
+   *         specified, if an unknown field is specified or if one
+   *         of the calendar fields receives an illegal value when
+   *         leniancy is not enabled.
+   */
+  public void roll(int field, boolean up)
+  {
+    roll(field, up ? 1 : -1);
+  }
+
+  /**
+   * Checks that the fields are still within their legal bounds,
+   * following use of the <code>roll()</code> method.
+   *
+   * @param field the field to check.
+   * @param delta multipler for alterations to the <code>time</code>.
+   * @see #roll(int, boolean)
+   * @see #roll(int, int)
+   */
+  private void cleanUpAfterRoll(int field, int delta)
+  {
+    switch (field)
+      {
+      case ERA:
+      case YEAR:
+      case MONTH:
+       // check that day of month is still in correct range
+       if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH))
+         fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH);
+       isTimeSet = false;
+       isSet[WEEK_OF_MONTH] = false;
+       isSet[DAY_OF_WEEK] = false;
+       isSet[DAY_OF_WEEK_IN_MONTH] = false;
+       isSet[DAY_OF_YEAR] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       break;
+      case DAY_OF_MONTH:
+       isSet[WEEK_OF_MONTH] = false;
+       isSet[DAY_OF_WEEK] = false;
+       isSet[DAY_OF_WEEK_IN_MONTH] = false;
+       isSet[DAY_OF_YEAR] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       time += delta * (24 * 60 * 60 * 1000L);
+       break;
+      case WEEK_OF_MONTH:
+       isSet[DAY_OF_MONTH] = false;
+       isSet[DAY_OF_WEEK_IN_MONTH] = false;
+       isSet[DAY_OF_YEAR] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       time += delta * (7 * 24 * 60 * 60 * 1000L);
+       break;
+      case DAY_OF_WEEK_IN_MONTH:
+       isSet[DAY_OF_MONTH] = false;
+       isSet[WEEK_OF_MONTH] = false;
+       isSet[DAY_OF_YEAR] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       time += delta * (7 * 24 * 60 * 60 * 1000L);
+       break;
+      case DAY_OF_YEAR:
+       isSet[MONTH] = false;
+       isSet[DAY_OF_MONTH] = false;
+       isSet[WEEK_OF_MONTH] = false;
+       isSet[DAY_OF_WEEK_IN_MONTH] = false;
+       isSet[DAY_OF_WEEK] = false;
+       isSet[WEEK_OF_YEAR] = false;
+       time += delta * (24 * 60 * 60 * 1000L);
+       break;
+      case WEEK_OF_YEAR:
+       isSet[MONTH] = false;
+       isSet[DAY_OF_MONTH] = false;
+       isSet[WEEK_OF_MONTH] = false;
+       isSet[DAY_OF_WEEK_IN_MONTH] = false;
+       isSet[DAY_OF_YEAR] = false;
+       time += delta * (7 * 24 * 60 * 60 * 1000L);
+       break;
+      case AM_PM:
+       isSet[HOUR_OF_DAY] = false;
+       time += delta * (12 * 60 * 60 * 1000L);
+       break;
+      case HOUR:
+       isSet[HOUR_OF_DAY] = false;
+       time += delta * (60 * 60 * 1000L);
+       break;
+      case HOUR_OF_DAY:
+       isSet[HOUR] = false;
+       isSet[AM_PM] = false;
+       time += delta * (60 * 60 * 1000L);
+       break;
+      case MINUTE:
+       time += delta * (60 * 1000L);
+       break;
+      case SECOND:
+       time += delta * (1000L);
+       break;
+      case MILLISECOND:
+       time += delta;
+       break;
+      }
+  }
+
+  /**
+   * Rolls the specified time field by the given amount.  This means
+   * add amount to the specified field, but don't change the other
+   * fields.  If the maximum for this field is reached, start over
+   * with the minimum value and vice versa for negative amounts.
+   *
+   * <strong>Note:</strong> There may be situation, where the other
+   * fields must be changed, e.g rolling the month on May, 31.
+   * The date June, 31 is automatically corrected to June, 30.
+   *
+   * @param field the time field. One of the time field constants.
+   * @param amount the amount by which we should roll.
+   * @throws IllegalArgumentException if one of the fields
+   *         <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
+   *         specified, if an unknown field is specified or if one
+   *         of the calendar fields receives an illegal value when
+   *         leniancy is not enabled.
+   */
+  public void roll(int field, int amount)
+  {
+    switch (field)
+      {
+      case DAY_OF_WEEK:
+       // day of week is special: it rolls automatically
+       add(field, amount);
+       return;
+      case ZONE_OFFSET:
+      case DST_OFFSET:
+       throw new IllegalArgumentException("Can't roll time zone");
+      }
+    complete();
+    int min = getActualMinimum(field);
+    int range = getActualMaximum(field) - min + 1;
+    int oldval = fields[field];
+    int newval = (oldval - min + range + amount) % range + min;
+    if (newval < min)
+      newval += range;
+    fields[field] = newval;
+    cleanUpAfterRoll(field, newval - oldval);
+  }
+
+  /**
+   * The minimum values for the calendar fields.
+   */
+  private static final int[] minimums = 
+                                        {
+                                          BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, AM,
+                                          1, 0, 0, 0, 0, -(12 * 60 * 60 * 1000),
+                                          0
+                                        };
+
+  /**
+   * The maximum values for the calendar fields.
+   */
+  private static final int[] maximums = 
+                                        {
+                                          AD, 5000000, 11, 53, 6, 31, 366,
+                                          SATURDAY, 5, PM, 12, 23, 59, 59, 999,
+                                          +(12 * 60 * 60 * 1000),
+                                          (12 * 60 * 60 * 1000)
+                                        };
+
+  /**
+   * Gets the smallest value that is allowed for the specified field.
+   *
+   * @param field one of the time field constants.
+   * @return the smallest value for the specified field.
+   */
+  public int getMinimum(int field)
+  {
+    return minimums[field];
+  }
+
+  /**
+   * Gets the biggest value that is allowed for the specified field.
+   *
+   * @param field one of the time field constants.
+   * @return the biggest value.
+   */
+  public int getMaximum(int field)
+  {
+    return maximums[field];
+  }
+
+  /**
+   * Gets the greatest minimum value that is allowed for the specified field.
+   * This is the largest value returned by the <code>getActualMinimum(int)</code>
+   * method.
+   *
+   * @param field the time field. One of the time field constants.
+   * @return the greatest minimum value.
+   * @see #getActualMinimum(int)
+   */
+  public int getGreatestMinimum(int field)
+  {
+    if (field == WEEK_OF_YEAR)
+      return 1;
+    return minimums[field];
+  }
+
+  /**
+   * Gets the smallest maximum value that is allowed for the
+   * specified field.  This is the smallest value returned
+   * by the <code>getActualMaximum(int)</code>.  For example,
+   * this is 28 for DAY_OF_MONTH (as all months have at least
+   * 28 days).
+   *
+   * @param field the time field. One of the time field constants.
+   * @return the least maximum value.
+   * @see #getActualMaximum(int)
+   * @since 1.2
+   */
+  public int getLeastMaximum(int field)
+  {
+    switch (field)
+      {
+      case WEEK_OF_YEAR:
+       return 52;
+      case DAY_OF_MONTH:
+       return 28;
+      case DAY_OF_YEAR:
+       return 365;
+      case DAY_OF_WEEK_IN_MONTH:
+      case WEEK_OF_MONTH:
+       return 4;
+      default:
+       return maximums[field];
+      }
+  }
+
+  /**
+   * Gets the actual minimum value that is allowed for the specified field.
+   * This value is dependent on the values of the other fields.  Note that
+   * this calls <code>complete()</code> if not enough fields are set.  This
+   * can have ugly side effects.  The value given depends on the current
+   * time used by this instance.
+   *
+   * @param field the time field. One of the time field constants.
+   * @return the actual minimum value.
+   * @since 1.2
+   */
+  public int getActualMinimum(int field)
+  {
+    if (field == WEEK_OF_YEAR)
+      {
+       int min = getMinimalDaysInFirstWeek();
+       if (min == 0)
+         return 1;
+       if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
+         complete();
+
+       int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
+       int weekday = getWeekDay(year, min);
+       if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1)
+         return 1;
+       return 0;
+      }
+    return minimums[field];
+  }
+
+  /**
+   * Gets the actual maximum value that is allowed for the specified field.
+   * This value is dependent on the values of the other fields.  Note that
+   * this calls <code>complete()</code> if not enough fields are set.  This
+   * can have ugly side effects.  The value given depends on the current time
+   * used by this instance; thus, leap years have a maximum day of month value of
+   * 29, rather than 28.
+   *
+   * @param field the time field. One of the time field constants.
+   * @return the actual maximum value.
+   */
+  public int getActualMaximum(int field)
+  {
+    switch (field)
+      {
+      case WEEK_OF_YEAR:
+        {
+         if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
+           complete();
+
+         // This is wrong for the year that contains the gregorian change.
+         // I.e it gives the weeks in the julian year or in the gregorian
+         // year in that case.
+         int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
+         int lastDay = isLeapYear(year) ? 366 : 365;
+         int weekday = getWeekDay(year, lastDay);
+         int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
+
+         int minimalDays = getMinimalDaysInFirstWeek();
+         int firstWeekday = getWeekDay(year, minimalDays);
+         /*
+          * Is there a set of days at the beginning of the year, before the
+          * first day of the week, equal to or greater than the minimum number
+          * of days required in the first week?
+          */
+         if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
+           return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */
+        }
+      case DAY_OF_MONTH:
+        {
+         if (! areFieldsSet || ! isSet[MONTH])
+           complete();
+         int month = fields[MONTH];
+
+         // If you change this, you should also change 
+         // SimpleTimeZone.getDaysInMonth();
+         if (month == FEBRUARY)
+           {
+             if (! isSet[YEAR] || ! isSet[ERA])
+               complete();
+             int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
+             return isLeapYear(year) ? 29 : 28;
+           }
+         else if (month < AUGUST)
+           return 31 - (month & 1);
+         else
+           return 30 + (month & 1);
+        }
+      case DAY_OF_YEAR:
+        {
+         if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
+           complete();
+         int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
+         return isLeapYear(year) ? 366 : 365;
+        }
+      case DAY_OF_WEEK_IN_MONTH:
+        {
+         // This is wrong for the month that contains the gregorian change.
+         int daysInMonth = getActualMaximum(DAY_OF_MONTH);
+
+         // That's black magic, I know
+         return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7;
+        }
+      case WEEK_OF_MONTH:
+        {
+         int daysInMonth = getActualMaximum(DAY_OF_MONTH);
+         int weekday = (daysInMonth - fields[DAY_OF_MONTH]
+                       + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY;
+         return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
+        }
+      default:
+       return maximums[field];
+      }
+  }
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/Locale.java b/Robust/src/ClassLibrary/MGC/gnu/Locale.java
new file mode 100644 (file)
index 0000000..2d4105a
--- /dev/null
@@ -0,0 +1,1029 @@
+/* Locale.java -- i18n locales
+   Copyright (C) 1998, 1999, 2001, 2002, 2005, 2006  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util;
+
+/*import gnu.classpath.SystemProperties;
+
+import gnu.java.lang.CPStringBuilder;
+
+import gnu.java.locale.LocaleHelper;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import java.util.spi.LocaleNameProvider;*/
+
+/**
+ * Locales represent a specific country and culture. Classes which can be
+ * passed a Locale object tailor their information for a given locale. For
+ * instance, currency number formatting is handled differently for the USA
+ * and France.
+ *
+ * <p>Locales are made up of a language code, a country code, and an optional
+ * set of variant strings. Language codes are represented by
+ * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
+ * ISO 639:1988</a> w/ additions from ISO 639/RA Newsletter No. 1/1989
+ * and a decision of the Advisory Committee of ISO/TC39 on August 8, 1997.
+ *
+ * <p>Country codes are represented by
+ * <a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
+ * ISO 3166</a>. Variant strings are vendor and browser specific. Standard
+ * variant strings include "POSIX" for POSIX, "WIN" for MS-Windows, and
+ * "MAC" for Macintosh. When there is more than one variant string, they must
+ * be separated by an underscore (U+005F).
+ *
+ * <p>The default locale is determined by the values of the system properties
+ * user.language, user.country (or user.region), and user.variant, defaulting
+ * to "en_US". Note that the locale does NOT contain the conversion and
+ * formatting capabilities (for that, use ResourceBundle and java.text).
+ * Rather, it is an immutable tag object for identifying a given locale, which
+ * is referenced by these other classes when they must make locale-dependent
+ * decisions.
+ *
+ * @see ResourceBundle
+ * @see java.text.Format
+ * @see java.text.NumberFormat
+ * @see java.text.Collator
+ * @author Jochen Hoenicke
+ * @author Paul Fisher
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public final class Locale //implements Serializable, Cloneable
+{
+  /** Locale which represents the English language. */
+  public static final Locale ENGLISH = getLocale("en");
+
+  /** Locale which represents the French language. */
+  public static final Locale FRENCH = getLocale("fr");
+
+  /** Locale which represents the German language. */
+  public static final Locale GERMAN = getLocale("de");
+
+  /** Locale which represents the Italian language. */
+  public static final Locale ITALIAN = getLocale("it");
+
+  /** Locale which represents the Japanese language. */
+  public static final Locale JAPANESE = getLocale("ja");
+
+  /** Locale which represents the Korean language. */
+  public static final Locale KOREAN = getLocale("ko");
+
+  /** Locale which represents the Chinese language. */
+  public static final Locale CHINESE = getLocale("zh");
+
+  /** Locale which represents the Chinese language as used in China. */
+  public static final Locale SIMPLIFIED_CHINESE = getLocale("zh", "CN");
+
+  /**
+   * Locale which represents the Chinese language as used in Taiwan.
+   * Same as TAIWAN Locale.
+   */
+  public static final Locale TRADITIONAL_CHINESE = getLocale("zh", "TW");
+
+  /** Locale which represents France. */
+  public static final Locale FRANCE = getLocale("fr", "FR");
+
+  /** Locale which represents Germany. */
+  public static final Locale GERMANY = getLocale("de", "DE");
+
+  /** Locale which represents Italy. */
+  public static final Locale ITALY = getLocale("it", "IT");
+
+  /** Locale which represents Japan. */
+  public static final Locale JAPAN = getLocale("ja", "JP");
+
+  /** Locale which represents Korea. */
+  public static final Locale KOREA = getLocale("ko", "KR");
+
+  /**
+   * Locale which represents China.
+   * Same as SIMPLIFIED_CHINESE Locale.
+   */
+  public static final Locale CHINA = SIMPLIFIED_CHINESE;
+
+  /**
+   * Locale which represents the People's Republic of China.
+   * Same as CHINA Locale.
+   */
+  public static final Locale PRC = CHINA;
+
+  /**
+   * Locale which represents Taiwan.
+   * Same as TRADITIONAL_CHINESE Locale.
+   */
+  public static final Locale TAIWAN = TRADITIONAL_CHINESE;
+
+  /** Locale which represents the United Kingdom. */
+  public static final Locale UK = getLocale("en", "GB");
+
+  /** Locale which represents the United States. */
+  public static final Locale US = getLocale("en", "US");
+
+  /** Locale which represents the English speaking portion of Canada. */
+  public static final Locale CANADA = getLocale("en", "CA");
+
+  /** Locale which represents the French speaking portion of Canada. */
+  public static final Locale CANADA_FRENCH = getLocale("fr", "CA");
+
+  /** The root locale, used as the base case in lookups by
+   *  locale-sensitive operations.
+   */
+  public static final Locale ROOT = new Locale("","","");
+
+  /**
+   * Compatible with JDK 1.1+.
+   */
+  private static final long serialVersionUID = 9149081749638150636L;
+
+  /**
+   * The language code, as returned by getLanguage().
+   *
+   * @serial the languange, possibly ""
+   */
+  private final String language;
+
+  /**
+   * The country code, as returned by getCountry().
+   *
+   * @serial the country, possibly ""
+   */
+  private final String country;
+
+  /**
+   * The variant code, as returned by getVariant().
+   *
+   * @serial the variant, possibly ""
+   */
+  private final String variant;
+
+  /**
+   * This is the cached hashcode. When writing to stream, we write -1.
+   *
+   * @serial should be -1 in serial streams
+   */
+  private int hashcode;
+
+  /**
+   * Array storing all available locales.
+   */
+  private static transient Locale[] availableLocales;
+
+  /**
+   * Locale cache. Only created locale objects are stored.
+   * Contains all supported locales when getAvailableLocales()
+   * got called.
+   */
+  private static transient HashMap localeMap;
+  
+  /**
+   * The default locale. Except for during bootstrapping, this should never be
+   * null. Note the logic in the main constructor, to detect when
+   * bootstrapping has completed.
+   */
+  private static Locale defaultLocale;
+
+  static {
+    String language = "en"; //SystemProperties.getProperty("user.language", "en");
+    String country  = "US"; //SystemProperties.getProperty("user.country", "US");
+    String region   = null; //SystemProperties.getProperty("user.region", null);
+    String variant  = ""; //SystemProperties.getProperty("user.variant", "");
+
+    defaultLocale = getLocale(language,
+                              (region != null) ? region : country,
+                              variant);
+  }
+
+  /**
+   * Array storing all the available two-letter ISO639 languages.
+   */
+  private static transient String[] languageCache;
+
+  /**
+   * Array storing all the available two-letter ISO3166 country codes.
+   */
+  private static transient String[] countryCache;
+
+  /**
+   * Retrieves the locale with the specified language from the cache.
+   *
+   * @param language the language of the locale to retrieve.
+   * @return the locale.
+   */ 
+  private static Locale getLocale(String language)
+  {
+    return getLocale(language, "", "");
+  }
+  
+  /**
+   * Retrieves the locale with the specified language and country
+   * from the cache.
+   *
+   * @param language the language of the locale to retrieve.
+   * @param country the country of the locale to retrieve.
+   * @return the locale.
+   */ 
+  private static Locale getLocale(String language, String country)
+  {
+    return getLocale(language, country, "");
+  }
+  
+  /**
+   * Retrieves the locale with the specified language, country
+   * and variant from the cache.
+   *
+   * @param language the language of the locale to retrieve.
+   * @param country the country of the locale to retrieve.
+   * @param variant the variant of the locale to retrieve.
+   * @return the locale.
+   */ 
+  private static Locale getLocale(String language, String country, String variant)
+  {
+    if (localeMap == null)
+      localeMap = new HashMap(256);
+
+    String name = language + "_" + country + "_" + variant;
+    Locale locale = (Locale) localeMap.get(name);
+
+    if (locale == null)
+      {
+       locale = new Locale(language, country, variant);
+       localeMap.put(name, locale);
+      }
+
+    return locale;
+  }
+  
+  /**
+   * Convert new iso639 codes to the old ones.
+   *
+   * @param language the language to check
+   * @return the appropriate code
+   */
+  private String convertLanguage(String language)
+  {
+    if (language.equals(""))
+      return language;
+    language = language.toLowerCase();
+    int index = "he,id,yi".indexOf(language);
+    if (index != -1)
+      return "iw,in,ji".substring(index, index + 2);
+    return language;
+  }
+
+  /**
+   * Creates a new locale for the given language and country.
+   *
+   * @param language lowercase two-letter ISO-639 A2 language code
+   * @param country uppercase two-letter ISO-3166 A2 contry code
+   * @param variant vendor and browser specific
+   * @throws NullPointerException if any argument is null
+   */
+  public Locale(String language, String country, String variant)
+  {
+    // During bootstrap, we already know the strings being passed in are
+    // the correct capitalization, and not null. We can't call
+    // String.toUpperCase during this time, since that depends on the
+    // default locale.
+    if (defaultLocale != null)
+      {
+        language = convertLanguage(language);
+        country = country.toUpperCase();
+      }
+    this.language = language.intern();
+    this.country = country.intern();
+    this.variant = variant.intern();
+    hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
+  }
+
+  /**
+   * Creates a new locale for the given language and country.
+   *
+   * @param language lowercase two-letter ISO-639 A2 language code
+   * @param country uppercase two-letter ISO-3166 A2 country code
+   * @throws NullPointerException if either argument is null
+   */
+  public Locale(String language, String country)
+  {
+    this(language, country, "");
+  }
+
+  /**
+   * Creates a new locale for a language.
+   *
+   * @param language lowercase two-letter ISO-639 A2 language code
+   * @throws NullPointerException if either argument is null
+   * @since 1.4
+   */
+  public Locale(String language)
+  {
+    this(language, "", "");
+  }
+
+  /**
+   * Returns the default Locale. The default locale is generally once set
+   * on start up and then never changed. Normally you should use this locale
+   * for everywhere you need a locale. The initial setting matches the
+   * default locale, the user has chosen.
+   *
+   * @return the default locale for this virtual machine
+   */
+  public static Locale getDefault()
+  {
+    return defaultLocale;
+  }
+
+  /**
+   * Changes the default locale. Normally only called on program start up.
+   * Note that this doesn't change the locale for other programs. This has
+   * a security check,
+   * <code>PropertyPermission("user.language", "write")</code>, because of
+   * its potential impact to running code.
+   *
+   * @param newLocale the new default locale
+   * @throws NullPointerException if newLocale is null
+   * @throws SecurityException if permission is denied
+   */
+  public static void setDefault(Locale newLocale)
+  {
+    if (newLocale == null)
+      throw new NullPointerException();
+    /*SecurityManager sm = System.getSecurityManager();
+    if (sm != null)
+      sm.checkPermission(new PropertyPermission("user.language", "write"));*/
+    defaultLocale = newLocale;
+  }
+
+  /**
+   * Returns the list of available locales.
+   *
+   * @return the installed locales
+   */
+  public static synchronized Locale[] getAvailableLocales()
+  {
+    if (availableLocales == null)
+      {
+        int len = LocaleHelper.getLocaleCount();
+        availableLocales = new Locale[len];
+
+        for (int i = 0; i < len; i++)
+          {
+            String language;
+            String country = "";
+            String variant = "";
+            String name = LocaleHelper.getLocaleName(i);
+
+            language = name.substring(0, 2);
+
+            if (name.length() > 2)
+              country = name.substring(3);
+
+           int index = country.indexOf("_");
+           if (index > 0)
+             {
+               variant = country.substring(index + 1);
+               country = country.substring(0, index - 1);
+             }
+
+            availableLocales[i] = getLocale(language, country, variant);
+          }
+      }
+    
+    return (Locale[]) availableLocales.clone();
+  }
+
+  /**
+   * Returns a list of all 2-letter uppercase country codes as defined
+   * in ISO 3166.
+   *
+   * @return a list of acceptable country codes
+   */
+  public static String[] getISOCountries()
+  {
+    if (countryCache == null)
+      {
+       countryCache = getISOStrings("territories");
+      }
+
+    return (String[]) countryCache.clone();
+  }
+
+  /**
+   * Returns a list of all 2-letter lowercase language codes as defined
+   * in ISO 639 (both old and new variant).
+   *
+   * @return a list of acceptable language codes
+   */
+  public static String[] getISOLanguages()
+  {
+    if (languageCache == null)
+      {
+       languageCache = getISOStrings("languages");
+      }
+    return (String[]) languageCache.clone();
+  }
+
+  /**
+   * Returns the set of keys from the specified resource hashtable, filtered
+   * so that only two letter strings are returned.
+   *
+   * @param tableName the name of the table from which to retrieve the keys.
+   * @return an array of two-letter strings.
+   */
+  /*private static String[] getISOStrings(String tableName)
+  {
+    int count = 0;
+    ResourceBundle bundle =
+      ResourceBundle.getBundle("gnu.java.locale.LocaleInformation");
+    Enumeration e = bundle.getKeys();
+    ArrayList tempList = new ArrayList();
+
+    while (e.hasMoreElements())
+      {
+       String key = (String) e.nextElement();
+       
+       if (key.startsWith(tableName + "."))
+         {
+           String str = key.substring(tableName.length() + 1);
+
+           if (str.length() == 2
+               && Character.isLetter(str.charAt(0))
+               && Character.isLetter(str.charAt(1)))
+             {
+               tempList.add(str);
+               ++count;
+             }
+         }
+      }
+
+    String[] strings = new String[count];
+    
+    for (int a = 0; a < count; ++a)
+      strings[a] = (String) tempList.get(a);
+    
+    return strings;
+  }*/
+
+  /**
+   * Returns the language code of this locale. Some language codes have changed
+   * as ISO 639 has evolved; this returns the old name, even if you built
+   * the locale with the new one.
+   *
+   * @return language code portion of this locale, or an empty String
+   */
+  public String getLanguage()
+  {
+    return language;
+  }
+
+  /**
+   * Returns the country code of this locale.
+   *
+   * @return country code portion of this locale, or an empty String
+   */
+  public String getCountry()
+  {
+    return country;
+  }
+
+  /**
+   * Returns the variant code of this locale.
+   *
+   * @return the variant code portion of this locale, or an empty String
+   */
+  public String getVariant()
+  {
+    return variant;
+  }
+
+  /**
+   * Gets the string representation of the current locale. This consists of
+   * the language, the country, and the variant, separated by an underscore.
+   * The variant is listed only if there is a language or country. Examples:
+   * "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC".
+   *
+   * @return the string representation of this Locale
+   * @see #getDisplayName()
+   */
+  /*public String toString()
+  {
+    if (language.length() == 0 && country.length() == 0)
+      return "";
+    else if (country.length() == 0 && variant.length() == 0)
+      return language;
+    CPStringBuilder result = new CPStringBuilder(language);
+    result.append('_').append(country);
+    if (variant.length() != 0)
+      result.append('_').append(variant);
+    return result.toString();
+  }*/
+
+  /**
+   * Returns the three-letter ISO language abbrevation of this locale.
+   *
+   * @throws MissingResourceException if the three-letter code is not known
+   */
+  public String getISO3Language()
+  {
+    // We know all strings are interned so we can use '==' for better performance.
+    if (language == "")
+      return "";
+    int index
+      = ("aa,ab,af,am,ar,as,ay,az,ba,be,bg,bh,bi,bn,bo,br,ca,co,cs,cy,da,"
+         + "de,dz,el,en,eo,es,et,eu,fa,fi,fj,fo,fr,fy,ga,gd,gl,gn,gu,ha,iw,"
+         + "hi,hr,hu,hy,ia,in,ie,ik,in,is,it,iu,iw,ja,ji,jw,ka,kk,kl,km,kn,"
+         + "ko,ks,ku,ky,la,ln,lo,lt,lv,mg,mi,mk,ml,mn,mo,mr,ms,mt,my,na,ne,"
+         + "nl,no,oc,om,or,pa,pl,ps,pt,qu,rm,rn,ro,ru,rw,sa,sd,sg,sh,si,sk,"
+         + "sl,sm,sn,so,sq,sr,ss,st,su,sv,sw,ta,te,tg,th,ti,tk,tl,tn,to,tr,"
+         + "ts,tt,tw,ug,uk,ur,uz,vi,vo,wo,xh,ji,yo,za,zh,zu")
+      .indexOf(language);
+
+    if (index % 3 != 0 || language.length() != 2)
+      throw new MissingResourceException
+        ("Can't find ISO3 language for " + language,
+         "java.util.Locale", language);
+
+    // Don't read this aloud. These are the three letter language codes.
+    return
+      ("aarabkaframharaasmaymazebakbelbulbihbisbenbodbrecatcoscescymdandeu"
+       + "dzoellengepospaesteusfasfinfijfaofrafrygaigdhglggrngujhauhebhinhrv"
+       + "hunhyeinaindileipkindislitaikuhebjpnyidjawkatkazkalkhmkankorkaskur"
+       + "kirlatlinlaolitlavmlgmrimkdmalmonmolmarmsamltmyanaunepnldnorociorm"
+       + "oripanpolpusporquerohrunronruskinsansndsagsrpsinslkslvsmosnasomsqi"
+       + "srpsswsotsunsweswatamteltgkthatirtuktgltsntonturtsotattwiuigukrurd"
+       + "uzbvievolwolxhoyidyorzhazhozul")
+      .substring(index, index + 3);
+  }
+
+  /**
+   * Returns the three-letter ISO country abbrevation of the locale.
+   *
+   * @throws MissingResourceException if the three-letter code is not known
+   */
+  public String getISO3Country()
+  {
+    // We know all strings are interned so we can use '==' for better performance.
+    if (country == "")
+      return "";
+    int index
+      = ("AD,AE,AF,AG,AI,AL,AM,AN,AO,AQ,AR,AS,AT,AU,AW,AZ,BA,BB,BD,BE,BF,"
+         + "BG,BH,BI,BJ,BM,BN,BO,BR,BS,BT,BV,BW,BY,BZ,CA,CC,CF,CG,CH,CI,CK,"
+         + "CL,CM,CN,CO,CR,CU,CV,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER,"
+         + "ES,ET,FI,FJ,FK,FM,FO,FR,FX,GA,GB,GD,GE,GF,GH,GI,GL,GM,GN,GP,GQ,"
+         + "GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IN,IO,IQ,IR,IS,IT,"
+         + "JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS,"
+         + "LT,LU,LV,LY,MA,MC,MD,MG,MH,MK,ML,MM,MN,MO,MP,MQ,MR,MS,MT,MU,MV,"
+         + "MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG,"
+         + "PH,PK,PL,PM,PN,PR,PT,PW,PY,QA,RE,RO,RU,RW,SA,SB,SC,SD,SE,SG,SH,"
+         + "SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TM,TN,"
+         + "TO,TP,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF,"
+         + "WS,YE,YT,YU,ZA,ZM,ZR,ZW")
+      .indexOf(country);
+
+    if (index % 3 != 0 || country.length() != 2)
+      throw new MissingResourceException
+        ("Can't find ISO3 country for " + country,
+         "java.util.Locale", country);
+
+    // Don't read this aloud. These are the three letter country codes.
+    return
+      ("ANDAREAFGATGAIAALBARMANTAGOATAARGASMAUTAUSABWAZEBIHBRBBGDBELBFABGR"
+       + "BHRBDIBENBMUBRNBOLBRABHSBTNBVTBWABLRBLZCANCCKCAFCOGCHECIVCOKCHLCMR"
+       + "CHNCOLCRICUBCPVCXRCYPCZEDEUDJIDNKDMADOMDZAECUESTEGYESHERIESPETHFIN"
+       + "FJIFLKFSMFROFRAFXXGABGBRGRDGEOGUFGHAGIBGRLGMBGINGLPGNQGRCSGSGTMGUM"
+       + "GNBGUYHKGHMDHNDHRVHTIHUNIDNIRLISRINDIOTIRQIRNISLITAJAMJORJPNKENKGZ"
+       + "KHMKIRCOMKNAPRKKORKWTCYMKAZLAOLBNLCALIELKALBRLSOLTULUXLVALBYMARMCO"
+       + "MDAMDGMHLMKDMLIMMRMNGMACMNPMTQMRTMSRMLTMUSMDVMWIMEXMYSMOZNAMNCLNER"
+       + "NFKNGANICNLDNORNPLNRUNIUNZLOMNPANPERPYFPNGPHLPAKPOLSPMPCNPRIPRTPLW"
+       + "PRYQATREUROMRUSRWASAUSLBSYCSDNSWESGPSHNSVNSJMSVKSLESMRSENSOMSURSTP"
+       + "SLVSYRSWZTCATCDATFTGOTHATJKTKLTKMTUNTONTMPTURTTOTUVTWNTZAUKRUGAUMI"
+       + "USAURYUZBVATVCTVENVGBVIRVNMVUTWLFWSMYEMMYTYUGZAFZMBZARZWE")
+      .substring(index, index + 3);
+  }
+
+  /**
+   * Gets the country name suitable for display to the user, formatted
+   * for the default locale.  This has the same effect as
+   * <pre>
+   * getDisplayLanguage(Locale.getDefault());
+   * </pre>
+   *
+   * @return the language name of this locale localized to the default locale,
+   *         with the ISO code as backup
+   */
+  public String getDisplayLanguage()
+  {
+    return getDisplayLanguage(defaultLocale);
+  }
+
+  /**
+   * <p>
+   * Gets the name of the language specified by this locale, in a form suitable
+   * for display to the user.  If possible, the display name will be localized
+   * to the specified locale.  For example, if the locale instance is
+   * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>,
+   * the result would be 'German'.  Using the German locale would instead give
+   * 'Deutsch'.  If the display name can not be localized to the supplied
+   * locale, it will fall back on other output in the following order:
+   * </p>
+   * <ul>
+   * <li>the display name in the default locale</li>
+   * <li>the display name in English</li>
+   * <li>the ISO code</li>
+   * </ul>
+   * <p>
+   * If the language is unspecified by this locale, then the empty string is
+   * returned.
+   * </p>
+   *
+   * @param inLocale the locale to use for formatting the display string.
+   * @return the language name of this locale localized to the given locale,
+   *         with the default locale, English and the ISO code as backups.
+   * @throws NullPointerException if the supplied locale is null.
+   */
+  /*public String getDisplayLanguage(Locale inLocale)
+  {
+    if (language.isEmpty())
+      return "";
+    try
+      {
+       ResourceBundle res =
+          ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+                                   inLocale,
+                                   ClassLoader.getSystemClassLoader());
+
+        return res.getString("languages." + language);
+      }
+    catch (MissingResourceException e)
+      {*/
+       /* This means runtime support for the locale
+        * is not available, so we check providers. */
+      /*}
+    for (LocaleNameProvider p :
+          ServiceLoader.load(LocaleNameProvider.class))
+      {
+       for (Locale loc : p.getAvailableLocales())
+         {
+           if (loc.equals(inLocale))
+             {
+               String locLang = p.getDisplayLanguage(language,
+                                                     inLocale);
+               if (locLang != null)
+                 return locLang;
+               break;
+             }
+         }
+      }
+    if (inLocale.equals(Locale.ROOT)) // Base case
+      return language;
+    return getDisplayLanguage(LocaleHelper.getFallbackLocale(inLocale));
+  }*/
+
+  /**
+   * Returns the country name of this locale localized to the
+   * default locale. If the localized is not found, the ISO code
+   * is returned. This has the same effect as
+   * <pre>
+   * getDisplayCountry(Locale.getDefault());
+   * </pre>
+   *
+   * @return the country name of this locale localized to the given locale,
+   *         with the ISO code as backup
+   */
+  public String getDisplayCountry()
+  {
+    return null;//getDisplayCountry(defaultLocale);
+  }
+
+  /**
+   * <p>
+   * Gets the name of the country specified by this locale, in a form suitable
+   * for display to the user.  If possible, the display name will be localized
+   * to the specified locale.  For example, if the locale instance is
+   * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>,
+   * the result would be 'Germany'.  Using the German locale would instead give
+   * 'Deutschland'.  If the display name can not be localized to the supplied
+   * locale, it will fall back on other output in the following order:
+   * </p>
+   * <ul>
+   * <li>the display name in the default locale</li>
+   * <li>the display name in English</li>
+   * <li>the ISO code</li>
+   * </ul>
+   * <p>
+   * If the country is unspecified by this locale, then the empty string is
+   * returned.
+   * </p>
+   *
+   * @param inLocale the locale to use for formatting the display string.
+   * @return the country name of this locale localized to the given locale,
+   *         with the default locale, English and the ISO code as backups.
+   * @throws NullPointerException if the supplied locale is null.
+   */
+  /*public String getDisplayCountry(Locale inLocale)
+  {
+    if (country.isEmpty())
+      return "";
+    try
+      {
+        ResourceBundle res =
+          ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+                                   inLocale,
+                                   ClassLoader.getSystemClassLoader());
+    
+        return res.getString("territories." + country);
+      }
+    catch (MissingResourceException e)
+      {*/
+       /* This means runtime support for the locale
+        * is not available, so we check providers. */
+      /*}
+    for (LocaleNameProvider p :
+          ServiceLoader.load(LocaleNameProvider.class))
+      {
+       for (Locale loc : p.getAvailableLocales())
+         {
+           if (loc.equals(inLocale))
+             {
+               String locCountry = p.getDisplayCountry(country,
+                                                       inLocale);
+               if (locCountry != null)
+                 return locCountry;
+               break;
+             }
+         }
+      }
+    if (inLocale.equals(Locale.ROOT)) // Base case
+      return country;
+    return getDisplayCountry(LocaleHelper.getFallbackLocale(inLocale));
+  }*/
+
+  /**
+   * Returns the variant name of this locale localized to the
+   * default locale. If the localized is not found, the variant code
+   * itself is returned. This has the same effect as
+   * <pre>
+   * getDisplayVariant(Locale.getDefault());
+   * </pre>
+   *
+   * @return the variant code of this locale localized to the given locale,
+   *         with the ISO code as backup
+   */
+  public String getDisplayVariant()
+  {
+    return getDisplayVariant(defaultLocale);
+  }
+
+
+  /**
+   * <p>
+   * Gets the name of the variant specified by this locale, in a form suitable
+   * for display to the user.  If possible, the display name will be localized
+   * to the specified locale.  For example, if the locale instance is a revised
+   * variant, and the specified locale is <code>Locale.UK</code>, the result
+   * would be 'REVISED'.  Using the German locale would instead give
+   * 'Revidiert'.  If the display name can not be localized to the supplied
+   * locale, it will fall back on other output in the following order:
+   * </p>
+   * <ul>
+   * <li>the display name in the default locale</li>
+   * <li>the display name in English</li>
+   * <li>the ISO code</li>
+   * </ul>
+   * <p>
+   * If the variant is unspecified by this locale, then the empty string is
+   * returned.
+   * </p>
+   *
+   * @param inLocale the locale to use for formatting the display string.
+   * @return the variant name of this locale localized to the given locale,
+   *         with the default locale, English and the ISO code as backups.
+   * @throws NullPointerException if the supplied locale is null.
+   */
+  /*public String getDisplayVariant(Locale inLocale)
+  {
+    if (variant.isEmpty())
+      return "";
+    try
+      {
+        ResourceBundle res =
+          ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+                                   inLocale,
+                                   ClassLoader.getSystemClassLoader());
+    
+        return res.getString("variants." + variant);
+      }
+    catch (MissingResourceException e)
+      {*/
+       /* This means runtime support for the locale
+        * is not available, so we check providers. */
+      /*}
+    for (LocaleNameProvider p :
+          ServiceLoader.load(LocaleNameProvider.class))
+      {
+       for (Locale loc : p.getAvailableLocales())
+         {
+           if (loc.equals(inLocale))
+             {
+               String locVar = p.getDisplayVariant(variant,
+                                                   inLocale);
+               if (locVar != null)
+                 return locVar;
+               break;
+             }
+         }
+      }
+    if (inLocale.equals(Locale.ROOT)) // Base case
+      return country;
+    return getDisplayVariant(LocaleHelper.getFallbackLocale(inLocale));
+  }*/
+
+  /**
+   * Gets all local components suitable for display to the user, formatted
+   * for the default locale. For the language component, getDisplayLanguage
+   * is called. For the country component, getDisplayCountry is called.
+   * For the variant set component, getDisplayVariant is called.
+   *
+   * <p>The returned String will be one of the following forms:<br>
+   * <pre>
+   * language (country, variant)
+   * language (country)
+   * language (variant)
+   * country (variant)
+   * language
+   * country
+   * variant
+   * </pre>
+   *
+   * @return String version of this locale, suitable for display to the user
+   */
+  public String getDisplayName()
+  {
+    return getDisplayName(defaultLocale);
+  }
+
+  /**
+   * Gets all local components suitable for display to the user, formatted
+   * for a specified locale. For the language component,
+   * getDisplayLanguage(Locale) is called. For the country component,
+   * getDisplayCountry(Locale) is called. For the variant set component,
+   * getDisplayVariant(Locale) is called.
+   *
+   * <p>The returned String will be one of the following forms:<br>
+   * <pre>
+   * language (country, variant)
+   * language (country)
+   * language (variant)
+   * country (variant)
+   * language
+   * country
+   * variant
+   * </pre>
+   *
+   * @param locale locale to use for formatting
+   * @return String version of this locale, suitable for display to the user
+   */
+  /*public String getDisplayName(Locale locale)
+  {
+    CPStringBuilder result = new CPStringBuilder();
+    int count = 0;
+    String[] delimiters = {"", " (", ","};
+    if (language.length() != 0)
+      {
+        result.append(delimiters[count++]);
+        result.append(getDisplayLanguage(locale));
+      }
+    if (country.length() != 0)
+      {
+        result.append(delimiters[count++]);
+        result.append(getDisplayCountry(locale));
+      }
+    if (variant.length() != 0)
+      {
+        result.append(delimiters[count++]);
+        result.append(getDisplayVariant(locale));
+      }
+    if (count > 1)
+      result.append(")");
+    return result.toString();
+  }*/
+
+  /**
+   * Does the same as <code>Object.clone()</code> but does not throw
+   * a <code>CloneNotSupportedException</code>. Why anyone would
+   * use this method is a secret to me, since this class is immutable.
+   *
+   * @return the clone
+   */
+  public Object clone()
+  {
+    // This class is final, so no need to use native super.clone().
+    return new Locale(language, country, variant);
+  }
+
+  /**
+   * Return the hash code for this locale. The hashcode is the logical
+   * xor of the hash codes of the language, the country and the variant.
+   * The hash code is precomputed, since <code>Locale</code>s are often
+   * used in hash tables.
+   *
+   * @return the hashcode
+   */
+  public int hashCode()
+  {
+    return hashcode;
+  }
+
+  /**
+   * Compares two locales. To be equal, obj must be a Locale with the same
+   * language, country, and variant code.
+   *
+   * @param obj the other locale
+   * @return true if obj is equal to this
+   */
+  public boolean equals(Object obj)
+  {
+    if (this == obj)
+      return true;
+    if (! (obj instanceof Locale))
+      return false;
+    Locale l = (Locale) obj;
+
+    return (language == l.language 
+            && country == l.country 
+            && variant == l.variant);
+  }
+
+  /**
+   * Write the locale to an object stream.
+   *
+   * @param s the stream to write to
+   * @throws IOException if the write fails
+   * @serialData The first three fields are Strings representing language,
+   *             country, and variant. The fourth field is a placeholder for 
+   *             the cached hashcode, but this is always written as -1, and 
+   *             recomputed when reading it back.
+   */
+  /*private void writeObject(ObjectOutputStream s)
+    throws IOException
+  {
+    ObjectOutputStream.PutField fields = s.putFields();
+    fields.put("hashcode", -1);
+    s.defaultWriteObject();
+  }*/
+
+  /**
+   * Reads a locale from the input stream.
+   *
+   * @param s the stream to read from
+   * @throws IOException if reading fails
+   * @throws ClassNotFoundException if reading fails
+   * @serialData the hashCode is always invalid and must be recomputed
+   */
+  /*private void readObject(ObjectInputStream s)
+    throws IOException, ClassNotFoundException
+  {
+    s.defaultReadObject();
+    hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
+  }*/
+} // class Locale
diff --git a/Robust/src/ClassLibrary/MGC/gnu/MPN.java b/Robust/src/ClassLibrary/MGC/gnu/MPN.java
new file mode 100644 (file)
index 0000000..8b6717e
--- /dev/null
@@ -0,0 +1,771 @@
+/* gnu.java.math.MPN
+   Copyright (C) 1999, 2000, 2001, 2004  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+// Included from Kawa 1.6.62 with permission of the author,
+// Per Bothner <per@bothner.com>.
+
+//package gnu.java.math;
+
+/** This contains various low-level routines for unsigned bigints.
+ * The interfaces match the mpn interfaces in gmp,
+ * so it should be easy to replace them with fast native functions
+ * that are trivial wrappers around the mpn_ functions in gmp
+ * (at least on platforms that use 32-bit "limbs").
+ */
+
+public class MPN
+{
+  /** Add x[0:size-1] and y, and write the size least
+   * significant words of the result to dest.
+   * Return carry, either 0 or 1.
+   * All values are unsigned.
+   * This is basically the same as gmp's mpn_add_1. */
+  public static int add_1 (int[] dest, int[] x, int size, int y)
+  {
+    long carry = (long) y & 0xffffffffL;
+    for (int i = 0;  i < size;  i++)
+      {
+       carry += ((long) x[i] & 0xffffffffL);
+       dest[i] = (int) carry;
+       carry >>= 32;
+      }
+    return (int) carry;
+  }
+
+  /** Add x[0:len-1] and y[0:len-1] and write the len least
+   * significant words of the result to dest[0:len-1].
+   * All words are treated as unsigned.
+   * @return the carry, either 0 or 1
+   * This function is basically the same as gmp's mpn_add_n.
+   */
+  public static int add_n (int dest[], int[] x, int[] y, int len)
+  {
+    long carry = 0;
+    for (int i = 0; i < len;  i++)
+      {
+       carry += ((long) x[i] & 0xffffffffL)
+         + ((long) y[i] & 0xffffffffL);
+       dest[i] = (int) carry;
+       carry >>>= 32;
+      }
+    return (int) carry;
+  }
+
+  /** Subtract Y[0:size-1] from X[0:size-1], and write
+   * the size least significant words of the result to dest[0:size-1].
+   * Return borrow, either 0 or 1.
+   * This is basically the same as gmp's mpn_sub_n function.
+   */
+
+  public static int sub_n (int[] dest, int[] X, int[] Y, int size)
+  {
+    int cy = 0;
+    for (int i = 0;  i < size;  i++)
+      {
+       int y = Y[i];
+       int x = X[i];
+       y += cy;        /* add previous carry to subtrahend */
+       // Invert the high-order bit, because: (unsigned) X > (unsigned) Y
+       // iff: (int) (X^0x80000000) > (int) (Y^0x80000000).
+       cy = (y^0x80000000) < (cy^0x80000000) ? 1 : 0;
+       y = x - y;
+       cy += (y^0x80000000) > (x ^ 0x80000000) ? 1 : 0;
+       dest[i] = y;
+      }
+    return cy;
+  }
+
+  /** Multiply x[0:len-1] by y, and write the len least
+   * significant words of the product to dest[0:len-1].
+   * Return the most significant word of the product.
+   * All values are treated as if they were unsigned
+   * (i.e. masked with 0xffffffffL).
+   * OK if dest==x (not sure if this is guaranteed for mpn_mul_1).
+   * This function is basically the same as gmp's mpn_mul_1.
+   */
+
+  public static int mul_1 (int[] dest, int[] x, int len, int y)
+  {
+    long yword = (long) y & 0xffffffffL;
+    long carry = 0;
+    for (int j = 0;  j < len; j++)
+      {
+        carry += ((long) x[j] & 0xffffffffL) * yword;
+        dest[j] = (int) carry;
+        carry >>>= 32;
+      }
+    return (int) carry;
+  }
+
+  /**
+   * Multiply x[0:xlen-1] and y[0:ylen-1], and
+   * write the result to dest[0:xlen+ylen-1].
+   * The destination has to have space for xlen+ylen words,
+   * even if the result might be one limb smaller.
+   * This function requires that xlen >= ylen.
+   * The destination must be distinct from either input operands.
+   * All operands are unsigned.
+   * This function is basically the same gmp's mpn_mul. */
+
+  public static void mul (int[] dest,
+                         int[] x, int xlen,
+                         int[] y, int ylen)
+  {
+    dest[xlen] = MPN.mul_1 (dest, x, xlen, y[0]);
+
+    for (int i = 1;  i < ylen; i++)
+      {
+       long yword = (long) y[i] & 0xffffffffL;
+       long carry = 0;
+       for (int j = 0;  j < xlen; j++)
+         {
+           carry += ((long) x[j] & 0xffffffffL) * yword
+             + ((long) dest[i+j] & 0xffffffffL);
+           dest[i+j] = (int) carry;
+           carry >>>= 32;
+         }
+       dest[i+xlen] = (int) carry;
+      }
+  }
+
+  /* Divide (unsigned long) N by (unsigned int) D.
+   * Returns (remainder << 32)+(unsigned int)(quotient).
+   * Assumes (unsigned int)(N>>32) < (unsigned int)D.
+   * Code transcribed from gmp-2.0's mpn_udiv_w_sdiv function.
+   */
+  public static long udiv_qrnnd (long N, int D)
+  {
+    long q, r;
+    long a1 = N >>> 32;
+    long a0 = N & 0xffffffffL;
+    if (D >= 0)
+      {
+       if (a1 < ((D - a1 - (a0 >>> 31)) & 0xffffffffL))
+         {
+           /* dividend, divisor, and quotient are nonnegative */
+           q = N / D;
+           r = N % D;
+         }
+       else
+         {
+           /* Compute c1*2^32 + c0 = a1*2^32 + a0 - 2^31*d */
+           long c = N - ((long) D << 31);
+           /* Divide (c1*2^32 + c0) by d */
+           q = c / D;
+           r = c % D;
+           /* Add 2^31 to quotient */
+           q += 1 << 31;
+         }
+      }
+    else
+      {
+       long b1 = D >>> 1;      /* d/2, between 2^30 and 2^31 - 1 */
+       //long c1 = (a1 >> 1); /* A/2 */
+       //int c0 = (a1 << 31) + (a0 >> 1);
+       long c = N >>> 1;
+       if (a1 < b1 || (a1 >> 1) < b1)
+         {
+           if (a1 < b1)
+             {
+               q = c / b1;
+               r = c % b1;
+             }
+           else /* c1 < b1, so 2^31 <= (A/2)/b1 < 2^32 */
+             {
+               c = ~(c - (b1 << 32));
+               q = c / b1;  /* (A/2) / (d/2) */
+               r = c % b1;
+               q = (~q) & 0xffffffffL;    /* (A/2)/b1 */
+               r = (b1 - 1) - r; /* r < b1 => new r >= 0 */
+             }
+           r = 2 * r + (a0 & 1);
+           if ((D & 1) != 0)
+             {
+               if (r >= q) {
+                       r = r - q;
+               } else if (q - r <= ((long) D & 0xffffffffL)) {
+                       r = r - q + D;
+                       q -= 1;
+               } else {
+                       r = r - q + D + D;
+                       q -= 2;
+               }
+             }
+         }
+       else                            /* Implies c1 = b1 */
+         {                             /* Hence a1 = d - 1 = 2*b1 - 1 */
+           if (a0 >= ((long)(-D) & 0xffffffffL))
+             {
+               q = -1;
+               r = a0 + D;
+             }
+           else
+             {
+               q = -2;
+               r = a0 + D + D;
+             }
+         }
+      }
+
+    return (r << 32) | (q & 0xFFFFFFFFl);
+  }
+
+    /** Divide divident[0:len-1] by (unsigned int)divisor.
+     * Write result into quotient[0:len-1.
+     * Return the one-word (unsigned) remainder.
+     * OK for quotient==dividend.
+     */
+
+  public static int divmod_1 (int[] quotient, int[] dividend,
+                             int len, int divisor)
+  {
+    int i = len - 1;
+    long r = dividend[i];
+    if ((r & 0xffffffffL) >= ((long)divisor & 0xffffffffL))
+      r = 0;
+    else
+      {
+       quotient[i--] = 0;
+       r <<= 32;
+      }
+
+    for (;  i >= 0;  i--)
+      {
+       int n0 = dividend[i];
+       r = (r & ~0xffffffffL) | (n0 & 0xffffffffL);
+       r = udiv_qrnnd (r, divisor);
+       quotient[i] = (int) r;
+      }
+    return (int)(r >> 32);
+  }
+
+  /* Subtract x[0:len-1]*y from dest[offset:offset+len-1].
+   * All values are treated as if unsigned.
+   * @return the most significant word of
+   * the product, minus borrow-out from the subtraction.
+   */
+  public static int submul_1 (int[] dest, int offset, int[] x, int len, int y)
+  {
+    long yl = (long) y & 0xffffffffL;
+    int carry = 0;
+    int j = 0;
+    do
+      {
+       long prod = ((long) x[j] & 0xffffffffL) * yl;
+       int prod_low = (int) prod;
+       int prod_high = (int) (prod >> 32);
+       prod_low += carry;
+       // Invert the high-order bit, because: (unsigned) X > (unsigned) Y
+       // iff: (int) (X^0x80000000) > (int) (Y^0x80000000).
+       carry = ((prod_low ^ 0x80000000) < (carry ^ 0x80000000) ? 1 : 0)
+         + prod_high;
+       int x_j = dest[offset+j];
+       prod_low = x_j - prod_low;
+       if ((prod_low ^ 0x80000000) > (x_j ^ 0x80000000))
+         carry++;
+       dest[offset+j] = prod_low;
+      }
+    while (++j < len);
+    return carry;
+  }
+
+  /** Divide zds[0:nx] by y[0:ny-1].
+   * The remainder ends up in zds[0:ny-1].
+   * The quotient ends up in zds[ny:nx].
+   * Assumes:  nx>ny.
+   * (int)y[ny-1] < 0  (i.e. most significant bit set)
+   */
+
+  public static void divide (int[] zds, int nx, int[] y, int ny)
+  {
+    // This is basically Knuth's formulation of the classical algorithm,
+    // but translated from in scm_divbigbig in Jaffar's SCM implementation.
+
+    // Correspondance with Knuth's notation:
+    // Knuth's u[0:m+n] == zds[nx:0].
+    // Knuth's v[1:n] == y[ny-1:0]
+    // Knuth's n == ny.
+    // Knuth's m == nx-ny.
+    // Our nx == Knuth's m+n.
+
+    // Could be re-implemented using gmp's mpn_divrem:
+    // zds[nx] = mpn_divrem (&zds[ny], 0, zds, nx, y, ny).
+
+    int j = nx;
+    do
+      {                          // loop over digits of quotient
+       // Knuth's j == our nx-j.
+       // Knuth's u[j:j+n] == our zds[j:j-ny].
+       int qhat;  // treated as unsigned
+       if (zds[j]==y[ny-1])
+         qhat = -1;  // 0xffffffff
+       else
+         {
+           long w = (((long)(zds[j])) << 32) + ((long)zds[j-1] & 0xffffffffL);
+           qhat = (int) udiv_qrnnd (w, y[ny-1]);
+         }
+       if (qhat != 0)
+         {
+           int borrow = submul_1 (zds, j - ny, y, ny, qhat);
+           int save = zds[j];
+           long num = ((long)save&0xffffffffL) - ((long)borrow&0xffffffffL);
+            while (num != 0)
+             {
+               qhat--;
+               long carry = 0;
+               for (int i = 0;  i < ny; i++)
+                 {
+                   carry += ((long) zds[j-ny+i] & 0xffffffffL)
+                     + ((long) y[i] & 0xffffffffL);
+                   zds[j-ny+i] = (int) carry;
+                   carry >>>= 32;
+                 }
+               zds[j] += carry;
+               num = carry - 1;
+             }
+         }
+       zds[j] = qhat;
+      } while (--j >= ny);
+  }
+
+  /** Number of digits in the conversion base that always fits in a word.
+   * For example, for base 10 this is 9, since 10**9 is the
+   * largest number that fits into a words (assuming 32-bit words).
+   * This is the same as gmp's __mp_bases[radix].chars_per_limb.
+   * @param radix the base
+   * @return number of digits */
+  public static int chars_per_word (int radix)
+  {
+    if (radix < 10)
+      {
+       if (radix < 8)
+         {
+           if (radix <= 2)
+             return 32;
+           else if (radix == 3)
+             return 20;
+           else if (radix == 4)
+             return 16;
+           else
+             return 18 - radix;
+         }
+       else
+         return 10;
+      }
+    else if (radix < 12)
+      return 9;
+    else if (radix <= 16)
+      return 8;
+    else if (radix <= 23)
+      return 7;
+    else if (radix <= 40)
+      return 6;
+    // The following are conservative, but we don't care.
+    else if (radix <= 256)
+      return 4;
+    else
+      return 1;
+  }
+
+  /** Count the number of leading zero bits in an int. */
+  public static int count_leading_zeros (int i)
+  {
+    if (i == 0)
+      return 32;
+    int count = 0;
+    for (int k = 16;  k > 0;  k = k >> 1) {
+      int j = i >>> k;
+      if (j == 0)
+       count += k;
+      else
+       i = j;
+    }
+    return count;
+  }
+
+  public static int set_str (int dest[], byte[] str, int str_len, int base)
+  {
+    int size = 0;
+    if ((base & (base - 1)) == 0)
+      {
+       // The base is a power of 2.  Read the input string from
+       // least to most significant character/digit.  */
+
+       int next_bitpos = 0;
+       int bits_per_indigit = 0;
+       for (int i = base; (i >>= 1) != 0; ) bits_per_indigit++;
+       int res_digit = 0;
+
+       for (int i = str_len;  --i >= 0; )
+         {
+           int inp_digit = str[i];
+           res_digit |= inp_digit << next_bitpos;
+           next_bitpos += bits_per_indigit;
+           if (next_bitpos >= 32)
+             {
+               dest[size++] = res_digit;
+               next_bitpos -= 32;
+               res_digit = inp_digit >> (bits_per_indigit - next_bitpos);
+             }
+         }
+
+       if (res_digit != 0)
+         dest[size++] = res_digit;
+      }
+    else
+      {
+       // General case.  The base is not a power of 2.
+       int indigits_per_limb = MPN.chars_per_word (base);
+       int str_pos = 0;
+
+       while (str_pos < str_len)
+         {
+           int chunk = str_len - str_pos;
+           if (chunk > indigits_per_limb)
+             chunk = indigits_per_limb;
+           int res_digit = str[str_pos++];
+           int big_base = base;
+
+           while (--chunk > 0)
+             {
+               res_digit = res_digit * base + str[str_pos++];
+               big_base *= base;
+             }
+
+           int cy_limb;
+           if (size == 0)
+             cy_limb = res_digit;
+           else
+             {
+               cy_limb = MPN.mul_1 (dest, dest, size, big_base);
+               cy_limb += MPN.add_1 (dest, dest, size, res_digit);
+             }
+           if (cy_limb != 0)
+             dest[size++] = cy_limb;
+         }
+       }
+    return size;
+  }
+
+  /** Compare x[0:size-1] with y[0:size-1], treating them as unsigned integers.
+   * @result -1, 0, or 1 depending on if x&lt;y, x==y, or x&gt;y.
+   * This is basically the same as gmp's mpn_cmp function.
+   */
+  public static int cmp (int[] x, int[] y, int size)
+  {
+    while (--size >= 0)
+      {
+       int x_word = x[size];
+       int y_word = y[size];
+       if (x_word != y_word)
+         {
+           // Invert the high-order bit, because:
+           // (unsigned) X > (unsigned) Y iff
+           // (int) (X^0x80000000) > (int) (Y^0x80000000).
+           return (x_word ^ 0x80000000) > (y_word ^0x80000000) ? 1 : -1;
+         }
+      }
+    return 0;
+  }
+
+  /**
+   * Compare x[0:xlen-1] with y[0:ylen-1], treating them as unsigned integers.
+   * 
+   * @return -1, 0, or 1 depending on if x&lt;y, x==y, or x&gt;y.
+   */
+  public static int cmp (int[] x, int xlen, int[] y, int ylen)
+  {
+    return xlen > ylen ? 1 : xlen < ylen ? -1 : cmp (x, y, xlen);
+  }
+
+  /**
+   * Shift x[x_start:x_start+len-1] count bits to the "right"
+   * (i.e. divide by 2**count).
+   * Store the len least significant words of the result at dest.
+   * The bits shifted out to the right are returned.
+   * OK if dest==x.
+   * Assumes: 0 &lt; count &lt; 32
+   */
+  public static int rshift (int[] dest, int[] x, int x_start,
+                           int len, int count)
+  {
+    int count_2 = 32 - count;
+    int low_word = x[x_start];
+    int retval = low_word << count_2;
+    int i = 1;
+    for (; i < len;  i++)
+      {
+       int high_word = x[x_start+i];
+       dest[i-1] = (low_word >>> count) | (high_word << count_2);
+       low_word = high_word;
+      }
+    dest[i-1] = low_word >>> count;
+    return retval;
+  }
+
+  /**
+   * Shift x[x_start:x_start+len-1] count bits to the "right"
+   * (i.e. divide by 2**count).
+   * Store the len least significant words of the result at dest.
+   * OK if dest==x.
+   * Assumes: 0 &lt;= count &lt; 32
+   * Same as rshift, but handles count==0 (and has no return value).
+   */
+  public static void rshift0 (int[] dest, int[] x, int x_start,
+                             int len, int count)
+  {
+    if (count > 0)
+      rshift(dest, x, x_start, len, count);
+    else
+      for (int i = 0;  i < len;  i++)
+       dest[i] = x[i + x_start];
+  }
+
+  /** Return the long-truncated value of right shifting.
+  * @param x a two's-complement "bignum"
+  * @param len the number of significant words in x
+  * @param count the shift count
+  * @return (long)(x[0..len-1] &gt;&gt; count).
+  */
+  public static long rshift_long (int[] x, int len, int count)
+  {
+    int wordno = count >> 5;
+    count &= 31;
+    int sign = x[len-1] < 0 ? -1 : 0;
+    int w0 = wordno >= len ? sign : x[wordno];
+    wordno++;
+    int w1 = wordno >= len ? sign : x[wordno];
+    if (count != 0)
+      {
+       wordno++;
+       int w2 = wordno >= len ? sign : x[wordno];
+       w0 = (w0 >>> count) | (w1 << (32-count));
+       w1 = (w1 >>> count) | (w2 << (32-count));
+      }
+    return ((long)w1 << 32) | ((long)w0 & 0xffffffffL);
+  }
+
+  /* Shift x[0:len-1] left by count bits, and store the len least
+   * significant words of the result in dest[d_offset:d_offset+len-1].
+   * Return the bits shifted out from the most significant digit.
+   * Assumes 0 &lt; count &lt; 32.
+   * OK if dest==x.
+   */
+
+  public static int lshift (int[] dest, int d_offset,
+                           int[] x, int len, int count)
+  {
+    int count_2 = 32 - count;
+    int i = len - 1;
+    int high_word = x[i];
+    int retval = high_word >>> count_2;
+    d_offset++;
+    while (--i >= 0)
+      {
+       int low_word = x[i];
+       dest[d_offset+i] = (high_word << count) | (low_word >>> count_2);
+       high_word = low_word;
+      }
+    dest[d_offset+i] = high_word << count;
+    return retval;
+  }
+
+  /** Return least i such that word &amp; (1&lt;&lt;i). Assumes word!=0. */
+
+  public static int findLowestBit (int word)
+  {
+    int i = 0;
+    while ((word & 0xF) == 0)
+      {
+       word >>= 4;
+       i += 4;
+      }
+    if ((word & 3) == 0)
+      {
+       word >>= 2;
+       i += 2;
+      }
+    if ((word & 1) == 0)
+      i += 1;
+    return i;
+  }
+
+  /** Return least i such that words &amp; (1&lt;&lt;i). Assumes there is such an i. */
+
+  public static int findLowestBit (int[] words)
+  {
+    for (int i = 0;  ; i++)
+      {
+       if (words[i] != 0)
+         return 32 * i + findLowestBit (words[i]);
+      }
+  }
+
+  /** Calculate Greatest Common Divisior of x[0:len-1] and y[0:len-1].
+    * Assumes both arguments are non-zero.
+    * Leaves result in x, and returns len of result.
+    * Also destroys y (actually sets it to a copy of the result). */
+
+  public static int gcd (int[] x, int[] y, int len)
+  {
+    int i, word;
+    // Find sh such that both x and y are divisible by 2**sh.
+    for (i = 0; ; i++)
+      {
+       word = x[i] | y[i];
+       if (word != 0)
+         {
+           // Must terminate, since x and y are non-zero.
+           break;
+         }
+      }
+    int initShiftWords = i;
+    int initShiftBits = findLowestBit (word);
+    // Logically: sh = initShiftWords * 32 + initShiftBits
+
+    // Temporarily devide both x and y by 2**sh.
+    len -= initShiftWords;
+    MPN.rshift0 (x, x, initShiftWords, len, initShiftBits);
+    MPN.rshift0 (y, y, initShiftWords, len, initShiftBits);
+
+    int[] odd_arg; /* One of x or y which is odd. */
+    int[] other_arg; /* The other one can be even or odd. */
+    if ((x[0] & 1) != 0)
+      {
+       odd_arg = x;
+       other_arg = y;
+      }
+    else
+      {
+       odd_arg = y;
+       other_arg = x;
+      }
+
+    for (;;)
+      {
+       // Shift other_arg until it is odd; this doesn't
+       // affect the gcd, since we divide by 2**k, which does not
+       // divide odd_arg.
+       for (i = 0; other_arg[i] == 0; ) i++;
+       if (i > 0)
+         {
+           int j;
+           for (j = 0; j < len-i; j++)
+               other_arg[j] = other_arg[j+i];
+           for ( ; j < len; j++)
+             other_arg[j] = 0;
+         }
+       i = findLowestBit(other_arg[0]);
+       if (i > 0)
+         MPN.rshift (other_arg, other_arg, 0, len, i);
+
+       // Now both odd_arg and other_arg are odd.
+
+       // Subtract the smaller from the larger.
+       // This does not change the result, since gcd(a-b,b)==gcd(a,b).
+       i = MPN.cmp(odd_arg, other_arg, len);
+       if (i == 0)
+           break;
+       if (i > 0)
+         { // odd_arg > other_arg
+           MPN.sub_n (odd_arg, odd_arg, other_arg, len);
+           // Now odd_arg is even, so swap with other_arg;
+           int[] tmp = odd_arg; odd_arg = other_arg; other_arg = tmp;
+         }
+       else
+         { // other_arg > odd_arg
+           MPN.sub_n (other_arg, other_arg, odd_arg, len);
+       }
+       while (odd_arg[len-1] == 0 && other_arg[len-1] == 0)
+         len--;
+    }
+    if (initShiftWords + initShiftBits > 0)
+      {
+       if (initShiftBits > 0)
+         {
+           int sh_out = MPN.lshift (x, initShiftWords, x, len, initShiftBits);
+           if (sh_out != 0)
+             x[(len++)+initShiftWords] = sh_out;
+         }
+       else
+         {
+           for (i = len; --i >= 0;)
+             x[i+initShiftWords] = x[i];
+         }
+       for (i = initShiftWords;  --i >= 0; )
+         x[i] = 0;
+       len += initShiftWords;
+      }
+    return len;
+  }
+
+  public static int intLength (int i)
+  {
+    return 32 - count_leading_zeros (i < 0 ? ~i : i);
+  }
+
+  /** Calcaulte the Common Lisp "integer-length" function.
+   * Assumes input is canonicalized:  len==BigInteger.wordsNeeded(words,len) */
+  public static int intLength (int[] words, int len)
+  {
+    len--;
+    return intLength (words[len]) + 32 * len;
+  }
+
+  /* DEBUGGING:
+  public static void dprint (BigInteger x)
+  {
+    if (x.words == null)
+      System.err.print(Long.toString((long) x.ival & 0xffffffffL, 16));
+    else
+      dprint (System.err, x.words, x.ival);
+  }
+  public static void dprint (int[] x) { dprint (System.err, x, x.length); }
+  public static void dprint (int[] x, int len) { dprint (System.err, x, len); }
+  public static void dprint (java.io.PrintStream ps, int[] x, int len)
+  {
+    ps.print('(');
+    for (int i = 0;  i < len; i++)
+      {
+       if (i > 0)
+         ps.print (' ');
+       ps.print ("#x" + Long.toString ((long) x[i] & 0xffffffffL, 16));
+      }
+    ps.print(')');
+  }
+  */
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/PrintStream.java b/Robust/src/ClassLibrary/MGC/gnu/PrintStream.java
new file mode 100644 (file)
index 0000000..85b5b47
--- /dev/null
@@ -0,0 +1,675 @@
+/* PrintStream.java -- OutputStream for printing output
+   Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+//package java.io;
+
+/*import java.util.Locale;
+import java.util.Formatter;
+
+import gnu.classpath.SystemProperties;
+*/
+/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
+ * "The Java Language Specification", ISBN 0-201-63451-1
+ * Status:  Believed complete and correct to 1.3
+ */
+
+/**
+ * This class prints Java primitive values and object to a stream as
+ * text.  None of the methods in this class throw an exception.  However,
+ * errors can be detected by calling the <code>checkError()</code> method.
+ * Additionally, this stream can be designated as "autoflush" when 
+ * created so that any writes are automatically flushed to the underlying
+ * output sink when the current line is terminated.
+ * <p>
+ * This class converts char's into byte's using the system default encoding.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+public class PrintStream extends FilterOutputStream //implements Appendable
+{
+  /* Notice the implementation is quite similar to OutputStreamWriter.
+   * This leads to some minor duplication, because neither inherits
+   * from the other, and we want to maximize performance. */
+
+  // Line separator string.
+  private static final char[] line_separator
+    = {}/*SystemProperties.getProperty("line.separator", "\n").toCharArray()*/;
+
+  /**
+   *  Encoding name
+   */
+  private final String encoding;
+
+  /**
+   * This boolean indicates whether or not an error has ever occurred
+   * on this stream.
+   */
+  private boolean error_occurred = false;
+
+  /**
+   * This is <code>true</code> if auto-flush is enabled, 
+   * <code>false</code> otherwise
+   */
+  private final boolean auto_flush;
+
+  /**
+   * This method initializes a new <code>PrintStream</code> object to write
+   * to the specified output File. Doesn't autoflush.
+   *
+   * @param file The <code>File</code> to write to.
+   * @throws FileNotFoundException if an error occurs while opening the file.
+   *
+   * @since 1.5
+   */
+  public PrintStream (File file)
+    //throws FileNotFoundException
+  {
+    this (new FileOutputStream(file), false);
+  }
+
+  /**
+   * This method initializes a new <code>PrintStream</code> object to write
+   * to the specified output File. Doesn't autoflush.
+   *
+   * @param file The <code>File</code> to write to.
+   * @param encoding The name of the character encoding to use for this
+   * object.
+   * @throws FileNotFoundException If an error occurs while opening the file.
+   * @throws UnsupportedEncodingException If the charset specified by
+   * <code>encoding</code> is invalid.
+   *
+   * @since 1.5
+   */
+  public PrintStream (File file, String encoding)
+    //throws FileNotFoundException,UnsupportedEncodingException
+  {
+    this (new FileOutputStream(file), false, encoding);
+  }
+
+  /**
+   * This method initializes a new <code>PrintStream</code> object to write
+   * to the specified output File. Doesn't autoflush.
+   *
+   * @param fileName The name of the <code>File</code> to write to.
+   * @throws FileNotFoundException if an error occurs while opening the file,
+   *
+   * @since 1.5
+   */
+  public PrintStream (String fileName)
+    //throws FileNotFoundException
+  {
+    this (new FileOutputStream(new File(fileName)), false);
+  }
+
+  /**
+   * This method initializes a new <code>PrintStream</code> object to write
+   * to the specified output File. Doesn't autoflush.
+   *
+   * @param fileName The name of the <code>File</code> to write to.
+   * @param encoding The name of the character encoding to use for this
+   * object.
+   * @throws FileNotFoundException if an error occurs while opening the file.
+   * @throws UnsupportedEncodingException If the charset specified by
+   * <code>encoding</code> is invalid.
+   *
+   * @since 1.5
+   */
+  public PrintStream (String fileName, String encoding)
+      //throws FileNotFoundException,UnsupportedEncodingException
+  {
+    this (new FileOutputStream(new File(fileName)), false, encoding);
+  }
+
+  /**
+   * This method initializes a new <code>PrintStream</code> object to write
+   * to the specified output sink. Doesn't autoflush.
+   *
+   * @param out The <code>OutputStream</code> to write to.
+   */
+  public PrintStream (OutputStream out)
+  {
+    this (out, false);
+  }
+
+  /**
+   * This method initializes a new <code>PrintStream</code> object to write
+   * to the specified output sink.  This constructor also allows "auto-flush"
+   * functionality to be specified where the stream will be flushed after
+   * every <code>print</code> or <code>println</code> call, when the 
+   * <code>write</code> methods with array arguments are called, or when a 
+   * single new-line character is written.
+   * <p>
+   *
+   * @param out The <code>OutputStream</code> to write to.
+   * @param auto_flush <code>true</code> to flush the stream after every 
+   * line, <code>false</code> otherwise
+   */
+  public PrintStream (OutputStream out, boolean auto_flush)
+  {
+    super (out);
+    /*String encoding;
+    try {
+       encoding = SystemProperties.getProperty("file.encoding");
+    } catch (SecurityException e){
+       encoding = "ISO8859_1";
+    } catch (IllegalArgumentException e){
+       encoding = "ISO8859_1";
+    } catch (NullPointerException e){
+       encoding = "ISO8859_1";
+    }*/
+    this.encoding = "ISO8859_1"; //encoding;
+    this.auto_flush = auto_flush;
+  }
+
+  /**
+   * This method initializes a new <code>PrintStream</code> object to write
+   * to the specified output sink.  This constructor also allows "auto-flush"
+   * functionality to be specified where the stream will be flushed after
+   * every <code>print</code> or <code>println</code> call, when the 
+   * <code>write</code> methods with array arguments are called, or when a 
+   * single new-line character is written.
+   * <p>
+   *
+   * @param out The <code>OutputStream</code> to write to.
+   * @param auto_flush <code>true</code> to flush the stream after every 
+   * line, <code>false</code> otherwise
+   * @param encoding The name of the character encoding to use for this
+   * object.
+   */
+  public PrintStream (OutputStream out, boolean auto_flush, String encoding)
+    //throws UnsupportedEncodingException
+  {
+    super (out);
+
+    new String(new byte[]{0}, encoding);    // check if encoding is supported
+    this.encoding = encoding;
+    this.auto_flush = auto_flush;
+  }
+
+  /**
+   * This method checks to see if an error has occurred on this stream.  Note
+   * that once an error has occurred, this method will continue to report
+   * <code>true</code> forever for this stream.  Before checking for an
+   * error condition, this method flushes the stream.
+   *
+   * @return <code>true</code> if an error has occurred, 
+   * <code>false</code> otherwise
+   */
+  public boolean checkError ()
+  {
+    flush ();
+    return error_occurred;
+  }
+
+  /**
+   * This method can be called by subclasses to indicate that an error
+   * has occurred and should be reported by <code>checkError</code>.
+   */
+  protected void setError ()
+  {
+    error_occurred = true;
+  }
+
+  /**
+   * This method closes this stream and all underlying streams.
+   */
+  public void close ()
+  {
+    /*try
+      {
+       flush();
+       out.close();
+      }
+    catch (InterruptedIOException iioe)
+      {
+       Thread.currentThread().interrupt();
+      }
+    catch (IOException e)
+      {
+       setError ();
+      }*/
+  }
+
+  /**
+   * This method flushes any buffered bytes to the underlying stream and
+   * then flushes that stream as well.
+   */
+  public void flush ()
+  {
+    /*try
+      {
+       out.flush();
+      }
+    catch (InterruptedIOException iioe)
+      {
+       Thread.currentThread().interrupt();
+      }
+    catch (IOException e)
+      {
+       setError ();
+      }*/
+  }
+
+  private synchronized void print (String str, boolean println)
+  {
+    /*try
+      {
+        writeChars(str, 0, str.length());
+       if (println)
+         writeChars(line_separator, 0, line_separator.length);
+       if (auto_flush)
+         flush();
+      }
+    catch (InterruptedIOException iioe)
+      {
+       Thread.currentThread().interrupt();
+      }
+    catch (IOException e)
+      {
+       setError ();
+      }*/
+  }
+
+  private synchronized void print (char[] chars, int pos, int len,
+                                  boolean println)
+  {
+    /*try
+      {
+        writeChars(chars, pos, len);
+       if (println)
+         writeChars(line_separator, 0, line_separator.length);
+       if (auto_flush)
+         flush();
+      }
+    catch (InterruptedIOException iioe)
+      {
+       Thread.currentThread().interrupt();
+      }
+    catch (IOException e)
+      {
+       setError ();
+      }*/
+  }
+
+  private void writeChars(char[] buf, int offset, int count)
+    //throws IOException
+  {
+      /*byte[] bytes = (new String(buf, offset, count)).getBytes(encoding);
+      out.write(bytes, 0, bytes.length);*/
+  }
+
+  private void writeChars(String str, int offset, int count)
+    //throws IOException
+  {
+      /*byte[] bytes = str.substring(offset, offset+count).getBytes(encoding);
+      out.write(bytes, 0, bytes.length);*/
+  }
+
+  /**
+   * This methods prints a boolean value to the stream.  <code>true</code>
+   * values are printed as "true" and <code>false</code> values are printed
+   * as "false".
+   *
+   * @param bool The <code>boolean</code> value to print
+   */
+  public void print (boolean bool)
+  {
+    print(String.valueOf(bool), false);
+  }
+
+  /**
+   * This method prints an integer to the stream.  The value printed is
+   * determined using the <code>String.valueOf()</code> method.
+   *
+   * @param inum The <code>int</code> value to be printed
+   */
+  public void print (int inum)
+  {
+    print(String.valueOf(inum), false);
+  }
+
+  /**
+   * This method prints a long to the stream.  The value printed is
+   * determined using the <code>String.valueOf()</code> method.
+   *
+   * @param lnum The <code>long</code> value to be printed
+   */
+  public void print (long lnum)
+  {
+    print(String.valueOf(lnum), false);
+  }
+
+  /**
+   * This method prints a float to the stream.  The value printed is
+   * determined using the <code>String.valueOf()</code> method.
+   *
+   * @param fnum The <code>float</code> value to be printed
+   */
+  public void print (float fnum)
+  {
+    print(String.valueOf(fnum), false);
+  }
+
+  /**
+   * This method prints a double to the stream.  The value printed is
+   * determined using the <code>String.valueOf()</code> method.
+   *
+   * @param dnum The <code>double</code> value to be printed
+   */
+  public void print (double dnum)
+  {
+    print(String.valueOf(dnum), false);
+  }
+
+  /**
+   * This method prints an <code>Object</code> to the stream.  The actual
+   * value printed is determined by calling the <code>String.valueOf()</code>
+   * method.
+   *
+   * @param obj The <code>Object</code> to print.
+   */
+  public void print (Object obj)
+  {
+    print(obj == null ? "null" : obj.toString(), false);
+  }
+
+  /**
+   * This method prints a <code>String</code> to the stream.  The actual
+   * value printed depends on the system default encoding.
+   *
+   * @param str The <code>String</code> to print.
+   */
+  public void print (String str)
+  {
+    print(str == null ? "null" : str, false);
+  }
+
+  /**
+   * This method prints a char to the stream.  The actual value printed is
+   * determined by the character encoding in use.
+   *
+   * @param ch The <code>char</code> value to be printed
+   */
+  public synchronized void print (char ch)
+  {
+    print(new char[]{ch}, 0, 1, false);
+  }
+
+  /**
+   * This method prints an array of characters to the stream.  The actual
+   * value printed depends on the system default encoding.
+   *
+   * @param charArray The array of characters to print.
+   */
+  public void print (char[] charArray)
+  {
+    print(charArray, 0, charArray.length, false);
+  }
+
+  /**
+   * This method prints a line separator sequence to the stream.  The value
+   * printed is determined by the system property <xmp>line.separator</xmp>
+   * and is not necessarily the Unix '\n' newline character.
+   */
+  /*public void println ()
+  {
+    print(line_separator, 0, line_separator.length, false);
+  }*/
+
+  /**
+   * This methods prints a boolean value to the stream.  <code>true</code>
+   * values are printed as "true" and <code>false</code> values are printed
+   * as "false".
+   * <p>
+   * This method prints a line termination sequence after printing the value.
+   *
+   * @param bool The <code>boolean</code> value to print
+   */
+  public void println (boolean bool)
+  {
+    print(String.valueOf(bool), true);
+  }
+
+  /**
+   * This method prints an integer to the stream.  The value printed is
+   * determined using the <code>String.valueOf()</code> method.
+   * <p>
+   * This method prints a line termination sequence after printing the value.
+   *
+   * @param inum The <code>int</code> value to be printed
+   */
+  public void println (int inum)
+  {
+    print(String.valueOf(inum), true);
+  }
+
+  /**
+   * This method prints a long to the stream.  The value printed is
+   * determined using the <code>String.valueOf()</code> method.
+   * <p>
+   * This method prints a line termination sequence after printing the value.
+   *
+   * @param lnum The <code>long</code> value to be printed
+   */
+  public void println (long lnum)
+  {
+    print(String.valueOf(lnum), true);
+  }
+
+  /**
+   * This method prints a float to the stream.  The value printed is
+   * determined using the <code>String.valueOf()</code> method.
+   * <p>
+   * This method prints a line termination sequence after printing the value.
+   *
+   * @param fnum The <code>float</code> value to be printed
+   */
+  public void println (float fnum)
+  {
+    print(String.valueOf(fnum), true);
+  }
+
+  /**
+   * This method prints a double to the stream.  The value printed is
+   * determined using the <code>String.valueOf()</code> method.
+   * <p>
+   * This method prints a line termination sequence after printing the value.
+   *
+   * @param dnum The <code>double</code> value to be printed
+   */
+  public void println (double dnum)
+  {
+    print(String.valueOf(dnum), true);
+  }
+
+  /**
+   * This method prints an <code>Object</code> to the stream.  The actual
+   * value printed is determined by calling the <code>String.valueOf()</code>
+   * method.
+   * <p>
+   * This method prints a line termination sequence after printing the value.
+   *
+   * @param obj The <code>Object</code> to print.
+   */
+  public void println (Object obj)
+  {
+    print(obj == null ? "null" : obj.toString(), true);
+  }
+
+  /**
+   * This method prints a <code>String</code> to the stream.  The actual
+   * value printed depends on the system default encoding.
+   * <p>
+   * This method prints a line termination sequence after printing the value.
+   *
+   * @param str The <code>String</code> to print.
+   */
+  public void println (String str)
+  {
+    print (str == null ? "null" : str, true);
+  }
+
+  /**
+   * This method prints a char to the stream.  The actual value printed is
+   * determined by the character encoding in use.
+   * <p>
+   * This method prints a line termination sequence after printing the value.
+   *
+   * @param ch The <code>char</code> value to be printed
+   */
+  public synchronized void println (char ch)
+  {
+    print(new char[]{ch}, 0, 1, true);
+  }
+
+  /**
+   * This method prints an array of characters to the stream.  The actual
+   * value printed depends on the system default encoding.
+   * <p>
+   * This method prints a line termination sequence after printing the value.
+   *
+   * @param charArray The array of characters to print.
+   */
+  public void println (char[] charArray)
+  {
+    print(charArray, 0, charArray.length, true);
+  }
+
+  /**
+   * This method writes a byte of data to the stream.  If auto-flush is
+   * enabled, printing a newline character will cause the stream to be
+   * flushed after the character is written.
+   * 
+   * @param oneByte The byte to be written
+   */
+  public void write (int oneByte)
+  {
+    /*try
+      {
+        out.write (oneByte & 0xff);
+        
+        if (auto_flush && (oneByte == '\n'))
+          flush ();
+      }
+    catch (InterruptedIOException iioe)
+      {
+       Thread.currentThread ().interrupt ();
+      }
+    catch (IOException e)
+      {
+        setError ();
+      }*/
+  }
+
+  /**
+   * This method writes <code>len</code> bytes from the specified array
+   * starting at index <code>offset</code> into the array.
+   *
+   * @param buffer The array of bytes to write
+   * @param offset The index into the array to start writing from
+   * @param len The number of bytes to write
+   */
+  public void write (byte[] buffer, int offset, int len)
+  {
+    /*try
+      {
+        out.write (buffer, offset, len);
+        
+        if (auto_flush)
+          flush ();
+      }
+    catch (InterruptedIOException iioe)
+      {
+       Thread.currentThread ().interrupt ();
+      }
+    catch (IOException e)
+      {
+        setError ();
+      }*/
+  }
+
+  /** @since 1.5 */
+  public PrintStream append(char c)
+  {
+    print(c);
+    return this;
+  }
+
+  /** @since 1.5 */
+  /*public PrintStream append(CharSequence cs)
+  {
+    print(cs == null ? "null" : cs.toString());
+    return this;
+  }*/
+
+  /** @since 1.5 */
+  /*public PrintStream append(CharSequence cs, int start, int end)
+  {
+    print(cs == null ? "null" : cs.subSequence(start, end).toString());
+    return this;
+  }*/
+
+  /** @since 1.5 */
+  /*public PrintStream printf(String format, Object... args)
+  {
+    return this; //format(format, args);
+  }*/
+
+  /** @since 1.5 */
+  /*public PrintStream printf(Locale locale, String format, Object... args)
+  {
+    return format(locale, format, args);
+  }*/
+
+  /** @since 1.5 */
+  /*public PrintStream format(String format, Object... args)
+  {
+    return this; //format(Locale.getDefault(), format, args);
+  }*/
+
+  /** @since 1.5 */
+  /*public PrintStream format(Locale locale, String format, Object... args)
+  {
+    Formatter f = new Formatter(this, locale);
+    f.format(format, args);
+    return this;
+  }*/
+} // class PrintStream
diff --git a/Robust/src/ClassLibrary/MGC/gnu/Reader.java b/Robust/src/ClassLibrary/MGC/gnu/Reader.java
new file mode 100644 (file)
index 0000000..ef2268f
--- /dev/null
@@ -0,0 +1,288 @@
+/* Reader.java -- base class of classes that read input as a stream of chars
+   Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005  Free Software Foundation
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+//package java.io;
+//import java.nio.CharBuffer;
+
+///* Written using "Java Class Libraries", 2nd edition, plus online
+ * API docs for JDK 1.2 beta from http://www.javasoft.com.
+ * Status:  Believed complete and correct.
+ */
+
+/**
+ * This abstract class forms the base of the hierarchy of classes that read
+ * input as a stream of characters.  It provides a common set of methods for
+ * reading characters from streams.  Subclasses implement and extend these
+ * methods to read characters from a particular input source such as a file
+ * or network connection.
+ *
+ * @author Per Bothner (bothner@cygnus.com)
+ * @date April 21, 1998.  
+ * @author Aaron M. Renn (arenn@urbanophile.com) 
+ */
+public abstract class Reader //implements Closeable, Readable
+{
+  /**
+   * This is the <code>Object</code> used for synchronizing critical code
+   * sections.  Subclasses should use this variable instead of a 
+   * synchronized method or an explicit synchronization on <code>this</code>
+   */
+  protected Object lock;
+  
+  /**
+    * Unitializes a <code>Reader</code> that will use the object
+    * itself for synchronization of critical code sections.
+    */
+  protected Reader()
+  {
+    this.lock = this;
+  }
+
+  /**
+    * Initializes a <code>Reader</code> that will use the specified
+    * <code>Object</code> for synchronization of critical code sections.
+    *
+    * @param lock The <code>Object</code> to use for synchronization
+    */
+  protected Reader(Object lock)
+  {
+    this.lock = lock;
+  }
+
+  /**
+   * Read chars from a stream and stores them into a caller
+   * supplied buffer.  It starts storing the data at index <code>offset</code> 
+   * into the buffer and attempts to read <code>len</code> chars.  This method 
+   * can return before reading the number of chars requested.  The actual 
+   * number of chars read is returned as an int.  A -1 is returned to indicate 
+   * the end of the stream.
+   * <p>
+   * This method will block until some data can be read.
+   * <p>
+   * This method operates by calling the single char <code>read()</code> method
+   * in a loop until the desired number of chars are read.  The read loop
+   * stops short if the end of the stream is encountered or if an IOException
+   * is encountered on any read operation except the first.  If the first
+   * attempt to read a chars fails, the IOException is allowed to propagate
+   * upward.  And subsequent IOException is caught and treated identically
+   * to an end of stream condition.  Subclasses can (and should if possible)
+   * override this method to provide a more efficient implementation.
+   *
+   * @param buf The array into which the chars read should be stored
+   * @param offset The offset into the array to start storing chars
+   * @param count The requested number of chars to read
+   *
+   * @return The actual number of chars read, or -1 if end of stream.
+   *
+   * @exception IOException If an error occurs.
+   */
+  public /*abstract*/ int read(char buf[], int offset, int count) {
+    return -1;
+  }
+    //throws IOException;
+    
+  /**
+   * Reads chars from a stream and stores them into a caller
+   * supplied buffer.  This method attempts to completely fill the buffer,
+   * but can return before doing so.  The actual number of chars read is
+   * returned as an int.  A -1 is returned to indicate the end of the stream.
+   * <p>
+   * This method will block until some data can be read.
+   * <p>
+   * This method operates by calling an overloaded read method like so:
+   * <code>read(buf, 0, buf.length)</code>
+   *
+   * @param buf The buffer into which the chars read will be stored.
+   *
+   * @return The number of chars read or -1 if end of stream.
+   *
+   * @exception IOException If an error occurs.
+   */
+  public int read(char buf[]) //throws IOException
+  {
+    return read(buf, 0, buf.length);
+  }
+
+  /**
+   * Reads an char from the input stream and returns it
+   * as an int in the range of 0-65535.  This method also will return -1 if
+   * the end of the stream has been reached.
+   * <p>
+   * This method will block until the char can be read.
+   *
+   * @return The char read or -1 if end of stream
+   *
+   * @exception IOException If an error occurs
+   */
+  public int read() //throws IOException
+  {
+    char[] buf = new char[1];
+    int count = read(buf, 0, 1);
+    return count > 0 ? buf[0] : -1;
+  }
+
+  /** @since 1.5 */
+  /*public int read(CharBuffer buffer) throws IOException
+  {
+    // We want to call put(), so we don't manipulate the CharBuffer
+    // directly.
+    int rem = buffer.remaining();
+    char[] buf = new char[rem];
+    int result = read(buf, 0, rem);
+    if (result != -1)
+      buffer.put(buf, 0, result);
+    return result;
+  }*/
+
+  /**
+   * Closes the stream.  Any futher attempts to read from the
+   * stream may generate an <code>IOException</code>.
+   *
+   * @exception IOException If an error occurs
+   */
+  public /*abstract*/ void close() {} //throws IOException;
+
+  /**
+   * Returns a boolean that indicates whether the mark/reset
+   * methods are supported in this class.  Those methods can be used to
+   * remember a specific point in the stream and reset the stream to that
+   * point.
+   * <p>
+   * This method always returns <code>false</code> in this class, but
+   * subclasses can override this method to return <code>true</code> if they 
+   * support mark/reset functionality.
+   *
+   * @return <code>true</code> if mark/reset functionality is supported, 
+   *         <code>false</code> otherwise
+   *
+   */
+  public boolean markSupported()
+  {
+    return false;
+  }
+
+  /**
+    * Marks a position in the input to which the stream can be
+    * "reset" by calling the <code>reset()</code> method.  The parameter
+    * <code>readlimit</code> is the number of chars that can be read from the 
+    * stream after setting the mark before the mark becomes invalid.  For
+    * example, if <code>mark()</code> is called with a read limit of 10, then 
+    * when 11 chars of data are read from the stream before the 
+    * <code>reset()</code> method is called, then the mark is invalid and the 
+    * stream object instance is not required to remember the mark.
+    *
+    * @param readLimit The number of chars that can be read before the mark 
+    *        becomes invalid
+    *
+    * @exception IOException If an error occurs such as mark not being 
+    *            supported for this class
+    */
+  public void mark(int readLimit) //throws IOException
+  {
+    throw new Error/*IOException*/("mark not supported");
+  }
+
+  /**
+    * Resets a stream to the point where the <code>mark()</code> 
+    * method was called.  Any chars that were read after the mark point was 
+    * set will be re-read during subsequent reads.
+    * <p>
+    * This method always throws an IOException in this class, but subclasses
+    * can override this method if they provide mark/reset functionality.
+    *
+    * @exception IOException Always thrown for this class
+    */
+  public void reset() //throws IOException
+  {
+    throw new Error/*IOException*/("reset not supported");
+  }
+
+  /**
+    * Determines whether or not this stream is ready to be
+    * read.  If it returns <code>false</code> the stream may block if a
+    * read is attempted, but it is not guaranteed to do so.
+    * <p>
+    * This method always returns <code>false</code> in this class
+    *
+    * @return <code>true</code> if the stream is ready to be read, 
+    * <code>false</code> otherwise.
+    *
+    * @exception IOException If an error occurs
+    */
+  public boolean ready() //throws IOException
+  {
+    return false;
+  }
+
+  /**
+    * Skips the specified number of chars in the stream.  It
+    * returns the actual number of chars skipped, which may be less than the
+    * requested amount.
+    * <p>
+    * This method reads and discards chars into a 256 char array until the
+    * specified number of chars were skipped or until either the end of stream
+    * is reached or a read attempt returns a short count.  Subclasses can
+    * override this method to provide a more efficient implementation where
+    * one exists.
+    *
+    * @param count The requested number of chars to skip
+    *
+    * @return The actual number of chars skipped.
+    *
+    * @exception IOException If an error occurs
+    */
+  public long skip(long count) //throws IOException
+  {
+    if (count <= 0)
+      return 0;
+    int bsize = count > 1024 ? 1024 : (int) count;
+    char[] buffer = new char[bsize];
+    long todo = count;
+    synchronized (lock)
+    {
+      while (todo > 0)
+       {
+         int skipped = read(buffer, 0, bsize > todo ? (int) todo : bsize);
+         if (skipped <= 0)
+           break;
+         todo -= skipped;
+       }
+    }
+    return count - todo;
+  }
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/RuntimeException.java b/Robust/src/ClassLibrary/MGC/gnu/RuntimeException.java
new file mode 100644 (file)
index 0000000..72cf087
--- /dev/null
@@ -0,0 +1,102 @@
+/* RuntimeException.java -- root of all unchecked exceptions
+   Copyright (C) 1998, 1999, 2001, 2002, 2005  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.lang;
+
+/**
+ * All exceptions which are subclasses of <code>RuntimeException</code>
+ * can be thrown at any time during the execution of a Java virtual machine.
+ * Methods which throw these exceptions are not required to declare them
+ * in their throws clause.
+ *
+ * @author Brian Jones
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @status updated to 1.4
+ */
+public class RuntimeException extends Exception
+{
+  /**
+   * Compatible with JDK 1.0+.
+   */
+  private static final long serialVersionUID = -7034897190745766939L;
+
+  /**
+   * Create an exception without a message. The cause remains uninitialized.
+   *
+   * @see #initCause(Throwable)
+   */
+  public RuntimeException()
+  {
+  }
+
+  /**
+   * Create an exception with a message. The cause remains uninitialized.
+   *
+   * @param s the message string
+   * @see #initCause(Throwable)
+   */
+  public RuntimeException(String s)
+  {
+    super(s);
+  }
+
+  /**
+   * Create an exception with a message and a cause.
+   *
+   * @param s the message string
+   * @param cause the cause of this exception
+   * @since 1.4
+   */
+  public RuntimeException(String s, Throwable cause)
+  {
+    super(s, cause);
+  }
+
+  /**
+   * Create an exception with the given cause, and a message of
+   * <code>cause == null ? null : cause.toString()</code>.
+   *
+   * @param cause the cause of this exception
+   * @since 1.4
+   */
+  public RuntimeException(Throwable cause)
+  {
+    super(cause);
+  }
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/SimpleTimeZone.java b/Robust/src/ClassLibrary/MGC/gnu/SimpleTimeZone.java
new file mode 100644 (file)
index 0000000..f1b7fa2
--- /dev/null
@@ -0,0 +1,1052 @@
+/* java.util.SimpleTimeZone
+   Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005, 2007
+   Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util;
+
+
+/**
+ * This class represents a simple time zone offset and handles
+ * daylight savings.  It can only handle one daylight savings rule, so
+ * it can't represent historical changes.
+ *
+ * This object is tightly bound to the Gregorian calendar.  It assumes
+ * a regular seven days week, and the month lengths are that of the
+ * Gregorian Calendar.  It can only handle daylight savings for years
+ * lying in the AD era.
+ *
+ * @see Calendar
+ * @see GregorianCalendar
+ * @author Jochen Hoenicke
+ */
+public class SimpleTimeZone extends TimeZone
+{
+  /**
+   * The raw time zone offset in milliseconds to GMT, ignoring
+   * daylight savings.
+   * @serial
+   */
+  private int rawOffset;
+
+  /**
+   * True, if this timezone uses daylight savings, false otherwise.
+   * @serial
+   */
+  private boolean useDaylight;
+
+  /**
+   * The daylight savings offset.  This is a positive offset in
+   * milliseconds with respect to standard time.  Typically this
+   * is one hour, but for some time zones this may be half an hour.
+   * @serial
+   * @since JDK1.1.4
+   */
+  private int dstSavings = 60 * 60 * 1000;
+
+  /**
+   * The first year, in which daylight savings rules applies.
+   * @serial
+   */
+  private int startYear;
+  private static final int DOM_MODE = 1;
+  private static final int DOW_IN_MONTH_MODE = 2;
+  private static final int DOW_GE_DOM_MODE = 3;
+  private static final int DOW_LE_DOM_MODE = 4;
+
+  /**
+   * The mode of the start rule. This takes one of the following values:
+   * <dl>
+   * <dt>DOM_MODE (1)</dt>
+   * <dd> startDay contains the day in month of the start date,
+   * startDayOfWeek is unused. </dd>
+   * <dt>DOW_IN_MONTH_MODE (2)</dt>
+   * <dd> The startDay gives the day of week in month, and
+   * startDayOfWeek the day of week.  For example startDay=2 and
+   * startDayOfWeek=Calender.SUNDAY specifies that the change is on
+   * the second sunday in that month.  You must make sure, that this
+   * day always exists (ie. don't specify the 5th sunday).
+   * </dd>
+   * <dt>DOW_GE_DOM_MODE (3)</dt>
+   * <dd> The start is on the first startDayOfWeek on or after
+   * startDay.  For example startDay=13 and
+   * startDayOfWeek=Calendar.FRIDAY specifies that the daylight
+   * savings start on the first FRIDAY on or after the 13th of that
+   * Month. Make sure that the change is always in the given month, or
+   * the result is undefined.
+   * </dd>
+   * <dt>DOW_LE_DOM_MONTH (4)</dt>
+   * <dd> The start is on the first startDayOfWeek on or before the
+   * startDay.  Make sure that the change is always in the given
+   * month, or the result is undefined.
+   </dd>
+   * </dl>
+   * @serial */
+  private int startMode;
+
+  /**
+   * The month in which daylight savings start.  This is one of the
+   * constants Calendar.JANUARY, ..., Calendar.DECEMBER.
+   * @serial
+   */
+  private int startMonth;
+
+  /**
+   * This variable can have different meanings.  See startMode for details
+   * @see #startMode
+   * @serial
+   */
+  private int startDay;
+
+  /**
+   * This variable specifies the day of week the change takes place.  If
+   * startMode == DOM_MODE, this is undefined.
+   * @serial
+   * @see #startMode
+   */
+  private int startDayOfWeek;
+
+  /**
+   * This variable specifies the time of change to daylight savings.
+   * This time is given in milliseconds after midnight in startTimeMode
+   * chosen time mode.
+   * @serial
+   */
+  private int startTime;
+
+  /**
+   * This variable specifies the mode that startTime is specified in.  By
+   * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME.  For
+   * startTime, STANDARD_TIME and WALL_TIME are equivalent.
+   * @serial
+   */
+  private int startTimeMode = WALL_TIME;
+
+  /**
+   * The month in which daylight savings ends.  This is one of the
+   * constants Calendar.JANUARY, ..., Calendar.DECEMBER.
+   * @serial
+   */
+  private int endMonth;
+
+  /**
+   * This variable gives the mode for the end of daylight savings rule.
+   * It can take the same values as startMode.
+   * @serial
+   * @see #startMode
+   */
+  private int endMode;
+
+  /**
+   * This variable can have different meanings.  See startMode for details
+   * @serial
+   * @see #startMode
+   */
+  private int endDay;
+
+  /**
+   * This variable specifies the day of week the change takes place.  If
+   * endMode == DOM_MODE, this is undefined.
+   * @serial
+   * @see #startMode
+   */
+  private int endDayOfWeek;
+
+  /**
+   * This variable specifies the time of change back to standard time.
+   * This time is given in milliseconds after midnight in endTimeMode
+   * chosen time mode.
+   * @serial
+   */
+  private int endTime;
+
+  /**
+   * This variable specifies the mode that endTime is specified in.  By
+   * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME.
+   * @serial
+   */
+  private int endTimeMode = WALL_TIME;
+
+  /**
+   * This variable points to a deprecated array from JDK 1.1.  It is
+   * ignored in JDK 1.2 but streamed out for compatibility with JDK 1.1.
+   * The array contains the lengths of the months in the year and is
+   * assigned from a private static final field to avoid allocating
+   * the array for every instance of the object.
+   * Note that static final fields are not serialized.
+   * @serial
+   */
+  private byte[] monthLength = monthArr;
+  private static final byte[] monthArr = 
+                                         {
+                                           31, 28, 31, 30, 31, 30, 31, 31, 30,
+                                           31, 30, 31
+                                         };
+
+  /**
+   * The version of the serialized data on the stream.
+   * <dl>
+   * <dt>0 or not present on stream</dt>
+   * <dd> JDK 1.1.3 or earlier, only provides this fields:
+   * rawOffset, startDay, startDayOfWeek, startMonth, startTime,
+   * startYear, endDay, endDayOfWeek, endMonth, endTime
+   * </dd>
+   * <dd> JDK 1.1.4 or later. This includes three new fields, namely
+   * startMode, endMode and dstSavings.  And there is a optional section
+   * as described in writeObject.
+   * </dd>
+   * </dl>
+   *
+   * XXX - JDK 1.2 Beta 4 docu states 1.1.4, but my 1.1.5 has the old
+   * version.
+   *
+   * When streaming out this class it is always written in the latest
+   * version.
+   * @serial
+   * @since JDK1.1.4
+   */
+  private int serialVersionOnStream = 2;
+  private static final long serialVersionUID = -403250971215465050L;
+
+  /**
+   * Constant to indicate that start and end times are specified in standard
+   * time, without adjusting for daylight savings.
+   */
+  public static final int STANDARD_TIME = 1;
+
+  /**
+   * Constant to indicate that start and end times are specified in wall
+   * time, adjusting for daylight savings.  This is the default.
+   */
+  public static final int WALL_TIME = 0;
+
+  /**
+   * Constant to indicate that start and end times are specified in UTC.
+   */
+  public static final int UTC_TIME = 2;
+
+  /**
+   * Create a <code>SimpleTimeZone</code> with the given time offset
+   * from GMT and without daylight savings.
+   * @param rawOffset the time offset from GMT in milliseconds.
+   * @param id The identifier of this time zone.
+   */
+  public SimpleTimeZone(int rawOffset, String id)
+  {
+    this.rawOffset = rawOffset;
+    setID(id);
+    useDaylight = false;
+    startYear = 0;
+  }
+
+  /**
+   * Create a <code>SimpleTimeZone</code> with the given time offset
+   * from GMT and with daylight savings.  The start/end parameters
+   * can have different meaning (replace WEEKDAY with a real day of
+   * week). Only the first two meanings were supported by earlier
+   * versions of jdk.
+   *
+   * <dl>
+   * <dt><code>day &gt; 0, dayOfWeek = Calendar.WEEKDAY</code></dt>
+   * <dd>The start/end of daylight savings is on the <code>day</code>-th
+   * <code>WEEKDAY</code> in the given month. </dd>
+   * <dt><code>day &lt; 0, dayOfWeek = Calendar.WEEKDAY</code></dt>
+   * <dd>The start/end of daylight savings is on the <code>-day</code>-th
+   * <code>WEEKDAY</code> counted from the <i>end</i> of the month. </dd>
+   * <dt><code>day &gt; 0, dayOfWeek = 0</code></dt>
+   * <dd>The start/end of daylight is on the <code>day</code>-th day of
+   * the month. </dd>
+   * <dt><code>day &gt; 0, dayOfWeek = -Calendar.WEEKDAY</code></dt>
+   * <dd>The start/end of daylight is on the first WEEKDAY on or after
+   * the <code>day</code>-th day of the month.  You must make sure that
+   * this day lies in the same month. </dd>
+   * <dt><code>day &lt; 0, dayOfWeek = -Calendar.WEEKDAY</code></dt>
+   * <dd>The start/end of daylight is on the first WEEKDAY on or
+   * <i>before</i> the <code>-day</code>-th day of the month.  You
+   * must make sure that this day lies in the same month. </dd>
+   * </dl>
+   *
+   * If you give a non existing month, a day that is zero, or too big,
+   * or a dayOfWeek that is too big,  the result is undefined.
+   *
+   * The start rule must have a different month than the end rule.
+   * This restriction shouldn't hurt for all possible time zones.
+   *
+   * @param rawOffset The time offset from GMT in milliseconds.
+   * @param id  The identifier of this time zone.
+   * @param startMonth The start month of daylight savings; use the
+   * constants in Calendar.
+   * @param startDayOfWeekInMonth A day in month or a day of week number, as
+   * described above.
+   * @param startDayOfWeek The start rule day of week; see above.
+   * @param startTime A time in millis in standard time.
+   * @param endMonth The end month of daylight savings; use the
+   * constants in Calendar.
+   * @param endDayOfWeekInMonth A day in month or a day of week number, as
+   * described above.
+   * @param endDayOfWeek The end rule day of week; see above.
+   * @param endTime A time in millis in standard time.
+   * @throws IllegalArgumentException if parameters are invalid or out of
+   * range.
+   */
+  public SimpleTimeZone(int rawOffset, String id, int startMonth,
+                        int startDayOfWeekInMonth, int startDayOfWeek,
+                        int startTime, int endMonth, int endDayOfWeekInMonth,
+                        int endDayOfWeek, int endTime)
+  {
+    this.rawOffset = rawOffset;
+    setID(id);
+    useDaylight = true;
+
+    setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime);
+    setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
+    if (startMonth == endMonth)
+      throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "startMonth and endMonth must be different");
+    this.startYear = 0;
+  }
+
+  /**
+   * This constructs a new SimpleTimeZone that supports a daylight savings
+   * rule.  The parameter are the same as for the constructor above, except
+   * there is the additional dstSavaings parameter.
+   *
+   * @param dstSavings the amount of savings for daylight savings
+   * time in milliseconds.  This must be positive.
+   * @since 1.2
+   */
+  public SimpleTimeZone(int rawOffset, String id, int startMonth,
+                        int startDayOfWeekInMonth, int startDayOfWeek,
+                        int startTime, int endMonth, int endDayOfWeekInMonth,
+                        int endDayOfWeek, int endTime, int dstSavings)
+  {
+    this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek,
+         startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
+
+    this.dstSavings = dstSavings;
+  }
+
+  /**
+   * This constructs a new SimpleTimeZone that supports a daylight savings
+   * rule.  The parameter are the same as for the constructor above, except
+   * there are the additional startTimeMode, endTimeMode, and dstSavings
+   * parameters.
+   *
+   * @param startTimeMode the mode that start times are specified in.  One of
+   * WALL_TIME, STANDARD_TIME, or UTC_TIME.
+   * @param endTimeMode the mode that end times are specified in.  One of
+   * WALL_TIME, STANDARD_TIME, or UTC_TIME.
+   * @param dstSavings the amount of savings for daylight savings
+   * time in milliseconds.  This must be positive.
+   * @throws IllegalArgumentException if parameters are invalid or out of
+   * range.
+   * @since 1.4
+   */
+  public SimpleTimeZone(int rawOffset, String id, int startMonth,
+                        int startDayOfWeekInMonth, int startDayOfWeek,
+                        int startTime, int startTimeMode, int endMonth,
+                        int endDayOfWeekInMonth, int endDayOfWeek,
+                        int endTime, int endTimeMode, int dstSavings)
+  {
+    this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek,
+        startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
+
+    if (startTimeMode < WALL_TIME || startTimeMode > UTC_TIME)
+      throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "startTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME");
+    if (endTimeMode < WALL_TIME || endTimeMode > UTC_TIME)
+      throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "endTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME");
+
+    this.dstSavings = dstSavings;
+    this.startTimeMode = startTimeMode;
+    this.endTimeMode = endTimeMode;
+  }
+
+  /**
+   * Sets the first year, where daylight savings applies.  The daylight
+   * savings rule never apply for years in the BC era.  Note that this
+   * is gregorian calendar specific.
+   * @param year the start year.
+   */
+  public void setStartYear(int year)
+  {
+    startYear = year;
+    useDaylight = true;
+  }
+
+  /**
+   * Checks if the month, day, dayOfWeek arguments are in range and
+   * returns the mode of the rule.
+   * @param month the month parameter as in the constructor
+   * @param day the day parameter as in the constructor
+   * @param dayOfWeek the day of week parameter as in the constructor
+   * @return the mode of this rule see startMode.
+   * @exception IllegalArgumentException if parameters are out of range.
+   * @see #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)
+   * @see #startMode
+   */
+  private int checkRule(int month, int day, int dayOfWeek)
+  {
+    if (month < 0 || month > 11)
+      throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "month out of range");
+
+    int daysInMonth = getDaysInMonth(month, 1);
+    if (dayOfWeek == 0)
+      {
+       if (day <= 0 || day > daysInMonth)
+         throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "day out of range");
+       return DOM_MODE;
+      }
+    else if (dayOfWeek > 0)
+      {
+       if (Math.abs(day) > (daysInMonth + 6) / 7)
+         throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "dayOfWeekInMonth out of range");
+       if (dayOfWeek > Calendar.SATURDAY)
+         throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "dayOfWeek out of range");
+       return DOW_IN_MONTH_MODE;
+      }
+    else
+      {
+       if (day == 0 || Math.abs(day) > daysInMonth)
+         throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "day out of range");
+       if (dayOfWeek < -Calendar.SATURDAY)
+         throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "dayOfWeek out of range");
+       if (day < 0)
+         return DOW_LE_DOM_MODE;
+       else
+         return DOW_GE_DOM_MODE;
+      }
+  }
+
+  /**
+   * Sets the daylight savings start rule.  You must also set the
+   * end rule with <code>setEndRule</code> or the result of
+   * getOffset is undefined.  For the parameters see the ten-argument
+   * constructor above.
+   *
+   * @param month The month where daylight savings start, zero
+   * based.  You should use the constants in Calendar.
+   * @param day A day of month or day of week in month.
+   * @param dayOfWeek The day of week where daylight savings start.
+   * @param time The time in milliseconds standard time where daylight
+   * savings start.
+   * @exception IllegalArgumentException if parameters are out of range.
+   * @see SimpleTimeZone
+   */
+  public void setStartRule(int month, int day, int dayOfWeek, int time)
+  {
+    this.startMode = checkRule(month, day, dayOfWeek);
+    this.startMonth = month;
+    this.startDay = day;
+    this.startDayOfWeek = Math.abs(dayOfWeek);
+    this.startTime = time;
+    this.startTimeMode = WALL_TIME;
+  }
+
+  /**
+   * Sets the daylight savings start rule.  You must also set the
+   * end rule with <code>setEndRule</code> or the result of
+   * getOffset is undefined.  For the parameters see the ten-argument
+   * constructor above.
+   *
+   * Note that this API isn't incredibly well specified.  It appears that the
+   * after flag must override the parameters, since normally, the day and
+   * dayofweek can select this.  I.e., if day < 0 and dayOfWeek < 0, on or
+   * before mode is chosen.  But if after == true, this implementation
+   * overrides the signs of the other arguments.  And if dayOfWeek == 0, it
+   * falls back to the behavior in the other APIs.  I guess this should be
+   * checked against Sun's implementation.
+   *
+   * @param month The month where daylight savings start, zero
+   * based.  You should use the constants in Calendar.
+   * @param day A day of month or day of week in month.
+   * @param dayOfWeek The day of week where daylight savings start.
+   * @param time The time in milliseconds standard time where daylight
+   * savings start.
+   * @param after If true, day and dayOfWeek specify first day of week on or
+   * after day, else first day of week on or before.
+   * @since 1.2
+   * @see SimpleTimeZone
+   */
+  public void setStartRule(int month, int day, int dayOfWeek, int time,
+                           boolean after)
+  {
+    if (after)
+      setStartRule(month, day, -dayOfWeek, time);
+    else
+      setStartRule(month, -day, -dayOfWeek, time);
+  }
+
+  /**
+   * Sets the daylight savings start rule.  You must also set the
+   * end rule with <code>setEndRule</code> or the result of
+   * getOffset is undefined.  For the parameters see the ten-argument
+   * constructor above.
+   *
+   * @param month The month where daylight savings start, zero
+   * based.  You should use the constants in Calendar.
+   * @param day A day of month or day of week in month.
+   * @param time The time in milliseconds standard time where daylight
+   * savings start.
+   * @see SimpleTimeZone
+   * @since 1.2
+   */
+  public void setStartRule(int month, int day, int time)
+  {
+    setStartRule(month, day, 0, time);
+  }
+
+  /**
+   * Sets the daylight savings end rule.  You must also set the
+   * start rule with <code>setStartRule</code> or the result of
+   * getOffset is undefined. For the parameters see the ten-argument
+   * constructor above.
+   *
+   * @param month The end month of daylight savings.
+   * @param day A day in month, or a day of week in month.
+   * @param dayOfWeek A day of week, when daylight savings ends.
+   * @param time A time in millis in standard time.
+   * @see #setStartRule(int, int, int, int)
+   */
+  public void setEndRule(int month, int day, int dayOfWeek, int time)
+  {
+    this.endMode = checkRule(month, day, dayOfWeek);
+    this.endMonth = month;
+    this.endDay = day;
+    this.endDayOfWeek = Math.abs(dayOfWeek);
+    this.endTime = time;
+    this.endTimeMode = WALL_TIME;
+    useDaylight = true;
+  }
+
+  /**
+   * Sets the daylight savings end rule.  You must also set the
+   * start rule with <code>setStartRule</code> or the result of
+   * getOffset is undefined. For the parameters see the ten-argument
+   * constructor above.
+   *
+   * Note that this API isn't incredibly well specified.  It appears that the
+   * after flag must override the parameters, since normally, the day and
+   * dayofweek can select this.  I.e., if day < 0 and dayOfWeek < 0, on or
+   * before mode is chosen.  But if after == true, this implementation
+   * overrides the signs of the other arguments.  And if dayOfWeek == 0, it
+   * falls back to the behavior in the other APIs.  I guess this should be
+   * checked against Sun's implementation.
+   *
+   * @param month The end month of daylight savings.
+   * @param day A day in month, or a day of week in month.
+   * @param dayOfWeek A day of week, when daylight savings ends.
+   * @param time A time in millis in standard time.
+   * @param after If true, day and dayOfWeek specify first day of week on or
+   * after day, else first day of week on or before.
+   * @since 1.2
+   * @see #setStartRule(int, int, int, int, boolean)
+   */
+  public void setEndRule(int month, int day, int dayOfWeek, int time,
+                         boolean after)
+  {
+    if (after)
+      setEndRule(month, day, -dayOfWeek, time);
+    else
+      setEndRule(month, -day, -dayOfWeek, time);
+  }
+
+  /**
+   * Sets the daylight savings end rule.  You must also set the
+   * start rule with <code>setStartRule</code> or the result of
+   * getOffset is undefined. For the parameters see the ten-argument
+   * constructor above.
+   *
+   * @param month The end month of daylight savings.
+   * @param day A day in month, or a day of week in month.
+   * @param time A time in millis in standard time.
+   * @see #setStartRule(int, int, int)
+   */
+  public void setEndRule(int month, int day, int time)
+  {
+    setEndRule(month, day, 0, time);
+  }
+
+  /**
+   * Gets the time zone offset, for current date, modified in case of
+   * daylight savings.  This is the offset to add to UTC to get the local
+   * time.
+   *
+   * In the standard JDK the results given by this method may result in
+   * inaccurate results at the end of February or the beginning of March.
+   * To avoid this, you should use Calendar instead:
+   * <code>offset = cal.get(Calendar.ZONE_OFFSET)
+   * + cal.get(Calendar.DST_OFFSET);</code>
+   *
+   * This version doesn't suffer this inaccuracy.
+   *
+   * The arguments don't follow the approach for setting start and end rules.
+   * The day must be a positive number and dayOfWeek must be a positive value
+   * from Calendar.  dayOfWeek is redundant, but must match the other values
+   * or an inaccurate result may be returned.
+   *
+   * @param era the era of the given date
+   * @param year the year of the given date
+   * @param month the month of the given date, 0 for January.
+   * @param day the day of month
+   * @param dayOfWeek the day of week; this must match the other fields.
+   * @param millis the millis in the day (in local standard time)
+   * @return the time zone offset in milliseconds.
+   * @throws IllegalArgumentException if arguments are incorrect.
+   */
+  public int getOffset(int era, int year, int month, int day, int dayOfWeek,
+                       int millis)
+  {
+    int daysInMonth = getDaysInMonth(month, year);
+    if (day < 1 || day > daysInMonth)
+      throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "day out of range");
+    if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
+      throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "dayOfWeek out of range");
+    if (month < Calendar.JANUARY || month > Calendar.DECEMBER)
+      throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "month out of range:" + month);
+
+    // This method is called by Calendar, so we mustn't use that class.
+    int daylightSavings = 0;
+    if (useDaylight && era == GregorianCalendar.AD && year >= startYear)
+      {
+       int orig_year = year;
+       int time = startTime + (startTimeMode == UTC_TIME ? rawOffset : 0);
+       // This does only work for Gregorian calendars :-(
+       // This is mainly because setStartYear doesn't take an era.
+       boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis,
+                                       startMode, startMonth, startDay,
+                                       startDayOfWeek, time);
+       millis += dstSavings;
+       if (millis >= 24 * 60 * 60 * 1000)
+         {
+           millis -= 24 * 60 * 60 * 1000;
+           dayOfWeek = (dayOfWeek % 7) + 1;
+           if (++day > daysInMonth)
+             {
+               day = 1;
+               if (month++ == Calendar.DECEMBER)
+                 {
+                   month = Calendar.JANUARY;
+                   year++;
+                 }
+             }
+         }
+       time = endTime + (endTimeMode == UTC_TIME ? rawOffset : 0);
+       if (endTimeMode != WALL_TIME)
+         time += dstSavings;
+       boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis,
+                                    endMode, endMonth, endDay, endDayOfWeek,
+                                    time);
+
+       if (year != orig_year)
+         afterStart = false;
+       if (startMonth < endMonth)
+         // use daylight savings, if the date is after the start of
+         // savings, and before the end of savings.
+         daylightSavings = afterStart && beforeEnd ? dstSavings : 0;
+       else
+         // use daylight savings, if the date is before the end of
+         // savings, or after the start of savings.
+         daylightSavings = beforeEnd || afterStart ? dstSavings : 0;
+      }
+    return rawOffset + daylightSavings;
+  }
+
+  /**
+   * Returns the time zone offset to GMT in milliseconds, ignoring
+   * day light savings.
+   * @return the time zone offset.
+   */
+  public int getRawOffset()
+  {
+    return rawOffset;
+  }
+
+  /**
+   * Sets the standard time zone offset to GMT.
+   * @param rawOffset The time offset from GMT in milliseconds.
+   */
+  public void setRawOffset(int rawOffset)
+  {
+    this.rawOffset = rawOffset;
+  }
+
+  /**
+   * Gets the daylight savings offset.  This is a positive offset in
+   * milliseconds with respect to standard time.  Typically this
+   * is one hour, but for some time zones this may be half an our.
+   * @return the daylight savings offset in milliseconds.
+   *
+   * @since 1.2
+   */
+  public int getDSTSavings()
+  {
+    return dstSavings;
+  }
+
+  /**
+   * Sets the daylight savings offset.  This is a positive offset in
+   * milliseconds with respect to standard time.
+   *
+   * @param dstSavings the daylight savings offset in milliseconds.
+   *
+   * @since 1.2
+   */
+  public void setDSTSavings(int dstSavings)
+  {
+    if (dstSavings <= 0)
+      throw new Exception/*IllegalArgumentException*/("IllegalArgumentException: " + "illegal value for dstSavings");
+
+    this.dstSavings = dstSavings;
+  }
+
+  /**
+   * Returns if this time zone uses daylight savings time.
+   * @return true, if we use daylight savings time, false otherwise.
+   */
+  public boolean useDaylightTime()
+  {
+    return useDaylight;
+  }
+
+  /**
+   * Returns the number of days in the given month.
+   * Uses gregorian rules prior to 1582 (The default and earliest cutover)
+   * @param month The month, zero based; use one of the Calendar constants.
+   * @param year  The year.
+   */
+  private int getDaysInMonth(int month, int year)
+  {    
+    if (month == Calendar.FEBRUARY)
+      {
+       if ((year & 3) != 0)
+         return 28;
+
+       // Assume default Gregorian cutover, 
+       // all years prior to this must be Julian
+       if (year < 1582)
+         return 29;
+
+       // Gregorian rules 
+       return ((year % 100) != 0 || (year % 400) == 0) ? 29 : 28;
+      }
+    else
+      return monthArr[month];
+  }
+
+  /**
+   * Checks if the date given in calXXXX, is before the change between
+   * dst and standard time.
+   * @param calYear the year of the date to check (for leap day checking).
+   * @param calMonth the month of the date to check.
+   * @param calDayOfMonth the day of month of the date to check.
+   * @param calDayOfWeek the day of week of the date to check.
+   * @param calMillis the millis of day of the date to check (standard time).
+   * @param mode  the change mode; same semantic as startMode.
+   * @param month the change month; same semantic as startMonth.
+   * @param day   the change day; same semantic as startDay.
+   * @param dayOfWeek the change day of week;
+   * @param millis the change time in millis since midnight standard time.
+   * same semantic as startDayOfWeek.
+   * @return true, if cal is before the change, false if cal is on
+   * or after the change.
+   */
+  private boolean isBefore(int calYear, int calMonth, int calDayOfMonth,
+                           int calDayOfWeek, int calMillis, int mode,
+                           int month, int day, int dayOfWeek, int millis)
+  {
+    // This method is called by Calendar, so we mustn't use that class.
+    // We have to do all calculations by hand.
+    // check the months:
+    // XXX - this is not correct:
+    // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may
+    // be in a different month.
+    if (calMonth != month)
+      return calMonth < month;
+
+    // check the day:
+    switch (mode)
+      {
+      case DOM_MODE:
+       if (calDayOfMonth != day)
+         return calDayOfMonth < day;
+       break;
+      case DOW_IN_MONTH_MODE:
+        {
+         // This computes the day of month of the day of type
+         // "dayOfWeek" that lies in the same (sunday based) week as cal.
+         calDayOfMonth += (dayOfWeek - calDayOfWeek);
+
+         // Now we convert it to 7 based number (to get a one based offset
+         // after dividing by 7).  If we count from the end of the
+         // month, we get want a -7 based number counting the days from 
+         // the end:
+         if (day < 0)
+           calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7;
+         else
+           calDayOfMonth += 6;
+
+         //  day > 0                    day < 0
+         //  S  M  T  W  T  F  S        S  M  T  W  T  F  S
+         //     7  8  9 10 11 12         -36-35-34-33-32-31
+         // 13 14 15 16 17 18 19      -30-29-28-27-26-25-24
+         // 20 21 22 23 24 25 26      -23-22-21-20-19-18-17
+         // 27 28 29 30 31 32 33      -16-15-14-13-12-11-10
+         // 34 35 36                   -9 -8 -7
+         // Now we calculate the day of week in month:
+         int week = calDayOfMonth / 7;
+
+         //  day > 0                    day < 0
+         //  S  M  T  W  T  F  S        S  M  T  W  T  F  S
+         //     1  1  1  1  1  1          -5 -5 -4 -4 -4 -4
+         //  1  2  2  2  2  2  2       -4 -4 -4 -3 -3 -3 -3
+         //  2  3  3  3  3  3  3       -3 -3 -3 -2 -2 -2 -2
+         //  3  4  4  4  4  4  4       -2 -2 -2 -1 -1 -1 -1
+         //  4  5  5                   -1 -1 -1
+         if (week != day)
+           return week < day;
+
+         if (calDayOfWeek != dayOfWeek)
+           return calDayOfWeek < dayOfWeek;
+
+         // daylight savings starts/ends  on the given day.
+         break;
+        }
+      case DOW_LE_DOM_MODE:
+       // The greatest sunday before or equal December, 12
+       // is the same as smallest sunday after or equal December, 6.
+       day = Math.abs(day) - 6;
+      case DOW_GE_DOM_MODE:
+       // Calculate the day of month of the day of type
+       // "dayOfWeek" that lies before (or on) the given date.
+       calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) + calDayOfWeek
+       - dayOfWeek;
+       if (calDayOfMonth < day)
+         return true;
+       if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7)
+         return false;
+
+       // now we have the same day
+       break;
+      }
+
+    // the millis decides:
+    return (calMillis < millis);
+  }
+
+  /**
+   * Determines if the given date is in daylight savings time.
+   * @return true, if it is in daylight savings time, false otherwise.
+   */
+  public boolean inDaylightTime(Date date)
+  {
+    Calendar cal = Calendar.getInstance(this);
+    cal.setTime(date);
+    return (cal.get(Calendar.DST_OFFSET) != 0);
+  }
+
+  /**
+   * Generates the hashCode for the SimpleDateFormat object.  It is
+   * the rawOffset, possibly, if useDaylightSavings is true, xored
+   * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime.
+   */
+  public synchronized int hashCode()
+  {
+    return rawOffset
+           ^ (useDaylight
+              ? startMonth ^ startDay ^ startDayOfWeek ^ startTime ^ endMonth
+              ^ endDay ^ endDayOfWeek ^ endTime : 0);
+  }
+
+  public synchronized boolean equals(Object o)
+  {
+    if (this == o)
+      return true;
+    if (! (o instanceof SimpleTimeZone))
+      return false;
+    SimpleTimeZone zone = (SimpleTimeZone) o;
+    if (zone.hashCode() != hashCode() || ! getID().equals(zone.getID())
+        || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight)
+      return false;
+    if (! useDaylight)
+      return true;
+    return (startYear == zone.startYear && startMonth == zone.startMonth
+           && startDay == zone.startDay
+           && startDayOfWeek == zone.startDayOfWeek
+           && startTime == zone.startTime
+           && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth
+           && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek
+           && endTime == zone.endTime && endTimeMode == zone.endTimeMode);
+  }
+
+  /**
+   * Test if the other time zone uses the same rule and only
+   * possibly differs in ID.  This implementation for this particular
+   * class will return true if the other object is a SimpleTimeZone,
+   * the raw offsets and useDaylight are identical and if useDaylight
+   * is true, also the start and end datas are identical.
+   * @return true if this zone uses the same rule.
+   */
+  public boolean hasSameRules(TimeZone other)
+  {
+    if (this == other)
+      return true;
+    if (! (other instanceof SimpleTimeZone))
+      return false;
+    SimpleTimeZone zone = (SimpleTimeZone) other;
+    if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset
+        || useDaylight != zone.useDaylight)
+      return false;
+    if (! useDaylight)
+      return true;
+    return (startYear == zone.startYear && startMonth == zone.startMonth
+           && startDay == zone.startDay
+           && startDayOfWeek == zone.startDayOfWeek
+           && startTime == zone.startTime
+           && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth
+           && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek
+           && endTime == zone.endTime && endTimeMode == zone.endTimeMode);
+  }
+
+  /**
+   * Returns a string representation of this SimpleTimeZone object.
+   * @return a string representation of this SimpleTimeZone object.
+   */
+  public String toString()
+  {
+    // the test for useDaylight is an incompatibility to jdk1.2, but
+    // I think this shouldn't hurt.
+    return "SimpleTimeZone"/*getClass().getName()*/ + "[" + "id=" + getID() + ",offset="
+           + rawOffset + ",dstSavings=" + dstSavings + ",useDaylight="
+           + useDaylight
+           + (useDaylight
+              ? ",startYear=" + startYear + ",startMode=" + startMode
+              + ",startMonth=" + startMonth + ",startDay=" + startDay
+              + ",startDayOfWeek=" + startDayOfWeek + ",startTime="
+              + startTime + ",startTimeMode=" + startTimeMode + ",endMode="
+              + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay
+              + ",endDayOfWeek=" + endDayOfWeek + ",endTime=" + endTime
+              + ",endTimeMode=" + endTimeMode : "") + "]";
+  }
+
+  /**
+   * Reads a serialized simple time zone from stream.
+   * @see #writeObject
+   */
+  private void readObject(java.io.ObjectInputStream input)
+    //throws java.io.IOException, ClassNotFoundException
+  {
+    input.defaultReadObject();
+    if (serialVersionOnStream == 0)
+      {
+       // initialize the new fields to default values.
+       dstSavings = 60 * 60 * 1000;
+       endMode = DOW_IN_MONTH_MODE;
+       startMode = DOW_IN_MONTH_MODE;
+       startTimeMode = WALL_TIME;
+       endTimeMode = WALL_TIME;
+       serialVersionOnStream = 2;
+      }
+    else
+      {
+       int length = input.readInt();
+       byte[] byteArray = new byte[length];
+       input.read(byteArray, 0, length);
+       if (length >= 4)
+         {
+           // Lets hope that Sun does extensions to the serialized
+           // form in a sane manner.
+           startDay = byteArray[0];
+           startDayOfWeek = byteArray[1];
+           endDay = byteArray[2];
+           endDayOfWeek = byteArray[3];
+         }
+      }
+  }
+
+  /**
+   * Serializes this object to a stream.  @serialdata The object is
+   * first written in the old JDK 1.1 format, so that it can be read
+   * by the old classes.  This means, that the
+   * <code>start/endDay(OfWeek)</code>-Fields are written in the
+   * DOW_IN_MONTH_MODE rule, since this was the only supported rule
+   * in 1.1.
+   *
+   * In the optional section, we write first the length of an byte
+   * array as int and afterwards the byte array itself.  The byte
+   * array contains in this release four elements, namely the real
+   * startDay, startDayOfWeek endDay, endDayOfWeek in that Order.
+   * These fields are needed, because for compatibility reasons only
+   * approximative values are written to the required section, as
+   * described above.
+   */
+  private void writeObject(java.io.ObjectOutputStream output)
+    //throws java.io.IOException
+  {
+    byte[] byteArray = new byte[]
+                       {
+                         (byte) startDay, (byte) startDayOfWeek, (byte) endDay,
+                         (byte) endDayOfWeek
+                       };
+
+    /* calculate the approximation for JDK 1.1 */
+    switch (startMode)
+      {
+      case DOM_MODE:
+       startDayOfWeek = Calendar.SUNDAY; // random day of week
+
+      // fall through
+      case DOW_GE_DOM_MODE:
+      case DOW_LE_DOM_MODE:
+       startDay = (startDay + 6) / 7;
+      }
+    switch (endMode)
+      {
+      case DOM_MODE:
+       endDayOfWeek = Calendar.SUNDAY;
+
+      // fall through
+      case DOW_GE_DOM_MODE:
+      case DOW_LE_DOM_MODE:
+       endDay = (endDay + 6) / 7;
+      }
+
+    // the required part:
+    output.defaultWriteObject();
+    // the optional part:
+    output.writeInt(byteArray.length);
+    output.write(byteArray, 0, byteArray.length);
+  }
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/Throwable.java b/Robust/src/ClassLibrary/MGC/gnu/Throwable.java
new file mode 100644 (file)
index 0000000..3efa9d4
--- /dev/null
@@ -0,0 +1,565 @@
+/* java.lang.Throwable -- Root class for all Exceptions and Errors
+   Copyright (C) 1998, 1999, 2002, 2004, 2005  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.lang;
+
+/*import gnu.classpath.SystemProperties;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Serializable;*/
+
+/**
+ * Throwable is the superclass of all exceptions that can be raised.
+ *
+ * <p>There are two special cases: {@link Error} and {@link RuntimeException}:
+ * these two classes (and their subclasses) are considered unchecked
+ * exceptions, and are either frequent enough or catastrophic enough that you
+ * do not need to declare them in <code>throws</code> clauses.  Everything
+ * else is a checked exception, and is ususally a subclass of
+ * {@link Exception}; these exceptions have to be handled or declared.
+ *
+ * <p>Instances of this class are usually created with knowledge of the
+ * execution context, so that you can get a stack trace of the problem spot
+ * in the code.  Also, since JDK 1.4, Throwables participate in "exception
+ * chaining."  This means that one exception can be caused by another, and
+ * preserve the information of the original.
+ *
+ * <p>One reason this is useful is to wrap exceptions to conform to an
+ * interface.  For example, it would be bad design to require all levels
+ * of a program interface to be aware of the low-level exceptions thrown
+ * at one level of abstraction. Another example is wrapping a checked
+ * exception in an unchecked one, to communicate that failure occured
+ * while still obeying the method throws clause of a superclass.
+ *
+ * <p>A cause is assigned in one of two ways; but can only be assigned once
+ * in the lifetime of the Throwable.  There are new constructors added to
+ * several classes in the exception hierarchy that directly initialize the
+ * cause, or you can use the <code>initCause</code> method. This second
+ * method is especially useful if the superclass has not been retrofitted
+ * with new constructors:<br>
+ * <pre>
+ * try
+ *   {
+ *     lowLevelOp();
+ *   }
+ * catch (LowLevelException lle)
+ *   {
+ *     throw (HighLevelException) new HighLevelException().initCause(lle);
+ *   }
+ * </pre>
+ * Notice the cast in the above example; without it, your method would need
+ * a throws clase that declared Throwable, defeating the purpose of chainig
+ * your exceptions.
+ *
+ * <p>By convention, exception classes have two constructors: one with no
+ * arguments, and one that takes a String for a detail message.  Further,
+ * classes which are likely to be used in an exception chain also provide
+ * a constructor that takes a Throwable, with or without a detail message
+ * string.
+ *
+ * <p>Another 1.4 feature is the StackTrace, a means of reflection that
+ * allows the program to inspect the context of the exception, and which is
+ * serialized, so that remote procedure calls can correctly pass exceptions.
+ *
+ * @author Brian Jones
+ * @author John Keiser
+ * @author Mark Wielaard
+ * @author Tom Tromey
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @since 1.0
+ * @status updated to 1.4
+ */
+public class Throwable //implements Serializable
+{
+  /**
+   * Compatible with JDK 1.0+.
+   */
+  private static final long serialVersionUID = -3042686055658047285L;
+
+  /**
+   * The detail message.
+   *
+   * @serial specific details about the exception, may be null
+   */
+  private final String detailMessage;
+
+  /**
+   * The cause of the throwable, including null for an unknown or non-chained
+   * cause. This may only be set once; so the field is set to
+   * <code>this</code> until initialized.
+   *
+   * @serial the cause, or null if unknown, or this if not yet set
+   * @since 1.4
+   */
+  private Throwable cause = this;
+
+  /**
+   * The stack trace, in a serialized form.
+   *
+   * @serial the elements of the stack trace; this is non-null, and has
+   *         no null entries
+   * @since 1.4
+   */
+  //private StackTraceElement[] stackTrace;
+
+  /**
+   * Instantiate this Throwable with an empty message. The cause remains
+   * uninitialized.  {@link #fillInStackTrace()} will be called to set
+   * up the stack trace.
+   */
+  public Throwable()
+  {
+    this((String) null);
+  }
+
+  /**
+   * Instantiate this Throwable with the given message. The cause remains
+   * uninitialized.  {@link #fillInStackTrace()} will be called to set
+   * up the stack trace.
+   *
+   * @param message the message to associate with the Throwable
+   */
+  public Throwable(String message)
+  {
+    //fillInStackTrace();
+    detailMessage = message;
+  }
+
+  /**
+   * Instantiate this Throwable with the given message and cause. Note that
+   * the message is unrelated to the message of the cause.
+   * {@link #fillInStackTrace()} will be called to set up the stack trace.
+   *
+   * @param message the message to associate with the Throwable
+   * @param cause the cause, may be null
+   * @since 1.4
+   */
+  public Throwable(String message, Throwable cause)
+  {
+    this(message);
+    this.cause = cause;
+  }
+
+  /**
+   * Instantiate this Throwable with the given cause. The message is then
+   * built as <code>cause == null ? null : cause.toString()</code>.
+   * {@link #fillInStackTrace()} will be called to set up the stack trace.
+   *
+   * @param cause the cause, may be null
+   * @since 1.4
+   */
+  public Throwable(Throwable cause)
+  {
+    this(cause == null ? null : cause.toString(), cause);
+  }
+
+  /**
+   * Get the message associated with this Throwable.
+   *
+   * @return the error message associated with this Throwable, may be null
+   */
+  public String getMessage()
+  {
+    return detailMessage;
+  }
+
+  /**
+   * Get a localized version of this Throwable's error message.
+   * This method must be overridden in a subclass of Throwable
+   * to actually produce locale-specific methods.  The Throwable
+   * implementation just returns getMessage().
+   *
+   * @return a localized version of this error message
+   * @see #getMessage()
+   * @since 1.1
+   */
+  public String getLocalizedMessage()
+  {
+    return getMessage();
+  }
+
+  /**
+   * Returns the cause of this exception, or null if the cause is not known
+   * or non-existant. This cause is initialized by the new constructors,
+   * or by calling initCause.
+   *
+   * @return the cause of this Throwable
+   * @since 1.4
+   */
+  public Throwable getCause()
+  {
+    return cause == this ? null : cause;
+  }
+
+  /**
+   * Initialize the cause of this Throwable.  This may only be called once
+   * during the object lifetime, including implicitly by chaining
+   * constructors.
+   *
+   * @param cause the cause of this Throwable, may be null
+   * @return this
+   * @throws IllegalArgumentException if cause is this (a Throwable can't be
+   *         its own cause!)
+   * @throws IllegalStateException if the cause has already been set
+   * @since 1.4
+   */
+  public Throwable initCause(Throwable cause)
+  {
+    /*if (cause == this)
+      throw new IllegalArgumentException();
+    if (this.cause != this)
+      throw new IllegalStateException();*/
+    this.cause = cause;
+    return this;
+  }
+
+  /**
+   * Get a human-readable representation of this Throwable. The detail message
+   * is retrieved by getLocalizedMessage().  Then, with a null detail
+   * message, this string is simply the object's class name; otherwise
+   * the string is <code>getClass().getName() + ": " + message</code>.
+   *
+   * @return a human-readable String represting this Throwable
+   */
+  /*public String toString()
+  {
+    String msg = getLocalizedMessage();
+    return getClass().getName() + (msg == null ? "" : ": " + msg);
+  }*/
+
+  /**
+   * Print a stack trace to the standard error stream. This stream is the
+   * current contents of <code>System.err</code>. The first line of output
+   * is the result of {@link #toString()}, and the remaining lines represent
+   * the data created by {@link #fillInStackTrace()}. While the format is
+   * unspecified, this implementation uses the suggested format, demonstrated
+   * by this example:<br>
+   * <pre>
+   * public class Junk
+   * {
+   *   public static void main(String args[])
+   *   {
+   *     try
+   *       {
+   *         a();
+   *       }
+   *     catch(HighLevelException e)
+   *       {
+   *         e.printStackTrace();
+   *       }
+   *   }
+   *   static void a() throws HighLevelException
+   *   {
+   *     try
+   *       {
+   *         b();
+   *       }
+   *     catch(MidLevelException e)
+   *       {
+   *         throw new HighLevelException(e);
+   *       }
+   *   }
+   *   static void b() throws MidLevelException
+   *   {
+   *     c();
+   *   }
+   *   static void c() throws MidLevelException
+   *   {
+   *     try
+   *       {
+   *         d();
+   *       }
+   *     catch(LowLevelException e)
+   *       {
+   *         throw new MidLevelException(e);
+   *       }
+   *   }
+   *   static void d() throws LowLevelException
+   *   {
+   *     e();
+   *   }
+   *   static void e() throws LowLevelException
+   *   {
+   *     throw new LowLevelException();
+   *   }
+   * }
+   * class HighLevelException extends Exception
+   * {
+   *   HighLevelException(Throwable cause) { super(cause); }
+   * }
+   * class MidLevelException extends Exception
+   * {
+   *   MidLevelException(Throwable cause)  { super(cause); }
+   * }
+   * class LowLevelException extends Exception
+   * {
+   * }
+   * </pre>
+   * <p>
+   * <pre>
+   *  HighLevelException: MidLevelException: LowLevelException
+   *          at Junk.a(Junk.java:13)
+   *          at Junk.main(Junk.java:4)
+   *  Caused by: MidLevelException: LowLevelException
+   *          at Junk.c(Junk.java:23)
+   *          at Junk.b(Junk.java:17)
+   *          at Junk.a(Junk.java:11)
+   *          ... 1 more
+   *  Caused by: LowLevelException
+   *          at Junk.e(Junk.java:30)
+   *          at Junk.d(Junk.java:27)
+   *          at Junk.c(Junk.java:21)
+   *          ... 3 more
+   * </pre>
+   */
+  public void printStackTrace()
+  {
+    //printStackTrace(System.err);
+  }
+
+  /**
+   * Print a stack trace to the specified PrintStream. See
+   * {@link #printStackTrace()} for the sample format.
+   *
+   * @param s the PrintStream to write the trace to
+   */
+  /*public void printStackTrace(PrintStream s)
+  {
+    s.print(stackTraceString());
+  }*/
+
+  /**
+   * Prints the exception, the detailed message and the stack trace
+   * associated with this Throwable to the given <code>PrintWriter</code>.
+   * The actual output written is implemention specific. Use the result of
+   * <code>getStackTrace()</code> when more precise information is needed.
+   *
+   * <p>This implementation first prints a line with the result of this
+   * object's <code>toString()</code> method.
+   * <br>
+   * Then for all elements given by <code>getStackTrace</code> it prints
+   * a line containing three spaces, the string "at " and the result of calling
+   * the <code>toString()</code> method on the <code>StackTraceElement</code>
+   * object. If <code>getStackTrace()</code> returns an empty array it prints
+   * a line containing three spaces and the string
+   * "&lt;&lt;No stacktrace available&gt;&gt;".
+   * <br>
+   * Then if <code>getCause()</code> doesn't return null it adds a line
+   * starting with "Caused by: " and the result of calling
+   * <code>toString()</code> on the cause.
+   * <br>
+   * Then for every cause (of a cause, etc) the stacktrace is printed the
+   * same as for the top level <code>Throwable</code> except that as soon
+   * as all the remaining stack frames of the cause are the same as the
+   * the last stack frames of the throwable that the cause is wrapped in
+   * then a line starting with three spaces and the string "... X more" is
+   * printed, where X is the number of remaining stackframes.
+   *
+   * @param pw the PrintWriter to write the trace to
+   * @since 1.1
+   */
+  /*public void printStackTrace (PrintWriter pw)
+  {
+    pw.print(stackTraceString());
+  }*/
+
+  /*
+   * We use inner class to avoid a static initializer in this basic class.
+   */
+  /*private static class StaticData
+  {
+    static final String nl = SystemProperties.getProperty("line.separator");
+  }*/
+
+  // Create whole stack trace in a stringbuffer so we don't have to print
+  // it line by line. This prevents printing multiple stack traces from
+  // different threads to get mixed up when written to the same PrintWriter.
+  /*private String stackTraceString()
+  {
+    CPStringBuilder sb = new CPStringBuilder();
+
+    // Main stacktrace
+    StackTraceElement[] stack = getStackTrace();
+    stackTraceStringBuffer(sb, this.toString(), stack, 0);
+
+    // The cause(s)
+    Throwable cause = getCause();
+    while (cause != null)
+      {
+       // Cause start first line
+        sb.append("Caused by: ");
+
+        // Cause stacktrace
+        StackTraceElement[] parentStack = stack;
+        stack = cause.getStackTrace();
+       if (parentStack == null || parentStack.length == 0)
+         stackTraceStringBuffer(sb, cause.toString(), stack, 0);
+       else
+         {
+           int equal = 0; // Count how many of the last stack frames are equal
+           int frame = stack.length-1;
+           int parentFrame = parentStack.length-1;
+           while (frame > 0 && parentFrame > 0)
+             {
+               if (stack[frame].equals(parentStack[parentFrame]))
+                 {
+                   equal++;
+                   frame--;
+                   parentFrame--;
+                 }
+               else
+                 break;
+             }
+           stackTraceStringBuffer(sb, cause.toString(), stack, equal);
+         }
+        cause = cause.getCause();
+      }
+
+    return sb.toString();
+  }*/
+
+  // Adds to the given StringBuffer a line containing the name and
+  // all stacktrace elements minus the last equal ones.
+  /*private static void stackTraceStringBuffer(CPStringBuilder sb, String name,
+                                       StackTraceElement[] stack, int equal)
+  {
+    String nl = StaticData.nl;
+    // (finish) first line
+    sb.append(name);
+    sb.append(nl);
+
+    // The stacktrace
+    if (stack == null || stack.length == 0)
+      {
+       sb.append("   <<No stacktrace available>>");
+       sb.append(nl);
+      }
+    else
+      {
+       for (int i = 0; i < stack.length-equal; i++)
+         {
+           sb.append("   at ");
+           sb.append(stack[i] == null ? "<<Unknown>>" : stack[i].toString());
+           sb.append(nl);
+         }
+       if (equal > 0)
+         {
+           sb.append("   ...");
+           sb.append(equal);
+           sb.append(" more");
+           sb.append(nl);
+         }
+      }
+  }*/
+
+  /**
+   * Fill in the stack trace with the current execution stack.
+   *
+   * @return this same throwable
+   * @see #printStackTrace()
+   */
+  /*public Throwable fillInStackTrace()
+  {
+    vmState = VMThrowable.fillInStackTrace(this);
+    stackTrace = null; // Should be regenerated when used.
+
+    return this;
+  }*/
+
+  /**
+   * Provides access to the information printed in {@link #printStackTrace()}.
+   * The array is non-null, with no null entries, although the virtual
+   * machine is allowed to skip stack frames.  If the array is not 0-length,
+   * then slot 0 holds the information on the stack frame where the Throwable
+   * was created (or at least where <code>fillInStackTrace()</code> was
+   * called).
+   *
+   * @return an array of stack trace information, as available from the VM
+   * @since 1.4
+   */
+  /*public StackTraceElement[] getStackTrace()
+  {
+    if (stackTrace == null)
+      if (vmState == null)
+       stackTrace = new StackTraceElement[0];
+      else 
+       {
+         stackTrace = vmState.getStackTrace(this);
+         vmState = null; // No longer needed
+       }
+
+    return stackTrace;
+  }*/
+
+  /**
+   * Change the stack trace manually. This method is designed for remote
+   * procedure calls, which intend to alter the stack trace before or after
+   * serialization according to the context of the remote call.
+   * <p>
+   * The contents of the given stacktrace is copied so changes to the
+   * original array do not change the stack trace elements of this
+   * throwable.
+   *
+   * @param stackTrace the new trace to use
+   * @throws NullPointerException if stackTrace is null or has null elements
+   * @since 1.4
+   */
+  /*public void setStackTrace(StackTraceElement[] stackTrace)
+  {
+    int i = stackTrace.length;
+    StackTraceElement[] st = new StackTraceElement[i];
+
+    while (--i >= 0)
+      {
+       st[i] = stackTrace[i];
+       if (st[i] == null)
+         throw new NullPointerException("Element " + i + " null");
+      }
+
+    this.stackTrace = st;
+  }*/
+
+  /**
+   * VM state when fillInStackTrace was called.
+   * Used by getStackTrace() to get an array of StackTraceElements.
+   * Cleared when no longer needed.
+   */
+  //private transient VMThrowable vmState;
+}
diff --git a/Robust/src/ClassLibrary/MGC/gnu/TimeZone.java b/Robust/src/ClassLibrary/MGC/gnu/TimeZone.java
new file mode 100644 (file)
index 0000000..db5dd13
--- /dev/null
@@ -0,0 +1,1777 @@
+/* java.util.TimeZone
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
+   Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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.
+
+GNU Classpath 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util;
+
+/*import gnu.classpath.SystemProperties;
+import gnu.java.lang.CPStringBuilder;
+import gnu.java.util.ZoneInfo;
+
+import java.io.File;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.DateFormatSymbols;*/
+
+/**
+ * This class represents a time zone offset and handles daylight savings.
+ * 
+ * You can get the default time zone with <code>getDefault</code>.
+ * This represents the time zone where program is running.
+ *
+ * Another way to create a time zone is <code>getTimeZone</code>, where
+ * you can give an identifier as parameter.  For instance, the identifier
+ * of the Central European Time zone is "CET".
+ *
+ * With the <code>getAvailableIDs</code> method, you can get all the
+ * supported time zone identifiers.
+ *
+ * @see Calendar
+ * @see SimpleTimeZone
+ * @author Jochen Hoenicke
+ */
+public abstract class TimeZone //implements java.io.Serializable, Cloneable
+{
+
+  /**
+   * Constant used to indicate that a short timezone abbreviation should
+   * be returned, such as "EST"
+   */
+  public static final int SHORT = 0;
+
+  /**
+   * Constant used to indicate that a long timezone name should be
+   * returned, such as "Eastern Standard Time".
+   */
+  public static final int LONG = 1;
+
+  /**
+   * The time zone identifier, e.g. PST.
+   */
+  private String ID;
+
+  /**
+   * The default time zone, as returned by getDefault.
+   */
+  private static TimeZone defaultZone0;
+
+  /**
+   * Tries to get the default TimeZone for this system if not already
+   * set.  It will call <code>getDefaultTimeZone(String)</code> with
+   * the result of <code>System.getProperty("user.timezone")</code>.
+   * If that fails it calls <code>VMTimeZone.getDefaultTimeZoneId()</code>.
+   * If that also fails GMT is returned.
+   */
+  private static synchronized TimeZone defaultZone()
+  {
+    /* Look up default timezone */
+    if (defaultZone0 == null) 
+      {
+       /*defaultZone0 = (TimeZone) AccessController.doPrivileged
+         (new PrivilegedAction()
+           {
+             public Object run()
+             {
+               TimeZone zone = null;
+               
+               // Prefer System property user.timezone.
+               String tzid = System.getProperty("user.timezone");
+               if (tzid != null && !tzid.equals(""))
+                 zone = getDefaultTimeZone(tzid);
+               
+               // Try platfom specific way.
+               if (zone == null)
+                 zone = VMTimeZone.getDefaultTimeZoneId();
+               
+               // Fall back on GMT.
+               if (zone == null)
+                 zone = getTimeZone ("GMT");
+               
+               return zone;
+             }
+           });*/
+      }
+    
+    return defaultZone0; 
+  }
+  
+  private static final long serialVersionUID = 3581463369166924961L;
+
+  /**
+   * Flag whether zoneinfo data should be used,
+   * otherwise builtin timezone data will be provided.
+   */
+  private static String zoneinfo_dir;
+
+  /**
+   * Cached copy of getAvailableIDs().
+   */
+  private static String[] availableIDs = null;
+
+  /**
+   * JDK 1.1.x compatibility aliases.
+   */
+  private static HashMap aliases0;
+
+  /**
+   * HashMap for timezones by ID.  
+   */
+  private static HashMap timezones0;
+  /* initialize this static field lazily to overhead if
+   * it is not needed: 
+   */
+  // Package-private to avoid a trampoline.
+  static HashMap timezones()
+  {
+    if (timezones0 == null) 
+      {
+       HashMap timezones = new HashMap();
+       timezones0 = timezones;
+
+       /*zoneinfo_dir = SystemProperties.getProperty("gnu.java.util.zoneinfo.dir");
+       if (zoneinfo_dir != null && !new File(zoneinfo_dir).isDirectory())
+         zoneinfo_dir = null;*/
+
+       if (zoneinfo_dir != null)
+         {
+           aliases0 = new HashMap();
+
+           // These deprecated aliases for JDK 1.1.x compatibility
+           // should take precedence over data files read from
+           // /usr/share/zoneinfo.
+           aliases0.put("ACT", "Australia/Darwin");
+           aliases0.put("AET", "Australia/Sydney");
+           aliases0.put("AGT", "America/Argentina/Buenos_Aires");
+           aliases0.put("ART", "Africa/Cairo");
+           aliases0.put("AST", "America/Juneau");
+           aliases0.put("BST", "Asia/Colombo");
+           aliases0.put("CAT", "Africa/Gaborone");
+           aliases0.put("CNT", "America/St_Johns");
+           aliases0.put("CST", "CST6CDT");
+           aliases0.put("CTT", "Asia/Brunei");
+           aliases0.put("EAT", "Indian/Comoro");
+           aliases0.put("ECT", "CET");
+           aliases0.put("EST", "EST5EDT");
+           aliases0.put("EST5", "EST5EDT");
+           aliases0.put("IET", "EST5EDT");
+           aliases0.put("IST", "Asia/Calcutta");
+           aliases0.put("JST", "Asia/Seoul");
+           aliases0.put("MIT", "Pacific/Niue");
+           aliases0.put("MST", "MST7MDT");
+           aliases0.put("MST7", "MST7MDT");
+           aliases0.put("NET", "Indian/Mauritius");
+           aliases0.put("NST", "Pacific/Auckland");
+           aliases0.put("PLT", "Indian/Kerguelen");
+           aliases0.put("PNT", "MST7MDT");
+           aliases0.put("PRT", "America/Anguilla");
+           aliases0.put("PST", "PST8PDT");
+           aliases0.put("SST", "Pacific/Ponape");
+           aliases0.put("VST", "Asia/Bangkok");
+           return timezones;
+         }
+
+       TimeZone tz;
+       // Automatically generated by scripts/timezones.pl
+       // XXX - Should we read this data from a file?
+       tz = new SimpleTimeZone(-11000 * 3600, "MIT");
+       timezones0.put("MIT", tz);
+       timezones0.put("Pacific/Apia", tz);
+       timezones0.put("Pacific/Midway", tz);
+       timezones0.put("Pacific/Niue", tz);
+       timezones0.put("Pacific/Pago_Pago", tz);
+       tz = new SimpleTimeZone
+         (-10000 * 3600, "America/Adak",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("America/Adak", tz);
+       tz = new SimpleTimeZone(-10000 * 3600, "HST");
+       timezones0.put("HST", tz);
+       timezones0.put("Pacific/Fakaofo", tz);
+       timezones0.put("Pacific/Honolulu", tz);
+       timezones0.put("Pacific/Johnston", tz);
+       timezones0.put("Pacific/Rarotonga", tz);
+       timezones0.put("Pacific/Tahiti", tz);
+       tz = new SimpleTimeZone(-9500 * 3600, "Pacific/Marquesas");
+       timezones0.put("Pacific/Marquesas", tz);
+       tz = new SimpleTimeZone
+         (-9000 * 3600, "AST",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("AST", tz);
+       timezones0.put("America/Anchorage", tz);
+       timezones0.put("America/Juneau", tz);
+       timezones0.put("America/Nome", tz);
+       timezones0.put("America/Yakutat", tz);
+       tz = new SimpleTimeZone(-9000 * 3600, "Pacific/Gambier");
+       timezones0.put("Pacific/Gambier", tz);
+       tz = new SimpleTimeZone
+         (-8000 * 3600, "America/Tijuana",
+          Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("America/Tijuana", tz);
+       tz = new SimpleTimeZone
+         (-8000 * 3600, "PST",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("PST", tz);
+       timezones0.put("PST8PDT", tz);
+       timezones0.put("America/Dawson", tz);
+       timezones0.put("America/Los_Angeles", tz);
+       timezones0.put("America/Vancouver", tz);
+       timezones0.put("America/Whitehorse", tz);
+       timezones0.put("US/Pacific-New", tz);
+       tz = new SimpleTimeZone(-8000 * 3600, "Pacific/Pitcairn");
+       timezones0.put("Pacific/Pitcairn", tz);
+       tz = new SimpleTimeZone
+         (-7000 * 3600, "America/Chihuahua",
+          Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("America/Chihuahua", tz);
+       timezones0.put("America/Mazatlan", tz);
+       tz = new SimpleTimeZone(-7000 * 3600, "MST7");
+       timezones0.put("MST7", tz);
+       timezones0.put("PNT", tz);
+       timezones0.put("America/Dawson_Creek", tz);
+       timezones0.put("America/Hermosillo", tz);
+       timezones0.put("America/Phoenix", tz);
+       tz = new SimpleTimeZone
+         (-7000 * 3600, "MST",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("MST", tz);
+       timezones0.put("MST7MDT", tz);
+       timezones0.put("America/Boise", tz);
+       timezones0.put("America/Cambridge_Bay", tz);
+       timezones0.put("America/Denver", tz);
+       timezones0.put("America/Edmonton", tz);
+       timezones0.put("America/Inuvik", tz);
+       timezones0.put("America/Shiprock", tz);
+       timezones0.put("America/Yellowknife", tz);
+       tz = new SimpleTimeZone
+         (-6000 * 3600, "America/Cancun",
+          Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("America/Cancun", tz);
+       timezones0.put("America/Merida", tz);
+       timezones0.put("America/Mexico_City", tz);
+       timezones0.put("America/Monterrey", tz);
+       tz = new SimpleTimeZone(-6000 * 3600, "America/Belize");
+       timezones0.put("America/Belize", tz);
+       timezones0.put("America/Costa_Rica", tz);
+       timezones0.put("America/El_Salvador", tz);
+       timezones0.put("America/Guatemala", tz);
+       timezones0.put("America/Managua", tz);
+       timezones0.put("America/Regina", tz);
+       timezones0.put("America/Swift_Current", tz);
+       timezones0.put("America/Tegucigalpa", tz);
+       timezones0.put("Pacific/Galapagos", tz);
+       tz = new SimpleTimeZone
+         (-6000 * 3600, "CST",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("CST", tz);
+       timezones0.put("CST6CDT", tz);
+       timezones0.put("America/Chicago", tz);
+       timezones0.put("America/Indiana/Knox", tz);
+       timezones0.put("America/Indiana/Petersburg", tz);
+       timezones0.put("America/Indiana/Vincennes", tz);
+       timezones0.put("America/Menominee", tz);
+       timezones0.put("America/North_Dakota/Center", tz);
+       timezones0.put("America/North_Dakota/New_Salem", tz);
+       timezones0.put("America/Rainy_River", tz);
+       timezones0.put("America/Rankin_Inlet", tz);
+       timezones0.put("America/Winnipeg", tz);
+       tz = new SimpleTimeZone
+         (-6000 * 3600, "Pacific/Easter",
+          Calendar.OCTOBER, 2, Calendar.SATURDAY, 22000 * 3600,
+          Calendar.MARCH, 2, Calendar.SATURDAY, 22000 * 3600);
+       timezones0.put("Pacific/Easter", tz);
+       tz = new SimpleTimeZone(-5000 * 3600, "EST5");
+       timezones0.put("EST5", tz);
+       timezones0.put("IET", tz);
+       timezones0.put("America/Atikokan", tz);
+       timezones0.put("America/Bogota", tz);
+       timezones0.put("America/Cayman", tz);
+       timezones0.put("America/Eirunepe", tz);
+       timezones0.put("America/Guayaquil", tz);
+       timezones0.put("America/Jamaica", tz);
+       timezones0.put("America/Lima", tz);
+       timezones0.put("America/Panama", tz);
+       timezones0.put("America/Rio_Branco", tz);
+       tz = new SimpleTimeZone
+         (-5000 * 3600, "America/Havana",
+          Calendar.APRIL, 1, Calendar.SUNDAY, 0 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600);
+       timezones0.put("America/Havana", tz);
+       tz = new SimpleTimeZone
+         (-5000 * 3600, "America/Grand_Turk",
+          Calendar.APRIL, 1, Calendar.SUNDAY, 0 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+       timezones0.put("America/Grand_Turk", tz);
+       timezones0.put("America/Port-au-Prince", tz);
+       tz = new SimpleTimeZone
+         (-5000 * 3600, "EST",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("EST", tz);
+       timezones0.put("EST5EDT", tz);
+       timezones0.put("America/Detroit", tz);
+       timezones0.put("America/Indiana/Indianapolis", tz);
+       timezones0.put("America/Indiana/Marengo", tz);
+       timezones0.put("America/Indiana/Vevay", tz);
+       timezones0.put("America/Iqaluit", tz);
+       timezones0.put("America/Kentucky/Louisville", tz);
+       timezones0.put("America/Kentucky/Monticello", tz);
+       timezones0.put("America/Montreal", tz);
+       timezones0.put("America/Nassau", tz);
+       timezones0.put("America/New_York", tz);
+       timezones0.put("America/Nipigon", tz);
+       timezones0.put("America/Pangnirtung", tz);
+       timezones0.put("America/Thunder_Bay", tz);
+       timezones0.put("America/Toronto", tz);
+       tz = new SimpleTimeZone
+         (-4000 * 3600, "America/Asuncion",
+          Calendar.OCTOBER, 3, Calendar.SUNDAY, 0 * 3600,
+          Calendar.MARCH, 2, Calendar.SUNDAY, 0 * 3600);
+       timezones0.put("America/Asuncion", tz);
+       tz = new SimpleTimeZone(-4000 * 3600, "PRT");
+       timezones0.put("PRT", tz);
+       timezones0.put("America/Anguilla", tz);
+       timezones0.put("America/Antigua", tz);
+       timezones0.put("America/Aruba", tz);
+       timezones0.put("America/Barbados", tz);
+       timezones0.put("America/Blanc-Sablon", tz);
+       timezones0.put("America/Boa_Vista", tz);
+       timezones0.put("America/Caracas", tz);
+       timezones0.put("America/Curacao", tz);
+       timezones0.put("America/Dominica", tz);
+       timezones0.put("America/Grenada", tz);
+       timezones0.put("America/Guadeloupe", tz);
+       timezones0.put("America/Guyana", tz);
+       timezones0.put("America/La_Paz", tz);
+       timezones0.put("America/Manaus", tz);
+       timezones0.put("America/Martinique", tz);
+       timezones0.put("America/Montserrat", tz);
+       timezones0.put("America/Port_of_Spain", tz);
+       timezones0.put("America/Porto_Velho", tz);
+       timezones0.put("America/Puerto_Rico", tz);
+       timezones0.put("America/Santo_Domingo", tz);
+       timezones0.put("America/St_Kitts", tz);
+       timezones0.put("America/St_Lucia", tz);
+       timezones0.put("America/St_Thomas", tz);
+       timezones0.put("America/St_Vincent", tz);
+       timezones0.put("America/Tortola", tz);
+       tz = new SimpleTimeZone
+         (-4000 * 3600, "America/Campo_Grande",
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 0 * 3600,
+          Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600);
+       timezones0.put("America/Campo_Grande", tz);
+       timezones0.put("America/Cuiaba", tz);
+       tz = new SimpleTimeZone
+         (-4000 * 3600, "America/Goose_Bay",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 60000,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 60000);
+       timezones0.put("America/Goose_Bay", tz);
+       tz = new SimpleTimeZone
+         (-4000 * 3600, "America/Glace_Bay",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("America/Glace_Bay", tz);
+       timezones0.put("America/Halifax", tz);
+       timezones0.put("America/Moncton", tz);
+       timezones0.put("America/Thule", tz);
+       timezones0.put("Atlantic/Bermuda", tz);
+       tz = new SimpleTimeZone
+         (-4000 * 3600, "America/Santiago",
+          Calendar.OCTOBER, 9, -Calendar.SUNDAY, 0 * 3600,
+          Calendar.MARCH, 9, -Calendar.SUNDAY, 0 * 3600);
+       timezones0.put("America/Santiago", tz);
+       timezones0.put("Antarctica/Palmer", tz);
+       tz = new SimpleTimeZone
+         (-4000 * 3600, "Atlantic/Stanley",
+          Calendar.SEPTEMBER, 1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.APRIL, 3, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("Atlantic/Stanley", tz);
+       tz = new SimpleTimeZone
+         (-3500 * 3600, "CNT",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 60000,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 60000);
+       timezones0.put("CNT", tz);
+       timezones0.put("America/St_Johns", tz);
+       tz = new SimpleTimeZone
+         (-3000 * 3600, "America/Godthab",
+          Calendar.MARCH, 30, -Calendar.SATURDAY, 22000 * 3600,
+          Calendar.OCTOBER, 30, -Calendar.SATURDAY, 23000 * 3600);
+       timezones0.put("America/Godthab", tz);
+       tz = new SimpleTimeZone
+         (-3000 * 3600, "America/Miquelon",
+          Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("America/Miquelon", tz);
+       tz = new SimpleTimeZone
+         (-3000 * 3600, "America/Montevideo",
+          Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("America/Montevideo", tz);
+       tz = new SimpleTimeZone
+         (-3000 * 3600, "America/Sao_Paulo",
+          Calendar.NOVEMBER, 1, Calendar.SUNDAY, 0 * 3600,
+          Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600);
+       timezones0.put("America/Sao_Paulo", tz);
+       tz = new SimpleTimeZone(-3000 * 3600, "AGT");
+       timezones0.put("AGT", tz);
+       timezones0.put("America/Araguaina", tz);
+       timezones0.put("America/Argentina/Buenos_Aires", tz);
+       timezones0.put("America/Argentina/Catamarca", tz);
+       timezones0.put("America/Argentina/Cordoba", tz);
+       timezones0.put("America/Argentina/Jujuy", tz);
+       timezones0.put("America/Argentina/La_Rioja", tz);
+       timezones0.put("America/Argentina/Mendoza", tz);
+       timezones0.put("America/Argentina/Rio_Gallegos", tz);
+       timezones0.put("America/Argentina/San_Juan", tz);
+       timezones0.put("America/Argentina/Tucuman", tz);
+       timezones0.put("America/Argentina/Ushuaia", tz);
+       timezones0.put("America/Bahia", tz);
+       timezones0.put("America/Belem", tz);
+       timezones0.put("America/Cayenne", tz);
+       timezones0.put("America/Fortaleza", tz);
+       timezones0.put("America/Maceio", tz);
+       timezones0.put("America/Paramaribo", tz);
+       timezones0.put("America/Recife", tz);
+       timezones0.put("Antarctica/Rothera", tz);
+       tz = new SimpleTimeZone(-2000 * 3600, "America/Noronha");
+       timezones0.put("America/Noronha", tz);
+       timezones0.put("Atlantic/South_Georgia", tz);
+       tz = new SimpleTimeZone
+         (-1000 * 3600, "America/Scoresbysund",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600);
+       timezones0.put("America/Scoresbysund", tz);
+       timezones0.put("Atlantic/Azores", tz);
+       tz = new SimpleTimeZone(-1000 * 3600, "Atlantic/Cape_Verde");
+       timezones0.put("Atlantic/Cape_Verde", tz);
+       tz = new SimpleTimeZone(0 * 3600, "GMT");
+       timezones0.put("GMT", tz);
+       timezones0.put("UTC", tz);
+       timezones0.put("Africa/Abidjan", tz);
+       timezones0.put("Africa/Accra", tz);
+       timezones0.put("Africa/Bamako", tz);
+       timezones0.put("Africa/Banjul", tz);
+       timezones0.put("Africa/Bissau", tz);
+       timezones0.put("Africa/Casablanca", tz);
+       timezones0.put("Africa/Conakry", tz);
+       timezones0.put("Africa/Dakar", tz);
+       timezones0.put("Africa/El_Aaiun", tz);
+       timezones0.put("Africa/Freetown", tz);
+       timezones0.put("Africa/Lome", tz);
+       timezones0.put("Africa/Monrovia", tz);
+       timezones0.put("Africa/Nouakchott", tz);
+       timezones0.put("Africa/Ouagadougou", tz);
+       timezones0.put("Africa/Sao_Tome", tz);
+       timezones0.put("America/Danmarkshavn", tz);
+       timezones0.put("Atlantic/Reykjavik", tz);
+       timezones0.put("Atlantic/St_Helena", tz);
+       tz = new SimpleTimeZone
+         (0 * 3600, "WET",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 1000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("WET", tz);
+       timezones0.put("Atlantic/Canary", tz);
+       timezones0.put("Atlantic/Faroe", tz);
+       timezones0.put("Atlantic/Madeira", tz);
+       timezones0.put("Europe/Dublin", tz);
+       timezones0.put("Europe/Guernsey", tz);
+       timezones0.put("Europe/Isle_of_Man", tz);
+       timezones0.put("Europe/Jersey", tz);
+       timezones0.put("Europe/Lisbon", tz);
+       timezones0.put("Europe/London", tz);
+       tz = new SimpleTimeZone(1000 * 3600, "Africa/Algiers");
+       timezones0.put("Africa/Algiers", tz);
+       timezones0.put("Africa/Bangui", tz);
+       timezones0.put("Africa/Brazzaville", tz);
+       timezones0.put("Africa/Douala", tz);
+       timezones0.put("Africa/Kinshasa", tz);
+       timezones0.put("Africa/Lagos", tz);
+       timezones0.put("Africa/Libreville", tz);
+       timezones0.put("Africa/Luanda", tz);
+       timezones0.put("Africa/Malabo", tz);
+       timezones0.put("Africa/Ndjamena", tz);
+       timezones0.put("Africa/Niamey", tz);
+       timezones0.put("Africa/Porto-Novo", tz);
+       tz = new SimpleTimeZone
+         (1000 * 3600, "Africa/Windhoek",
+          Calendar.SEPTEMBER, 1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600);
+       timezones0.put("Africa/Windhoek", tz);
+       tz = new SimpleTimeZone
+         (1000 * 3600, "CET",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("CET", tz);
+       timezones0.put("ECT", tz);
+       timezones0.put("MET", tz);
+       timezones0.put("Africa/Ceuta", tz);
+       timezones0.put("Africa/Tunis", tz);
+       timezones0.put("Arctic/Longyearbyen", tz);
+       timezones0.put("Atlantic/Jan_Mayen", tz);
+       timezones0.put("Europe/Amsterdam", tz);
+       timezones0.put("Europe/Andorra", tz);
+       timezones0.put("Europe/Belgrade", tz);
+       timezones0.put("Europe/Berlin", tz);
+       timezones0.put("Europe/Bratislava", tz);
+       timezones0.put("Europe/Brussels", tz);
+       timezones0.put("Europe/Budapest", tz);
+       timezones0.put("Europe/Copenhagen", tz);
+       timezones0.put("Europe/Gibraltar", tz);
+       timezones0.put("Europe/Ljubljana", tz);
+       timezones0.put("Europe/Luxembourg", tz);
+       timezones0.put("Europe/Madrid", tz);
+       timezones0.put("Europe/Malta", tz);
+       timezones0.put("Europe/Monaco", tz);
+       timezones0.put("Europe/Oslo", tz);
+       timezones0.put("Europe/Paris", tz);
+       timezones0.put("Europe/Podgorica", tz);
+       timezones0.put("Europe/Prague", tz);
+       timezones0.put("Europe/Rome", tz);
+       timezones0.put("Europe/San_Marino", tz);
+       timezones0.put("Europe/Sarajevo", tz);
+       timezones0.put("Europe/Skopje", tz);
+       timezones0.put("Europe/Stockholm", tz);
+       timezones0.put("Europe/Tirane", tz);
+       timezones0.put("Europe/Vaduz", tz);
+       timezones0.put("Europe/Vatican", tz);
+       timezones0.put("Europe/Vienna", tz);
+       timezones0.put("Europe/Warsaw", tz);
+       timezones0.put("Europe/Zagreb", tz);
+       timezones0.put("Europe/Zurich", tz);
+       tz = new SimpleTimeZone
+         (2000 * 3600, "ART",
+          Calendar.APRIL, -1, Calendar.FRIDAY, 0 * 3600,
+          Calendar.SEPTEMBER, -1, Calendar.THURSDAY, 24000 * 3600);
+       timezones0.put("ART", tz);
+       timezones0.put("Africa/Cairo", tz);
+       tz = new SimpleTimeZone(2000 * 3600, "CAT");
+       timezones0.put("CAT", tz);
+       timezones0.put("Africa/Blantyre", tz);
+       timezones0.put("Africa/Bujumbura", tz);
+       timezones0.put("Africa/Gaborone", tz);
+       timezones0.put("Africa/Harare", tz);
+       timezones0.put("Africa/Johannesburg", tz);
+       timezones0.put("Africa/Kigali", tz);
+       timezones0.put("Africa/Lubumbashi", tz);
+       timezones0.put("Africa/Lusaka", tz);
+       timezones0.put("Africa/Maputo", tz);
+       timezones0.put("Africa/Maseru", tz);
+       timezones0.put("Africa/Mbabane", tz);
+       timezones0.put("Africa/Tripoli", tz);
+       timezones0.put("Asia/Jerusalem", tz);
+       tz = new SimpleTimeZone
+         (2000 * 3600, "Asia/Amman",
+          Calendar.MARCH, -1, Calendar.THURSDAY, 0 * 3600,
+          Calendar.OCTOBER, -1, Calendar.FRIDAY, 1000 * 3600);
+       timezones0.put("Asia/Amman", tz);
+       tz = new SimpleTimeZone
+         (2000 * 3600, "Asia/Beirut",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+       timezones0.put("Asia/Beirut", tz);
+       tz = new SimpleTimeZone
+         (2000 * 3600, "Asia/Damascus",
+          Calendar.APRIL, 1, 0, 0 * 3600,
+          Calendar.OCTOBER, 1, 0, 0 * 3600);
+       timezones0.put("Asia/Damascus", tz);
+       tz = new SimpleTimeZone
+         (2000 * 3600, "Asia/Gaza",
+          Calendar.APRIL, 1, 0, 0 * 3600,
+          Calendar.OCTOBER, 3, Calendar.FRIDAY, 0 * 3600);
+       timezones0.put("Asia/Gaza", tz);
+       tz = new SimpleTimeZone
+         (2000 * 3600, "EET",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 4000 * 3600);
+       timezones0.put("EET", tz);
+       timezones0.put("Asia/Istanbul", tz);
+       timezones0.put("Asia/Nicosia", tz);
+       timezones0.put("Europe/Athens", tz);
+       timezones0.put("Europe/Bucharest", tz);
+       timezones0.put("Europe/Chisinau", tz);
+       timezones0.put("Europe/Helsinki", tz);
+       timezones0.put("Europe/Istanbul", tz);
+       timezones0.put("Europe/Kiev", tz);
+       timezones0.put("Europe/Mariehamn", tz);
+       timezones0.put("Europe/Nicosia", tz);
+       timezones0.put("Europe/Riga", tz);
+       timezones0.put("Europe/Simferopol", tz);
+       timezones0.put("Europe/Sofia", tz);
+       timezones0.put("Europe/Tallinn", tz);
+       timezones0.put("Europe/Uzhgorod", tz);
+       timezones0.put("Europe/Vilnius", tz);
+       timezones0.put("Europe/Zaporozhye", tz);
+       tz = new SimpleTimeZone
+         (2000 * 3600, "Europe/Kaliningrad",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Europe/Kaliningrad", tz);
+       timezones0.put("Europe/Minsk", tz);
+       tz = new SimpleTimeZone
+         (3000 * 3600, "Asia/Baghdad",
+          Calendar.APRIL, 1, 0, 3000 * 3600,
+          Calendar.OCTOBER, 1, 0, 4000 * 3600);
+       timezones0.put("Asia/Baghdad", tz);
+       tz = new SimpleTimeZone
+         (3000 * 3600, "Europe/Moscow",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Europe/Moscow", tz);
+       timezones0.put("Europe/Volgograd", tz);
+       tz = new SimpleTimeZone(3000 * 3600, "EAT");
+       timezones0.put("EAT", tz);
+       timezones0.put("Africa/Addis_Ababa", tz);
+       timezones0.put("Africa/Asmara", tz);
+       timezones0.put("Africa/Dar_es_Salaam", tz);
+       timezones0.put("Africa/Djibouti", tz);
+       timezones0.put("Africa/Kampala", tz);
+       timezones0.put("Africa/Khartoum", tz);
+       timezones0.put("Africa/Mogadishu", tz);
+       timezones0.put("Africa/Nairobi", tz);
+       timezones0.put("Antarctica/Syowa", tz);
+       timezones0.put("Asia/Aden", tz);
+       timezones0.put("Asia/Bahrain", tz);
+       timezones0.put("Asia/Kuwait", tz);
+       timezones0.put("Asia/Qatar", tz);
+       timezones0.put("Asia/Riyadh", tz);
+       timezones0.put("Indian/Antananarivo", tz);
+       timezones0.put("Indian/Comoro", tz);
+       timezones0.put("Indian/Mayotte", tz);
+       tz = new SimpleTimeZone(3500 * 3600, "Asia/Tehran");
+       timezones0.put("Asia/Tehran", tz);
+       tz = new SimpleTimeZone
+         (4000 * 3600, "Asia/Baku",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 4000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 5000 * 3600);
+       timezones0.put("Asia/Baku", tz);
+       tz = new SimpleTimeZone
+         (4000 * 3600, "Asia/Yerevan",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Asia/Yerevan", tz);
+       timezones0.put("Europe/Samara", tz);
+       tz = new SimpleTimeZone(4000 * 3600, "NET");
+       timezones0.put("NET", tz);
+       timezones0.put("Asia/Dubai", tz);
+       timezones0.put("Asia/Muscat", tz);
+       timezones0.put("Asia/Tbilisi", tz);
+       timezones0.put("Indian/Mahe", tz);
+       timezones0.put("Indian/Mauritius", tz);
+       timezones0.put("Indian/Reunion", tz);
+       tz = new SimpleTimeZone(4500 * 3600, "Asia/Kabul");
+       timezones0.put("Asia/Kabul", tz);
+       tz = new SimpleTimeZone
+         (5000 * 3600, "Asia/Yekaterinburg",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Asia/Yekaterinburg", tz);
+       tz = new SimpleTimeZone(5000 * 3600, "PLT");
+       timezones0.put("PLT", tz);
+       timezones0.put("Asia/Aqtau", tz);
+       timezones0.put("Asia/Aqtobe", tz);
+       timezones0.put("Asia/Ashgabat", tz);
+       timezones0.put("Asia/Dushanbe", tz);
+       timezones0.put("Asia/Karachi", tz);
+       timezones0.put("Asia/Oral", tz);
+       timezones0.put("Asia/Samarkand", tz);
+       timezones0.put("Asia/Tashkent", tz);
+       timezones0.put("Indian/Kerguelen", tz);
+       timezones0.put("Indian/Maldives", tz);
+       tz = new SimpleTimeZone(5500 * 3600, "BST");
+       timezones0.put("BST", tz);
+       timezones0.put("IST", tz);
+       timezones0.put("Asia/Calcutta", tz);
+       timezones0.put("Asia/Colombo", tz);
+       tz = new SimpleTimeZone(5750 * 3600, "Asia/Katmandu");
+       timezones0.put("Asia/Katmandu", tz);
+       tz = new SimpleTimeZone(6000 * 3600, "Antarctica/Mawson");
+       timezones0.put("Antarctica/Mawson", tz);
+       timezones0.put("Antarctica/Vostok", tz);
+       timezones0.put("Asia/Almaty", tz);
+       timezones0.put("Asia/Bishkek", tz);
+       timezones0.put("Asia/Dhaka", tz);
+       timezones0.put("Asia/Qyzylorda", tz);
+       timezones0.put("Asia/Thimphu", tz);
+       timezones0.put("Indian/Chagos", tz);
+       tz = new SimpleTimeZone
+         (6000 * 3600, "Asia/Novosibirsk",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Asia/Novosibirsk", tz);
+       timezones0.put("Asia/Omsk", tz);
+       tz = new SimpleTimeZone(6500 * 3600, "Asia/Rangoon");
+       timezones0.put("Asia/Rangoon", tz);
+       timezones0.put("Indian/Cocos", tz);
+       tz = new SimpleTimeZone(7000 * 3600, "VST");
+       timezones0.put("VST", tz);
+       timezones0.put("Antarctica/Davis", tz);
+       timezones0.put("Asia/Bangkok", tz);
+       timezones0.put("Asia/Jakarta", tz);
+       timezones0.put("Asia/Phnom_Penh", tz);
+       timezones0.put("Asia/Pontianak", tz);
+       timezones0.put("Asia/Saigon", tz);
+       timezones0.put("Asia/Vientiane", tz);
+       timezones0.put("Indian/Christmas", tz);
+       tz = new SimpleTimeZone
+         (7000 * 3600, "Asia/Hovd",
+          Calendar.MARCH, -1, Calendar.SATURDAY, 2000 * 3600,
+          Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 2000 * 3600);
+       timezones0.put("Asia/Hovd", tz);
+       tz = new SimpleTimeZone
+         (7000 * 3600, "Asia/Krasnoyarsk",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Asia/Krasnoyarsk", tz);
+       tz = new SimpleTimeZone(8000 * 3600, "CTT");
+       timezones0.put("CTT", tz);
+       timezones0.put("Antarctica/Casey", tz);
+       timezones0.put("Asia/Brunei", tz);
+       timezones0.put("Asia/Chongqing", tz);
+       timezones0.put("Asia/Harbin", tz);
+       timezones0.put("Asia/Hong_Kong", tz);
+       timezones0.put("Asia/Kashgar", tz);
+       timezones0.put("Asia/Kuala_Lumpur", tz);
+       timezones0.put("Asia/Kuching", tz);
+       timezones0.put("Asia/Macau", tz);
+       timezones0.put("Asia/Makassar", tz);
+       timezones0.put("Asia/Manila", tz);
+       timezones0.put("Asia/Shanghai", tz);
+       timezones0.put("Asia/Singapore", tz);
+       timezones0.put("Asia/Taipei", tz);
+       timezones0.put("Asia/Urumqi", tz);
+       timezones0.put("Australia/Perth", tz);
+       tz = new SimpleTimeZone
+         (8000 * 3600, "Asia/Irkutsk",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Asia/Irkutsk", tz);
+       tz = new SimpleTimeZone
+         (8000 * 3600, "Asia/Ulaanbaatar",
+          Calendar.MARCH, -1, Calendar.SATURDAY, 2000 * 3600,
+          Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 2000 * 3600);
+       timezones0.put("Asia/Ulaanbaatar", tz);
+       tz = new SimpleTimeZone(8750 * 3600, "Australia/Eucla");
+       timezones0.put("Australia/Eucla", tz);
+       tz = new SimpleTimeZone
+         (9000 * 3600, "Asia/Choibalsan",
+          Calendar.MARCH, -1, Calendar.SATURDAY, 2000 * 3600,
+          Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 2000 * 3600);
+       timezones0.put("Asia/Choibalsan", tz);
+       tz = new SimpleTimeZone(9000 * 3600, "JST");
+       timezones0.put("JST", tz);
+       timezones0.put("Asia/Dili", tz);
+       timezones0.put("Asia/Jayapura", tz);
+       timezones0.put("Asia/Pyongyang", tz);
+       timezones0.put("Asia/Seoul", tz);
+       timezones0.put("Asia/Tokyo", tz);
+       timezones0.put("Pacific/Palau", tz);
+       tz = new SimpleTimeZone
+         (9000 * 3600, "Asia/Yakutsk",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Asia/Yakutsk", tz);
+       tz = new SimpleTimeZone
+         (9500 * 3600, "Australia/Adelaide",
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Australia/Adelaide", tz);
+       timezones0.put("Australia/Broken_Hill", tz);
+       tz = new SimpleTimeZone(9500 * 3600, "ACT");
+       timezones0.put("ACT", tz);
+       timezones0.put("Australia/Darwin", tz);
+       tz = new SimpleTimeZone(10000 * 3600, "Antarctica/DumontDUrville");
+       timezones0.put("Antarctica/DumontDUrville", tz);
+       timezones0.put("Australia/Brisbane", tz);
+       timezones0.put("Australia/Lindeman", tz);
+       timezones0.put("Pacific/Guam", tz);
+       timezones0.put("Pacific/Port_Moresby", tz);
+       timezones0.put("Pacific/Saipan", tz);
+       timezones0.put("Pacific/Truk", tz);
+       tz = new SimpleTimeZone
+         (10000 * 3600, "Asia/Sakhalin",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Asia/Sakhalin", tz);
+       timezones0.put("Asia/Vladivostok", tz);
+       tz = new SimpleTimeZone
+         (10000 * 3600, "Australia/Currie",
+          Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Australia/Currie", tz);
+       timezones0.put("Australia/Hobart", tz);
+       tz = new SimpleTimeZone
+         (10000 * 3600, "AET",
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("AET", tz);
+       timezones0.put("Australia/Melbourne", tz);
+       timezones0.put("Australia/Sydney", tz);
+       tz = new SimpleTimeZone
+         (10500 * 3600, "Australia/Lord_Howe",
+         Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
+         Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, 500 * 3600);
+       timezones0.put("Australia/Lord_Howe", tz);
+       tz = new SimpleTimeZone
+         (11000 * 3600, "Asia/Magadan",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Asia/Magadan", tz);
+       tz = new SimpleTimeZone(11000 * 3600, "SST");
+       timezones0.put("SST", tz);
+       timezones0.put("Pacific/Efate", tz);
+       timezones0.put("Pacific/Guadalcanal", tz);
+       timezones0.put("Pacific/Kosrae", tz);
+       timezones0.put("Pacific/Noumea", tz);
+       timezones0.put("Pacific/Ponape", tz);
+       tz = new SimpleTimeZone(11500 * 3600, "Pacific/Norfolk");
+       timezones0.put("Pacific/Norfolk", tz);
+       tz = new SimpleTimeZone
+         (12000 * 3600, "NST",
+          Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.MARCH, 3, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("NST", tz);
+       timezones0.put("Antarctica/McMurdo", tz);
+       timezones0.put("Antarctica/South_Pole", tz);
+       timezones0.put("Pacific/Auckland", tz);
+       tz = new SimpleTimeZone
+         (12000 * 3600, "Asia/Anadyr",
+          Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+          Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+       timezones0.put("Asia/Anadyr", tz);
+       timezones0.put("Asia/Kamchatka", tz);
+       tz = new SimpleTimeZone(12000 * 3600, "Pacific/Fiji");
+       timezones0.put("Pacific/Fiji", tz);
+       timezones0.put("Pacific/Funafuti", tz);
+       timezones0.put("Pacific/Kwajalein", tz);
+       timezones0.put("Pacific/Majuro", tz);
+       timezones0.put("Pacific/Nauru", tz);
+       timezones0.put("Pacific/Tarawa", tz);
+       timezones0.put("Pacific/Wake", tz);
+       timezones0.put("Pacific/Wallis", tz);
+       tz = new SimpleTimeZone
+         (12750 * 3600, "Pacific/Chatham",
+          Calendar.OCTOBER, 1, Calendar.SUNDAY, 2750 * 3600,
+          Calendar.MARCH, 3, Calendar.SUNDAY, 3750 * 3600);
+       timezones0.put("Pacific/Chatham", tz);
+       tz = new SimpleTimeZone(13000 * 3600, "Pacific/Enderbury");
+       timezones0.put("Pacific/Enderbury", tz);
+       timezones0.put("Pacific/Tongatapu", tz);
+       tz = new SimpleTimeZone(14000 * 3600, "Pacific/Kiritimati");
+       timezones0.put("Pacific/Kiritimati", tz);
+      }
+    return timezones0;
+  }
+
+  /**
+   * Maps a time zone name (with optional GMT offset and daylight time
+   * zone name) to one of the known time zones.  This method called
+   * with the result of <code>System.getProperty("user.timezone")</code>
+   * or <code>getDefaultTimeZoneId()</code>.  Note that giving one of
+   * the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is
+   * preferred.  
+   * The time zone name can be given as follows:
+   * <code>(standard zone name)[(GMT offset)[(DST zone name)[DST offset]]]
+   * </code>
+   * <p>
+   * If only a (standard zone name) is given (no numbers in the
+   * String) then it gets mapped directly to the TimeZone with that
+   * name, if that fails null is returned.
+   * <p>
+   * Alternately, a POSIX-style TZ string can be given, defining the time zone:
+   * <code>std offset dst offset,date/time,date/time</code>
+   * See the glibc manual, or the man page for <code>tzset</code> for details
+   * of this format.
+   * <p>
+   * A GMT offset is the offset to add to the local time to get GMT.
+   * If a (GMT offset) is included (either in seconds or hours) then
+   * an attempt is made to find a TimeZone name matching both the name
+   * and the offset (that doesn't observe daylight time, if the
+   * timezone observes daylight time then you must include a daylight
+   * time zone name after the offset), if that fails then a TimeZone
+   * with the given GMT offset is returned (whether or not the
+   * TimeZone observes daylight time is ignored), if that also fails
+   * the GMT TimeZone is returned.
+   * <p>
+   * If the String ends with (GMT offset)(daylight time zone name)
+   * then an attempt is made to find a TimeZone with the given name and
+   * GMT offset that also observes (the daylight time zone name is not
+   * currently used in any other way), if that fails a TimeZone with
+   * the given GMT offset that observes daylight time is returned, if
+   * that also fails the GMT TimeZone is returned.
+   * <p>
+   * Examples: In Chicago, the time zone id could be "CST6CDT", but
+   * the preferred name would be "America/Chicago".  In Indianapolis
+   * (which does not have Daylight Savings Time) the string could be
+   * "EST5", but the preferred name would be "America/Indianapolis".
+   * The standard time zone name for The Netherlands is "Europe/Amsterdam",
+   * but can also be given as "CET-1CEST".
+   */
+  static TimeZone getDefaultTimeZone(String sysTimeZoneId)
+  {
+    String stdName = null;
+    int stdOffs;
+    int dstOffs;
+    try
+      {
+       int idLength = sysTimeZoneId.length();
+
+       int index = 0;
+       int prevIndex;
+       char c;
+
+       // get std
+       do
+         c = sysTimeZoneId.charAt(index);
+       while (c != '+' && c != '-' && c != ',' && c != ':'
+              && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
+
+       if (index >= idLength)
+         return getTimeZoneInternal(sysTimeZoneId);
+
+       stdName = sysTimeZoneId.substring(0, index);
+       prevIndex = index;
+
+       // get the std offset
+       do
+         c = sysTimeZoneId.charAt(index++);
+       while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
+              && index < idLength);
+       if (index < idLength)
+         index--;
+
+       { // convert the dst string to a millis number
+           String offset = sysTimeZoneId.substring(prevIndex, index);
+           prevIndex = index;
+
+           if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
+             stdOffs = parseTime(offset.substring(1));
+           else
+             stdOffs = parseTime(offset);
+
+           if (offset.charAt(0) == '-')
+             stdOffs = -stdOffs;
+
+           // TZ timezone offsets are positive when WEST of the meridian.
+           stdOffs = -stdOffs;
+       }
+
+       // Done yet? (Format: std offset)
+       if (index >= idLength)
+         {
+           // Do we have an existing timezone with that name and offset?
+           TimeZone tz = getTimeZoneInternal(stdName);
+           if (tz != null)
+             if (tz.getRawOffset() == stdOffs)
+               return tz;
+
+           // Custom then.
+           return new SimpleTimeZone(stdOffs, stdName);
+         }
+
+       // get dst
+       do
+         c = sysTimeZoneId.charAt(index);
+       while (c != '+' && c != '-' && c != ',' && c != ':'
+              && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
+
+       // Done yet? (Format: std offset dst)
+       if (index >= idLength)
+         {
+           // Do we have an existing timezone with that name and offset 
+           // which has DST?
+           TimeZone tz = getTimeZoneInternal(stdName);
+           if (tz != null)
+             if (tz.getRawOffset() == stdOffs && tz.useDaylightTime())
+               return tz;
+
+           // Custom then.
+           return new SimpleTimeZone(stdOffs, stdName);
+         }
+
+       // get the dst offset
+       prevIndex = index;
+       do
+         c = sysTimeZoneId.charAt(index++);
+       while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
+              && index < idLength);
+       if (index < idLength)
+         index--;
+
+       if (index == prevIndex && (c == ',' || c == ';'))
+         {
+           // Missing dst offset defaults to one hour ahead of standard
+           // time.  
+           dstOffs = stdOffs + 60 * 60 * 1000;
+         }
+       else
+         { // convert the dst string to a millis number
+           String offset = sysTimeZoneId.substring(prevIndex, index);
+           prevIndex = index;
+
+           if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
+             dstOffs = parseTime(offset.substring(1));
+           else
+             dstOffs = parseTime(offset);
+
+           if (offset.charAt(0) == '-')
+             dstOffs = -dstOffs;
+
+           // TZ timezone offsets are positive when WEST of the meridian.
+           dstOffs = -dstOffs;
+         }
+
+       // Done yet? (Format: std offset dst offset)
+       // FIXME: We don't support DST without a rule given. Should we?
+       if (index >= idLength)
+         {
+           // Time Zone existing with same name, dst and offsets?
+           TimeZone tz = getTimeZoneInternal(stdName);
+           if (tz != null)
+             if (tz.getRawOffset() == stdOffs && tz.useDaylightTime()
+                 && tz.getDSTSavings() == (dstOffs - stdOffs))
+               return tz;
+
+           return new SimpleTimeZone(stdOffs, stdName);
+         }
+
+       // get the DST rule
+       if (sysTimeZoneId.charAt(index) == ','
+           || sysTimeZoneId.charAt(index) == ';')
+         {
+           index++;
+           int offs = index;
+           while (sysTimeZoneId.charAt(index) != ','
+                  && sysTimeZoneId.charAt(index) != ';')
+             index++;
+           String startTime = sysTimeZoneId.substring(offs, index);
+           index++;
+           String endTime = sysTimeZoneId.substring(index);
+
+           index = startTime.indexOf('/');
+           int startMillis;
+           int endMillis;
+           String startDate;
+           String endDate;
+           if (index != -1)
+             {
+               startDate = startTime.substring(0, index);
+               startMillis = parseTime(startTime.substring(index + 1));
+             }
+           else
+             {
+               startDate = startTime;
+               // if time isn't given, default to 2:00:00 AM.
+               startMillis = 2 * 60 * 60 * 1000;
+             }
+           index = endTime.indexOf('/');
+           if (index != -1)
+             {
+               endDate = endTime.substring(0, index);
+               endMillis = parseTime(endTime.substring(index + 1));
+             }
+           else
+             {
+               endDate = endTime;
+               // if time isn't given, default to 2:00:00 AM.
+               endMillis = 2 * 60 * 60 * 1000;
+             }
+
+           int[] start = getDateParams(startDate);
+           int[] end = getDateParams(endDate);
+           return new SimpleTimeZone(stdOffs, stdName, start[0], start[1],
+                                     start[2], startMillis, end[0], end[1],
+                                     end[2], endMillis, (dstOffs - stdOffs));
+         }
+      }
+
+    // FIXME: Produce a warning here?
+    catch (/*IndexOutOfBounds*/Exception _)
+      {
+      }
+    /*catch (NumberFormatException _)
+      {
+      }*/
+
+    return null;
+  }
+
+  /**
+   * Parses and returns the params for a POSIX TZ date field,
+   * in the format int[]{ month, day, dayOfWeek }, following the
+   * SimpleTimeZone constructor rules.
+   */
+  private static int[] getDateParams(String date)
+  {
+    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+    int month;
+
+    if (date.charAt(0) == 'M' || date.charAt(0) == 'm')
+      {
+       int day;
+
+       // Month, week of month, day of week
+       month = Integer.parseInt(date.substring(1, date.indexOf('.')));
+       int week = Integer.parseInt(date.substring(date.indexOf('.') + 1,
+                                                  date.lastIndexOf('.')));
+       int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.')
+                                                       + 1));
+       if (week == 5)
+         day = -1; // last day of month is -1 in java, 5 in TZ
+       else
+         // first day of week starting on or after.
+         day = (week - 1) * 7 + 1;
+
+       dayOfWeek++; // Java day of week is one-based, Sunday is first day.
+       month--; // Java month is zero-based.
+       return new int[] { month, day, dayOfWeek };
+      }
+
+    // julian day, either zero-based 0<=n<=365 (incl feb 29)
+    // or one-based 1<=n<=365 (no feb 29)
+    int julianDay; // Julian day, 
+
+    if (date.charAt(0) != 'J' || date.charAt(0) != 'j')
+      {
+       julianDay = Integer.parseInt(date.substring(1));
+       julianDay++; // make 1-based
+       // Adjust day count to include feb 29.
+       dayCount = new int[]
+                  {
+                    0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
+                  };
+      }
+    else
+      // 1-based julian day
+      julianDay = Integer.parseInt(date);
+
+    int i = 11;
+    while (i > 0)
+      if (dayCount[i] < julianDay)
+       break;
+      else
+       i--;
+    julianDay -= dayCount[i];
+    month = i;
+    return new int[] { month, julianDay, 0 };
+  }
+
+  /**
+   * Parses a time field hh[:mm[:ss]], returning the result
+   * in milliseconds. No leading sign.
+   */
+  private static int parseTime(String time)
+  {
+    int millis = 0;
+    int i = 0;
+
+    while (i < time.length())
+      if (time.charAt(i) == ':')
+       break;
+      else
+       i++;
+    millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i));
+    if (i >= time.length())
+      return millis;
+
+    int iprev = ++i;
+    while (i < time.length())
+      if (time.charAt(i) == ':')
+       break;
+      else
+       i++;
+    millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
+    if (i >= time.length())
+      return millis;
+
+    millis += 1000 * Integer.parseInt(time.substring(++i));
+    return millis;
+  }
+
+  /**
+   * Gets the time zone offset, for current date, modified in case of 
+   * daylight savings.  This is the offset to add to UTC to get the local
+   * time.
+   * @param era the era of the given date
+   * @param year the year of the given date
+   * @param month the month of the given date, 0 for January.
+   * @param day the day of month
+   * @param dayOfWeek the day of week
+   * @param milliseconds the millis in the day (in local standard time)
+   * @return the time zone offset in milliseconds.
+   */
+  public abstract int getOffset(int era, int year, int month,
+                               int day, int dayOfWeek, int milliseconds);
+
+  /**
+   * Get the time zone offset for the specified date, modified in case of
+   * daylight savings.  This is the offset to add to UTC to get the local
+   * time.
+   * @param date the date represented in millisecends
+   * since January 1, 1970 00:00:00 GMT.
+   * @since 1.4
+   */
+  public int getOffset(long date)
+  {
+    return (inDaylightTime(new Date(date))
+            ? getRawOffset() + getDSTSavings()
+            : getRawOffset());
+  }
+  
+  /**
+   * Gets the time zone offset, ignoring daylight savings.  This is
+   * the offset to add to UTC to get the local time.
+   * @return the time zone offset in milliseconds.  
+   */
+  public abstract int getRawOffset();
+
+  /**
+   * Sets the time zone offset, ignoring daylight savings.  This is
+   * the offset to add to UTC to get the local time.
+   * @param offsetMillis the time zone offset to GMT.
+   */
+  public abstract void setRawOffset(int offsetMillis);
+
+  /**
+   * Gets the identifier of this time zone. For instance, PST for
+   * Pacific Standard Time.
+   * @returns the ID of this time zone.  
+   */
+  public String getID()
+  {
+    return ID;
+  }
+
+  /**
+   * Sets the identifier of this time zone. For instance, PST for
+   * Pacific Standard Time.
+   * @param id the new time zone ID.
+   * @throws NullPointerException if <code>id</code> is <code>null</code>
+   */
+  public void setID(String id)
+  {
+    if (id == null)
+      throw new Exception/*NullPointerException*/("NullPointerException");
+    
+    this.ID = id;
+  }
+
+  /**
+   * This method returns a string name of the time zone suitable
+   * for displaying to the user.  The string returned will be the long
+   * description of the timezone in the current locale.  The name
+   * displayed will assume daylight savings time is not in effect.
+   *
+   * @return The name of the time zone.
+   */
+  public final String getDisplayName()
+  {
+    return (getDisplayName(false, LONG, Locale.getDefault()));
+  }
+
+  /**
+   * This method returns a string name of the time zone suitable
+   * for displaying to the user.  The string returned will be the long
+   * description of the timezone in the specified locale. The name
+   * displayed will assume daylight savings time is not in effect.
+   *
+   * @param locale The locale for this timezone name.
+   *
+   * @return The name of the time zone.
+   */
+  public final String getDisplayName(Locale locale)
+  {
+    return (getDisplayName(false, LONG, locale));
+  }
+
+  /**
+   * This method returns a string name of the time zone suitable
+   * for displaying to the user.  The string returned will be of the
+   * specified type in the current locale. 
+   *
+   * @param dst Whether or not daylight savings time is in effect.
+   * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
+   * a short abbreviation.
+   *
+   * @return The name of the time zone.
+   */
+  public final String getDisplayName(boolean dst, int style)
+  {
+    return (getDisplayName(dst, style, Locale.getDefault()));
+  }
+
+
+  /**
+   * This method returns a string name of the time zone suitable
+   * for displaying to the user.  The string returned will be of the
+   * specified type in the specified locale. 
+   *
+   * @param dst Whether or not daylight savings time is in effect.
+   * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
+   * a short abbreviation.
+   * @param locale The locale for this timezone name.
+   *
+   * @return The name of the time zone.
+   */
+  /*public String getDisplayName(boolean dst, int style, Locale locale)
+  {
+    DateFormatSymbols dfs;
+    try
+      {
+       dfs = new DateFormatSymbols(locale);
+
+       // The format of the value returned is defined by us.
+       String[][]zoneinfo = dfs.getZoneStrings();
+       for (int i = 0; i < zoneinfo.length; i++)
+         {
+           if (zoneinfo[i][0].equals(getID()))
+             {
+               if (!dst)
+                 {
+                   if (style == SHORT)
+                     return (zoneinfo[i][2]);
+                   else
+                     return (zoneinfo[i][1]);
+                 }
+               else
+                 {
+                   if (style == SHORT)
+                     return (zoneinfo[i][4]);
+                   else
+                     return (zoneinfo[i][3]);
+                 }
+             }
+         }
+      }
+    catch (MissingResourceException e)
+      {
+      }
+
+    return getDefaultDisplayName(dst);
+  }*/
+
+  /*private String getDefaultDisplayName(boolean dst)
+  {
+    int offset = getRawOffset();
+    if (dst && this instanceof SimpleTimeZone)
+      {
+       // ugly, but this is a design failure of the API:
+       // getDisplayName takes a dst parameter even though
+       // TimeZone knows nothing about daylight saving offsets.
+       offset += ((SimpleTimeZone) this).getDSTSavings();
+      }
+
+    CPStringBuilder sb = new CPStringBuilder(9);
+    sb.append("GMT");
+
+    offset = offset / (1000 * 60);
+    int hours = Math.abs(offset) / 60;
+    int minutes = Math.abs(offset) % 60;
+
+    if (minutes != 0 || hours != 0)
+      {
+       sb.append(offset >= 0 ? '+' : '-');
+       sb.append((char) ('0' + hours / 10));
+       sb.append((char) ('0' + hours % 10));
+       sb.append(':');
+       sb.append((char) ('0' + minutes / 10));
+       sb.append((char) ('0' + minutes % 10));
+      }
+
+    return sb.toString();
+  }*/
+
+  /** 
+   * Returns true, if this time zone uses Daylight Savings Time.
+   */
+  public abstract boolean useDaylightTime();
+
+  /**
+   * Returns true, if the given date is in Daylight Savings Time in this
+   * time zone.
+   * @param date the given Date.
+   */
+  public abstract boolean inDaylightTime(Date date);
+
+  /**
+   * Gets the daylight savings offset.  This is a positive offset in
+   * milliseconds with respect to standard time.  Typically this
+   * is one hour, but for some time zones this may be half an our.
+   * <p>The default implementation returns 3600000 milliseconds
+   * (one hour) if the time zone uses daylight savings time
+   * (as specified by {@link #useDaylightTime()}), otherwise
+   * it returns 0.
+   * @return the daylight savings offset in milliseconds.
+   * @since 1.4
+   */
+  public int getDSTSavings ()
+  {
+    return useDaylightTime () ? 3600000 : 0;
+  }
+
+  /**
+   * Gets the TimeZone for the given ID.
+   * @param ID the time zone identifier.
+   * @return The time zone for the identifier or GMT, if no such time
+   * zone exists.
+   */
+  private static TimeZone getTimeZoneInternal(String ID)
+  {
+    // First check timezones hash
+    TimeZone tz = null;
+    TimeZone tznew = null;
+    for (int pass = 0; pass < 2; pass++)
+      {
+       synchronized (TimeZone.class)
+         {
+           tz = (TimeZone) timezones().get(ID);
+           if (tz != null)
+             {
+               if (!tz.getID().equals(ID))
+                 {
+                   // We always return a timezone with the requested ID.
+                   // This is the same behaviour as with JDK1.2.
+                   tz = (TimeZone) tz.clone();
+                   tz.setID(ID);
+                   // We also save the alias, so that we return the same
+                   // object again if getTimeZone is called with the same
+                   // alias.
+                   timezones().put(ID, tz);
+                 }
+               return tz;
+             }
+           else if (tznew != null)
+             {
+               timezones().put(ID, tznew);
+               return tznew;
+             }
+         }
+
+       if (pass == 1 || zoneinfo_dir == null)
+         return null;
+
+       // aliases0 is never changing after first timezones(), so should
+       // be safe without synchronization.
+       String zonename = (String) aliases0.get(ID);
+       if (zonename == null)
+         zonename = ID;
+
+       // Read the file outside of the critical section, it is expensive.
+       tznew = ZoneInfo.readTZFile (ID, zoneinfo_dir
+                                    + File.separatorChar + zonename);
+       if (tznew == null)
+         return null;
+      }
+
+    return null;
+  }
+
+  /**
+   * Gets the TimeZone for the given ID.
+   * @param ID the time zone identifier.
+   * @return The time zone for the identifier or GMT, if no such time
+   * zone exists.
+   */
+  public static TimeZone getTimeZone(String ID)
+  {
+    // Check for custom IDs first
+    if (ID.startsWith("GMT") && ID.length() > 3)
+      {
+       int pos = 3;
+       int offset_direction = 1;
+
+       if (ID.charAt(pos) == '-')
+         {
+           offset_direction = -1;
+           pos++;
+         }
+       else if (ID.charAt(pos) == '+')
+         {
+           pos++;
+         }
+
+       try
+         {
+           int hour, minute;
+
+           String offset_str = ID.substring(pos);
+           int idx = offset_str.indexOf(":");
+           if (idx != -1)
+             {
+               hour = Integer.parseInt(offset_str.substring(0, idx));
+               minute = Integer.parseInt(offset_str.substring(idx + 1));
+             }
+           else
+             {
+               int offset_length = offset_str.length();
+               if (offset_length <= 2)
+                 {
+                   // Only hour
+                   hour = Integer.parseInt(offset_str);
+                   minute = 0;
+                 }
+               else
+                 {
+                   // hour and minute, not separated by colon
+                   hour = Integer.parseInt
+                     (offset_str.substring(0, offset_length - 2));
+                   minute = Integer.parseInt
+                     (offset_str.substring(offset_length - 2));
+                 }
+             }
+
+           // Custom IDs have to be normalized
+           /*CPStringBuilder sb = new CPStringBuilder(9);
+           sb.append("GMT");
+
+           sb.append(offset_direction >= 0 ? '+' : '-');
+           sb.append((char) ('0' + hour / 10));
+           sb.append((char) ('0' + hour % 10));
+           sb.append(':');
+           sb.append((char) ('0' + minute / 10));
+           sb.append((char) ('0' + minute % 10));*/
+           ID = "GMT" + (offset_direction >= 0 ? "+" : "-") + (char) ('0' + hour / 10)
+        + (char) ('0' + hour % 10) + ":" + (char) ('0' + minute / 10) + (char) ('0' + minute % 10);//sb.toString();
+
+           return new SimpleTimeZone((hour * (60 * 60 * 1000)
+                                      + minute * (60 * 1000))
+                                     * offset_direction, ID);
+         }
+       catch (/*NumberFormat*/Exception e)
+         {
+         }
+      }
+
+    TimeZone tz = getTimeZoneInternal(ID);
+    if (tz != null)
+      return tz;
+
+    return new SimpleTimeZone(0, "GMT");
+  }
+
+  /**
+   * Gets the available IDs according to the given time zone
+   * offset.  
+   * @param rawOffset the given time zone GMT offset.
+   * @return An array of IDs, where the time zone has the specified GMT
+   * offset. For example <code>{"Phoenix", "Denver"}</code>, since both have
+   * GMT-07:00, but differ in daylight savings behaviour.
+   */
+  public static String[] getAvailableIDs(int rawOffset)
+  {
+    synchronized (TimeZone.class)
+      {
+       HashMap h = timezones();
+       int count = 0;
+       if (zoneinfo_dir == null)
+         {
+           Iterator iter = h.entrySet().iterator();
+           while (iter.hasNext())
+             {
+               // Don't iterate the values, since we want to count
+               // doubled values (aliases)
+               Map.Entry entry = (Map.Entry) iter.next();
+               if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
+                 count++;
+             }
+
+           String[] ids = new String[count];
+           count = 0;
+           iter = h.entrySet().iterator();
+           while (iter.hasNext())
+             {
+               Map.Entry entry = (Map.Entry) iter.next();
+               if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
+                 ids[count++] = (String) entry.getKey();
+             }
+           return ids;
+         }
+      }
+
+    String[] s = getAvailableIDs();
+    int count = 0;
+    for (int i = 0; i < s.length; i++)
+      {
+       TimeZone t = getTimeZoneInternal(s[i]);
+       if (t == null || t.getRawOffset() != rawOffset)
+         s[i] = null;
+       else
+         count++;
+      }
+    String[] ids = new String[count];
+    count = 0;
+    for (int i = 0; i < s.length; i++)
+    if (s[i] != null)
+      ids[count++] = s[i];
+
+    return ids;
+  }
+
+  private static int getAvailableIDs(File d, String prefix, ArrayList list)
+    {
+      String[] files = d.list();
+      int count = files.length;
+      boolean top = prefix.length() == 0;
+      list.add (files);
+      for (int i = 0; i < files.length; i++)
+       {
+         if (top
+             && (files[i].equals("posix")
+                 || files[i].equals("right")
+                 || files[i].endsWith(".tab")
+                 || aliases0.get(files[i]) != null))
+           {
+             files[i] = null;
+             count--;
+             continue;
+           }
+
+         File f = new File(d, files[i]);
+         if (f.isDirectory())
+           {
+             count += getAvailableIDs(f, prefix + files[i]
+                                      + File.separatorChar, list) - 1;
+             files[i] = null;
+           }
+         else
+           files[i] = prefix + files[i];
+       }
+      return count;
+    }
+
+  /**
+   * Gets all available IDs.
+   * @return An array of all supported IDs.
+   */
+  /*public static String[] getAvailableIDs()
+  {
+    synchronized (TimeZone.class)
+      {
+       HashMap h = timezones();
+       if (zoneinfo_dir == null)
+         return (String[]) h.keySet().toArray(new String[h.size()]);
+
+       if (availableIDs != null)
+         {
+           String[] ids = new String[availableIDs.length];
+           for (int i = 0; i < availableIDs.length; i++)
+             ids[i] = availableIDs[i];
+           return ids;
+         }
+
+       File d = new File(zoneinfo_dir);
+       ArrayList list = new ArrayList(30);
+       int count = getAvailableIDs(d, "", list) + aliases0.size();
+       availableIDs = new String[count];
+       String[] ids = new String[count];
+
+       count = 0;
+       for (int i = 0; i < list.size(); i++)
+         {
+           String[] s = (String[]) list.get(i);
+           for (int j = 0; j < s.length; j++)
+             if (s[j] != null)
+               {
+                 availableIDs[count] = s[j];
+                 ids[count++] = s[j];
+               }
+         }
+
+       Iterator iter = aliases0.entrySet().iterator();
+       while (iter.hasNext())
+         {
+           Map.Entry entry = (Map.Entry) iter.next();
+           availableIDs[count] = (String) entry.getKey();
+           ids[count++] = (String) entry.getKey();
+         }
+
+       return ids;
+      }
+  }*/
+
+  /**
+   * Returns the time zone under which the host is running.  This
+   * can be changed with setDefault.
+   *
+   * @return A clone of the current default time zone for this host.
+   * @see #setDefault
+   */
+  public static TimeZone getDefault()
+  {
+    return (TimeZone) defaultZone().clone();
+  }
+
+  public static void setDefault(TimeZone zone)
+  {
+    // Hmmmm. No Security checks?
+    defaultZone0 = zone;
+  }
+
+  /**
+   * Test if the other time zone uses the same rule and only
+   * possibly differs in ID.  This implementation for this particular
+   * class will return true if the raw offsets are identical.  Subclasses
+   * should override this method if they use daylight savings.
+   * @return true if this zone has the same raw offset
+   */
+  public boolean hasSameRules(TimeZone other)
+  {
+    return other.getRawOffset() == getRawOffset();
+  }
+
+  /**
+   * Returns a clone of this object.  I can't imagine, why this is
+   * useful for a time zone.
+   */
+  public Object clone()
+  {
+    try
+      {
+       return super.clone();
+      }
+    catch (/*CloneNotSupported*/Exception ex)
+      {
+       return null;
+      }
+  }
+}