/**
 *
 * Diet problem in choco v2.
 * 
 * Problem from http://www.mcs.vuw.ac.nz/courses/OPRE251/2006T1/Labs/lab09.pdf
 * """
 *  My diet requires that all the food I eat come from one of the four .basic 
 *  food groups. (chocolate cake, ice cream, soft drink, and cheesecake). 
 *  Each (large) slice of chocolate cake costs 50c, 
 *  each scoop of chocolate ice cream costs 20c, 
 *  each bottle of cola costs 30c, 
 *  and each piece of pineapple cheesecake costs 80c. 
 *
 *  Each day, I must ingest at least 500 calories, 
 *  6 oz of chocolate, 
 *  10 oz of sugar, 
 *  and 8 oz of fat.
 *  The nutritional content per unit of each food is shown in the table below. 
 * 
 *  Formulate a linear programming model that can be used to satisfy my daily 
 *  nutritional requirement at minimum cost.

 *  Type of                        Calories   Chocolate    Sugar    Fat
 *  Food                                      (ounces)     (ounces) (ounces)
 *  Chocolate Cake (1 slice)       400           3            2      2
 *  Chocolate ice cream (1 scoop)  200           2            2      4
 *  Cola (1 bottle)                150           0            4      1
 *  Pineapple cheesecake (1 piece) 500           0            4      5
 *
 * """  
 *
 * Compare with my MiniZinc model:
 * http://www.hakank.org/minizinc/diet1.mzn
 * Also, compare with the JaCoP mode:
 * http://www.hakank.org/JaCoP/Diet.java
 *
 * choco model by Hakan Kjellerstrand (hakank@bonetmail.com)
 * http://www.hakank.org/choco
 *
 */
import static choco.Choco.*;
import choco.cp.model.CPModel;
import choco.cp.model.CPModel.*;
import choco.cp.solver.CPSolver;
import choco.cp.solver.CPSolver.*;
import choco.cp.solver.constraints.*;
import choco.cp.solver.*;
import choco.kernel.model.variables.integer.*;
import choco.kernel.*;
import choco.kernel.solver.*;
import choco.kernel.model.*;
import choco.kernel.model.variables.*;
import choco.kernel.model.constraints.*;
import choco.kernel.model.variables.set.*;
import choco.cp.solver.search.integer.varselector.*;
import choco.cp.solver.search.integer.valiterator.*;
import choco.cp.solver.search.integer.valselector.*;
import choco.cp.solver.search.integer.branching.*;

import java.io.*;
import java.util.*;
import java.text.*;

public class Diet {

    Model model;
    Solver s;

    ArrayList<IntegerVariable> allVars;
    IntegerVariable[] x;
    IntegerVariable cost;

    int n; // number of ingredients
    int m; // number of food types

    String[] food = {"Chocolate Cake", "Chocolate ice cream", "Cola", "Pineapple cheesecake"};
    String[] ingredients = {"Calories", "Chocolate", "Sugar", "Fat"};
    

    /**
     *
     *  Model
     *
     */
    public void model() {

        model = new CPModel();

        n = 4; // number of ingredients
        m = 4; // number of food types

        int[] maxVals = {200000, 2000000, 2000000, 200000};
        int[] price   = {50, 20, 30, 80}; // in cents
        int[] limits  = {500, 6, 10, 8};  // minimum required for a diet

                    // Food: 0   1     2    3
        int[][] matrix = {{400, 200, 150, 500},  // calories
                          {  3,   2,   0,   0},  // chocolate
                          {  2,   2,   4,   4},  // sugar
                          {  2,   4,   1,   5}}; // fat


        // create x before using it in SumWeight
        x = new IntegerVariable[m];
        x = makeIntVarArray("x", m, 0, 10);
        // for(int i = 0; i < m; i++) {
        //    x[i] = new IntegerVariable(store, "x_" + i, 0, 10);
        //}

        IntegerVariable[] sums = new IntegerVariable[n];
        sums = makeIntVarArray("sums", n, 0, 10000);
        for(int i = 0; i < n; i++) {
            // sums[i] = new IntegerVariable(store, "sums_" + i, 0, 10000);
            // store.impose(new SumWeight(x, matrix[i], sums[i]));
            model.addConstraint(eq(scalar(x, matrix[i]), sums[i]));
            model.addConstraint(geq(sums[i], limits[i]));
        }


        // Cost to minimize: x * price
        cost = makeIntVar("cost", 0, 100000);
        model.addConstraint(eq(scalar(x, price), cost) );
        
        // store.impose( new XlteqC(cost, 90)); // generating all solutions


    } // end model


    /**
     *
     * Search and printing.
     *
     */
    public void search() {

        s = new CPSolver();
        s.read(model);

        s.monitorTimeLimit(true);
        s.monitorBackTrackLimit(true);
        s.monitorNodeLimit(true);
        s.monitorFailLimit(true);

        s.minimize(s.getVar(cost), true); // minimize 
        // s.solve(); // all solutions

        s.printRuntimeStatistics();

        //
        // output
        //
        if (s.isFeasible()) {
            System.out.println(s.pretty());
            System.out.println("Cost: " + s.getVar(cost).getVal());
            for(int i = 0; i < m; i++) {
                System.out.println(food[i] + ": " + s.getVar(x[i]).getVal());
            }
            
        }  else {
            System.out.println("No solution.");
        }

    } // end search


    public static void main(String args[]) {

      Diet diet = new Diet();
      diet.model();
      diet.search();

    } // end main

} // end class Diet

