package csp.smart;

import csp.CSPInstance;
import csp.CSPSolution;
import csp.Clause;
import csp.Var;
import edu.neu.ccs.demeterf.lib.Cons;
import edu.neu.ccs.demeterf.lib.List;
import edu.neu.ccs.demeterf.lib.Set;
import edu.neu.ccs.demeterf.lib.ident;
import edu.neu.ccs.demeterf.lib.Entry;
import edu.neu.ccs.demeterf.lib.Map;
import scg.SolveRequest;
import edu.neu.ccs.evergreen.ir.Relation;
import edu.neu.ccs.satsolver.InputInitialI;
import edu.neu.ccs.satsolver.OutputI;
import edu.neu.ccs.satsolver.PairI;
import edu.neu.ccs.satsolver.SATSolverUtil;

/** Various Tools for the Generic Player and Friends */
public class Tools{

    /** Create a symmetric formula using the given Relation numbers */
    public static Cons<Clause> symmetric(List<Integer> rs, int numVars, int rank){
        final List<List<Var>> vars = Tools.combs(numVars, rank, new List.Build<Var>(){
            public Var build(int n){ return new Var(new ident("v"+n)); }
        });
        //** Create a list of Clauses for-all Relations with each the variable lists
        return (Cons<Clause>) rs.fold(new List.Fold<Integer,List<Clause>>(){
            public List<Clause> fold(final Integer r, final List<Clause> rst){
                return vars.fold(new List.Fold<List<Var>,List<Clause>>(){
                    public List<Clause> fold(List<Var> vs, List<Clause> othrs){
                        return othrs.push(new Clause(r, 1, vs));
                    }
                }, rst);
            }
        }, List.<Clause>create());
    }
    
    /** N choose K, the dynamic programming way! ;) */
    public static <X> List<List<X>> combs(int n, int k, List.Build<X> b){
        List<List<X>>[][] tbl = new List[n+1][k+1];

        for(int ik = 0; ik <= k; ik++)
            for(int in = 0; in <= n; in++){
                List<List<X>> newlst;
                if(ik == 0)newlst = List.<List<X>>create(List.<X>create());
                else if(in < ik)newlst = List.create();
                else newlst = tbl[in-1][ik].append(tbl[in-1][ik-1].map(new PushN<X>(b.build(in))));   
                tbl[in][ik] = newlst;
            }
        return tbl[n][k];
    }

    /** Push the given number onto the front of each list */
    private static class PushN<X> extends List.Map<List<X>,List<X>>{
        X x;
        public PushN(X xx){ x = xx; }
        public List<X> map(List<X> l){ return l.push(x); }
    }

    /** Compute the Payback for a given assignment of a Derivative */
    public static double payback(SolveRequest c, CSPSolution s, double factor){
        return payback(c.getClaim().inner().getConfidence(), quality((CSPInstance) c.getInstance(), s.getAssign().toList()), factor);
    }
    /** Compute the Payback for a given quality */
    public static double payback(double price, double q, double factor){
        return q >= price ? factor*price : 0;
    }
    /** Compute the quality of an assignment == satRatio(reduce(rm,a)) */
    public static double quality(final CSPInstance p, List<Entry<Var,Boolean>> s){
    	CSPInstance reduced = s.fold(new List.Fold<Entry<Var,Boolean>,CSPInstance>(){
            public CSPInstance fold(Entry<Var,Boolean> a, CSPInstance p){
                return reduce(p, a.getKey(), a.getVal());
            }
        },p);
        return satisfiedRatio(reduced);
    }
    
    /** Reduce a given Problem based on a single Literal assignment */
    public static CSPInstance reduce(CSPInstance p, final Var var, final boolean val){
        return new CSPInstance(p.getVars(),
                (Cons<Clause>) p.getClauses().map(new List.Map<Clause,Clause>(){
                    public Clause map(Clause c){
                        int i = c.getVars().index(var);
                        if(i < 0 || (c.getRelnum() == 255))return c;
                        return new Clause(new Relation(3, c.getRelnum())
                                             .reduce(i, val?1:0),1, c.getVars());
                    }
                }));
    }
    
    /** Accumulation for the satisfaction ratio */
    static class R{
        double top, bot;
        R(double t, double b){ top = t; bot = b; }
        R add(int t, int b){ return new R(top+t, bot+b); }
        double result(){ return bot==0.0?1.0:top/(double)bot; }
    }
    /** Calculate the satifaction ratio of a (reduced) Problem */
    public static double satisfiedRatio(CSPInstance p){
        return p.getClauses().fold(new List.Fold<Clause,R>(){
            public R fold(Clause c, R r)
            { return r.add((c.getRelnum() == 255)?c.getWeight():0, c.getWeight()); }
        }, new R(0,0)).result();
    }
    
    /** For computing the break-even prices */
    public static class Input implements InputInitialI{
        int rn;
        public Input(int r){ rn = r; }
        public java.util.Set getPairs(){
            java.util.Set<PairI> s = new java.util.HashSet<PairI>();
            s.add(new PairI(){
                public double getFraction(){ return 1; }
                public int getRelationNumber(){ return rn; }
            });
            return s;
        }
    }
    
