/**********************************************
 *  AOSD 10 Submission Files                  *
 *  Simplify.java :                           *
 *    Runs performance tests for the simplify *
 *    compiler function                       *
 *                                            *
 *********************************************/

import gen.*;
import edu.neu.ccs.demeterf.lib.*;
import edu.neu.ccs.demeterf.*;
import java.io.FileInputStream;

public class Simplify extends StaticTP{
    static void p(String s){ System.out.println(s); }
    public static void main(String[] args) throws Exception{
        String names[] = {"Hand","Vis","Inln","Reflect"};
        if(args.length != 2){
            System.err.println("usage: java Simplify <file> <which>");
            return;
        }
        Exp e = Exp.parse(new FileInputStream(args[0]));
        int what = Integer.parseInt(args[1]);
        long st = System.currentTimeMillis();
        switch(what){
        case 0:e = new Simplify().simplifyHand(e);break;
        case 1:e = new Simplify().simplifyVis(e);break;
        case 2:e = new Simplify().simplifyInln(e);break;
        case 3:e = new Simplify().simplifyRefl(e);break;
        }
        p("Simplify["+names[what]+"]: "+(System.currentTimeMillis()-st));
        //p("Result:\n"+e);
    }

    public static class Zero extends Num{ Zero(){ super(0); } }
    public Num combine(Num n, int i)
    { return (i==0) ? new Zero() : new Num(i); }

    Exp combine(Bin b, Sub p, Exp l, Zero r){ return l;}
    Exp combine(Bin b, Sub p, Num l, Num r)
    { return combine(l, l.getVal()-r.getVal()); }

    Exp combine(Ifz f, Zero z, Exp t, Exp e){ return t;}
    Exp combine(Ifz f, Num n, Exp t, Exp e){ return e; }
    Exp combine(Def d, ident i, Exp e, Num n){ return n; }

    Exp combine(Bin b, Sub p, Num l, Zero r){ return l;}

    Exp simplifyHand(Exp e)
    { return e.simplify(); }
    Exp simplifyVis(Exp e)
    { return e.accept(new SimpEVis()); }
    Exp simplifyInln(Exp e)
    { return new InlineSimplify(this).traverse(e); }
    Exp simplifyRefl(Exp e)
    { return new Traversal(this).traverse(e); }
}

class SimpEVis extends Exp.Vis<Exp>{
    static class Zero extends Num{
        Zero(){ super(0); }
        public Exp accept(SimpEVis v){ return v.visit(this); }
    }

    public Exp visit(Num n){ return n.getVal() == 0?new Zero():n; }
  
    Exp visit(Zero z){ return z; }

    public Exp visit(final Bin b){
        final SimpEVis self = this;
        final Exp lft = b.getLeft().accept(this),
                  rht = b.getRight().accept(this);
        return
            b.getOp().accept(new Oper.Vis<Exp>(){
                    public Exp visit(final Sub s){
                        return lft.accept(new Exp.Vis<Exp>(){
                                public Exp visit(Bin bl){ return step(bl); }
                                public Exp visit(Var vl){ return step(vl); }
                                public Exp visit(Def dl){ return step(dl); }
                                public Exp visit(Ifz fl){ return step(fl); }

                                public Exp visit(final Num nlft){
                                    return rht.accept(new SimpEVis(){
                                            public Exp visit(Bin br){ return br; }
                                            public Exp visit(Var vr){ return vr; }
                                            public Exp visit(Def dr){ return dr; }
                                            public Exp visit(Ifz fr){ return fr; }
                                            public Exp visit(Zero n){ return nlft; }
                                            public Exp visit(Num n){ return self.visit(new Num(nlft.getVal()-n.getVal())); }
                                        });
                                }
                                Exp step(final Exp nlft){
                                    return rht.accept(new SimpEVis(){
                                            public Exp visit(Bin br){ return new Bin(s, nlft, br); }
                                            public Exp visit(Var vr){ return new Bin(s, nlft, vr); }
                                            public Exp visit(Def dr){ return new Bin(s, nlft, dr); }
                                            public Exp visit(Ifz fr){ return new Bin(s, nlft, fr); }
                                            public Exp visit(Zero nrht){ return nlft; }
                                            public Exp visit(Num nrht){ return new Bin(s,nlft,nrht); }
                                        });
                                }
                            });
                    }
                });
    }

    public Exp visit(Var v){ return v; }
    public Exp visit(Ifz f){
        final Exp c = f.getCnd().accept(this),
            t = f.getThn().accept(this),
            e = f.getEls().accept(this);
        return c.accept(new SimpEVis(){
                public Exp visit(Bin b){ return new Ifz(c,t,e); }
                public Exp visit(Var v){ return new Ifz(c,t,e); }
                public Exp visit(Def d){ return new Ifz(c,t,e); }
                public Exp visit(Ifz f){ return new Ifz(c,t,e); }
                public Exp visit(Zero n){ return t; }
                public Exp visit(Num n){ return e; }
            });
    }
    public Exp visit(final Def d){
        final Exp e = d.getE().accept(this),
                  b = d.getBody().accept(this);
        return b.accept(new SimpEVis(){
                public Exp visit(Bin b){ return new Def(d.getId(),e,b); }
                public Exp visit(Var v){ return new Def(d.getId(),e,b); }
                public Exp visit(Def d){ return new Def(d.getId(),e,b); }
                public Exp visit(Ifz f){ return new Def(d.getId(),e,b); }
                public Exp visit(Num n){ return b; }
                public Exp visit(Zero n){ return b; }
            });
    }
}