/**
  *
  * Furniture Moving problem in JaCoP.
  *
  * Problem from Marriott & Stuckey: 'Programming with constraints', page  112f
  *
  * Feature: testing cumulative.
  *
  * Other models of this problem:
  *   Choco (version 1): http://www.hakank.org/constraints/FurnitureMoving.java 
  *   MiniZinc: http://www.hakank.org/minizinc/furniture_moving.mzn
  *
  *
  * JaCoP Model by Hakan Kjellerstrand (hakank@bonetmail.com)
  * Also see http://www.hakank.org/JaCoP/
  *
  */

import JaCoP.constraints.*;
import JaCoP.core.*;
import JaCoP.search.*;

import java.util.*;

public class FurnitureMoving {

    public static void main(String args[]) {

        // boolean generateAll = true;
        boolean generateAll = false;

        Store store = new FDstore();

        FDV numPersons = new FDV(store, "numPersons", 2, 5); // will be minimized
        FDV maxTime    = new FDV(store, "maxTime", 60,60);

        // Start times
        FDV Sp = new FDV(store, "Sp", 0, 60); // Piano
        FDV Sc = new FDV(store, "Sc", 0, 60); // Chair 
        FDV Sb = new FDV(store, "Sb", 0, 60); // Bed
        FDV St = new FDV(store, "St", 0, 60); // Table
        FDV sumStartTimes = new FDV(store, "SumStartTimes", 0, 1000);

        FDV[] starts = {Sp,Sc,Sb,St};
        store.impose(new Sum(starts, sumStartTimes));

        FDV[] durations     = new FDV[4];
        FDV[] resources     = new FDV[4];
        FDV[] endTimes      = new FDV[4];
        int durationsInts[] = {30,10,15,15}; // duration of task
        int resourcesInts[] = {3,1,3,2};     // resources: num persons required for each task
        for (int i = 0; i < durationsInts.length; i++) {
            // converts to FDV
            durations[i] = new FDV(store, "dur_"+i, durationsInts[i], durationsInts[i]);
            // converts to FDV
            resources[i] = new FDV(store, "res_"+i, resourcesInts[i], resourcesInts[i]);

            // all tasks must be finished in 60 minutes
            endTimes[i]  = new FDV(store, "end_"+i, 0, 120);
            store.impose(new XplusYeqZ(starts[i], durations[i], endTimes[i]));
            store.impose(new XlteqY(endTimes[i], maxTime));
        }

        store.impose(new Cumulative(starts, durations, resources, numPersons));

        if (generateAll) {
            // generate all optimal solutions
            store.impose(new XeqC(numPersons, 3));
        }

        // HakankUtil.toXML(store, -1, ".", "FurnitureMoving.xml");

        ArrayList<FDV> allVars = new ArrayList<FDV>();
        for(FDV s: starts) 
            allVars.add(s);

        for(FDV e: endTimes) 
            allVars.add(e);

        allVars.add(numPersons);


        //
        // Search
        //
        SelectChoicePoint select = new SimpleSelect (
                                                     allVars.toArray(new Variable[1]),
                                                     new SmallestDomain(),
                                                     new IndomainMin ());
        Search label = new DepthFirstSearch ();
        label.getSolutionListener().searchAll(true);
        label.getSolutionListener().recordSolutions(true);

        boolean result;
        if (generateAll) {
            // Generate all optimal solutions. 
            // Note: Gives null pointer exception when searchAll(true)
            result = label.labeling(store, select); 
        } else {
            // minimize over numPersons
            result = label.labeling(store, select, numPersons); 
        }


        Variable[] variables = label.getSolutionListener().getVariables();
        for(int i = 0; i < variables.length; i++) {
            System.out.println("Variable " + i + " " + variables[i]);
        }

        if(result) {
            int numSolutions = label.getSolutionListener().solutionsNo();
            HakankUtil.printAllSolutions(label);

            System.out.println("\nNumber of persons needed: " + numPersons.value());
            System.out.println(
                               "Piano: " + starts[0].value() + ".." + endTimes[0].value() + "\n" +
                               "Chair: " + starts[1].value() + ".." + endTimes[1].value() + "\n" +
                               "Bed  : " + starts[2].value() + ".." + endTimes[2].value() + "\n" + 
                               "Table: " + starts[3].value() + ".." + endTimes[3].value()
                               );

                               
            
        } // end if result

    } // end main

} // end class FurnitureMoving
