package testprocs;

import simulator.*;
import utils.*;
import java.awt.*;
import java.util.*;
import java.io.*;

public class PNHAnalyze {
  // records
  static public void main(String argv[]) {
    int max = (new Integer(argv[0])).intValue();
    File f = new File("pnhtest.out");
    //PNHSimpleAnalysis.run(max,f);
    PNHTreeAnalysis.run(max,f);
  }
}

class PNHAnalysis {
  Vector getID(StreamTokenizer tok) throws Exception  {
    Vector v = new Vector();
    int ttype;
    do { 
      ttype = tok.nextToken(); 
    } while(ttype!='['); // do until '['
    ttype = tok.nextToken();
    if(ttype==']') return v;
    else {
      while(true) {
        if(ttype==StreamTokenizer.TT_NUMBER)
          v.add(new Double(tok.nval)); // add current number
        else if(ttype==StreamTokenizer.TT_WORD) {
          if(tok.sval.equals("NaN"))
            v.add(new Double(tok.sval));
          else
            System.out.println("anomalous: "+tok.sval);
        } else {
          System.out.println("anomaly ttype: "+ttype);
        }
        do {
          ttype = tok.nextToken();
          if(ttype==']') return v;
        } while(ttype!=',');
        ttype = tok.nextToken();
      }
    }
  }
}

class PNHTreeAnalysis extends PNHAnalysis {
  static void run(int max, File f) {
    PNHTreeAnalysis ta = new PNHTreeAnalysis();
    ta.analyze(max,f);
  }

  static PrintWriter out;
  void analyze(int max, File f) {
    try {
      System.out.println("Running Tree Analysis:");
      out = new PrintWriter(new FileWriter("pnhtree-analysis.out"),true);
      out.println("Analysis log for PNH Tree");
      FileReader fr = new FileReader(f);
      StreamTokenizer tok = new StreamTokenizer(fr);
      // strip off the header
      tok.nextToken();tok.nextToken();tok.nextToken();tok.nextToken();
      tok.nextToken();tok.nextToken();tok.nextToken();tok.nextToken();
      tok.nextToken();tok.nextToken();tok.nextToken();
      // cycle the readings
      System.out.print("Processing");
      while(true) {
        count++;
        if(!processLine(tok,max)) break;
        if(count%1000==0) System.out.print(".");
      } 
      System.out.println("\n");
      System.out.println("Results for "+t+" time units:");
      System.out.println("Total Number of Assertions: "+count);
      System.out.println("Total Number of Transitions: "+transcount);
      System.out.println("Total Number of Nodes: "+nodes.size());
      System.out.println("Node ID Changes:");
      System.out.println("  Transitions at Level 0: "+basecount);
      for(int i=1;i<nodecounts.size();i++) {
        System.out.println("  Nodes at Level "+i+": "+nodecounts.get(i));
        System.out.println("  Transitions at Level "+i+": "+
                           transitions.get(i));
        Iterator j=nodes.values().iterator();
        while(j.hasNext()) {
          TreeNode n = (TreeNode)j.next();
          if(n.level==i)
            System.out.println("\tNode "+i+", size "+n.members.size()+
                               ": "+n.id+" "+(n.history.size()-n.adjustment));
        }
      }
    } catch(Exception e) {
      e.printStackTrace();
    }
  }

  int count=0;
  static class TreeNode {
    static class NParent {
      HashSet members=new HashSet();
      int max=0; int startTime;
      NParent() { startTime=t; }
      void add(Object x) { 
        members.add(x); 
        if(members.size()>max) max=members.size();
      }
    }
    int level;
    Double id;
    Hashtable parents=new Hashtable(); // hash of hashsets containing members
    Vector history=new Vector(); // previous par/transitions
    Hashtable members=new Hashtable(); // individual processors in the node
    int adjustment=1; // number of "invalid" transitions in history
    TreeNode(Double id, int level) { 
      this.id=id; this.level=level; 
      ensureParent(new Double(Double.NaN)); // there's always a NaN parent
    }
    
    NParent ensureParent(Double id) {
      NParent p = (NParent)parents.get(id);
      if(p==null) { 
        p=new NParent(); parents.put(id,p); history.add(new Pair(id,p));
        if(!id.isNaN()) {
          Set k = new HashSet(parents.keySet());
          k.remove(new Double(Double.NaN));
          out.println(t+"\tJoin "+level+"\t"+this.id+" joins "+id+" [with: "+
                      k+"]");
        }
      }
      return p;
    }
    static final int kThreshold=10;
    boolean deleteMember(Double member) {
      Double par = (Double)members.get(member);
      if(par==null) return false;
      members.remove(member);
      NParent np = ensureParent(par);
      np.members.remove(member);
      if(np.members.isEmpty()) {
        if(!par.isNaN()) {
          parents.remove(par); 
          Set k = new HashSet(parents.keySet());
          k.remove(new Double(Double.NaN));
          out.println(t+"\tLeave "+level+"\t"+this.id+" leaves "+par+
                      " [with: "+k+"] ("+np.max+", "+(t-np.startTime)+")");
          if(np.max>kThreshold) {
            return true;
          } else {
            adjustment++;
            return false;
          }
        } else
          return false;
      } else {
        return false;
      }
    }
    boolean setMember(Double member, Double parent) {
      boolean dirty = false;
      NParent np = ensureParent(parent);
      Object oldpar = members.get(member);
      if(oldpar==null) {
        members.put(member,parent);
        np.add(member);
      } else if(oldpar.equals(parent)) {
        // do nothing
      } else { // replace
        dirty = deleteMember(member);
        members.put(member,parent);
        np.add(member);
      }
      return dirty;
    }
  }
  Vector nodecounts = new Vector();
  Vector transitions = new Vector();
  Hashtable nodes = new Hashtable();
  int transcount=0;
  int basecount=0;

