package edu.neu.ccs.demeterf.examples;
/** Container Checking using the Transform Library
 *    Uses a type-unifying traversal to produce the violations
 *    and total weight of each Container (C) 
 */

//Import the Transformation Package
import edu.neu.ccs.demeterf.*;
import edu.neu.ccs.demeterf.IDb;

// Items
abstract class I{}
// Containers
class C extends I{
    int cap;
    IList items;
    C(int c, IList i){ cap = c; items = i; }
    public String toString(){ return "C["+cap+", "+items+"]"; }
}
// Elements
class E extends I{
    int weight;
    E(int w){ weight = w; }
    public String toString(){ return "E["+weight+"]"; }
}

// ItemLists
abstract class IList{
    static IList from(I ... items){
        IList list = new IEmpty();
        for(I i:items)list = new ICons(i,list);
        return list;
    }
    public String toString(){ return ""; }
}
// Cons ItemList
class ICons extends IList{
    I first;
    IList rest;
    ICons(I f, IList r){ first = f; rest = r; }
    public String toString(){ return "("+first+" "+rest+")"; }
}
// Empty ItemList
class IEmpty extends IList{}

// Pair of ints, <weight, violations>
class Pair{
    int w,v;
    Pair(int ww, int vv){ w = ww; v = vv; }
    static Pair make(int ww, int vv){ return new Pair(ww,vv); }
    Pair add(int ww, int vv){ return make(w+ww,v+vv); }
}

// Checker, Type-unifying to Pair(int,int)
class Check extends IDb{
    public Pair combine(E e, int i){ return Pair.make(e.weight,0); }
    public Pair combine(C c, int i, Pair wv){ return wv.add(0, (wv.w > c.cap)?1:0); }
    
    public Pair combine(IEmpty l){ return Pair.make(0,0); }
    public Pair combine(ICons l, Pair f, Pair r){ return f.add(r.w, r.v); }
}

// Main Test Class
public class ContainerTest{
    public static void main(String args[]){
        I i = new C(10, IList.from(new E(5),
                 new C(8, IList.from(
                    new C(6, IList.from(new E(2),new E(4))),
                    new E(3)))));
        Traversal t = new Traversal(new Check());
    
        Pair p = t.traverse(i);
        System.out.println("       Item = "+i);
        System.out.println("     Weight = "+p.w);
        System.out.println(" Violations = "+p.v);
    }
}