package edu.neu.ccs.demeterf.examples;
/** Thunk/Lazy Test example.  A simple numerical example with if.  We
 * write the reduction function as a single builder which can reduce
 * succ/pred and ifzero expressions in a single DFS. */

import edu.neu.ccs.demeterf.*;
import edu.neu.ccs.demeterf.lazy.Thunk;
import edu.neu.ccs.demeterf.lazy.Traversal;

import java.util.Random;

// Definitions of zerp,succ,pred, and ifzero
class Expr{
    static Random rand = new Random(System.currentTimeMillis());
    static Expr makeRand(int n){ 
        if(n == 0)return new Zero();
        if(rand.nextInt(6) < 2)
            return new Pred(makeRand(n+1));
        return new Succ(makeRand(n-1)); 

class Zero extends Expr{
    public String toString(){ return "zero"; }
class Succ extends Expr{
    Expr e;
    Succ(Expr ee){ e = ee; }
    public String toString(){ return "(succ "+e+")"; }
class Pred extends Expr{
    Expr e;
    Pred(Expr ee){ e = ee; }
    public String toString(){ return "(pred "+e+")"; }
class IfZ extends Expr{
    Expr c,t,f;
    IfZ(Expr cc, Expr tt, Expr ff){ c = cc; t = tt; f = ff; }
    public String toString(){ return "(ifzero "+c+" "+t+" "+f+")"; }

// Classic Reductions for integers with ifs
class ReduceThunk extends ID{
    Expr combine(Zero z){ return z; }
    Expr combine(Succ s, Pred p){ return p.e; }
    Expr combine(Pred p, Succ s){ return s.e; }
    Expr combine(IfZ i, Zero z, Expr e1, Thunk<Expr> e2){ return e1; }
    Expr combine(IfZ i, Expr z, Thunk<Expr> e1, Thunk<Expr> e2){ return e2.go(); }
    Expr combine(Succ s, Expr e){ return new Succ(e); }
    Expr combine(Pred p, Zero z){ return z; }

// Simple Evaluator for non-IfZ expressions
class EvalB extends ID{
    int combine(Zero z){ return 0; }
    int combine(Succ s, int i){ return i+1; }
    int combine(Pred p, int i){ return i-1; }

public class ThunkTest {
    static void p(String s){ System.out.println(s); }
    public static void main(String arg[]){
        Traversal t = new Traversal(new ReduceThunk());
        Traversal eval = new Traversal(new EvalB());
        // (ifzero (ifzero 1 2 3) 3 2) --> 2
        Expr e = new IfZ( 
                new IfZ(new Succ(new Pred(new Succ(new Zero()))),
                        new Succ(new Succ(new Pred(new Succ(new Zero())))),
                        new Pred(new Succ(new Succ(new Succ(new Succ(new Zero())))))),
                new Succ(new Succ(new Succ(new Pred(new Zero())))),
                new Succ(new Succ(new Pred(new Succ(new Zero())))));
        p("    Expr: "+e);
        p("  Reduce: "+(e = t.traverse(e)));
        p(" EvalRed: "+eval.traverse(e));
        e = Expr.makeRand(11);
        p(" GenRand[11]: "+e);
        p("  Reduce[11]: "+(e = t.traverse(e)));
        p(" EvalRed[11]: "+eval.traverse(e));