    /** get the relations numbers */
	public static List<Integer> getRelNums(Cons<csp.Clause> clauses) {
		int relNum;
		List<Integer> relNums = List.create();
		java.util.List<Integer> relNumsList = relNums.toJavaList();
		for (int i = 0; i < clauses.length(); i++) {
			relNum = clauses.lookup(i).getRelnum();
			relNumsList.add(relNum);
		}
		return getRelNums(relNums, relNumsList, 0);
	}
	
	private static List<Integer> getRelNums(List<Integer> r,
			java.util.List<Integer> l, int i) {
		if (i == l.size()-1) {
			return r.append(l.get(i));
		} else {
			i += 1;
			getRelNums(r.append(l.get(i)), l, i);
		}
		return r;
	}
    
    /** Sort the relations to find the most important one... */
    public static int importantRelation(CSPInstance ins){
    	Cons<csp.Clause> clauses = ins.getClauses();
        int rel = getRelNums(clauses).sort(new List.Comp<Integer>(){
            public boolean comp(Integer ra, Integer rb){
                return (// Implied
                        ((ra & rb) == ra) ||
                        // A is even, B is odd
                        (ra%2 == 0 && rb%2 == 1) ||
                        // Both even or both odd
                        ((ra%2 == rb%2) && bitsSet(ra) < bitsSet(rb)));
            }
        }).top();
        return rel;
    }
    /** Determin the number of bits set in the given relation number */
    public static int bitsSet(int i){
        int c = 0;
        while(i > 0){
            if((i&1) > 0)c++;
            i >>= 1;
        }
        return c;
    }

    /** Figure out what the best single constraint derivatives are so we can be a
     *    little smarter about offering them */
    static void createPreference(){
        List<Entry<Integer,Triple<Integer,Double,Double>>> means = List.buildlist(new List.Build<Triple<Integer,Double,Double>>(){
            public Triple<Integer,Double,Double> build(int i){
                OutputI out = SATSolverUtil.calculateBias(new Input(i+1));
                System.err.println(i+",");
                long t1 = System.currentTimeMillis();
                return new Triple<Integer,Double,Double>(i+1, out.getPolynomial().eval(out.getMaxBias()),
                        CSPAvatarSmart.findBest(new CSPInstance(setOfVars((Cons)Tools.symmetric(List.create(i+1), 18, 3)).toList() ,(Cons)Tools.symmetric(List.create(i+1), 18, 3)), 100, 40000, t1).quality);
            }}, 254).filter(new List.Pred<Triple<Integer,Double,Double>>(){
                public boolean huh(Triple<Integer,Double,Double> p){ return p.getY() != 1.0; }
            }).map(new List.Map<Triple<Integer,Double,Double>, Entry<Integer,Triple<Integer,Double,Double>>>(){
                public Entry<Integer,Triple<Integer,Double,Double>> map(Triple<Integer,Double,Double> t){
                    return new Entry(t.getX(),t);
                }
            });
        
    }
    
    /** Collect all the (distinct) Variables in a List of Clauses */
    public static Set<Var> setOfVars(List<Clause> cs){
        return cs.fold(new List.Fold<Clause,Set<Var>>(){
                public Set<Var> fold(Clause c, Set<Var> r)
                { return r.union(Set.create(c.getVars())); }
            }, Set.<Var>create());
    }
    
    public static void printPrefs(){
        List<Entry<Integer,Triple<Integer,Double,Double>>> prefs = PREFS_MAP.toList();
        System.out.println(prefs.sort(new List.Comp<Entry<Integer,Triple<Integer,Double,Double>>>(){
            public boolean comp(Entry<Integer,Triple<Integer,Double,Double>> a, Entry<Integer,Triple<Integer,Double,Double>> b)
            { return a.getVal().getY() < b.getVal().getY(); }
        }).toString(new List.Stringer<Entry<Integer,Triple<Integer,Double,Double>>>(){
            public String toString(Entry<Integer,Triple<Integer,Double,Double>> t, List<Entry<Integer,Triple<Integer,Double,Double>>> r){
                return "     new Entry("+t.getKey()+", new Triple("+t.getVal().getX()+", "+t.getVal().getY()+", "+t.getVal().getZ()+")),\n";
            }
        }));
    }
    
    /** Update the accepting preferences... I know that our offering preferences are
     *    good, based on our attempt to solve them, but we may not know what others
     *    will provide. */
    public static void updatePreferences(CSPInstance i, double quality){
//        int rel = importantRelation(i);
    	int rel = i.getClauses().first.getRelnum();
        PREFS_MAP = PREFS_MAP.remap(rel, new Triple<Integer,Double,Double>(rel, 0.0, quality));
    } 
    
    /** BasicPlayer's ProblemType offering preferences (in order) */
    public static class Triple<X,Y,Z>{
        X x; Y y; Z z;
        Triple(X xx, Y yy, Z zz){ x = xx; y = yy; z = zz; }
        public X getX(){ return x; }
        public Y getY(){ return y; }
        public Z getZ(){ return z; }
    }
    
