package utils;

import java.util.*;

public class Tuple implements Comparable {
  private final Object[] data;

  public Tuple(Object a) 
  { data = new Object[]{a}; calcHashCode(); }
  public Tuple(Object a, Object b)
  { data = new Object[]{a,b}; calcHashCode(); }
  public Tuple(Object a, Object b, Object c)
  { data = new Object[]{a,b,c}; calcHashCode(); }
  public Tuple(Object a, Object b, Object c, Object d)
  { data = new Object[]{a,b,c,d}; calcHashCode(); }
  public Tuple(Object a, Object b, Object c, Object d, Object e)
  { data = new Object[]{a,b,c,d,e}; calcHashCode(); }
  public Tuple(Object a, Object b, Object c, Object d, Object e,
               Object f)
  { data = new Object[]{a,b,c,d,e,f}; calcHashCode(); }
  public Tuple(Object a, Object b, Object c, Object d, Object e,
               Object f, Object g)
  { data = new Object[]{a,b,c,d,e,f,g}; calcHashCode(); }
  public Tuple(Object a, Object b, Object c, Object d, Object e,
               Object f, Object g, Object h)
  { data = new Object[]{a,b,c,d,e,f,g,h}; calcHashCode(); }
  public Tuple(Object a, Object b, Object c, Object d, Object e,
               Object f, Object g, Object h, Object i)
  { data = new Object[]{a,b,c,d,e,f,g,h,i}; calcHashCode(); }
  public Tuple(Object a, Object b, Object c, Object d, Object e,
               Object f, Object g, Object h, Object i, Object j)
  { data = new Object[]{a,b,c,d,e,f,g,h,i,j}; calcHashCode(); }
  public Tuple(Object[] obj) {
    data=new Object[obj.length];
    for(int i=0;i<obj.length;i++) data[i]=obj[i];
    calcHashCode(); 
  }

  public Object first() { return ref(0); }
  public Object second() { return ref(1); }
  public Object third() { return ref(2); }
  public Object fourth() { return ref(3); }
  public Object fifth() { return ref(4); }
  public Object sixth() { return ref(5); }
  public Object seventh() { return ref(6); }
  public Object eigth() { return ref(7); }
  public Object ninth() { return ref(8); }
  public Object tenth() { return ref(9); }
  public Object ref(int i) { return data[i]; }

  public int size() { return data.length; }

  int hc;
  private void calcHashCode() {
    hc=0; int mul=1;
    for(int i=0;i<data.length;i++) {
      if(data[i]==null) hc+=mul*1; else hc+=data[i].hashCode()*mul;
      mul*=2;
    }
  }

  String s=null;
  public String toString() {
    if(s==null) {
      s="(";
      for(int i=0;i<data.length;i++) {
        if(data[i]==null) s+="#f"; else s+=data[i].toString();
        if(i<data.length-1) s+=" ";
      }
      s+=")";
    }
    return s;
  }

  public boolean equals(Object o) {
    if(!(o instanceof Tuple)) return false;
    Tuple t = (Tuple)o;
    if(t.hashCode()!=hashCode() || t.size()!=size()) return false;
    for(int i=0;i<data.length;i++) {
      if(!((data[i]!=null && data[i].equals(t.data[i])) ||
           (data[i]==null && t.data[i]==null)))
        return false;
    }
    return true;
  }
  public int hashCode() { return hc; }
  public Object clone() { return new Tuple(data); }

  public int compareTo(Object o) {
    if(!(o instanceof Tuple) || ((Tuple)o).size() != size()) {
      //System.out.println("Bad match: "+o+" vs "+this);
      throw new ClassCastException();
    }
    for(int i=0;i<size();i++) {
      Object a = ref(i), b = ((Tuple)o).ref(i);
      if(a==null && b==null) continue; // follow it down
      if(a==null && b!=null) return -1;
      if(a!=null && b==null) return 1;
      int v = 0;
      if(!(a instanceof Comparable)) {
        if(a.equals(b)) v=0;
        else
          throw new ClassCastException();
      } else {
        v = ((Comparable)a).compareTo(b);
      }
      if(v!=0) return v;
    }
    return 0;
  }
}
