import edu.neu.ccs.demeterf.*;
import edu.neu.ccs.demeterf.control.*;
import java.text.DecimalFormat;
import java.io.*;

public class threetree{
    public static void main(String[] a) throws Exception{
        tree t = new none().insert(6).insert(10)
            .insert(12).insert(4).insert(9)
            .insert(3).insert(7).insert(2)
            .insert(5).insert(8).insert(13)
            .insert(11);
        
        System.out.println("     tree: "+t);
        System.out.println("      min: "+t.min());
        System.out.println("   minalt: "+Min.minalt(t));
        System.out.println("     min3: "+Min2.min(t));
        System.out.println("      max: "+t.max());
        System.out.println("isLeaf(n): "+new none().isLeaf());
        System.out.println("isLeaf(o): "+new one(5).isLeaf());
        System.out.println("isLeaf(t): "+t.isLeaf());

        /*
          System.out.println("  n == n : "+Equal.equal(new none(),new none()));
          System.out.println("  n == o : "+Equal.equal(new none(),new one(4)));
          System.out.println("  o == o : "+Equal.equal(new one(5),new one(5)));
          System.out.println("  o != o : "+Equal.equal(new one(5),new one(4)));
          System.out.println("  t != o : "+Equal.equal(t,new one(4)));
          System.out.println("  o != t : "+Equal.equal(new one(4),t));
          System.out.println("  t == t : "+Equal.equal(t,t));

          tree t1 = new none().insert(3).insert(11);
          tree t2 = new none().insert(11).insert(3);
          
          System.out.println("  t == t : "+Equal.equal(t1,t2));
          System.out.println("  t == t : "+Equal.equal(t2,t1));
          System.out.println("  t == t : "+Equal.equal(t2.insert(4),t1.insert(4)));
          System.out.println("  t != n : "+Equal.equal(t2,new none()));
          System.out.println("  t != t : "+Equal.equal(t2,t1.insert(4)));

          System.out.println("  t != s : "+Equal.equal(t2,"TEST"));
          System.out.println("  t != i : "+Equal.equal(t2,5));*/

        
        tree[] ts = {new none(),new one(6),new one(10).insert(6),t};
        int[] ws = {50,50,50,200};
        int[] hs = {50,50,60,100};
        PrintStream s = new PrintStream(new FileOutputStream("test.tex"));
        s.println(tex.fileHead());
        for(int i = 0; i < ts.length; i++)
            s.println(Display.display(ts[i],ws[i],hs[i]));
        s.println(tex.fileFoot());
    }
}

// Ternary Trees
abstract class tree{
    abstract tree insert(int d);
    public String toString(){
        return new Traversal(new ToString()).traverse(this);
    }
    int min(){ return Min.min(this); }
    int max(){ return Max.max(this); }
    boolean isLeaf(){ return Leaf.isLeaf3(this); }
}

// Empty tree
class none extends tree{
    tree insert(int d){ return new one(d); }
}
// Single integer data
class one extends tree{
    static tree e = new none();
    int data;
    one(int d){ data = d; }
    tree insert(int d){
        if(d <= data)
            return new three(e,d,e,data,e);
        return new three(e,data,e,d,e);
    }
    public static class data extends Fields.any{}
}
// Three branches, two datas
class three extends tree{
    tree left;
    int ldata; tree mid;
    int rdata; tree right;

    three(tree l, int ld, tree m, int rd, tree r){
        left = l; ldata = ld;
        mid = m;
        rdata = rd; right = r;
    }
    tree insert(int d){
        if(d <= ldata)
            return new three(left.insert(d),ldata,mid,rdata,right);
        if(d <= rdata)
            return new three(left,ldata,mid.insert(d),rdata,right);
        return new three(left,ldata,mid,rdata,right.insert(d));
    }

    public static class left extends Fields.any{}
    public static class ldata extends Fields.any{}
    public static class mid extends Fields.any{}
    public static class rdata extends Fields.any{}
    public static class right extends Fields.any{}
}


class ToString extends ID{
    String combine(none n){ return "."; } 
    String combine(one o, int i){ return "("+i+")"; }
    String combine(three t, String l, int ld, String m, int rd, String r){
        return "["+l+","+ld+","+m+","+rd+","+r+"]";
    }
}

class Min extends ID{
    none combine(none n){ return n; }
    int combine(tree t, int i){ return i; }
    int combine(three t, tree l, int ld){ return ld; }

    static int min(tree t){
        Control c = Control.bypass(new Edge(three.class,"mid"),
                                   new Edge(three.class,"right"));
        return new Traversal(new Min(),c).<Integer>traverse(t);
    }
    static int minalt(tree t){
        Control c = Control.only("three.left");
        return new Traversal(new Min(),c).<Integer>traverse(t);
    }

}
class Max extends ID{
    none combine(none n){ return n; }
    int combine(one t, int i){ return i; }
    int combine(three t, tree l, int ld, tree m, int rd){ return rd; }
    int combine(three t, tree l, int ld, tree m, int rd, int r){ return r; }

    static int max(tree t){
        Control c = Control.bypass("three.left three.mid");
        return new Traversal(new Max(),c).<Integer>traverse(t);
    }
}