    /** The Basic Player's Offering Preferences */
    public static List<Entry<Integer,Triple<Integer,Double,Double>>> PREFS =
        List.<Entry<Integer,Triple<Integer,Double,Double>>>create(
                new Entry(2, new Triple(2, 0.0, 0.18)),
                new Entry(4, new Triple(4, 0.0, 0.18)),
                new Entry(6, new Triple(6, 0.0, 0.342)),
                new Entry(8, new Triple(8, 0.0, 0.18)),
                new Entry(10, new Triple(10, 0.0, 0.294)),
                new Entry(12, new Triple(12, 0.0, 0.273)),
                new Entry(14, new Triple(14, 0.0, 0.435)),
                new Entry(16, new Triple(16, 0.0, 0.18)),
                new Entry(18, new Triple(18, 0.0, 0.34)),
                new Entry(20, new Triple(20, 0.0, 0.36)),
                new Entry(22, new Triple(22, 0.0, 0.428)),
                new Entry(24, new Triple(24, 0.0, 0.273)),
                new Entry(30, new Triple(30, 0.0, 0.585)),
                new Entry(34, new Triple(34, 0.0, 0.294)),
                new Entry(38, new Triple(38, 0.0, 0.435)),
                new Entry(40, new Triple(40, 0.0, 0.294)),
                new Entry(42, new Triple(42, 0.0, 0.456)),
                new Entry(44, new Triple(44, 0.0, 0.435)),
                new Entry(48, new Triple(48, 0.0, 0.273)),
                new Entry(50, new Triple(50, 0.0, 0.432)),
                new Entry(52, new Triple(52, 0.0, 0.436)),
                new Entry(64, new Triple(64, 0.0, 0.18)),
                new Entry(70, new Triple(70, 0.0, 0.428)),
                new Entry(72, new Triple(72, 0.0, 0.273)),
                new Entry(74, new Triple(74, 0.0, 0.428)),
                new Entry(76, new Triple(76, 0.0, 0.432)),
                new Entry(78, new Triple(78, 0.0, 0.546)),
                new Entry(80, new Triple(80, 0.0, 0.273)),
                new Entry(82, new Triple(82, 0.0, 0.428)),
                new Entry(84, new Triple(84, 0.0, 0.436)),
                new Entry(88, new Triple(88, 0.0, 0.432)),
                new Entry(90, new Triple(90, 0.0, 0.539)),
                new Entry(92, new Triple(92, 0.0, 0.5635)),
                new Entry(110, new Triple(110, 0.0, 0.696)),
                new Entry(112, new Triple(112, 0.0, 0.435)),
                new Entry(116, new Triple(116, 0.0, 0.567)),
                new Entry(118, new Triple(118, 0.0, 0.696)),
                new Entry(32, new Triple(32, 0.14814814814814814, 0.22794117647058823)),
                new Entry(36, new Triple(36, 0.25, 0.28921568627450983)),
                new Entry(66, new Triple(66, 0.25, 0.3174019607843137)),
                new Entry(68, new Triple(68, 0.25, 0.37745098039215685)),
                new Entry(96, new Triple(96, 0.2962962962962963, 0.4411764705882353)),
                new Entry(26, new Triple(26, 0.38490017945975047, 0.6605392156862745)),
                new Entry(28, new Triple(28, 0.38490017945975047, 0.47058823529411764)),
                new Entry(56, new Triple(56, 0.3849001794597505, 0.47794117647058826)),
                new Entry(98, new Triple(98, 0.3849001794597505, 0.4497549019607843)),
                new Entry(100, new Triple(100, 0.3849001794597505, 0.4840686274509804)),
                new Entry(104, new Triple(104, 0.4444444444444444, 0.4852941176470588)),
                new Entry(46, new Triple(46, 0.5, 0.7058823529411765)),
                new Entry(58, new Triple(58, 0.5, 0.6127450980392157)),
                new Entry(60, new Triple(60, 0.5, 0.5404411764705882)),
                new Entry(102, new Triple(102, 0.5, 0.5428921568627451)),
                new Entry(114, new Triple(114, 0.5, 0.6262254901960784)),
                new Entry(54, new Triple(54, 0.5281529477305951, 0.6017156862745098)),
                new Entry(86, new Triple(86, 0.5281529477305951, 0.6482843137254902)),
                new Entry(106, new Triple(106, 0.5281529477305951, 0.6458333333333334)),
                new Entry(108, new Triple(108, 0.5281529477305951, 0.6115196078431373)),
                new Entry(120, new Triple(120, 0.5281529477305951, 0.6274509803921569)),
                new Entry(122, new Triple(122, 0.6311303094408988, 0.7365196078431373)),
                new Entry(124, new Triple(124, 0.6311303094408988, 0.7107843137254902)),
                new Entry(62, new Triple(62, 0.6311303094408989, 0.7181372549019608)),
                new Entry(94, new Triple(94, 0.6311303094408989, 0.7144607843137255)),
                new Entry(126, new Triple(126, 0.75, 0.7941176470588235))
        );
    
    public static Map<Integer, Triple<Integer,Double,Double>> PREFS_MAP = Map.create(PREFS);
    
    public static void main(String[] args){
        createPreference();
    }
}