  void ensurenodes(Vector id) {
    for(int i=1;i<id.size();i++) {
      while(nodecounts.size()<=i) { nodecounts.add(new Integer(0)); }
      while(transitions.size()<=i) { transitions.add(new Integer(0)); }
      Double nid = (Double)id.get(i);
      TreeNode n = (TreeNode)nodes.get(nid);
      if(n==null && !nid.isNaN()) {
        nodecounts.set(i,new Integer(((Integer)nodecounts.get(i)).intValue()+1));
        TreeNode tn = new TreeNode(nid,i);
        nodes.put(nid,tn);
        out.println(t+"\tAdd "+i+"\t"+nid);
      }
    }
  }
  void analyzeTransition(Vector oldID,Vector newID) {
    ensurenodes(oldID); ensurenodes(newID);
    Double baseid = (Double)newID.get(0);
    if(oldID.size()>1 && newID.size()>1 &&
       !((oldID.get(1)).equals(newID.get(1))))
      basecount++;
    for(int i=1;i<newID.size();i++) {
      Double nid = (Double)newID.get(i);
      Double oldid = new Double(Double.NaN); 
      if(i<oldID.size()) oldid=(Double)oldID.get(i);
      Double oldpar = new Double(Double.NaN); 
      if(i+1<oldID.size()) oldpar=(Double)oldID.get(i+1);
      Double newpar = new Double(Double.NaN); 
      if(i+1<newID.size()) newpar=(Double)newID.get(i+1);
      if(!nid.equals(oldid)) {
        TreeNode n = (TreeNode)nodes.get(oldid);
        if(n!=null && n.deleteMember(baseid)) transtick(i);
        TreeNode n2 = (TreeNode)nodes.get(nid);
        if(n2!=null && n2.setMember(baseid,newpar)) transtick(i);
      } else {
        TreeNode n = (TreeNode)nodes.get(nid);
        if(n!=null && n.setMember(baseid,newpar)) transtick(i);
      }
    }
  }
  void transtick(int l) {
    transcount++;
    transitions.set(l,new Integer(((Integer)transitions.get(l)).intValue()+1));
  }

  // returns false to terminate
  public static int t=0;
  boolean processLine(StreamTokenizer tok, int max) throws Exception {
    // 1. collect info
    tok.nextToken(); t = (int)tok.nval;
    if(t>max) return false;
    tok.nextToken(); double pid = tok.nval;
    Vector oldID = getID(tok);
    Vector newID = getID(tok);
    //out.println(t+", "+pid+", "+oldID+", "+newID);

    // 2. analysis
    analyzeTransition(oldID,newID);
    return true;
  }
}



/*********************** SIMPLE ANALYSIS *************************/
class PNHSimpleAnalysis extends PNHAnalysis{
  static void run(int max, File f) {
    PNHSimpleAnalysis sa = new PNHSimpleAnalysis();
    sa.analyze(max,f);
  }
  
  void analyze(int max, File f) {
    try {
      System.out.println("Running Simple Analysis:");
      FileReader fr = new FileReader(f);
      StreamTokenizer tok = new StreamTokenizer(fr);
      // strip off the header
      tok.nextToken();tok.nextToken();tok.nextToken();tok.nextToken();
      tok.nextToken();tok.nextToken();tok.nextToken();tok.nextToken();
      tok.nextToken();tok.nextToken();tok.nextToken();
      // cycle the readings
      System.out.print("Processing");
      while(t<max) {
        count++;
        processLine(tok,max);
        if(count%1000==0) System.out.print(".");
      } 
      System.out.println("\n");
      System.out.println("Results for "+t+" time units:");
      System.out.println("Total Number of Transitions: "+count);
      System.out.println("Level Increases: "+up);
      System.out.println("Level Decreases: "+down);
      System.out.println("Level ID Changes:");
      for(int i=0;i<levels.size();i++) {
        System.out.println("\t"+i+": "+levels.get(i)+"/"+primelevels.get(i));
      }
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
  int count=0;

  int t=0;
  int up=0,down=0;
  Vector levels = new Vector();
  Vector primelevels = new Vector();
  void processLine(StreamTokenizer tok, int max) throws Exception {
    // 1. collect info
    tok.nextToken(); t = (int)tok.nval;
    if(t>max) return;
    tok.nextToken(); double pid = tok.nval;
    Vector oldID = getID(tok);
    Vector newID = getID(tok);
    //System.out.println(t+", "+pid+", "+oldID+", "+newID);

    // 2. classify as up or down
    if(oldID.size()<newID.size()) up++;
    if(oldID.size()>newID.size()) down++;
    
    // 3. record ID changes
    int l = Math.min(oldID.size(),newID.size());
    boolean latch=false;
    for(int i=0;i<l;i++) {
      if(levels.size()<i+1) levels.add(new Integer(0));
      if(primelevels.size()<i+1) primelevels.add(new Integer(0));
      if(!oldID.get(i).equals(newID.get(i))) {
        int old = ((Integer)levels.get(i)).intValue();
        levels.set(i,new Integer(old+1));
        if(!latch) {
          old = ((Integer)primelevels.get(i)).intValue();
          primelevels.set(i,new Integer(old+1));
          latch=true;
        }
      }
    }
  }

}