class Leaf extends ID{
    boolean combine(none n){ return true; }
    boolean combine(tree t){ return false; }

    static boolean isLeaf(tree t){
        return new Traversal(new Leaf(), Control.nowhere())
                   .<Boolean>traverse(t);
    }
    static boolean isLeaf2(tree t){
        return Traversal.onestep(new Leaf()).<Boolean>traverse(t);
    }
    static boolean isLeaf3(tree t){
        return new Traversal(new Leaf(), Control.builtins(tree.class))
                   .<Boolean>traverse(t);
    }
}

class Equal extends ID{
    int update(one o, one.data f, one t){ return t.data; }

    int  update(three th,  three.ldata f, three t){ return t.ldata; }
    int  update(three th,  three.rdata f, three t){ return t.rdata; }
    tree update(three th, three.left  f, three t){ return t.left; }
    tree update(three th, three.mid   f, three t){ return t.mid; }
    tree update(three th, three.right f, three t){ return t.right; }

    boolean combine(){ return false; }
    boolean combine(none a, none b){ return true; }
    boolean combine(int a, int b){ return a == b; }
    boolean combine(one a, boolean d, one b){ return d; }
    boolean combine(three a, boolean l, boolean ld, boolean m,
                    boolean rd, boolean r, three b){
        return l && ld && m && rd && r;
    }
    
    static boolean equal(Object a, Object b){ 
        return new Traversal(new Equal()).<Boolean>traverse(a,b);    
    }
} 

class Min2 extends Min{
    int combine(three t, none l, int ld){ return ld; }
    int combine(three t, tree l, int ld){ return min(l); }

    static Traversal trav = Traversal.onestep(new Min2());
    static int min(tree t){ return trav.<Integer>traverse(t); }
}


class trip{
    double lx,rx,h;
    trip(double lxx, double rxx, double hh)
    { lx = lxx; rx = rxx; h = hh; }
    
    trip left(double dh){ return new trip(lx, lx+w()-20, h+dh); }
    trip right(double dh){ return new trip(rx-w()+20, rx, h+dh); }
    trip mid(double dh){ return new trip(lx+w()+20, rx-w()-20, h+dh); }
    double w(){ return (rx-lx)/3; }
    double x(){ return (lx+rx)/2; }
    double y(){ return h; }
}

class Display extends ID{
    static double dH = -30;

    trip update(three t, three.left f, trip c){ return c.left(dH); }
    trip update(three t, three.mid f, trip c){ return c.mid(dH); }
    trip update(three t, three.right f, trip c){ return c.right(dH); }
    
    String combine(none n, trip c)
    { return tex.circle(c.x(),c.y()+4,4); }
    String combine(one t, int i, trip c)
    { return tex.box(c.x(),c.y(),12,12,""+i); }
    String combine(three t, String l, int ld, String m, int rd, String r, trip c){
        return (l+m+r+lines(c)+
                tex.box(c.x(),c.y(),28,12,ld+tex.space()+rd));
    }
    static String lines(trip c){
        double h = c.y()-6,
               nh = h+dH+12,
               cx = c.x();
        return (tex.line(cx,h,c.mid(0).x(),nh)+
                tex.line(cx-12,h,c.left(0).x(),nh)+
                tex.line(cx+12,h,c.right(0).x(),nh));
    }
    static String display(tree t, int w, int h){
        return (tex.head(w,h)+
                new Traversal(new Display())
                .<String>traverse(t,new trip(12,w-12,h-20))
                +tex.foot());
    }
}

class tex{
    static DecimalFormat f = new DecimalFormat("0.0");
    static String d(double v){ return f.format(v); }
    static String fileHead(){
        return ("\\documentclass{article}\n"+
                "\\usepackage{amsmath,amssymb,epic,eepic}\n"+
                "\\title{\\TeX Document}\\date{}"+
                "\\begin{document}\\maketitle\n");
    }
    static String head(int w, int h){
        return ("{\\small\n"+
                //"\\setlength{\\unitlength}{0.05in}\n"+
                "\\begin{picture}("+w+","+h+")\n"+
                box(w/2,h/2,w+20,h));
    }
    static String fileFoot(){ return "\\end{document}\n"; }
    static String foot(){ return "\\end{picture}}\n"; }

    static String circle(double x, double y, double r)
    { return put(x,y, "\\circle{"+d(r)+"}"); }
    static String box(double x, double y, double w, double h)
    { return box(x,y,w,h,""); }
    static String box(double x, double y, double w, double h, String s)
    { return put(x-w/2,y-h/2, "\\framebox("+d(w)+","+d(h)+"){"+s+"}"); }
    static String put(double x, double y, String tex)
    { return "   \\put("+d(x)+", "+d(y)+"){"+tex+"}\n"; }
    static String text(double x, double y, String t)
    { return put(x,y,t); }
    static String line(double x1, double y1, double x2, double y2){
        return "    \\drawline("+d(x1)+","+d(y1)+")("+d(x2)+","+d(y2)+")\n";
    }
    static String space(){ return " \\  "; }
}

