/**
 *
 * Diet problem in JaCoP.
 * 
 * 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
 *
 * JaCoP model by Hakan Kjellerstrand (hakank@bonetmail.com)
 * http://www.hakank.org/JaCoP
 *
 */
import JaCoP.constraints.*;
import JaCoP.core.*;
import JaCoP.search.*;

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

public class Diet {

    Store store;
    ArrayList<IntVar> allVars;
    IntVar[] x;
    IntVar 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() {

        store = new Store();

        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 IntVar[m];
        for(int i = 0; i < m; i++) {
            x[i] = new IntVar(store, "x_" + i, 0, 10);
        }

        IntVar[] sums = new IntVar[n];
        for(int i = 0; i < n; i++) {
            sums[i] = new IntVar(store, "sums_" + i, 0, 10000);
            store.impose(new SumWeight(x, matrix[i], sums[i]));
            store.impose(new XgteqC(sums[i], limits[i]));
        }


        // Cost to minimize: x * price
        cost = new IntVar(store, "cost", 0, 100000);
        store.impose( new SumWeight(x, price, cost) );
        
        // store.impose( new XlteqC(cost, 90)); // generating all solutions

        allVars = new ArrayList<IntVar>();
        for(IntVar v : x) 
            allVars.add(v);

        allVars.add(cost);


    } // end model


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

        SelectChoicePoint select = 
            new SimpleSelect (allVars.toArray(new IntVar[1]),
                              new SmallestDomain(),
                              new IndomainMin ()
                              );

        Search label = new DepthFirstSearch ();
        label.getSolutionListener().searchAll(true);
        label.getSolutionListener().recordSolutions(true);

        boolean result = label.labeling(store, select, cost); // minimizing
        // boolean result = label.labeling(store, select); // generating all solutions

        //
        // output
        //
        if (result) {
            System.out.println("Cost: " + cost.value());
            for(int i = 0; i < m; i++) {
                System.out.println(food[i] + ": " + x[i].value());
            }
        }  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

